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