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