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 groupDirectory.setId(id); 128 groupDirectory.setLabel(label); 129 groupDirectory.init(modelId, paramsValues); 130 } 131 catch (Exception e) 132 { 133 getLogger().error("An error occured during the initialization of the GroupDirectory " + id + " (" + modelId + ")", e); 134 return null; 135 } 136 137 return groupDirectory; 138 } 139 140 return null; 141 } 142 143 @Override 144 public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException 145 { 146 if (getLogger().isDebugEnabled()) 147 { 148 getLogger().debug("Adding group directory model from plugin " + pluginName + "/" + featureName); 149 } 150 151 try 152 { 153 addGroupDirectoryModel(pluginName, configuration); 154 } 155 catch (ConfigurationException e) 156 { 157 if (getLogger().isWarnEnabled()) 158 { 159 getLogger().warn("The plugin '" + pluginName + "." + featureName + "' has a group directory model extension but has an incorrect configuration", e); 160 } 161 } 162 } 163 164 /** 165 * Add a group directory model 166 * @param pluginName The plugin name 167 * @param configuration The configuration 168 * @throws ConfigurationException when a configuration problem occurs 169 */ 170 protected void addGroupDirectoryModel (String pluginName, Configuration configuration) throws ConfigurationException 171 { 172 String id = configuration.getAttribute("id"); 173 I18nizableText label = I18nizableText.parseI18nizableText(configuration.getChild("label"), "plugin." + pluginName); 174 I18nizableText description = I18nizableText.parseI18nizableText(configuration.getChild("description"), "plugin." + pluginName); 175 176 String className = null; 177 Class<?> groupDirectoryClass = null; 178 Configuration classConfig = null; 179 try 180 { 181 className = configuration.getChild("class").getAttribute("name"); 182 groupDirectoryClass = Class.forName(className); 183 classConfig = configuration.getChild("class"); 184 } 185 catch (ClassNotFoundException | ConfigurationException e) 186 { 187 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); 188 } 189 190 if (!GroupDirectory.class.isAssignableFrom(groupDirectoryClass)) 191 { 192 throw new ConfigurationException("Group directory model with id '" + id + "' has an invalid configuration: '" + className + "' is not an instance of GroupDirectory"); 193 } 194 195 Map<String, ElementDefinition> parameters = new LinkedHashMap<>(); 196 197 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 198 validatorManager.setLogger(getLogger()); 199 validatorManager.contextualize(_context); 200 validatorManager.service(_smanager); 201 202 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 203 enumeratorManager.setLogger(getLogger()); 204 enumeratorManager.contextualize(_context); 205 enumeratorManager.service(_smanager); 206 207 GroupDirectoryModelParameterParser groupDirectoryParser = new GroupDirectoryModelParameterParser(_groupDirectoryParameterTypeExtensionPoint, enumeratorManager, validatorManager); 208 209 Configuration[] paramsConfiguration = configuration.getChild("parameters").getChildren("param"); 210 for (Configuration paramConfiguration : paramsConfiguration) 211 { 212 configureParameters(groupDirectoryParser, paramConfiguration, pluginName, parameters); 213 } 214 215 try 216 { 217 groupDirectoryParser.lookupComponents(); 218 } 219 catch (Exception e) 220 { 221 throw new ConfigurationException("Unable to lookup parameter local components", configuration, e); 222 } 223 224 @SuppressWarnings("unchecked") 225 GroupDirectoryModel groupDirectoryModel = new DefaultGroupDirectoryModel(id, (Class<GroupDirectory>) groupDirectoryClass, classConfig, label, description, parameters, pluginName); 226 if (_models.containsKey(id)) 227 { 228 GroupDirectoryModel oldUDModel = _models.get(id); 229 throw new IllegalArgumentException("Group directory model with id '" + id + "' is already declared in plugin '" + oldUDModel.getPluginName() + "'. This second declaration is ignored."); 230 } 231 232 _models.put(id, groupDirectoryModel); 233 } 234 235 /** 236 * Configure a parameter to access the group directory 237 * @param paramParser the parameter parser. 238 * @param configuration The parameter configuration. 239 * @param pluginName The plugin name 240 * @param parameters The model's parameters 241 * @throws ConfigurationException if configuration is incomplete or invalid. 242 */ 243 protected void configureParameters(GroupDirectoryModelParameterParser paramParser, Configuration configuration, String pluginName, Map<String, ElementDefinition> parameters) throws ConfigurationException 244 { 245 ElementDefinition parameter = paramParser.parse(_smanager, pluginName, configuration, null, null); 246 String id = parameter.getName(); 247 248 if (parameters.containsKey(id)) 249 { 250 throw new ConfigurationException("The parameter '" + id + "' is already declared. IDs must be unique.", configuration); 251 } 252 253 parameters.put(id, parameter); 254 } 255 256 @Override 257 public void initializeExtensions() throws Exception 258 { 259 // Nothing to do 260 } 261 262 @Override 263 public boolean hasExtension(String id) 264 { 265 return _models.containsKey(id); 266 } 267 268 @Override 269 public GroupDirectoryModel getExtension(String id) 270 { 271 return _models.get(id); 272 } 273 274 @Override 275 public Set<String> getExtensionsIds() 276 { 277 return _models.keySet(); 278 } 279 280 /** 281 * Class for parsing parameters of a {@link GroupDirectoryModel} 282 */ 283 public class GroupDirectoryModelParameterParser extends ElementDefinitionParser 284 { 285 /** 286 * Constructor 287 * @param groupDirectoryParameterTypeExtensionPoint the extension point to use to get available element types 288 * @param enumeratorManager The manager for enumeration 289 * @param validatorManager The manager for validation 290 */ 291 public GroupDirectoryModelParameterParser(GroupDirectoryParameterTypeExtensionPoint groupDirectoryParameterTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 292 { 293 super(groupDirectoryParameterTypeExtensionPoint, enumeratorManager, validatorManager); 294 } 295 296 @Override 297 protected String _getNameConfigurationAttribute() 298 { 299 return "id"; 300 } 301 } 302 303}