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