001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.request.resource; 018 019import static org.apache.wicket.util.resource.ResourceUtils.MIN_POSTFIX_DEFAULT_AS_EXTENSION; 020 021import java.util.Locale; 022import java.util.concurrent.ConcurrentMap; 023 024import org.apache.wicket.Application; 025import org.apache.wicket.Session; 026import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator; 027import org.apache.wicket.request.Url; 028import org.apache.wicket.request.cycle.RequestCycle; 029import org.apache.wicket.resource.ResourceUtil; 030import org.apache.wicket.util.lang.Generics; 031import org.apache.wicket.util.lang.Packages; 032import org.apache.wicket.util.resource.IResourceStream; 033import org.apache.wicket.util.resource.ResourceUtils; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037/** 038 * This is a ResourceReference that knows how to find and serve resources located in the Java 039 * package (i.e. next to the class files). 040 * 041 * @author Tobias Soloschenko 042 */ 043public class PackageResourceReference extends ResourceReference 044{ 045 private static final long serialVersionUID = 1L; 046 047 private static final Logger log = LoggerFactory.getLogger(PackageResourceReference.class); 048 049 private static final String CSS_EXTENSION = "css"; 050 private static final String JAVASCRIPT_EXTENSION = "js"; 051 052 private transient ConcurrentMap<UrlAttributes, UrlAttributes> urlAttributesCacheMap; 053 054 /** 055 * Reads the resource buffered - the content is copied into memory 056 */ 057 private boolean readBuffered = true; 058 059 /** 060 * Construct. 061 * 062 * @param key 063 */ 064 public PackageResourceReference(final ResourceReference.Key key) 065 { 066 super(key); 067 } 068 069 /** 070 * Construct. 071 * 072 * @param scope 073 * @param name 074 * @param locale 075 * @param style 076 * @param variation 077 */ 078 public PackageResourceReference(final Class<?> scope, final String name, final Locale locale, 079 final String style, String variation) 080 { 081 super(scope, name, locale, style, variation); 082 } 083 084 /** 085 * Construct. 086 * 087 * @param scope 088 * @param name 089 */ 090 public PackageResourceReference(final Class<?> scope, final String name) 091 { 092 super(scope, name); 093 } 094 095 /** 096 * Construct. 097 * 098 * @param name 099 */ 100 public PackageResourceReference(final String name) 101 { 102 super(name); 103 } 104 105 /** 106 * @see org.apache.wicket.request.resource.ResourceReference#getResource() 107 */ 108 @Override 109 public PackageResource getResource() 110 { 111 final String extension = getExtension(); 112 113 final PackageResource resource; 114 115 RequestCycle requestCycle = RequestCycle.get(); 116 UrlAttributes urlAttributes = null; 117 if (requestCycle != null) 118 { 119 //resource attributes (locale, style, variation) might be encoded in the URL 120 final Url url = requestCycle.getRequest().getUrl(); 121 urlAttributes = ResourceUtil.decodeResourceReferenceAttributes(url); 122 } 123 124 final String currentVariation = getCurrentVariation(urlAttributes); 125 final String currentStyle = getCurrentStyle(urlAttributes); 126 final Locale currentLocale = getCurrentLocale(urlAttributes); 127 final Class<?> scope = getScope(); 128 final String name = getName(); 129 130 if (CSS_EXTENSION.equals(extension)) 131 { 132 resource = new CssPackageResource(scope, name, currentLocale, 133 currentStyle, currentVariation); 134 } 135 else if (JAVASCRIPT_EXTENSION.equals(extension)) 136 { 137 resource = new JavaScriptPackageResource(scope, name, currentLocale, 138 currentStyle, currentVariation); 139 } 140 else 141 { 142 resource = new PackageResource(scope, name, currentLocale, 143 currentStyle, currentVariation); 144 } 145 resource.readBuffered(readBuffered); 146 147 removeCompressFlagIfUnnecessary(resource); 148 149 return resource; 150 } 151 152 /** 153 * Method allowing to remove the compress flag if the resource has been detected as a minified 154 * one (i.e. ending with .min.EXT) This method is to be called by subclasses overriding 155 * <code>getResource</code> if they want to rely on default minification detection handling 156 * 157 * see WICKET-5250 for further explanation 158 * 159 * @param resource 160 * resource to check 161 */ 162 protected final void removeCompressFlagIfUnnecessary(final PackageResource resource) 163 { 164 String minifiedName = getName(); 165 if (minifiedName != null && minifiedName.contains(MIN_POSTFIX_DEFAULT_AS_EXTENSION)) 166 { 167 resource.setCompress(false); 168 } 169 } 170 171 private ResourceReference.UrlAttributes getUrlAttributes(Locale locale, String style, 172 String variation) 173 { 174 IResourceStreamLocator locator = Application.get() 175 .getResourceSettings() 176 .getResourceStreamLocator(); 177 178 String absolutePath = Packages.absolutePath(getScope(), getName()); 179 180 IResourceStream stream = locator.locate(getScope(), absolutePath, style, variation, locale, 181 null, false); 182 183 if (stream == null) 184 return new ResourceReference.UrlAttributes(null, null, null); 185 186 return new ResourceReference.UrlAttributes(stream.getLocale(), stream.getStyle(), 187 stream.getVariation()); 188 } 189 190 private Locale getCurrentLocale(UrlAttributes attributes) 191 { 192 Locale currentLocale = getCurrentLocale(); 193 194 return currentLocale != null 195 ? currentLocale 196 : attributes != null 197 ? attributes.getLocale() 198 : null; 199 } 200 201 private Locale getCurrentLocale() 202 { 203 final Locale locale = getLocale(); 204 205 if (locale != null) 206 { 207 return locale; 208 } 209 210 if (Session.exists()) 211 { 212 return Session.get().getLocale(); 213 } 214 215 return locale; 216 } 217 218 private String getCurrentStyle(UrlAttributes attributes) 219 { 220 String currentStyle = getCurrentStyle(); 221 222 return currentStyle != null 223 ? currentStyle 224 : attributes != null 225 ? attributes.getStyle() 226 : null; 227 } 228 229 private String getCurrentStyle() 230 { 231 final String style = getStyle(); 232 233 if (style != null) 234 { 235 return style; 236 } 237 238 if (Session.exists()) 239 { 240 return Session.get().getStyle(); 241 } 242 243 return style; 244 } 245 246 private String getCurrentVariation(UrlAttributes attributes) 247 { 248 final String variation = getVariation(); 249 250 return variation != null 251 ? variation 252 : attributes != null 253 ? attributes.getVariation() 254 : null; 255 } 256 257 /** 258 * @return How the minified file should be named. 259 */ 260 protected String getMinifiedName() 261 { 262 String name = super.getName(); 263 return ResourceUtils.getMinifiedName(name, ResourceUtils.MIN_POSTFIX_DEFAULT); 264 } 265 266 @Override 267 public ResourceReference.UrlAttributes getUrlAttributes() 268 { 269 Locale locale = getCurrentLocale(); 270 String style = getCurrentStyle(); 271 String variation = getVariation(); 272 273 ResourceReference.UrlAttributes key = new ResourceReference.UrlAttributes(locale, style, 274 variation); 275 276 if (urlAttributesCacheMap == null) 277 { 278 urlAttributesCacheMap = Generics.newConcurrentHashMap(); 279 } 280 ResourceReference.UrlAttributes value = urlAttributesCacheMap.get(key); 281 if (value == null) 282 { 283 value = getUrlAttributes(locale, style, variation); 284 UrlAttributes tmpValue = urlAttributesCacheMap.putIfAbsent(key, value); 285 if (tmpValue != null) 286 { 287 value = tmpValue; 288 } 289 } 290 291 return value; 292 } 293 294 /** 295 * If the package resource should be read buffered.<br> 296 * <br> 297 * WARNING - if the stream is not read buffered compressors will not work, because they require the 298 * whole content to be read into memory.<br> 299 * ({@link org.apache.wicket.javascript.IJavaScriptCompressor}, <br> 300 * {@link org.apache.wicket.css.ICssCompressor}, <br> 301 * {@link org.apache.wicket.resource.IScopeAwareTextResourceProcessor}) 302 * 303 * @param readBuffered 304 * if the package resource should be read buffered 305 * @return the current package resource 306 */ 307 public PackageResourceReference readBuffered(boolean readBuffered) 308 { 309 this.readBuffered = readBuffered; 310 return this; 311 } 312}