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