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 */ 016 017package org.ametys.runtime.plugin.component; 018 019import java.util.HashMap; 020import java.util.Map; 021import java.util.Set; 022 023import org.apache.avalon.framework.activity.Disposable; 024import org.apache.avalon.framework.activity.Initializable; 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.logger.AbstractLogEnabled; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.avalon.framework.thread.ThreadSafe; 036import org.apache.cocoon.components.LifecycleHelper; 037import org.slf4j.LoggerFactory; 038 039import org.ametys.runtime.plugin.ExtensionPoint; 040 041/** 042 * Avalon based implementation of an ExtensionPoint. 043 * @param <T> the type of the managed extensions 044 */ 045public abstract class AbstractComponentExtensionPoint<T> extends AbstractLogEnabled implements ExtensionPoint<T>, Component, ThreadSafe, Serviceable, Contextualizable, Initializable, Disposable 046{ 047 /** Avalon service manager */ 048 protected ServiceManager _manager; 049 /** Avalon context */ 050 protected Context _context; 051 /** Extensions id and configuration associated */ 052 protected Map<String, ExtensionConfiguration> _extensions; 053 054 public void contextualize(Context context) throws ContextException 055 { 056 _context = context; 057 } 058 059 public void service(ServiceManager manager) throws ServiceException 060 { 061 _manager = manager; 062 } 063 064 @Override 065 public void initialize() throws Exception 066 { 067 _extensions = new HashMap<>(); 068 } 069 070 @Override 071 public void dispose() 072 { 073 _extensions = null; 074 } 075 076 @SuppressWarnings("unchecked") 077 @Override 078 public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException 079 { 080 if (getLogger().isDebugEnabled()) 081 { 082 getLogger().debug("Configuring extension '" + id + "' in plugin '" + pluginName + "' id '" + featureName + "'."); 083 } 084 085 String className = configuration.getAttribute("class", null); 086 087 if (className == null) 088 { 089 throw new ConfigurationException("In plugin '" + pluginName + "' id '" + featureName + "', extension '" + id + "' does not defines any class", configuration); 090 } 091 092 Class<T> extensionClass; 093 094 try 095 { 096 extensionClass = (Class<T>) Class.forName(className); 097 } 098 catch (ClassNotFoundException ex) 099 { 100 throw new ConfigurationException("Unable to instanciate class '" + className + "' for plugin '" + pluginName + "' / '" + featureName + "'", configuration, ex); 101 } 102 103 if (getLogger().isDebugEnabled()) 104 { 105 getLogger().debug("Extension configured"); 106 } 107 108 ExtensionConfiguration ec = new ExtensionConfiguration(pluginName, featureName, configuration, extensionClass); 109 _extensions.put(id, ec); 110 } 111 112 @Override 113 public T getExtension(String id) 114 { 115 ExtensionConfiguration ec = _extensions.get(id); 116 if (ec == null) 117 { 118 throw new IllegalArgumentException("Id " + id + " is not a correct component identifier."); 119 } 120 121 Class<T> extensionClass = ec.getExtensionClass(); 122 T t; 123 try 124 { 125 t = extensionClass.getDeclaredConstructor().newInstance(); 126 } 127 catch (Exception e) 128 { 129 throw new IllegalArgumentException("Cannot instanciate the class " + extensionClass.getCanonicalName() + ". Check that there is a public constructor with no arguments."); 130 } 131 132 try 133 { 134 if (t instanceof PluginAware) 135 { 136 ((PluginAware) t).setPluginInfo(ec.getPluginName(), ec.getFeatureName(), id); 137 } 138 139 if (t instanceof LogEnabled) 140 { 141 Configuration config = ec.getConfiguration(); 142 String logger = config == null ? null : config.getAttribute("logger", null); 143 logger = logger != null ? logger : extensionClass.getName(); 144 145 ((LogEnabled) t).setLogger(LoggerFactory.getLogger(logger)); 146 } 147 148 LifecycleHelper.setupComponent(t, getLogger(), _context, _manager, ec.getConfiguration(), true); 149 } 150 catch (Exception e) 151 { 152 throw new IllegalArgumentException("Cannot initiate the class " + extensionClass.getCanonicalName() + ".", e); 153 } 154 155 return t; 156 } 157 158 @Override 159 public Set<String> getExtensionsIds() 160 { 161 return _extensions.keySet(); 162 } 163 164 @Override 165 public boolean hasExtension(String id) 166 { 167 return _extensions.containsKey(id); 168 } 169 170 @Override 171 public void initializeExtensions() throws Exception 172 { 173 // nothing 174 } 175 176 /** 177 * This class is a storage for an extension informations 178 */ 179 protected class ExtensionConfiguration 180 { 181 private String _pluginName; 182 private String _featureName; 183 private Configuration _configuration; 184 private Class<T> _extensionClass; 185 186 /** 187 * Store elements about an extension 188 * @param pluginName The name of the plugin declaring the extension 189 * @param featureName The name of the feature declaring the extension 190 * @param configuration The configuration declaring the extension 191 * @param extensionClass The class of the extension 192 */ 193 protected ExtensionConfiguration(String pluginName, String featureName, Configuration configuration, Class<T> extensionClass) 194 { 195 _pluginName = pluginName; 196 _featureName = featureName; 197 _configuration = configuration; 198 _extensionClass = extensionClass; 199 } 200 201 /** 202 * Returns the extension class 203 * @return the extension class 204 */ 205 public Class<T> getExtensionClass() 206 { 207 return _extensionClass; 208 } 209 210 /** 211 * Returns the configuration 212 * @return the configuration 213 */ 214 public Configuration getConfiguration() 215 { 216 return _configuration; 217 } 218 219 /** 220 * Returns the feature name 221 * @return the feature name 222 */ 223 public String getFeatureName() 224 { 225 return _featureName; 226 } 227 228 /** 229 * Returns the plugin name 230 * @return the plugin name 231 */ 232 public String getPluginName() 233 { 234 return _pluginName; 235 } 236 } 237}