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}