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 Map<String, ConfigParameterInfo> getConfigParameters() 167 { 168 return _configParameters; 169 } 170 171 Collection<String> getConfigParametersReferences() 172 { 173 return _configParametersRefs; 174 } 175 176 Map<String, ConfigParameterInfo> getParameterCheckers() 177 { 178 return _paramCheckers; 179 } 180 181 void configure(Configuration configuration) 182 { 183 _configuration = configuration; 184 _passive = configuration.getAttributeAsBoolean("passive", false); 185 _safe = configuration.getAttributeAsBoolean("safe", false); 186 187 _configureDependencies(); 188 _configureDeactivations(); 189 _configureOverrides(); 190 191 _configureExtensions(); 192 _configureComponents(); 193 194 Configuration configConfiguration = configuration.getChild("config"); 195 196 _configureConfigParameters(configConfiguration); 197 _configureConfigParameterReferences(configConfiguration); 198 _configureParametersCheckers(configConfiguration); 199 } 200 201 private void _configureDependencies() 202 { 203 String depends = _configuration.getAttribute("depends", null); 204 205 if (depends != null) 206 { 207 List<String> dependencies = Arrays.stream(StringUtils.split(depends, ',')) 208 .map(String::trim) 209 .filter(StringUtils::isNotEmpty) 210 .collect(Collectors.toList()); 211 212 for (String dependency : dependencies) 213 { 214 String dependingFeatureId = dependency; 215 216 int i = dependency.indexOf('/'); 217 if (i == -1) 218 { 219 dependingFeatureId = _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + dependency; 220 } 221 222 _dependencies.add(dependingFeatureId); 223 } 224 } 225 } 226 227 private void _configureDeactivations() 228 { 229 _deactivations.addAll(_configureDeactivationsOrOverrides(_configuration, "deactivates", _pluginName)); 230 } 231 232 private void _configureOverrides() 233 { 234 _overrides.addAll(_configureDeactivationsOrOverrides(_configuration, "overrides", _pluginName)); 235 } 236 237 private static Collection<String> _configureDeactivationsOrOverrides(Configuration configuration, String attributeName, String pluginName) 238 { 239 Collection<String> result = new ArrayList<>(); 240 String deactivates = configuration.getAttribute(attributeName, null); 241 242 if (deactivates != null) 243 { 244 List<String> deactivations = Arrays.stream(StringUtils.split(deactivates, ',')) 245 .map(String::trim) 246 .filter(StringUtils::isNotEmpty) 247 .collect(Collectors.toList()); 248 249 for (String deactivation : deactivations) 250 { 251 String deactivatedFeatureId = deactivation; 252 253 int i = deactivation.indexOf('/'); 254 if (i == -1) 255 { 256 deactivatedFeatureId = pluginName + PluginsManager.FEATURE_ID_SEPARATOR + deactivation; 257 } 258 259 result.add(deactivatedFeatureId); 260 } 261 } 262 return result; 263 } 264 265 private void _configureConfigParameters(Configuration configConfiguration) 266 { 267 Configuration[] parameterConfigurations = configConfiguration.getChildren("param"); 268 for (Configuration parameterConfiguration : parameterConfigurations) 269 { 270 String id = parameterConfiguration.getAttribute("id", null); 271 272 // Add the new parameter to the list of declared parameters 273 _configParameters.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration)); 274 } 275 } 276 277 private void _configureConfigParameterReferences(Configuration configConfiguration) 278 { 279 Configuration[] parameterConfigurations = configConfiguration.getChildren("param-ref"); 280 for (Configuration parameterConfiguration : parameterConfigurations) 281 { 282 String id = parameterConfiguration.getAttribute("id", null); 283 _configParametersRefs.add(id); 284 } 285 } 286 287 private void _configureParametersCheckers(Configuration configConfiguration) 288 { 289 Configuration[] parameterConfigurations = configConfiguration.getChildren("param-checker"); 290 for (Configuration parameterConfiguration : parameterConfigurations) 291 { 292 String id = parameterConfiguration.getAttribute("id", null); 293 294 // Add the new parameter to the list of declared parameters 295 _paramCheckers.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration)); 296 } 297 } 298 299 private void _configureExtensions() 300 { 301 Configuration[] extsConf = _configuration.getChild("extensions").getChildren("extension"); 302 for (Configuration extConf : extsConf) 303 { 304 // XML schema requires attributes id and point and enforces that the combination (id,point) is unique 305 String id = extConf.getAttribute("id", null); 306 String point = extConf.getAttribute("point", null); 307 308 Map<String, ExtensionDefinition> confs = _extensions.get(point); 309 if (confs == null) 310 { 311 confs = new HashMap<>(); 312 _extensions.put(point, confs); 313 } 314 315 confs.put(id, new ExtensionDefinition(id, point, _pluginName, _featureName, extConf)); 316 } 317 } 318 319 private void _configureComponents() 320 { 321 Configuration[] componentsConf = _configuration.getChild("components").getChildren("component"); 322 for (Configuration componentConf : componentsConf) 323 { 324 // XML schema requires attributes id and role 325 String id = componentConf.getAttribute("id", null); 326 String role = componentConf.getAttribute("role", null); 327 328 _components.put(role, new ComponentDefinition(id, role, _pluginName, _featureName, componentConf)); 329 } 330 } 331}