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.source; 017 018import java.io.IOException; 019import java.net.MalformedURLException; 020import java.nio.file.Path; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Map; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import org.apache.avalon.framework.configuration.Configurable; 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.avalon.framework.configuration.ConfigurationException; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.commons.lang.StringUtils; 034import org.apache.excalibur.source.Source; 035import org.apache.excalibur.source.SourceFactory; 036import org.apache.excalibur.source.SourceNotFoundException; 037 038import org.ametys.runtime.plugin.component.AbstractLogEnabled; 039import org.ametys.web.repository.site.SiteManager; 040import org.ametys.web.skin.Skin; 041import org.ametys.web.skin.SkinsManager; 042 043/** 044 * Resolve the protocol skin:// seeking file in skins/{siteConfiguration:skin}/ 045 */ 046public class SkinSourceFactory extends AbstractLogEnabled implements SourceFactory, Serviceable, Configurable 047{ 048 /** The regexp for a protocol */ 049 protected static final Pattern __SOURCE_PATTERN = Pattern.compile("^[\\w-]+(:([^:#]+)?(#([^:]+))?)?://([^?]*)(?:\\?.*)?$"); 050 /** Scheme for skin */ 051 protected static final String __SKIN_SCHEME = "skin"; 052 053 /** The skins manager */ 054 protected SkinsManager _skinsManager; 055 /** The site manager */ 056 protected SiteManager _siteManager; 057 /** Cocoon context */ 058 protected org.apache.cocoon.environment.Context _cocoonContext; 059 060 private ServiceManager _manager; 061 062 private boolean _supportInheritance; 063 064 065 @Override 066 public void service(ServiceManager manager) throws ServiceException 067 { 068 _manager = manager; 069 } 070 071 public void configure(Configuration configuration) throws ConfigurationException 072 { 073 _supportInheritance = configuration.getChild("inheritance").getValueAsBoolean(true); 074 } 075 076 /** 077 * Lookup {@link SiteManager} and {@link SkinsManager} for lazy initialization. 078 */ 079 protected void initializeComponents() 080 { 081 try 082 { 083 if (_siteManager == null) 084 { 085 _siteManager = (SiteManager) _manager.lookup(SiteManager.ROLE); 086 } 087 088 if (_skinsManager == null) 089 { 090 _skinsManager = (SkinsManager) _manager.lookup(SkinsManager.ROLE); 091 } 092 } 093 catch (ServiceException e) 094 { 095 throw new IllegalStateException("Exception while getting components", e); 096 } 097 } 098 099 /** 100 * Return the current skin 101 * @param name the skin name. Can be null or empty 102 * @param defaultValue switch to this value if skin cannot be determined, or if the determined skin does not exist. Can be null 103 * @return the skin. Cannot be null. 104 * @throws SourceNotFoundException If the skin do not exist 105 */ 106 protected Skin getSkin(String name, String defaultValue) throws SourceNotFoundException 107 { 108 String skinName = name; 109 110 if (StringUtils.isEmpty(skinName)) 111 { 112 skinName = _skinsManager.getSkinNameFromRequest(); 113 } 114 115 // Then, try the default value. 116 if (StringUtils.isEmpty(skinName)) 117 { 118 skinName = defaultValue; 119 } 120 121 if (StringUtils.isEmpty(skinName)) 122 { 123 String message = "The current skin name has to be set in current request to use this protocol."; 124 getLogger().info(message); 125 throw new SourceNotFoundException(message); 126 } 127 128 Skin skin = _skinsManager.getSkin(skinName); 129 if (skin == null && defaultValue != null) 130 { 131 skin = _skinsManager.getSkin(defaultValue); 132 } 133 134 if (skin == null) 135 { 136 String message = "The skin '" + skinName + "' currently setup for this site does not exist."; 137 getLogger().info(message); 138 throw new SourceNotFoundException(message); 139 } 140 141 return skin; 142 } 143 144 @Override 145 public Source getSource(String location, Map parameters) throws IOException 146 { 147 // lazy initialization to prevent chicken/egg scenario on startup 148 initializeComponents(); 149 150 Matcher m = __SOURCE_PATTERN.matcher(location); 151 if (!m.matches()) 152 { 153 throw new MalformedURLException("URI must be like protocol:<name>://path/to/resource. Location was '" + location + "'"); 154 } 155 156 Skin skin = getSkin(m.group(2), m.group(4)); 157 158 String fileLocation = m.group(5); 159 160 161 List<Path> paths = new ArrayList<>(); 162 if (_supportInheritance) 163 { 164 for (Skin s : _skinsManager.getSkinAndParents(skin)) 165 { 166 final Path resource = s.getRawPath().resolve(fileLocation); 167 paths.add(resource); 168 } 169 } 170 else 171 { 172 final Path resource = skin.getRawPath().resolve(fileLocation); 173 paths.add(resource); 174 } 175 return new SkinSource(__SKIN_SCHEME, location, paths); 176 } 177 178 public void release(Source source) 179 { 180 // Nothing to do 181 } 182}