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