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