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        // for extension point annotated with LazyInitializeExtensions, 
065        // only extensions used during initialization are already initialized
066        // next, we should initialize all remaining extensions
067        for (String id : _extensionPoints.keySet())
068        {
069            ExtensionPoint point = (ExtensionPoint) lookup(id);
070            
071            if (point.getClass().isAnnotationPresent(LazyInitializeExtensions.class))
072            {
073                getLogger().debug("Initializing extensions for point {}", id);
074                point.initializeExtensions();
075            }
076        }
077    }
078    
079    public boolean hasComponent(String role)
080    {
081        if (super.hasRole(role))
082        {
083            return true;
084        }
085        else if (_parentManager != null)
086        {
087            return _parentManager.hasComponent(role);
088        }
089        
090        return false;
091    }
092    
093    @Override
094    public Component lookup(String role) throws ComponentException
095    {
096        Component component = (Component) super.lookup(role);
097        
098        if (component != null)
099        {
100            return component;
101        }
102        else if (_parentManager != null)
103        {
104            return _parentManager.lookup(role);
105        }
106        
107        return null;
108    }
109
110    public void release(Component object)
111    {
112        if (super.hasComponent(object))
113        {
114            // does nothing, all components are ThreadSafe
115        }
116        else if (_parentManager != null)
117        {
118            _parentManager.release(object);
119        }
120    }
121    
122    /**
123     * Add a new extension point, and its extensions, to the manager.
124     * @param pluginName the plugin containing the extension point
125     * @param point the extension point name.
126     * @param extensionPoint the class of the extension point.
127     * @param configuration the configuration for the extension point.
128     * @param extensions definitions of extensions.
129     */
130    public void addExtensionPoint(String pluginName, String point, Class<? extends ExtensionPoint> extensionPoint, Configuration configuration, Collection<ExtensionDefinition> extensions)
131    {
132        ExtensionPointFactory factory = new ExtensionPointFactory(pluginName, null, point, extensionPoint, configuration, new WrapperServiceManager(this), getLogger(), extensions);
133        _addComponent(point, factory);
134        _extensionPoints.put(point, extensions);
135    }
136    
137    @Override
138    ComponentFactory getComponentFactory(String pluginName, String featureName, String role, Class<? extends Object> componentClass, Configuration configuration)
139    {
140        return new ProxyComponentFactory(pluginName, featureName, role, componentClass, configuration, _manager, getLogger());
141    }
142    
143    private class ProxyComponentFactory extends ComponentFactory
144    {
145        ProxyComponentFactory(String pluginName, String featureName, String role, Class<? extends Object> componentClass, Configuration configuration, ServiceManager serviceManager, Logger logger)
146        {
147            super(pluginName, featureName, role, componentClass, configuration, serviceManager, logger);
148        }
149        
150        Component proxify(Object component)
151        {
152            if (component instanceof Component)
153            {
154                return (Component) component;
155            }
156            
157            List<Class<?>> interfaces = ClassUtils.getAllInterfaces(component.getClass());
158            interfaces.add(Component.class);
159
160            Class[] proxyInterfaces = interfaces.toArray(new Class[0]);
161
162            return (Component) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), proxyInterfaces, new ComponentInvocationHandler(component));
163        }
164        
165        @Override
166        Component newInstance() throws Exception
167        {
168            Object component = instanciate();
169            
170            _componentsInitializing.put(_role, component);
171            
172            if (component instanceof ParentAware && _parentManager != null)
173            {
174                Object parent = _parentManager.lookup(_role);
175                ((ParentAware) component).setParent(parent);
176            }
177            
178            configureAndStart(component);
179            
180            _componentsInitializing.remove(_role);
181            
182            return proxify(component);
183        }
184    }
185
186    private class ComponentInvocationHandler implements InvocationHandler
187    {
188        private final Object _delegate;
189
190        ComponentInvocationHandler(Object delegate)
191        {
192            _delegate = delegate;
193        }
194
195        public Object invoke(Object proxy, Method meth, Object[] args) throws Throwable
196        {
197            try
198            {
199                return meth.invoke(_delegate, args);
200            }
201            catch (InvocationTargetException ite)
202            {
203                throw ite.getTargetException();
204            }
205        }
206    }
207    
208    private class ExtensionPointFactory extends ProxyComponentFactory
209    {
210        private Collection<ExtensionDefinition> _extensions;
211        
212        public ExtensionPointFactory(String pluginName, String featureName, String role, Class<? extends ExtensionPoint> extensionPointClass, Configuration configuration, ServiceManager serviceManager, Logger logger, Collection<ExtensionDefinition> extensions)
213        {
214            super(pluginName, featureName, role, extensionPointClass, configuration, serviceManager, logger);
215            _extensions = extensions;
216        }
217        
218        @Override
219        Component newInstance() throws Exception
220        {
221            Object component = instanciate();
222            
223            _componentsInitializing.put(_role, component);
224            
225            if (component instanceof ParentAware && _parentManager != null)
226            {
227                Object parent = _parentManager.lookup(_role);
228                ((ParentAware) component).setParent(parent);
229            }
230            
231            configureAndStart(component);
232            
233            ExtensionPoint extPoint = (ExtensionPoint) component;
234            
235            for (ExtensionDefinition extension : _extensions)
236            {
237                extPoint.addExtension(extension.getId(), extension.getPluginName(), extension.getFeatureName(), extension.getConfiguration());
238            }
239            
240            if (!extPoint.getClass().isAnnotationPresent(LazyInitializeExtensions.class))
241            {
242                getLogger().debug("Initializing extensions for point {}", _role);
243                extPoint.initializeExtensions();
244            }
245            
246            _componentsInitializing.remove(_role);
247            
248            return proxify(component);
249        }
250    }
251}