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.repository.page; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.util.Date; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 029import org.apache.avalon.framework.logger.AbstractLogEnabled; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.excalibur.source.Source; 034import org.apache.excalibur.source.SourceResolver; 035import org.xml.sax.SAXException; 036 037import org.ametys.web.repository.site.Site; 038import org.ametys.web.repository.site.SiteType; 039import org.ametys.web.repository.site.SiteTypesExtensionPoint; 040import org.ametys.web.service.ServiceExtensionPoint; 041 042/** 043 * This implementation of the services handler is based on services declared in the whole application 044 */ 045public class DefaultServicesAssignmentHandler extends AbstractLogEnabled implements ServicesAssignmentHandler, Serviceable 046{ 047 /** The services manager */ 048 protected ServiceExtensionPoint _serviceEP; 049 /** The site type manager */ 050 protected SiteTypesExtensionPoint _siteTypeExtensionPoint; 051 /** The source resolver */ 052 protected SourceResolver _srcResolver; 053 054 private Map<String, Cache> _cache = new HashMap<>(); 055 056 057 @Override 058 public void service(ServiceManager manager) throws ServiceException 059 { 060 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 061 _siteTypeExtensionPoint = (SiteTypesExtensionPoint) manager.lookup(SiteTypesExtensionPoint.ROLE); 062 _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 063 } 064 065 @Override 066 public Set<String> getAvailableServices(Page page, String zoneName) 067 { 068 Site site = page.getSite(); 069 SiteType siteType = _siteTypeExtensionPoint.getExtension(site.getType()); 070 071 Set<String> services = _getServicesForZone(siteType.getName(), site.getSkinId(), page.getTemplate(), zoneName); 072 if (services != null) 073 { 074 services.retainAll(_getPublicServices()); 075 return services; 076 } 077 078 services = _getServicesForTemplate(siteType.getName(), site.getSkinId(), page.getTemplate()); 079 if (services != null) 080 { 081 services.retainAll(_getPublicServices()); 082 return services; 083 } 084 085 services = _getServicesForSkin(siteType.getName(), site.getSkinId()); 086 if (services != null) 087 { 088 services.retainAll(_getPublicServices()); 089 return services; 090 } 091 092 services = _getServicesForSiteType(siteType.getName()); 093 if (services != null) 094 { 095 services.retainAll(_getPublicServices()); 096 return services; 097 } 098 099 return _getPublicServices(); 100 } 101 102 private Set<String> _getServicesForZone(String siteType, String skinName, String templateName, String zoneName) 103 { 104 String key = siteType + "/" + skinName + "/" + templateName + "/" + zoneName; 105 String file = "context://skins/" + skinName + "/templates/" + templateName + "/conf/services-" + siteType + ".xml"; 106 107 Source configFile = null; 108 try 109 { 110 configFile = _srcResolver.resolveURI(file); 111 if (!configFile.exists()) 112 { 113 return null; 114 } 115 116 Cache data = _cache.get(key); 117 if (data == null || !data.isValid(configFile.getLastModified())) 118 { 119 data = _parseZoneServices(configFile, zoneName); 120 _cache.put(key, data); 121 } 122 return data; 123 124 } 125 catch (IOException e) 126 { 127 getLogger().error("Unable to read the services configuration file", e); 128 return null; 129 } 130 finally 131 { 132 _srcResolver.release(configFile); 133 } 134 } 135 136 private Set<String> _getServicesForTemplate (String siteType, String skinName, String templateName) 137 { 138 String key = siteType + "/" + skinName + "/" + templateName; 139 String file = "context://skins/" + skinName + "/templates/" + templateName + "/conf/services-" + siteType + ".xml"; 140 141 Source configFile = null; 142 try 143 { 144 configFile = _srcResolver.resolveURI(file); 145 if (!configFile.exists()) 146 { 147 return null; 148 } 149 150 Cache data = _cache.get(key); 151 if (data == null || !data.isValid(configFile.getLastModified())) 152 { 153 data = _parseTemplateServices(configFile); 154 _cache.put(key, data); 155 } 156 return data; 157 158 } 159 catch (IOException e) 160 { 161 getLogger().error("Unable to read the services configuration file", e); 162 return null; 163 } 164 finally 165 { 166 _srcResolver.release(configFile); 167 } 168 } 169 170 private Set<String> _getServicesForSkin (String siteType, String skinName) 171 { 172 String key = siteType + "/" + skinName; 173 String file = "context://skins/" + skinName + "/conf/services-" + siteType + ".xml"; 174 175 Source configFile = null; 176 try 177 { 178 configFile = _srcResolver.resolveURI(file); 179 if (!configFile.exists()) 180 { 181 return null; 182 } 183 184 Cache data = _cache.get(key); 185 if (data == null || !data.isValid(configFile.getLastModified())) 186 { 187 data = _parseServices(configFile); 188 _cache.put(key, data); 189 } 190 return data; 191 192 } 193 catch (IOException e) 194 { 195 getLogger().error("Unable to read the services configuration file", e); 196 return null; 197 } 198 finally 199 { 200 _srcResolver.release(configFile); 201 } 202 } 203 204 private Set<String> _getServicesForSiteType (String siteType) 205 { 206 String key = siteType; 207 String file = "context://WEB-INF/param/services-" + siteType + ".xml"; 208 209 Source configFile = null; 210 try 211 { 212 configFile = _srcResolver.resolveURI(file); 213 if (!configFile.exists()) 214 { 215 return null; 216 } 217 218 Cache data = _cache.get(key); 219 if (data == null || !data.isValid(configFile.getLastModified())) 220 { 221 data = _parseServices(configFile); 222 _cache.put(key, data); 223 } 224 return data; 225 226 } 227 catch (IOException e) 228 { 229 getLogger().error("Unable to read the services configuration file", e); 230 return null; 231 } 232 finally 233 { 234 _srcResolver.release(configFile); 235 } 236 } 237 238 /** 239 * Get the public services. 240 * @return the public services. 241 */ 242 protected Cache _getPublicServices() 243 { 244 Cache publicServices = new Cache(new Date().getTime()); 245 246 for (String id : _serviceEP.getExtensionsIds()) 247 { 248 if (!_serviceEP.getExtension(id).isPrivate()) 249 { 250 publicServices.add(id); 251 } 252 } 253 254 return publicServices; 255 } 256 257 /** 258 * Parse a template configuration file. 259 * @param configFile the template configuration file. 260 * @return the set of available services for the template. 261 */ 262 protected Cache _parseTemplateServices(Source configFile) 263 { 264 Cache services = null; 265 266 if (!configFile.exists()) 267 { 268 return null; 269 } 270 271 try (InputStream is = configFile.getInputStream()) 272 { 273 Configuration configuration = new DefaultConfigurationBuilder().build(is); 274 275 Configuration tplConf = configuration.getChild("template", false); 276 if (tplConf != null) 277 { 278 services = _parseServices(tplConf, configFile.getLastModified()); 279 } 280 } 281 catch (IOException e) 282 { 283 getLogger().error("Unable to read the services configuration file", e); 284 return null; 285 } 286 catch (ConfigurationException e) 287 { 288 getLogger().error("Unable to parse the services configuration file", e); 289 return null; 290 } 291 catch (SAXException e) 292 { 293 getLogger().error("Unable parse the services configuration file", e); 294 return null; 295 } 296 297 return services; 298 } 299 300 /** 301 * Parse a template configuration file and get the set of available services for a given zone. 302 * @param configFile the template configuration file. 303 * @param zoneName the zone name. 304 * @return the set of available services for the given zone. 305 */ 306 protected Cache _parseZoneServices(Source configFile, String zoneName) 307 { 308 Cache services = null; 309 310 if (!configFile.exists()) 311 { 312 return null; 313 } 314 315 try (InputStream is = configFile.getInputStream()) 316 { 317 Configuration configuration = new DefaultConfigurationBuilder().build(is); 318 319 Configuration zoneConfs = configuration.getChild("zones", false); 320 if (zoneConfs != null) 321 { 322 for (Configuration zoneConf : zoneConfs.getChildren("zone")) 323 { 324 if (zoneConf.getAttribute("id").equals(zoneName)) 325 { 326 services = _parseServices(zoneConf, configFile.getLastModified()); 327 } 328 } 329 } 330 } 331 catch (IOException e) 332 { 333 getLogger().error("Unable to read the services configuration file", e); 334 return null; 335 } 336 catch (ConfigurationException e) 337 { 338 getLogger().error("Unable to parse the services configuration file", e); 339 return null; 340 } 341 catch (SAXException e) 342 { 343 getLogger().error("Unable parse the services configuration file", e); 344 return null; 345 } 346 347 return services; 348 } 349 350 /** 351 * Parses the valid services for the site type 352 * @param configFile the configuration file. 353 * @return the services id in a Set 354 */ 355 protected Cache _parseServices (Source configFile) 356 { 357 if (!configFile.exists()) 358 { 359 return null; 360 } 361 362 Cache services = null; 363 364 try (InputStream is = configFile.getInputStream()) 365 { 366 Configuration configuration = new DefaultConfigurationBuilder().build(is); 367 368 services = _parseServices(configuration, configFile.getLastModified()); 369 } 370 catch (IOException e) 371 { 372 getLogger().error("Unable to read the services configuration file", e); 373 return null; 374 } 375 catch (ConfigurationException e) 376 { 377 getLogger().error("Unable to parse the services configuration file", e); 378 return null; 379 } 380 catch (SAXException e) 381 { 382 getLogger().error("Unable parse the services configuration file", e); 383 return null; 384 } 385 386 return services; 387 } 388 389 /** 390 * Parses the valid services in a configuration. 391 * @param configuration the configuration. 392 * @param lastModificationDade date de dernière modification 393 * @return the services id in a Set 394 * @throws ConfigurationException if configuration is invalid 395 */ 396 protected Cache _parseServices(Configuration configuration, long lastModificationDade) throws ConfigurationException 397 { 398 Cache services = new Cache(lastModificationDade); 399 400 String mode = configuration.getAttribute("mode", "include"); 401 if ("exclude".equals(mode)) 402 { 403 services = _getPublicServices(); 404 for (Configuration serviceConf : configuration.getChildren("service")) 405 { 406 String id = serviceConf.getAttribute("id"); 407 services.remove(id); 408 } 409 } 410 else 411 { 412 for (Configuration serviceConf : configuration.getChildren("service")) 413 { 414 String id = serviceConf.getAttribute("id"); 415 services.add(id); 416 } 417 } 418 419 return services; 420 } 421 422 /** 423 * The cache is a HashSet of String + a date 424 */ 425 protected static class Cache extends HashSet<String> 426 { 427 private long _sourceLastModified; 428 429 /** 430 * Build the cache 431 * @param sourceLastModified The last modification date 432 */ 433 public Cache(long sourceLastModified) 434 { 435 super(); 436 } 437 438 /** 439 * Determine if the cache is valid 440 * @param newSourceLastModified The new last modification date 441 * @return true if the cache is still valid 442 */ 443 public boolean isValid(long newSourceLastModified) 444 { 445 return newSourceLastModified <= _sourceLastModified; 446 } 447 } 448}