001/* 002 * Copyright 2015 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.runtime.plugin; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.commons.lang3.StringUtils; 030 031import org.ametys.runtime.config.ConfigParameterInfo; 032 033/** 034 * A plugin is composed by features, containing components definitions and extensions. 035 */ 036public class Feature 037{ 038 /** Name for fake features, eg. declared on the fly */ 039 public static final String DEFAULT_NAME = "_default"; 040 041 private String _pluginName; 042 private String _featureName; 043 private Configuration _configuration; 044 private boolean _safe; 045 private boolean _passive; 046 private Collection<String> _dependencies = new ArrayList<>(); 047 private Collection<String> _deactivations = new ArrayList<>(); 048 private Collection<String> _overrides = new ArrayList<>(); 049 050 private Map<String, ConfigParameterInfo> _configParameters = new LinkedHashMap<>(); 051 private Collection<String> _configParametersRefs = new ArrayList<>(); 052 private Map<String, ConfigParameterInfo> _paramCheckers = new LinkedHashMap<>(); 053 private Map<String, Map<String, ExtensionDefinition>> _extensions = new LinkedHashMap<>(); 054 private Map<String, ComponentDefinition> _components = new LinkedHashMap<>(); 055 056 Feature(String pluginName, String featureName) 057 { 058 _pluginName = pluginName; 059 _featureName = featureName; 060 } 061 062 /** 063 * Returns the declaring plugin name 064 * @return the declaring plugin name 065 */ 066 public String getPluginName() 067 { 068 return _pluginName; 069 } 070 071 /** 072 * Returns this feature name 073 * @return this feature name 074 */ 075 public String getFeatureName() 076 { 077 return _featureName; 078 } 079 080 /** 081 * Returns the feature id, ie. <code>getPluginName() + '/' + getFeatureName()</code> 082 * @return the feature id. 083 */ 084 public String getFeatureId() 085 { 086 return _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + _featureName; 087 } 088 089 /** 090 * Returns true if this feature is passive. 091 * @return true if this feature is passive. 092 */ 093 public boolean isPassive() 094 { 095 return _passive; 096 } 097 098 /** 099 * Returns true if this feature is declared as safe. 100 * @return true if this feature is declared as safe. 101 */ 102 public boolean isSafe() 103 { 104 return _safe; 105 } 106 107 /** 108 * Returns the extensions declared within this feature, grouped by extension point. 109 * @return the extensions declared within this feature, grouped by extension point. 110 */ 111 public Map<String, Collection<String>> getExtensionsIds() 112 { 113 Map<String, Collection<String>> result = new LinkedHashMap<>(); 114 115 for (String point : _extensions.keySet()) 116 { 117 result.put(point, _extensions.get(point).keySet()); 118 } 119 120 return Collections.unmodifiableMap(result); 121 } 122 123 /** 124 * Returns the components declared within this feature, stored by role. 125 * @return the components declared within this feature, stored by role. 126 */ 127 public Map<String, String> getComponentsIds() 128 { 129 Map<String, String> result = new LinkedHashMap<>(); 130 131 for (String role : _components.keySet()) 132 { 133 result.put(role, _components.get(role).getId()); 134 } 135 136 return Collections.unmodifiableMap(result); 137 } 138 139 Configuration getConfiguration() 140 { 141 return _configuration; 142 } 143 144 Collection<String> getDependencies() 145 { 146 return _dependencies; 147 } 148 149 Collection<String> getDeactivations() 150 { 151 return _deactivations; 152 } 153 154 Collection<String> getOverrides() 155 { 156 return _overrides; 157 } 158 159 Map<String, Map<String, ExtensionDefinition>> getExtensions() 160 { 161 return _extensions; 162 } 163 164 Map<String, ComponentDefinition> getComponents() 165 { 166 return _components; 167 } 168 169 /** 170 * List the configuration parameters declared in the feature. See #getConfigParametersReferences 171 * @return The non null map of parameters 172 */ 173 public Map<String, ConfigParameterInfo> getConfigParameters() 174 { 175 return _configParameters; 176 } 177 178 /** 179 * List the configuration parameters referenced from the feature. See #getConfigParameters 180 * @return The non null map of parameters 181 */ 182 public Collection<String> getConfigParametersReferences() 183 { 184 return _configParametersRefs; 185 } 186 187 Map<String, ConfigParameterInfo> getParameterCheckers() 188 { 189 return _paramCheckers; 190 } 191 192 void configure(Configuration configuration) 193 { 194 _configuration = configuration; 195 _passive = configuration.getAttributeAsBoolean("passive", false); 196 _safe = configuration.getAttributeAsBoolean("safe", false); 197 198 _configureDependencies(); 199 _configureDeactivations(); 200 _configureOverrides(); 201 202 _configureExtensions(); 203 _configureComponents(); 204 205 Configuration configConfiguration = configuration.getChild("config"); 206 207 _configureConfigParameters(configConfiguration); 208 _configureConfigParameterReferences(configConfiguration); 209 _configureParametersCheckers(configConfiguration); 210 } 211 212 private void _configureDependencies() 213 { 214 String depends = _configuration.getAttribute("depends", null); 215 216 if (depends != null) 217 { 218 List<String> dependencies = Arrays.stream(StringUtils.split(depends, ',')) 219 .map(String::trim) 220 .filter(StringUtils::isNotEmpty) 221 .collect(Collectors.toList()); 222 223 for (String dependency : dependencies) 224 { 225 String dependingFeatureId = dependency; 226 227 int i = dependency.indexOf('/'); 228 if (i == -1) 229 { 230 dependingFeatureId = _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + dependency; 231 } 232 233 _dependencies.add(dependingFeatureId); 234 } 235 } 236 } 237 238 private void _configureDeactivations() 239 { 240 _deactivations.addAll(_configureDeactivationsOrOverrides(_configuration, "deactivates", _pluginName)); 241 } 242 243 private void _configureOverrides() 244 { 245 _overrides.addAll(_configureDeactivationsOrOverrides(_configuration, "overrides", _pluginName)); 246 } 247 248 private static Collection<String> _configureDeactivationsOrOverrides(Configuration configuration, String attributeName, String pluginName) 249 { 250 Collection<String> result = new ArrayList<>(); 251 String deactivates = configuration.getAttribute(attributeName, null); 252 253 if (deactivates != null) 254 { 255 List<String> deactivations = Arrays.stream(StringUtils.split(deactivates, ',')) 256 .map(String::trim) 257 .filter(StringUtils::isNotEmpty) 258 .collect(Collectors.toList()); 259 260 for (String deactivation : deactivations) 261 { 262 String deactivatedFeatureId = deactivation; 263 264 int i = deactivation.indexOf('/'); 265 if (i == -1) 266 { 267 deactivatedFeatureId = pluginName + PluginsManager.FEATURE_ID_SEPARATOR + deactivation; 268 } 269 270 result.add(deactivatedFeatureId); 271 } 272 } 273 return result; 274 } 275 276 private void _configureConfigParameters(Configuration configConfiguration) 277 { 278 Configuration[] parameterConfigurations = configConfiguration.getChildren("param"); 279 for (Configuration parameterConfiguration : parameterConfigurations) 280 { 281 String id = parameterConfiguration.getAttribute("id", null); 282 283 // Add the new parameter to the list of declared parameters 284 _configParameters.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration)); 285 } 286 } 287 288 private void _configureConfigParameterReferences(Configuration configConfiguration) 289 { 290 Configuration[] parameterConfigurations = configConfiguration.getChildren("param-ref"); 291 for (Configuration parameterConfiguration : parameterConfigurations) 292 { 293 String id = parameterConfiguration.getAttribute("id", null); 294 _configParametersRefs.add(id); 295 } 296 } 297 298 private void _configureParametersCheckers(Configuration configConfiguration) 299 { 300 Configuration[] parameterConfigurations = configConfiguration.getChildren("param-checker"); 301 for (Configuration parameterConfiguration : parameterConfigurations) 302 { 303 String id = parameterConfiguration.getAttribute("id", null); 304 305 // Add the new parameter to the list of declared parameters 306 _paramCheckers.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration)); 307 } 308 } 309 310 private void _configureExtensions() 311 { 312 Configuration[] extsConf = _configuration.getChild("extensions").getChildren("extension"); 313 for (Configuration extConf : extsConf) 314 { 315 // XML schema requires attributes id and point and enforces that the combination (id,point) is unique 316 String id = extConf.getAttribute("id", null); 317 String point = extConf.getAttribute("point", null); 318 319 Map<String, ExtensionDefinition> confs = _extensions.get(point); 320 if (confs == null) 321 { 322 confs = new HashMap<>(); 323 _extensions.put(point, confs); 324 } 325 326 confs.put(id, new ExtensionDefinition(id, point, _pluginName, _featureName, extConf)); 327 } 328 } 329 330 private void _configureComponents() 331 { 332 Configuration[] componentsConf = _configuration.getChild("components").getChildren("component"); 333 for (Configuration componentConf : componentsConf) 334 { 335 // XML schema requires class attribute 336 String clazz = componentConf.getAttribute("class", null); 337 String role = componentConf.getAttribute("role", clazz); 338 String id = componentConf.getAttribute("id", clazz); 339 340 _components.put(role, new ComponentDefinition(id, role, _pluginName, _featureName, componentConf)); 341 } 342 } 343}