001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.runtime.plugin.component;
017
018import java.lang.reflect.InvocationHandler;
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021import java.lang.reflect.Proxy;
022import java.util.Collection;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.component.ComponentException;
029import org.apache.avalon.framework.component.ComponentManager;
030import org.apache.avalon.framework.configuration.Configuration;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.WrapperServiceManager;
033import org.apache.commons.lang3.ClassUtils;
034import org.slf4j.Logger;
035
036import org.ametys.runtime.plugin.ExtensionDefinition;
037import org.ametys.runtime.plugin.ExtensionPoint;
038
039/**
040 * Implementation of an Avalon ComponentManager handling the PluginAware and ParentAware interfaces.<br>
041 * See the {@link ThreadSafeComponentManager} for more details.
042 */
043public class PluginsComponentManager extends ThreadSafeComponentManager<Object> implements ComponentManager
044{
045    ComponentManager _parentManager;
046    private Map<String, Collection<ExtensionDefinition>> _extensionPoints = new HashMap<>();
047    
048    /**
049     * Constructor.
050     * @param parentManager the parent manager
051     */
052    public PluginsComponentManager(ComponentManager parentManager)
053    {
054        _parentManager = parentManager;
055        _manager = new WrapperServiceManager(this);
056    }
057    
058    @Override
059    public void initialize() throws Exception
060    {
061        super.initialize();
062        
063        // at this point, all components and extension points are initialized
064        // only extensions used during initialization are already initialized
065        
066        // and next, we should initialize all remaining extensions
067        // for extension points annotated with LazyInitializeExtensions 
068        for (String id : _extensionPoints.keySet())
069        {
070            ExtensionPoint point = (ExtensionPoint) lookup(id);
071            
072            if (point.getClass().isAnnotationPresent(LazyInitializeExtensions.class))
073            {
074                getLogger().debug("Initializing extensions for point {}", id);
075                point.initializeExtensions();
076            }
077        }
078        
079        // finally, we should wire all deferred serviceable extensions
080        for (String id : _extensionPoints.keySet())
081        {
082            ExtensionPoint point = (ExtensionPoint) lookup(id);
083            point.deferredInitializeExtensions();
084        }
085    }
086    
087    public boolean hasComponent(String role)
088    {
089        if (super.hasRole(role))
090        {
091            return true;
092        }
093        else if (_parentManager != null)
094        {
095            return _parentManager.hasComponent(role);
096        }
097        
098        return false;
099    }
100    
101    @Override
102    public Component lookup(String role) throws ComponentException
103    {
104        Component component = (Component) super.lookup(role);
105        
106        if (component != null)
107        {
108            return component;
109        }
110        else if (_parentManager != null)
111        {
112            return _parentManager.lookup(role);
113        }
114        
115        return null;
116    }
117
118    public void release(Component object)
119    {
120        if (super.hasComponent(object))
121        {
122            // does nothing, all components are ThreadSafe
123        }
124        else if (_parentManager != null)
125        {
126            _parentManager.release(object);
127        }
128    }
129    
130    /**
131     * Add a new extension point, and its extensions, to the manager.
132     * @param pluginName the plugin containing the extension point
133     * @param point the extension point name.
134     * @param extensionPoint the class of the extension point.
135     * @param configuration the configuration for the extension point.
136     * @param extensions definitions of extensions.
137     */
138    public void addExtensionPoint(String pluginName, String point, Class<? extends ExtensionPoint> extensionPoint, Configuration configuration, Collection<ExtensionDefinition> extensions)
139    {
140        ExtensionPointFactory factory = new ExtensionPointFactory(pluginName, null, point, extensionPoint, configuration, new WrapperServiceManager(this), getLogger(), extensions);
141        _addComponent(point, factory);
142        _extensionPoints.put(point, extensions);
143    }
144    
145    @Override
146    ComponentFactory getComponentFactory(String pluginName, String featureName, String role, Class<? extends Object> componentClass, Configuration configuration)
147    {
148        return new ProxyComponentFactory(pluginName, featureName, role, componentClass, configuration, _manager, getLogger());
149    }
150    
151    private class ProxyComponentFactory extends ComponentFactory
152    {
153        ProxyComponentFactory(String pluginName, String featureName, String role, Class<? extends Object> componentClass, Configuration configuration, ServiceManager serviceManager, Logger logger)
154        {
155            super(pluginName, featureName, role, componentClass, configuration, serviceManager, logger);
156        }
157        
158        Component proxify(Object component)
159        {
160            if (component instanceof Component)
161            {
162                return (Component) component;
163            }
164            
165            List<Class<?>> interfaces = ClassUtils.getAllInterfaces(component.getClass());
166            interfaces.add(Component.class);
167
168            Class[] proxyInterfaces = interfaces.toArray(new Class[0]);
169
170            return (Component) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), proxyInterfaces, new ComponentInvocationHandler(component));
171        }
172        
173        @Override
174        Component newInstance() throws Exception
175        {
176            Object component = instanciate();
177            
178            _componentsInitializing.put(_role, component);
179            
180            if (component instanceof ParentAware && _parentManager != null)
181            {
182                Object parent = _parentManager.lookup(_role);
183                ((ParentAware) component).setParent(parent);
184            }
185            
186            configureAndStart(component);
187            
188            _componentsInitializing.remove(_role);
189            
190            return proxify(component);
191        }
192    }
193
194    private class ComponentInvocationHandler implements InvocationHandler
195    {
196        private final Object _delegate;
197
198        ComponentInvocationHandler(Object delegate)
199        {
200            _delegate = delegate;
201        }
202
203        public Object invoke(Object proxy, Method meth, Object[] args) throws Throwable
204        {
205            try
206            {
207                return meth.invoke(_delegate, args);
208            }
209            catch (InvocationTargetException ite)
210            {
211                throw ite.getTargetException();
212            }
213        }
214    }
215    
216    private class ExtensionPointFactory extends ProxyComponentFactory
217    {
218        private Collection<ExtensionDefinition> _extensions;
219        
220        public ExtensionPointFactory(String pluginName, String featureName, String role, Class<? extends ExtensionPoint> extensionPointClass, Configuration configuration, ServiceManager serviceManager, Logger logger, Collection<ExtensionDefinition> extensions)
221        {
222            super(pluginName, featureName, role, extensionPointClass, configuration, serviceManager, logger);
223            _extensions = extensions;
224        }
225        
226        @Override
227        Component newInstance() throws Exception
228        {
229            Object component = instanciate();
230            
231            _componentsInitializing.put(_role, component);
232            
233            if (component instanceof ParentAware && _parentManager != null)
234            {
235                Object parent = _parentManager.lookup(_role);
236                ((ParentAware) component).setParent(parent);
237            }
238            
239            configureAndStart(component);
240            
241            ExtensionPoint extPoint = (ExtensionPoint) component;
242            
243            for (ExtensionDefinition extension : _extensions)
244            {
245                extPoint.addExtension(extension.getId(), extension.getPluginName(), extension.getFeatureName(), extension.getConfiguration());
246            }
247            
248            if (!extPoint.getClass().isAnnotationPresent(LazyInitializeExtensions.class))
249            {
250                getLogger().debug("Initializing extensions for point {}", _role);
251                extPoint.initializeExtensions();
252            }
253            
254            _componentsInitializing.remove(_role);
255            
256            return proxify(component);
257        }
258    }
259}