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}