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 }