001/* 002 * Copyright 2010 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.web.service; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.activity.Disposable; 025import org.apache.avalon.framework.configuration.Configurable; 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.solr.common.SolrInputDocument; 035 036import org.ametys.cms.content.indexing.solr.SolrContentIndexer; 037import org.ametys.core.ui.ClientSideElement.Script; 038import org.ametys.core.ui.ClientSideElement.ScriptFile; 039import org.ametys.plugins.core.ui.util.ConfigurationHelper; 040import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 041import org.ametys.plugins.repository.model.parsing.RepeaterDefinitionParser; 042import org.ametys.runtime.i18n.I18nizableText; 043import org.ametys.runtime.model.ElementDefinition; 044import org.ametys.runtime.model.Enumerator; 045import org.ametys.runtime.model.ModelItem; 046import org.ametys.runtime.model.View; 047import org.ametys.runtime.model.type.ElementType; 048import org.ametys.runtime.parameter.Validator; 049import org.ametys.runtime.plugin.component.AbstractLogEnabled; 050import org.ametys.runtime.plugin.component.PluginAware; 051import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 052import org.ametys.web.parameters.ViewAndParametersParser; 053import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 054import org.ametys.web.repository.page.Page; 055import org.ametys.web.repository.page.ZoneItem; 056 057/** 058 * Class representing a business service. <br> 059 * A service is identified by an id and a Cocoon-URL.<br> 060 * This URL corresponds to a pipeline called by a page template.<br> 061 * URL must be relative to the sitemap of the plugin containing the service. 062 */ 063public class StaticService extends AbstractLogEnabled implements Service, Contextualizable, Configurable, PluginAware, Serviceable, Disposable 064{ 065 /** The plugin name */ 066 protected String _pluginName; 067 /** The feature name */ 068 protected String _featureName; 069 /** The service manager */ 070 protected ServiceManager _manager; 071 /** The context. */ 072 protected Context _context; 073 /** The script configured */ 074 protected Script _paramsScript; 075 076 /** The view of the service */ 077 protected View _view; 078 079 /** The map of model items */ 080 protected Map<String, ModelItem> _modelItems; 081 082 /** The view and parameters parser */ 083 protected ViewAndParametersParser _viewAndParametersParser; 084 085 /** The service parameter definition parser */ 086 protected ServiceParameterDefinitionParser _serviceParameterDefinitionParser; 087 088 /** The repeater definition parser */ 089 protected RepeaterDefinitionParser _repeaterDefinitionParser; 090 091 private String _id; 092 private I18nizableText _label; 093 private I18nizableText _description; 094 private I18nizableText _category; 095 096 private String _iconGlyph; 097 private String _iconDecorator; 098 private String _smallIcon; 099 private String _mediumIcon; 100 private String _largeIcon; 101 private Integer _creationBoxHeight; 102 private Integer _creationBoxWidth; 103 private List<ScriptFile> _cssFiles; 104 105 private boolean _isCacheable; 106 private boolean _isPrivate; 107 /** The right needed to create an instance of this service, blank or null if no right is needed. */ 108 private String _right; 109 110 private String _url; 111 112 private List<String> _parametersToIndex; 113 114 // ComponentManager for validators 115 private ThreadSafeComponentManager<Validator> _validatorManager; 116 117 // ComponentManager for enumerators 118 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 119 120 private ServiceParameterTypeExtensionPoint _serviceParameterTypeExtensionPoint; 121 122 123 @Override 124 public void service(ServiceManager smanager) throws ServiceException 125 { 126 _manager = smanager; 127 _serviceParameterTypeExtensionPoint = (ServiceParameterTypeExtensionPoint) smanager.lookup(ServiceParameterTypeExtensionPoint.ROLE); 128 _viewAndParametersParser = (ViewAndParametersParser) smanager.lookup(ViewAndParametersParser.ROLE); 129 } 130 131 @Override 132 public void contextualize(Context context) throws ContextException 133 { 134 _context = context; 135 } 136 137 @Override 138 public void dispose() 139 { 140 _validatorManager.dispose(); 141 _validatorManager = null; 142 143 _enumeratorManager.dispose(); 144 _enumeratorManager = null; 145 } 146 147 @Override 148 public void configure(Configuration configuration) throws ConfigurationException 149 { 150 _validatorManager = new ThreadSafeComponentManager<>(); 151 _validatorManager.setLogger(getLogger()); 152 _validatorManager.contextualize(_context); 153 _validatorManager.service(_manager); 154 155 _enumeratorManager = new ThreadSafeComponentManager<>(); 156 _enumeratorManager.setLogger(getLogger()); 157 _enumeratorManager.contextualize(_context); 158 _enumeratorManager.service(_manager); 159 160 _label = _parseI18nizableText(configuration, "label"); 161 _description = _parseI18nizableText(configuration, "description"); 162 _category = _parseI18nizableText(configuration, "category"); 163 164 _isCacheable = configuration.getChild("cacheable").getValueAsBoolean(false); 165 _isPrivate = configuration.getChild("private").getValueAsBoolean(false); 166 _right = configuration.getChild("right").getValue(null); 167 168 this._iconGlyph = configuration.getChild("thumbnail").getChild("glyph").getValue(null); 169 this._iconDecorator = configuration.getChild("thumbnail").getChild("decorator").getValue(null); 170 this._smallIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("small"), "/plugins/web/resources/img/service/servicepage_16.png"); 171 this._mediumIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("medium"), "/plugins/web/resources/img/service/servicepage_32.png"); 172 this._largeIcon = _configureThumbnail(configuration.getChild("thumbnail").getChild("marge"), "/plugins/web/resources/img/service/servicepage_50.png"); 173 this._creationBoxHeight = configureDialogBoxDimension(configuration, "height"); 174 this._creationBoxWidth = configureDialogBoxDimension(configuration, "width"); 175 176 _cssFiles = _configureImports(configuration.getChild("css")); 177 _paramsScript = _configureScript(configuration.getChild("parameters")); 178 179 _url = configuration.getChild("url").getValue(); 180 181 _serviceParameterDefinitionParser = new ServiceParameterDefinitionParser(_serviceParameterTypeExtensionPoint, _enumeratorManager, _validatorManager); 182 _repeaterDefinitionParser = new RepeaterDefinitionParser(_serviceParameterTypeExtensionPoint); 183 184 Configuration parametersConfiguration = configuration.getChild("parameters"); 185 configureParameters(parametersConfiguration); 186 187 try 188 { 189 _serviceParameterDefinitionParser.lookupComponents(); 190 } 191 catch (Exception e) 192 { 193 throw new ConfigurationException("Unable to lookup parameter local components", configuration, e); 194 } 195 196 configureIndexation(configuration.getChild("indexation")); 197 } 198 199 /** 200 * Configure the service parameters 201 * @param parametersConfiguration the parameters configuration 202 * @throws ConfigurationException if a configuration exception occurred 203 */ 204 protected void configureParameters(Configuration parametersConfiguration) throws ConfigurationException 205 { 206 ViewAndParameters viewAndParameters = _viewAndParametersParser.parseParameters(parametersConfiguration, _pluginName, "plugin." + _pluginName, this, _serviceParameterDefinitionParser, _repeaterDefinitionParser); 207 _view = viewAndParameters.getView(); 208 _modelItems = viewAndParameters.getParameters(); 209 } 210 211 /** 212 * Configure the indexation process.<br> 213 * This class only allow to index the value of some parameters (not repeaters). 214 * @param configuration the indexation configuration. 215 * @throws ConfigurationException if an error occurs. 216 */ 217 protected void configureIndexation(Configuration configuration) throws ConfigurationException 218 { 219 _parametersToIndex = new ArrayList<>(); 220 221 for (Configuration config : configuration.getChildren("parameter")) 222 { 223 String id = config.getValue(""); 224 ModelItem param = _modelItems.get(id); 225 226 if (param == null || !(param instanceof ElementDefinition)) 227 { 228 if (getLogger().isWarnEnabled()) 229 { 230 getLogger().warn("Invalid indexation configuration for service " + _id + " : there's no parameter '" + id + "'"); 231 } 232 } 233 else 234 { 235 _parametersToIndex.add(id); 236 } 237 } 238 } 239 240 @Override 241 public void setPluginInfo(String pluginName, String featureName, String id) 242 { 243 _pluginName = pluginName; 244 _featureName = featureName; 245 _id = id; 246 } 247 248 @Override 249 public String getPluginName() 250 { 251 return _pluginName; 252 } 253 254 @Override 255 public String getId() 256 { 257 return _id; 258 } 259 260 public String getName() 261 { 262 return _id; 263 } 264 265 @Override 266 public boolean isCacheable(Page currentPage, ZoneItem zoneItem) 267 { 268 return _isCacheable; 269 } 270 271 @Override 272 public I18nizableText getLabel() 273 { 274 return _label; 275 } 276 277 @Override 278 public I18nizableText getDescription() 279 { 280 return _description; 281 } 282 283 @Override 284 public I18nizableText getCategory() 285 { 286 return _category; 287 } 288 289 @Override 290 public String getIconGlyph() 291 { 292 return _iconGlyph; 293 } 294 295 @Override 296 public String getIconDecorator() 297 { 298 return _iconDecorator; 299 } 300 301 @Override 302 public String getSmallIcon() 303 { 304 return _smallIcon; 305 } 306 307 @Override 308 public String getMediumIcon() 309 { 310 return _mediumIcon; 311 } 312 313 @Override 314 public String getLargeIcon() 315 { 316 return _largeIcon; 317 } 318 319 @Override 320 public Integer getCreationBoxHeight() 321 { 322 return _creationBoxHeight; 323 } 324 325 @Override 326 public Integer getCreationBoxWidth() 327 { 328 return _creationBoxWidth; 329 } 330 331 @Override 332 public String getURL() 333 { 334 return "cocoon://_plugins/" + _pluginName + "/" + _url; 335 } 336 337 @Override 338 public Script getParametersScript() 339 { 340 return _paramsScript; 341 } 342 343 @Override 344 public List<ScriptFile> getCSSFiles() 345 { 346 return _cssFiles; 347 } 348 349 @Override 350 public boolean isPrivate() 351 { 352 return _isPrivate; 353 } 354 355 @Override 356 public String getRight() 357 { 358 return _right; 359 } 360 361 @SuppressWarnings("unchecked") 362 @Override 363 public void index(ZoneItem zoneItem, SolrInputDocument document) 364 { 365 for (String id : _parametersToIndex) 366 { 367 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 368 if (dataHolder.hasValue(id)) 369 { 370 String language = zoneItem.getZone().getPage().getSitemapName(); 371 Object value = dataHolder.getValue(id); 372 373 ElementDefinition parameter = (ElementDefinition) _modelItems.get(id); 374 ElementType type = parameter.getType(); 375 376 if (parameter.isMultiple()) 377 { 378 for (Object singleValue : (Object[]) value) 379 { 380 SolrContentIndexer.indexFulltextValue(document, type.toString(singleValue), language); 381 } 382 } 383 else 384 { 385 SolrContentIndexer.indexFulltextValue(document, type.toString(value), language); 386 } 387 } 388 } 389 } 390 391 /** 392 * Parse an i18n text. 393 * @param config the configuration to use. 394 * @param name the child name. 395 * @return the i18n text. 396 */ 397 protected I18nizableText _parseI18nizableText(Configuration config, String name) 398 { 399 return I18nizableText.parseI18nizableText(config.getChild(name), "plugin." + _pluginName, ""); 400 } 401 402 private String _configureThumbnail(Configuration valueConf, String defaultImage) 403 { 404 String value = valueConf.getValue(null); 405 if (value == null) 406 { 407 return defaultImage; 408 } 409 else 410 { 411 String pluginName = valueConf.getAttribute("plugin", this._pluginName); 412 return "/plugins/" + pluginName + "/resources/" + value; 413 } 414 } 415 416 /** 417 * Configure the dimensions of the dialog box 418 * @param configuration the global configuration 419 * @param dimensionName the name of the dimension to configure 420 * @return the value of the dimension 421 * @throws ConfigurationException if configuration is invalid 422 */ 423 protected Integer configureDialogBoxDimension(Configuration configuration, String dimensionName) throws ConfigurationException 424 { 425 Configuration dimensionConfiguration = configuration.getChild("dialogBox").getChild(dimensionName, false); 426 if (dimensionConfiguration != null) 427 { 428 return dimensionConfiguration.getValueAsInteger(); 429 } 430 else 431 { 432 return null; 433 } 434 } 435 436 /** 437 * Configure the script 438 * @param configuration the global configuration 439 * @return The script created 440 * @throws ConfigurationException if configuration is invalid 441 */ 442 protected Script _configureScript(Configuration configuration) throws ConfigurationException 443 { 444 List<ScriptFile> scriptsImports = _configureImports(configuration.getChild("scripts")); 445 List<ScriptFile> cssImports = _configureImports(configuration.getChild("css")); 446 String jsClassName = _configureClass(configuration.getChild("action")); 447 448 return new Script(this.getId(), jsClassName, scriptsImports, cssImports, new HashMap<>()); 449 } 450 451 452 /** 453 * Configure the js class name 454 * @param configuration The configuration on action tag 455 * @return The js class name 456 * @throws ConfigurationException If an error occurs 457 */ 458 protected String _configureClass(Configuration configuration) throws ConfigurationException 459 { 460 String jsClassName = configuration.getAttribute("class", ""); 461 if (getLogger().isDebugEnabled()) 462 { 463 getLogger().debug("Js class configured is '" + jsClassName + "'"); 464 } 465 return jsClassName; 466 } 467 468 /** 469 * Configure the import part 470 * @param configuration The imports configuration 471 * @return The set of the complete url of imported file 472 * @throws ConfigurationException If an error occurs 473 */ 474 protected List<ScriptFile> _configureImports(Configuration configuration) throws ConfigurationException 475 { 476 return ConfigurationHelper.parsePluginResourceList(configuration, getPluginName(), getLogger()); 477 } 478 479 public Collection<ModelItem> getModelItems() 480 { 481 return _modelItems.values(); 482 } 483 484 public Map<String, ModelItem> getParameters() 485 { 486 return _modelItems; 487 } 488 489 public View getView() 490 { 491 return _view; 492 } 493}