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 /** 119 * Filter a list of sources to return thoses matching the _filter 120 * @param files A non null list of files to filter 121 * @return The non null filtered list of names 122 */ 123 protected List<String> _filterNames(Collection<TraversableSource> files) 124 { 125 List<String> matched = new ArrayList<>(); 126 127 for (TraversableSource f : files) 128 { 129 if (!f.isCollection() && _fileFilter.matcher(f.getName()).matches()) 130 { 131 matched.add(f.getName()); 132 } 133 } 134 135 return matched; 136 } 137 138 @Override 139 public Map<Object, I18nizableText> getEntries() throws Exception 140 { 141 // 1 - Compute the list of files 142 Set<String> files = new HashSet<>(); 143 144 TraversableSource source = null; 145 try 146 { 147 source = (TraversableSource) _resolver.resolveURI("skin://services/" + _pluginName + "/" + _path); 148 if (source.exists()) 149 { 150 if (source.isCollection()) 151 { 152 @SuppressWarnings("unchecked") 153 List<String> sourceFiles = _filterNames(source.getChildren()); 154 files.addAll(sourceFiles); 155 } 156 else 157 { 158 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"); 159 } 160 } 161 } 162 finally 163 { 164 _resolver.release(source); 165 } 166 167 if (_defaultValue != null) 168 { 169 Source pluginSource = null; 170 try 171 { 172 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + _defaultValue); 173 if (pluginSource.exists()) 174 { 175 int i = _defaultValue.lastIndexOf('/'); 176 String name = _defaultValue; 177 if (i > 0) 178 { 179 name = _defaultValue.substring(i + 1); 180 } 181 files.add(name); 182 } 183 } 184 finally 185 { 186 _resolver.release(pluginSource); 187 } 188 } 189 190 if (_values != null) 191 { 192 for (String value : _values) 193 { 194 Source pluginSource = null; 195 try 196 { 197 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + value); 198 if (pluginSource.exists()) 199 { 200 int i = value.lastIndexOf('/'); 201 String name = value; 202 if (i > 0) 203 { 204 name = value.substring(i + 1); 205 } 206 files.add(name); 207 } 208 } 209 finally 210 { 211 _resolver.release(pluginSource); 212 } 213 } 214 } 215 216 // 2 - Convert the files into entries 217 Map<Object, I18nizableText> entries = new HashMap<>(); 218 for (String filename : files) 219 { 220 String s = _path + "/" + filename; 221 222 I18nizableText i18ntext = getEntry(s); 223 if (i18ntext != null) 224 { 225 entries.put(s, i18ntext); 226 } 227 } 228 229 return entries; 230 } 231 232 @Override 233 public I18nizableText getEntry(String value) throws Exception 234 { 235 String valueWithNoExtension = value.substring(0, value.length() - 4); 236 String url = "service:" + _pluginName + "://" + valueWithNoExtension + ".xml"; 237 238 Source source = null; 239 try 240 { 241 source = _resolver.resolveURI(url); 242 if (source.exists()) 243 { 244 String defaultCatalogue = "plugin." + _pluginName; 245 246 if (source instanceof ServiceSourceFactory.ServiceSource) 247 { 248 if (((ServiceSourceFactory.ServiceSource) source).getSourceType() != ServiceSourceFactory.SourceType.PLUGIN) 249 { 250 String skinName = (String) ContextHelper.getRequest(_context).getAttribute("skin"); 251 defaultCatalogue = "skin." + skinName; 252 } 253 } 254 255 try (InputStream inputStream = source.getInputStream()) 256 { 257 Configuration conf = new DefaultConfigurationBuilder().build(inputStream); 258 259 if (!conf.getAttributeAsBoolean("exclude", false)) 260 { 261 Configuration node = conf.getChild("label"); 262 return I18nizableText.parseI18nizableText(node, defaultCatalogue); 263 } 264 else 265 { 266 return null; 267 } 268 } 269 } 270 } 271 catch (IOException e) 272 { 273 // Nothing 274 } 275 finally 276 { 277 _resolver.release(source); 278 } 279 280 int i = valueWithNoExtension.lastIndexOf('/'); 281 String shortFilename = valueWithNoExtension.substring(i + 1); 282 return new I18nizableText(shortFilename); 283 } 284 285 private Set<String> _getPluginOtherXSL (Configuration configuration) 286 { 287 Set<String> otherFiles = new HashSet<>(); 288 289 Configuration[] filesConf = configuration.getChild("values", true).getChildren("value"); 290 for (Configuration fileConf : filesConf) 291 { 292 otherFiles.add(fileConf.getValue(null)); 293 } 294 295 return otherFiles; 296 } 297 298}