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    }