001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.hivemind.impl; 016 017 import java.beans.Introspector; 018 import java.util.Collections; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.LinkedList; 022 import java.util.List; 023 import java.util.Locale; 024 import java.util.Map; 025 026 import org.apache.commons.logging.LogFactory; 027 import org.apache.hivemind.ApplicationRuntimeException; 028 import org.apache.hivemind.ErrorHandler; 029 import org.apache.hivemind.HiveMind; 030 import org.apache.hivemind.HiveMindMessages; 031 import org.apache.hivemind.Location; 032 import org.apache.hivemind.ShutdownCoordinator; 033 import org.apache.hivemind.SymbolSource; 034 import org.apache.hivemind.SymbolSourceContribution; 035 import org.apache.hivemind.internal.ConfigurationPoint; 036 import org.apache.hivemind.internal.Module; 037 import org.apache.hivemind.internal.RegistryInfrastructure; 038 import org.apache.hivemind.internal.ServiceModelFactory; 039 import org.apache.hivemind.internal.ServicePoint; 040 import org.apache.hivemind.internal.ser.ServiceSerializationHelper; 041 import org.apache.hivemind.internal.ser.ServiceSerializationSupport; 042 import org.apache.hivemind.internal.ser.ServiceToken; 043 import org.apache.hivemind.order.Orderer; 044 import org.apache.hivemind.schema.Translator; 045 import org.apache.hivemind.service.ThreadEventNotifier; 046 import org.apache.hivemind.util.Defense; 047 import org.apache.hivemind.util.PropertyUtils; 048 import org.apache.hivemind.util.ToStringBuilder; 049 050 /** 051 * Implementation of {@link RegistryInfrastructure}. 052 * 053 * @author Howard Lewis Ship 054 */ 055 public final class RegistryInfrastructureImpl implements RegistryInfrastructure, 056 ServiceSerializationSupport 057 { 058 private static final String SYMBOL_SOURCES = "hivemind.SymbolSources"; 059 060 /** 061 * Map of {@link ServicePoint} keyed on fully qualified service id. 062 */ 063 private Map _servicePoints = new HashMap(); 064 065 /** 066 * Map of List (of {@link ServicePoint}, keyed on class name service interface. 067 */ 068 private Map _servicePointsByInterfaceClassName = new HashMap(); 069 070 /** 071 * Map of {@link ConfigurationPoint} keyed on fully qualified configuration id. 072 */ 073 private Map _configurationPoints = new HashMap(); 074 075 private SymbolSource[] _variableSources; 076 077 private ErrorHandler _errorHandler; 078 079 private Locale _locale; 080 081 private ShutdownCoordinator _shutdownCoordinator; 082 083 /** 084 * Map of {@link org.apache.hivemind.internal.ser.ServiceToken}, keyed on service id. 085 * 086 * @since 1.1 087 */ 088 089 private Map _serviceTokens; 090 091 /** 092 * Map of {@link ServiceModelFactory}, keyed on service model name, loaded from 093 * <code>hivemind.ServiceModels</code> configuration point. 094 */ 095 private Map _serviceModelFactories; 096 097 private boolean _started = false; 098 099 private boolean _shutdown = false; 100 101 private ThreadEventNotifier _threadEventNotifier; 102 103 private TranslatorManager _translatorManager; 104 105 private SymbolExpander _expander; 106 107 public RegistryInfrastructureImpl(ErrorHandler errorHandler, Locale locale) 108 { 109 _errorHandler = errorHandler; 110 _locale = locale; 111 112 _translatorManager = new TranslatorManager(this, errorHandler); 113 114 _expander = new SymbolExpander(_errorHandler, this); 115 } 116 117 public Locale getLocale() 118 { 119 return _locale; 120 } 121 122 public void addServicePoint(ServicePoint point) 123 { 124 checkStarted(); 125 126 _servicePoints.put(point.getExtensionPointId(), point); 127 128 addServicePointByInterface(point); 129 } 130 131 private void addServicePointByInterface(ServicePoint point) 132 { 133 String key = point.getServiceInterfaceClassName(); 134 135 List l = (List) _servicePointsByInterfaceClassName.get(key); 136 137 if (l == null) 138 { 139 l = new LinkedList(); 140 _servicePointsByInterfaceClassName.put(key, l); 141 } 142 143 l.add(point); 144 } 145 146 public void addConfigurationPoint(ConfigurationPoint point) 147 { 148 checkStarted(); 149 150 _configurationPoints.put(point.getExtensionPointId(), point); 151 } 152 153 public ServicePoint getServicePoint(String serviceId, Module module) 154 { 155 checkShutdown(); 156 ServicePoint result = (ServicePoint) _servicePoints.get(serviceId); 157 if (result == null) 158 { 159 if (serviceId.indexOf('.') == -1) 160 { 161 final List possibleMatches = getMatchingServiceIds(serviceId); 162 if (!possibleMatches.isEmpty()) 163 { 164 final StringBuffer sb = new StringBuffer(); 165 for (Iterator i = possibleMatches.iterator(); i.hasNext();) 166 { 167 final String matching = (String) i.next(); 168 sb.append('\"'); 169 sb.append(matching); 170 sb.append('\"'); 171 if (i.hasNext()) 172 { 173 sb.append(", "); 174 } 175 } 176 throw new ApplicationRuntimeException(ImplMessages.unqualifiedServicePoint( 177 serviceId, 178 sb.toString())); 179 } 180 } 181 throw new ApplicationRuntimeException(ImplMessages.noSuchServicePoint(serviceId)); 182 } 183 184 if (!result.visibleToModule(module)) 185 throw new ApplicationRuntimeException(ImplMessages.serviceNotVisible(serviceId, module)); 186 187 return result; 188 } 189 190 private List getMatchingServiceIds(String serviceId) 191 { 192 final List possibleMatches = new LinkedList(); 193 for (Iterator i = _servicePoints.values().iterator(); i.hasNext();) 194 { 195 final ServicePoint servicePoint = (ServicePoint) i.next(); 196 if (servicePoint.getExtensionPointId().equals( 197 servicePoint.getModule().getModuleId() + "." + serviceId)) 198 { 199 possibleMatches.add(servicePoint.getExtensionPointId()); 200 } 201 } 202 return possibleMatches; 203 } 204 205 public Object getService(String serviceId, Class serviceInterface, Module module) 206 { 207 ServicePoint point = getServicePoint(serviceId, module); 208 209 return point.getService(serviceInterface); 210 } 211 212 public Object getService(Class serviceInterface, Module module) 213 { 214 String key = serviceInterface.getName(); 215 216 List servicePoints = (List) _servicePointsByInterfaceClassName.get(key); 217 218 if (servicePoints == null) 219 servicePoints = Collections.EMPTY_LIST; 220 221 ServicePoint point = null; 222 int count = 0; 223 224 Iterator i = servicePoints.iterator(); 225 while (i.hasNext()) 226 { 227 ServicePoint sp = (ServicePoint) i.next(); 228 229 if (!sp.visibleToModule(module)) 230 continue; 231 232 point = sp; 233 234 count++; 235 } 236 237 if (count == 0) 238 throw new ApplicationRuntimeException(ImplMessages 239 .noServicePointForInterface(serviceInterface)); 240 241 if (count > 1) 242 throw new ApplicationRuntimeException(ImplMessages.multipleServicePointsForInterface( 243 serviceInterface, 244 servicePoints)); 245 246 return point.getService(serviceInterface); 247 } 248 249 public ConfigurationPoint getConfigurationPoint(String configurationId, Module module) 250 { 251 checkShutdown(); 252 253 ConfigurationPoint result = (ConfigurationPoint) _configurationPoints.get(configurationId); 254 255 if (result == null) 256 throw new ApplicationRuntimeException(ImplMessages.noSuchConfiguration(configurationId)); 257 258 if (!result.visibleToModule(module)) 259 throw new ApplicationRuntimeException(ImplMessages.configurationNotVisible( 260 configurationId, 261 module)); 262 263 return result; 264 } 265 266 public List getConfiguration(String configurationId, Module module) 267 { 268 ConfigurationPoint point = getConfigurationPoint(configurationId, module); 269 270 return point.getElements(); 271 } 272 273 public boolean isConfigurationMappable(String configurationId, Module module) 274 { 275 ConfigurationPoint point = getConfigurationPoint(configurationId, module); 276 277 return point.areElementsMappable(); 278 } 279 280 public Map getConfigurationAsMap(String configurationId, Module module) 281 { 282 ConfigurationPoint point = getConfigurationPoint(configurationId, module); 283 284 return point.getElementsAsMap(); 285 } 286 287 public String toString() 288 { 289 ToStringBuilder builder = new ToStringBuilder(this); 290 291 builder.append("locale", _locale); 292 293 return builder.toString(); 294 } 295 296 public String expandSymbols(String text, Location location) 297 { 298 return _expander.expandSymbols(text, location); 299 } 300 301 public String valueForSymbol(String name) 302 { 303 checkShutdown(); 304 305 SymbolSource[] sources = getSymbolSources(); 306 307 for (int i = 0; i < sources.length; i++) 308 { 309 String value = sources[i].valueForSymbol(name); 310 311 if (value != null) 312 return value; 313 } 314 315 return null; 316 } 317 318 private synchronized SymbolSource[] getSymbolSources() 319 { 320 if (_variableSources != null) 321 return _variableSources; 322 323 List contributions = getConfiguration(SYMBOL_SOURCES, null); 324 325 Orderer o = new Orderer(LogFactory.getLog(SYMBOL_SOURCES), _errorHandler, ImplMessages 326 .symbolSourceContribution()); 327 328 Iterator i = contributions.iterator(); 329 while (i.hasNext()) 330 { 331 SymbolSourceContribution c = (SymbolSourceContribution) i.next(); 332 333 o.add(c, c.getName(), c.getPrecedingNames(), c.getFollowingNames()); 334 } 335 336 List sources = o.getOrderedObjects(); 337 338 int count = sources.size(); 339 340 _variableSources = new SymbolSource[count]; 341 342 for (int j = 0; j < count; j++) 343 { 344 SymbolSourceContribution c = (SymbolSourceContribution) sources.get(j); 345 _variableSources[j] = c.getSource(); 346 } 347 348 return _variableSources; 349 } 350 351 public void setShutdownCoordinator(ShutdownCoordinator coordinator) 352 { 353 _shutdownCoordinator = coordinator; 354 } 355 356 /** 357 * Invokes {@link ShutdownCoordinator#shutdown()}, then releases the coordinator, modules and 358 * variable sources. 359 */ 360 public synchronized void shutdown() 361 { 362 checkShutdown(); 363 364 ServiceSerializationHelper.setServiceSerializationSupport(null); 365 366 // Allow service implementations and such to shutdown. 367 368 ShutdownCoordinator coordinatorService = (ShutdownCoordinator) getService( 369 "hivemind.ShutdownCoordinator", 370 ShutdownCoordinator.class, 371 null); 372 373 coordinatorService.shutdown(); 374 375 // TODO: Should this be moved earlier? 376 377 _shutdown = true; 378 379 // Shutdown infrastructure items, such as proxies. 380 381 _shutdownCoordinator.shutdown(); 382 383 _servicePoints = null; 384 _servicePointsByInterfaceClassName = null; 385 _configurationPoints = null; 386 _shutdownCoordinator = null; 387 _variableSources = null; 388 _serviceModelFactories = null; 389 _threadEventNotifier = null; 390 _serviceTokens = null; 391 392 // It is believed that the cache held by PropertyUtils can affect application shutdown 393 // and reload in some servlet containers (such as Tomcat); this should clear that up. 394 395 PropertyUtils.clearCache(); 396 397 synchronized (HiveMind.INTROSPECTOR_MUTEX) 398 { 399 Introspector.flushCaches(); 400 } 401 } 402 403 /** 404 * Technically, this should be a synchronized method, but the _shutdown variable hardly ever 405 * changes, and the consequences are pretty minimal. See HIVEMIND-104. 406 */ 407 408 private void checkShutdown() 409 { 410 if (_shutdown) 411 throw new ApplicationRuntimeException(HiveMindMessages.registryShutdown()); 412 } 413 414 private void checkStarted() 415 { 416 if (_started) 417 throw new IllegalStateException(ImplMessages.registryAlreadyStarted()); 418 } 419 420 /** 421 * Starts up the Registry after all service and configuration points have been defined. This 422 * locks down the Registry so that no further extension points may be added. This method may 423 * only be invoked once. 424 * <p> 425 * This instance is stored into 426 * {@link ServiceSerializationHelper#setServiceSerializationSupport(ServiceSerializationSupport)}. 427 * This may cause errors (and incorrect behavior) if multiple Registries exist in a single JVM. 428 * <p> 429 * In addition, the service <code>hivemind.Startup</code> is obtained and <code>run()</code> 430 * is invoked on it. This allows additional startup, provided in the 431 * <code>hivemind.Startup</code> configuration point, to be executed. 432 */ 433 public void startup() 434 { 435 checkStarted(); 436 437 ServiceSerializationHelper.setServiceSerializationSupport(this); 438 439 _started = true; 440 441 Runnable startup = (Runnable) getService("hivemind.Startup", Runnable.class, null); 442 443 startup.run(); 444 } 445 446 public synchronized ServiceModelFactory getServiceModelFactory(String name) 447 { 448 if (_serviceModelFactories == null) 449 readServiceModelFactories(); 450 451 ServiceModelFactory result = (ServiceModelFactory) _serviceModelFactories.get(name); 452 453 if (result == null) 454 throw new ApplicationRuntimeException(ImplMessages.unknownServiceModel(name)); 455 456 return result; 457 } 458 459 private void readServiceModelFactories() 460 { 461 List l = getConfiguration("hivemind.ServiceModels", null); 462 463 _serviceModelFactories = new HashMap(); 464 465 Iterator i = l.iterator(); 466 467 while (i.hasNext()) 468 { 469 ServiceModelContribution smc = (ServiceModelContribution) i.next(); 470 471 String name = smc.getName(); 472 473 _serviceModelFactories.put(name, smc.getFactory()); 474 } 475 } 476 477 public synchronized void cleanupThread() 478 { 479 if (_threadEventNotifier == null) 480 _threadEventNotifier = (ThreadEventNotifier) getService( 481 "hivemind.ThreadEventNotifier", 482 ThreadEventNotifier.class, 483 null); 484 485 _threadEventNotifier.fireThreadCleanup(); 486 } 487 488 public boolean containsConfiguration(String configurationId, Module module) 489 { 490 checkShutdown(); 491 492 ConfigurationPoint result = (ConfigurationPoint) _configurationPoints.get(configurationId); 493 494 return result != null && result.visibleToModule(module); 495 } 496 497 public boolean containsService(Class serviceInterface, Module module) 498 { 499 checkShutdown(); 500 501 String key = serviceInterface.getName(); 502 503 List servicePoints = (List) _servicePointsByInterfaceClassName.get(key); 504 505 if (servicePoints == null) 506 return false; 507 508 int count = 0; 509 510 Iterator i = servicePoints.iterator(); 511 while (i.hasNext()) 512 { 513 ServicePoint point = (ServicePoint) i.next(); 514 515 if (point.visibleToModule(module)) 516 count++; 517 } 518 519 return count == 1; 520 } 521 522 public boolean containsService(String serviceId, Class serviceInterface, Module module) 523 { 524 checkShutdown(); 525 526 ServicePoint point = (ServicePoint) _servicePoints.get(serviceId); 527 528 if (point == null) 529 return false; 530 531 return point.visibleToModule(module) 532 && point.getServiceInterface().equals(serviceInterface); 533 } 534 535 public ErrorHandler getErrorHander() 536 { 537 return _errorHandler; 538 } 539 540 public Translator getTranslator(String constructor) 541 { 542 return _translatorManager.getTranslator(constructor); 543 } 544 545 public Object getServiceFromToken(ServiceToken token) 546 { 547 Defense.notNull(token, "token"); 548 549 checkShutdown(); 550 551 String serviceId = token.getServiceId(); 552 553 ServicePoint sp = (ServicePoint) _servicePoints.get(serviceId); 554 555 return sp.getService(Object.class); 556 } 557 558 public synchronized ServiceToken getServiceTokenForService(String serviceId) 559 { 560 Defense.notNull(serviceId, "serviceId"); 561 562 checkShutdown(); 563 564 if (_serviceTokens == null) 565 _serviceTokens = new HashMap(); 566 567 ServiceToken result = (ServiceToken) _serviceTokens.get(serviceId); 568 569 if (result == null) 570 { 571 result = new ServiceToken(serviceId); 572 _serviceTokens.put(serviceId, result); 573 } 574 575 return result; 576 } 577 578 /** 579 * Sets the current RI up as the ServiceSerializationSupport. Any service proxy tokens that are 580 * de-serialized will find their proxies within this Registry. 581 * 582 * @since 1.1 583 */ 584 585 public void setupThread() 586 { 587 ServiceSerializationHelper.setServiceSerializationSupport(this); 588 } 589 590 public Module getModule(String moduleId) 591 { 592 for (Iterator i = _servicePoints.values().iterator(); i.hasNext();) 593 { 594 final ServicePoint servicePoint = (ServicePoint) i.next(); 595 596 if (servicePoint.getModule().getModuleId().equals(moduleId)) 597 { 598 return servicePoint.getModule(); 599 } 600 } 601 return null; 602 } 603 604 /* 605 * (non-Javadoc) 606 * 607 * @see org.apache.hivemind.internal.RegistryInfrastructure#getServiceIds(java.lang.Class) 608 */ 609 public List getServiceIds(Class serviceInterface) 610 { 611 final List serviceIds = new LinkedList(); 612 if( serviceInterface == null ) 613 { 614 return serviceIds; 615 } 616 for (Iterator i = _servicePoints.values().iterator(); i.hasNext();) 617 { 618 final ServicePoint servicePoint = (ServicePoint) i.next(); 619 620 if (serviceInterface.getName().equals( servicePoint.getServiceInterfaceClassName() ) 621 && servicePoint.visibleToModule(null)) 622 { 623 serviceIds.add(servicePoint.getExtensionPointId()); 624 } 625 626 } 627 return serviceIds; 628 } 629 }