001/* 002 * Copyright 2016 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.core.group.directory; 017 018import java.lang.reflect.InvocationTargetException; 019import java.util.HashMap; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.avalon.framework.activity.Disposable; 025import org.apache.avalon.framework.activity.Initializable; 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.configuration.ConfigurationException; 029import org.apache.avalon.framework.context.Context; 030import org.apache.avalon.framework.context.ContextException; 031import org.apache.avalon.framework.context.Contextualizable; 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.apache.cocoon.util.log.SLF4JLoggerAdapter; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import org.ametys.runtime.i18n.I18nizableText; 042import org.ametys.runtime.model.ElementDefinition; 043import org.ametys.runtime.model.ElementDefinitionParser; 044import org.ametys.runtime.model.Enumerator; 045import org.ametys.runtime.parameter.Validator; 046import org.ametys.runtime.plugin.ExtensionPoint; 047import org.ametys.runtime.plugin.component.AbstractLogEnabled; 048import org.ametys.runtime.plugin.component.LogEnabled; 049import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 050 051/** 052 * This extension point handles a list of {@link GroupDirectoryModel} handled by the plugins. 053 */ 054public class GroupDirectoryFactory extends AbstractLogEnabled implements ExtensionPoint<GroupDirectoryModel>, Initializable, ThreadSafe, Component, Serviceable, Contextualizable, Disposable 055{ 056 /** The avalon role */ 057 public static final String ROLE = GroupDirectoryFactory.class.getName(); 058 059 /** The Group Directory parameters Extension Point */ 060 protected GroupDirectoryParameterTypeExtensionPoint _groupDirectoryParameterTypeExtensionPoint; 061 062 private Map<String, GroupDirectoryModel> _models; 063 064 private ServiceManager _smanager; 065 066 private Context _context; 067 068 @Override 069 public void initialize() throws Exception 070 { 071 _models = new HashMap<>(); 072 } 073 074 @Override 075 public void dispose() 076 { 077 _models.clear(); 078 } 079 080 @Override 081 public void service(ServiceManager smanager) throws ServiceException 082 { 083 _smanager = smanager; 084 _groupDirectoryParameterTypeExtensionPoint = (GroupDirectoryParameterTypeExtensionPoint) _smanager.lookup(GroupDirectoryParameterTypeExtensionPoint.ROLE); 085 } 086 087 @Override 088 public void contextualize(Context context) throws ContextException 089 { 090 _context = context; 091 } 092 093 /** 094 * Creates a instance of {@link GroupDirectory} 095 * @param id The id of the group directory 096 * @param label The label of the group directory 097 * @param modelId The id of the group directory model 098 * @param paramsValues The parameters's values 099 * @return a group directory 100 */ 101 public GroupDirectory createGroupDirectory (String id, I18nizableText label, String modelId, Map<String, Object> paramsValues) 102 { 103 if (_models.containsKey(modelId)) 104 { 105 GroupDirectoryModel groupDirectoryModel = _models.get(modelId); 106 107 GroupDirectory groupDirectory = null; 108 Class<GroupDirectory> groupDirectoryClass = groupDirectoryModel.getGroupDirectoryClass(); 109 try 110 { 111 groupDirectory = groupDirectoryClass.getDeclaredConstructor().newInstance(); 112 } 113 catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) 114 { 115 throw new IllegalArgumentException("Cannot instanciate the class " + groupDirectoryClass.getCanonicalName() + ". Check that there is a public constructor with no arguments."); 116 } 117 118 Logger logger = LoggerFactory.getLogger(groupDirectoryClass); 119 try 120 { 121 if (groupDirectory instanceof LogEnabled) 122 { 123 ((LogEnabled) groupDirectory).setLogger(logger); 124 } 125 126 LifecycleHelper.setupComponent(groupDirectory, new SLF4JLoggerAdapter(logger), _context, _smanager, groupDirectoryModel.getGroupDirectoryConfiguration()); 127 128 groupDirectory.setId(id); 129 groupDirectory.setLabel(label); 130 groupDirectory.init(modelId, paramsValues); 131 } 132 catch (Exception e) 133 { 134 getLogger().error("An error occured during the initialization of the GroupDirectory " + id + " (" + modelId + ")", e); 135 return null; 136 } 137 138 return groupDirectory; 139 } 140 141 return null; 142 } 143 144 @Override 145 public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException 146 { 147 if (getLogger().isDebugEnabled()) 148 { 149 getLogger().debug("Adding group directory model from plugin " + pluginName + "/" + featureName); 150 } 151 152 try 153 { 154 addGroupDirectoryModel(pluginName, configuration); 155 } 156 catch (ConfigurationException e) 157 { 158 if (getLogger().isWarnEnabled()) 159 { 160 getLogger().warn("The plugin '" + pluginName + "." + featureName + "' has a group directory model extension but has an incorrect configuration", e); 161 } 162 } 163 } 164 165 /** 166 * Add a group directory model 167 * @param pluginName The plugin name 168 * @param configuration The configuration 169 * @throws ConfigurationException when a configuration problem occurs 170 */ 171 protected void addGroupDirectoryModel (String pluginName, Configuration configuration) throws ConfigurationException 172 { 173 String id = configuration.getAttribute("id"); 174 I18nizableText label = I18nizableText.parseI18nizableText(configuration.getChild("label"), "plugin." + pluginName); 175 I18nizableText description = I18nizableText.parseI18nizableText(configuration.getChild("description"), "plugin." + pluginName); 176 177 String className = null; 178 Class<?> groupDirectoryClass = null; 179 Configuration classConfig = null; 180 try 181 { 182 className = configuration.getChild("class").getAttribute("name"); 183 groupDirectoryClass = Class.forName(className); 184 classConfig = configuration.getChild("class"); 185 } 186 catch (ClassNotFoundException | ConfigurationException e) 187 { 188 throw new ConfigurationException("Group directory model with id '" + id + "' has an invalid configuration for class name " + (className != null ? className + " <class not found>" : "<missing tag <class>") + "'", e); 189 } 190 191 if (!GroupDirectory.class.isAssignableFrom(groupDirectoryClass)) 192 { 193 throw new ConfigurationException("Group directory model with id '" + id + "' has an invalid configuration: '" + className + "' is not an instance of GroupDirectory"); 194 } 195 196 Map<String, ElementDefinition> parameters = new LinkedHashMap<>(); 197 198 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 199 validatorManager.setLogger(getLogger()); 200 validatorManager.contextualize(_context); 201 validatorManager.service(_smanager); 202 203 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 204 enumeratorManager.setLogger(getLogger()); 205 enumeratorManager.contextualize(_context); 206 enumeratorManager.service(_smanager); 207 208 GroupDirectoryModelParameterParser groupDirectoryParser = new GroupDirectoryModelParameterParser(_groupDirectoryParameterTypeExtensionPoint, enumeratorManager, validatorManager); 209 210 Configuration[] paramsConfiguration = configuration.getChild("parameters").getChildren("param"); 211 for (Configuration paramConfiguration : paramsConfiguration) 212 { 213 configureParameters(groupDirectoryParser, paramConfiguration, pluginName, parameters); 214 } 215 216 try 217 { 218 groupDirectoryParser.lookupComponents(); 219 } 220 catch (Exception e) 221 { 222 throw new ConfigurationException("Unable to lookup parameter local components", configuration, e); 223 } 224 225 @SuppressWarnings("unchecked") 226 GroupDirectoryModel groupDirectoryModel = new DefaultGroupDirectoryModel(id, (Class<GroupDirectory>) groupDirectoryClass, classConfig, label, description, parameters, pluginName); 227 if (_models.containsKey(id)) 228 { 229 GroupDirectoryModel oldUDModel = _models.get(id); 230 throw new IllegalArgumentException("Group directory model with id '" + id + "' is already declared in plugin '" + oldUDModel.getPluginName() + "'. This second declaration is ignored."); 231 } 232 233 _models.put(id, groupDirectoryModel); 234 } 235 236 /** 237 * Configure a parameter to access the group directory 238 * @param paramParser the parameter parser. 239 * @param configuration The parameter configuration. 240 * @param pluginName The plugin name 241 * @param parameters The model's parameters 242 * @throws ConfigurationException if configuration is incomplete or invalid. 243 */ 244 protected void configureParameters(GroupDirectoryModelParameterParser paramParser, Configuration configuration, String pluginName, Map<String, ElementDefinition> parameters) throws ConfigurationException 245 { 246 ElementDefinition parameter = paramParser.parse(_smanager, pluginName, configuration, null, null); 247 String id = parameter.getName(); 248 249 if (parameters.containsKey(id)) 250 { 251 throw new ConfigurationException("The parameter '" + id + "' is already declared. IDs must be unique.", configuration); 252 } 253 254 parameters.put(id, parameter); 255 } 256 257 @Override 258 public void initializeExtensions() throws Exception 259 { 260 // Nothing to do 261 } 262 263 @Override 264 public boolean hasExtension(String id) 265 { 266 return _models.containsKey(id); 267 } 268 269 @Override 270 public GroupDirectoryModel getExtension(String id) 271 { 272 return _models.get(id); 273 } 274 275 @Override 276 public Set<String> getExtensionsIds() 277 { 278 return _models.keySet(); 279 } 280 281 /** 282 * Class for parsing parameters of a {@link GroupDirectoryModel} 283 */ 284 public class GroupDirectoryModelParameterParser extends ElementDefinitionParser 285 { 286 /** 287 * Constructor 288 * @param groupDirectoryParameterTypeExtensionPoint the extension point to use to get available element types 289 * @param enumeratorManager The manager for enumeration 290 * @param validatorManager The manager for validation 291 */ 292 public GroupDirectoryModelParameterParser(GroupDirectoryParameterTypeExtensionPoint groupDirectoryParameterTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 293 { 294 super(groupDirectoryParameterTypeExtensionPoint, enumeratorManager, validatorManager); 295 } 296 297 @Override 298 protected String _getNameConfigurationAttribute() 299 { 300 return "id"; 301 } 302 } 303 304}