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.plugins.contentio.synchronize; 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; 035 036import org.ametys.plugins.contentio.synchronize.impl.DefaultSynchronizableContentsCollectionModel; 037import org.ametys.runtime.i18n.I18nizableText; 038import org.ametys.runtime.parameter.AbstractParameterParser; 039import org.ametys.runtime.parameter.Enumerator; 040import org.ametys.runtime.parameter.Parameter; 041import org.ametys.runtime.parameter.ParameterHelper; 042import org.ametys.runtime.parameter.ParameterHelper.ParameterType; 043import org.ametys.runtime.parameter.Validator; 044import org.ametys.runtime.plugin.ExtensionPoint; 045import org.ametys.runtime.plugin.component.AbstractLogEnabled; 046import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 047 048/** 049 * Extension point for {@link SynchronizableContentsCollectionModel}s. 050 */ 051public class SynchronizeContentsCollectionModelExtensionPoint extends AbstractLogEnabled implements ExtensionPoint<SynchronizableContentsCollectionModel>, Initializable, ThreadSafe, Component, Serviceable, Contextualizable, Disposable 052{ 053 /** Avalon Role */ 054 public static final String ROLE = SynchronizeContentsCollectionModelExtensionPoint.class.getName(); 055 056 private Map<String, SynchronizableContentsCollectionModel> _sccModels; 057 058 private ServiceManager _smanager; 059 060 private Context _context; 061 062 @Override 063 public void initialize() throws Exception 064 { 065 _sccModels = new HashMap<>(); 066 } 067 068 @Override 069 public void dispose() 070 { 071 _sccModels.clear(); 072 } 073 074 @Override 075 public void service(ServiceManager smanager) throws ServiceException 076 { 077 _smanager = smanager; 078 } 079 080 @Override 081 public void contextualize(Context context) throws ContextException 082 { 083 _context = context; 084 } 085 086 @Override 087 public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException 088 { 089 getLogger().debug("Adding user directory model from plugin {}/{}", pluginName, featureName); 090 091 try 092 { 093 addModel(pluginName, configuration); 094 } 095 catch (ConfigurationException e) 096 { 097 getLogger().warn("The feature '{}/{}' has a synchronizable contents collection model extension but has an incorrect configuration", pluginName, featureName, e); 098 } 099 } 100 101 /** 102 * Add a synchronizable contents collection model 103 * @param pluginName The plugin name 104 * @param configuration The configuration 105 * @throws ConfigurationException when a configuration problem occurs 106 */ 107 protected void addModel(String pluginName, Configuration configuration) throws ConfigurationException 108 { 109 String id = configuration.getAttribute("id"); 110 I18nizableText label = I18nizableText.parseI18nizableText(configuration.getChild("label"), "plugin." + pluginName); 111 I18nizableText description = I18nizableText.parseI18nizableText(configuration.getChild("description"), "plugin." + pluginName); 112 113 String className = null; 114 Class<?> sccClass = null; 115 Configuration classConfig = null; 116 try 117 { 118 className = configuration.getChild("class").getAttribute("name"); 119 sccClass = Class.forName(className); 120 classConfig = configuration.getChild("class"); 121 } 122 catch (ClassNotFoundException | ConfigurationException e) 123 { 124 throw new ConfigurationException("SynchronizableContentsCollection model with id '" + id + "' has an invalid configuration for class name " + (className != null ? className + " <class not found>" : "<missing tag <class>") + "'", e); 125 } 126 127 if (!SynchronizableContentsCollection.class.isAssignableFrom(sccClass)) 128 { 129 throw new ConfigurationException("SynchronizableContentsCollection model with id '" + id + "' has an invalid configuration: '" + className + "' is not an instance of SynchronizableContentsCollection"); 130 } 131 132 // Parse parameters 133 Map<String, Parameter<ParameterType>> parameters = new LinkedHashMap<>(); 134 135 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 136 validatorManager.setLogger(getLogger()); 137 validatorManager.contextualize(_context); 138 validatorManager.service(_smanager); 139 140 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 141 enumeratorManager.setLogger(getLogger()); 142 enumeratorManager.contextualize(_context); 143 enumeratorManager.service(_smanager); 144 145 SynchronizableContentsCollectionModelParameterParser sccmParser = new SynchronizableContentsCollectionModelParameterParser(enumeratorManager, validatorManager); 146 147 Configuration[] paramsConfiguration = configuration.getChild("parameters").getChildren("param"); 148 for (Configuration paramConfiguration : paramsConfiguration) 149 { 150 configureParameters(sccmParser, paramConfiguration, pluginName, parameters); 151 } 152 153 try 154 { 155 sccmParser.lookupComponents(); 156 } 157 catch (Exception e) 158 { 159 throw new ConfigurationException("Unable to lookup parameter local components", configuration, e); 160 } 161 162 // Create and reference the model 163 @SuppressWarnings("unchecked") 164 SynchronizableContentsCollectionModel sccModel = new DefaultSynchronizableContentsCollectionModel(id, (Class<SynchronizableContentsCollection>) sccClass, classConfig, label, description, parameters, pluginName); 165 if (_sccModels.containsKey(id)) 166 { 167 SynchronizableContentsCollectionModel oldSCCModel = _sccModels.get(id); 168 throw new IllegalArgumentException("SynchronizableContentsCollection model with id '" + id + "' is already declared in plugin '" + oldSCCModel.getPluginName() + "'. This second declaration is ignored."); 169 } 170 171 _sccModels.put(id, sccModel); 172 } 173 174 /** 175 * Configure a parameter to access the SynchronizableContentsCollection 176 * @param paramParser the parameter parser. 177 * @param configuration The parameter configuration. 178 * @param pluginName The plugin name 179 * @param parameters The model parameters 180 * @throws ConfigurationException if configuration is incomplete or invalid. 181 */ 182 protected void configureParameters(SynchronizableContentsCollectionModelParameterParser paramParser, Configuration configuration, String pluginName, Map<String, Parameter<ParameterType>> parameters) throws ConfigurationException 183 { 184 Parameter<ParameterType> parameter = paramParser.parseParameter(_smanager, pluginName, configuration); 185 String id = parameter.getId(); 186 187 if (parameters.containsKey(id)) 188 { 189 throw new ConfigurationException("The parameter '" + id + "' is already declared. IDs must be unique.", configuration); 190 } 191 192 parameters.put(id, parameter); 193 } 194 195 @Override 196 public void initializeExtensions() throws Exception 197 { 198 // Nothing to do 199 } 200 201 @Override 202 public boolean hasExtension(String id) 203 { 204 return _sccModels.containsKey(id); 205 } 206 207 @Override 208 public SynchronizableContentsCollectionModel getExtension(String id) 209 { 210 return _sccModels.get(id); 211 } 212 213 @Override 214 public Set<String> getExtensionsIds() 215 { 216 return _sccModels.keySet(); 217 } 218 219 /** 220 * Class for parsing parameters of a {@link SynchronizableContentsCollectionModel} 221 */ 222 public class SynchronizableContentsCollectionModelParameterParser extends AbstractParameterParser<Parameter<ParameterType>, ParameterType> 223 { 224 /** 225 * Constructor 226 * @param enumeratorManager The manager for enumeration 227 * @param validatorManager The manager for validation 228 */ 229 public SynchronizableContentsCollectionModelParameterParser(ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 230 { 231 super(enumeratorManager, validatorManager); 232 } 233 234 @Override 235 protected Parameter<ParameterType> _createParameter(Configuration parameterConfig) throws ConfigurationException 236 { 237 return new Parameter<>(); 238 } 239 240 @Override 241 protected String _parseId(Configuration parameterConfig) throws ConfigurationException 242 { 243 return parameterConfig.getAttribute("id"); 244 } 245 246 @Override 247 protected ParameterType _parseType(Configuration parameterConfig) throws ConfigurationException 248 { 249 try 250 { 251 return ParameterType.valueOf(parameterConfig.getAttribute("type").toUpperCase()); 252 } 253 catch (IllegalArgumentException e) 254 { 255 throw new ConfigurationException("Invalid parameter type", parameterConfig, e); 256 } 257 } 258 259 @Override 260 protected Object _parseDefaultValue(Configuration parameterConfig, Parameter<ParameterType> parameter) throws ConfigurationException 261 { 262 String defaultValue = parameterConfig.getChild("default-value").getValue(null); 263 return ParameterHelper.castValue(defaultValue, parameter.getType()); 264 } 265 266 @Override 267 protected void _additionalParsing(ServiceManager manager, String pluginName, Configuration parameterConfig, String parameterId, Parameter<ParameterType> parameter) 268 throws ConfigurationException 269 { 270 super._additionalParsing(manager, pluginName, parameterConfig, parameterId, parameter); 271 parameter.setId(parameterId); 272 } 273 } 274}