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 }