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