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