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 */ 016 017package org.ametys.web.service; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.regex.Pattern; 029 030import org.apache.avalon.framework.component.Component; 031import org.apache.avalon.framework.configuration.Configurable; 032import org.apache.avalon.framework.configuration.Configuration; 033import org.apache.avalon.framework.configuration.ConfigurationException; 034import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 035import org.apache.avalon.framework.context.Context; 036import org.apache.avalon.framework.context.ContextException; 037import org.apache.avalon.framework.context.Contextualizable; 038import org.apache.avalon.framework.logger.AbstractLogEnabled; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.avalon.framework.service.Serviceable; 042import org.apache.cocoon.components.ContextHelper; 043import org.apache.commons.lang.StringUtils; 044import org.apache.excalibur.source.Source; 045import org.apache.excalibur.source.SourceResolver; 046import org.apache.excalibur.source.TraversableSource; 047 048import org.ametys.runtime.i18n.I18nizableText; 049import org.ametys.runtime.parameter.Enumerator; 050import org.ametys.runtime.plugin.component.PluginAware; 051import org.ametys.web.source.ServiceSourceFactory; 052 053/** 054 * This enuerator return the list of available files for services. 055 * The files are searched in the skin and in the plugin 056 * Configuration is 057 * <path> to specify the path where file are stored 058 * <file> an optionnal regexp to list files in the path (default is ^.*\.xsl$ to list all xsl files) 059 */ 060public class ServiceXSLTEnumerator extends AbstractLogEnabled implements Enumerator, Component, Configurable, PluginAware, Serviceable, Contextualizable 061{ 062 /** The relative path to search */ 063 protected String _path; 064 065 /** The file pattern */ 066 protected Pattern _fileFilter; 067 /** The plugin declaring the enumerator */ 068 protected String _pluginName; 069 /** The feature declaring the enumerator */ 070 protected String _featureName; 071 /** The excalibur source resolver */ 072 protected SourceResolver _resolver; 073 /** The default value of the xsl to use. Can be null. */ 074 protected String _defaultValue; 075 /** The plugin xsl to use. Can be empty. */ 076 protected Set<String> _values; 077 /** The avalon context */ 078 protected Context _context; 079 080 @Override 081 public void configure(Configuration configuration) throws ConfigurationException 082 { 083 _defaultValue = configuration.getChild("default-value").getValue(null); 084 085 Configuration enumConf = configuration.getChild("enumeration").getChild("custom-enumerator"); 086 087 _values = _getPluginOtherXSL (enumConf); 088 089 _path = enumConf.getChild("path").getValue(""); 090 if (StringUtils.isBlank(_path)) 091 { 092 throw new ConfigurationException("The configuration of the path is mandatory", configuration); 093 } 094 095 String regexp = enumConf.getChild("file").getValue("^.*\\.xsl$"); 096 _fileFilter = Pattern.compile(regexp); 097 } 098 099 @Override 100 public void contextualize(Context context) throws ContextException 101 { 102 _context = context; 103 } 104 105 @Override 106 public void service(ServiceManager manager) throws ServiceException 107 { 108 _resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 109 } 110 111 @Override 112 public void setPluginInfo(String pluginName, String featureName, String id) 113 { 114 _pluginName = pluginName; 115 _featureName = featureName; 116 } 117 118 @Override 119 public Map<String, Object> getConfiguration() 120 { 121 Map<String, Object> config = new HashMap<>(); 122 123 config.put("defaultValue", _defaultValue); 124 config.put("values", _values); 125 config.put("path", _path); 126 config.put("fileFilter", _fileFilter.pattern()); 127 128 return config; 129 } 130 131 /** 132 * Filter a list of sources to return thoses matching the _filter 133 * @param files A non null list of files to filter 134 * @return The non null filtered list of names 135 */ 136 protected List<String> _filterNames(Collection<TraversableSource> files) 137 { 138 List<String> matched = new ArrayList<>(); 139 140 for (TraversableSource f : files) 141 { 142 if (!f.isCollection() && _fileFilter.matcher(f.getName()).matches()) 143 { 144 matched.add(f.getName()); 145 } 146 } 147 148 return matched; 149 } 150 151 @Override 152 public Map<Object, I18nizableText> getEntries() throws Exception 153 { 154 // 1 - Compute the list of files 155 Set<String> files = new HashSet<>(); 156 157 TraversableSource source = null; 158 try 159 { 160 source = (TraversableSource) _resolver.resolveURI("skin://services/" + _pluginName + "/" + _path); 161 if (source.exists()) 162 { 163 if (source.isCollection()) 164 { 165 @SuppressWarnings("unchecked") 166 List<String> sourceFiles = _filterNames(source.getChildren()); 167 files.addAll(sourceFiles); 168 } 169 else 170 { 171 getLogger().warn("Can not enumerate files for service defined in '" + _pluginName + "/" + _featureName + "' on the path '" + _path + "', because it exists on the skin but is not a directory in the skin"); 172 } 173 } 174 } 175 finally 176 { 177 _resolver.release(source); 178 } 179 180 if (_defaultValue != null) 181 { 182 Source pluginSource = null; 183 try 184 { 185 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + _defaultValue); 186 if (pluginSource.exists()) 187 { 188 int i = _defaultValue.lastIndexOf('/'); 189 String name = _defaultValue; 190 if (i > 0) 191 { 192 name = _defaultValue.substring(i + 1); 193 } 194 files.add(name); 195 } 196 } 197 finally 198 { 199 _resolver.release(pluginSource); 200 } 201 } 202 203 if (_values != null) 204 { 205 for (String value : _values) 206 { 207 Source pluginSource = null; 208 try 209 { 210 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + value); 211 if (pluginSource.exists()) 212 { 213 int i = value.lastIndexOf('/'); 214 String name = value; 215 if (i > 0) 216 { 217 name = value.substring(i + 1); 218 } 219 files.add(name); 220 } 221 } 222 finally 223 { 224 _resolver.release(pluginSource); 225 } 226 } 227 } 228 229 // 2 - Convert the files into entries 230 Map<Object, I18nizableText> entries = new HashMap<>(); 231 for (String filename : files) 232 { 233 String s = _path + "/" + filename; 234 235 I18nizableText i18ntext = getEntry(s); 236 if (i18ntext != null) 237 { 238 entries.put(s, i18ntext); 239 } 240 } 241 242 return entries; 243 } 244 245 @Override 246 public I18nizableText getEntry(String value) throws Exception 247 { 248 String valueWithNoExtension = value.substring(0, value.length() - 4); 249 String url = "service:" + _pluginName + "://" + valueWithNoExtension + ".xml"; 250 251 Source source = null; 252 try 253 { 254 source = _resolver.resolveURI(url); 255 if (source.exists()) 256 { 257 String defaultCatalogue = "plugin." + _pluginName; 258 259 if (source instanceof ServiceSourceFactory.ServiceSource) 260 { 261 if (((ServiceSourceFactory.ServiceSource) source).getSourceType() != ServiceSourceFactory.SourceType.PLUGIN) 262 { 263 String skinName = (String) ContextHelper.getRequest(_context).getAttribute("skin"); 264 defaultCatalogue = "skin." + skinName; 265 } 266 } 267 268 try (InputStream inputStream = source.getInputStream()) 269 { 270 Configuration conf = new DefaultConfigurationBuilder().build(inputStream); 271 272 if (!conf.getAttributeAsBoolean("exclude", false)) 273 { 274 Configuration node = conf.getChild("label"); 275 return I18nizableText.parseI18nizableText(node, defaultCatalogue); 276 } 277 else 278 { 279 return null; 280 } 281 } 282 } 283 } 284 catch (IOException e) 285 { 286 // Nothing 287 } 288 finally 289 { 290 _resolver.release(source); 291 } 292 293 int i = valueWithNoExtension.lastIndexOf('/'); 294 String shortFilename = valueWithNoExtension.substring(i + 1); 295 return new I18nizableText(shortFilename); 296 } 297 298 private Set<String> _getPluginOtherXSL (Configuration configuration) 299 { 300 Set<String> otherFiles = new HashSet<>(); 301 302 Configuration[] filesConf = configuration.getChild("values", true).getChildren("value"); 303 for (Configuration fileConf : filesConf) 304 { 305 otherFiles.add(fileConf.getValue(null)); 306 } 307 308 return otherFiles; 309 } 310 311}