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 }