/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.wicket.request.resource; import static org.apache.wicket.util.resource.ResourceUtils.MIN_POSTFIX_DEFAULT_AS_EXTENSION; import java.util.Locale; import java.util.concurrent.ConcurrentMap; import org.apache.wicket.Application; import org.apache.wicket.Session; import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.resource.ResourceUtil; import org.apache.wicket.util.lang.Generics; import org.apache.wicket.util.lang.Packages; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.ResourceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a ResourceReference that knows how to find and serve resources located in the Java * package (i.e. next to the class files). * * @author Tobias Soloschenko */ public class PackageResourceReference extends ResourceReference { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(PackageResourceReference.class); private static final String CSS_EXTENSION = "css"; private static final String JAVASCRIPT_EXTENSION = "js"; private transient ConcurrentMap<UrlAttributes, UrlAttributes> urlAttributesCacheMap; /** * Reads the resource buffered - the content is copied into memory */ private boolean readBuffered = true; /** * Construct. * * @param key */ public PackageResourceReference(final ResourceReference.Key key) { super(key); } /** * Construct. * * @param scope * @param name * @param locale * @param style * @param variation */ public PackageResourceReference(final Class<?> scope, final String name, final Locale locale, final String style, String variation) { super(scope, name, locale, style, variation); } /** * Construct. * * @param scope * @param name */ public PackageResourceReference(final Class<?> scope, final String name) { super(scope, name); } /** * Construct. * * @param name */ public PackageResourceReference(final String name) { super(name); } /** * @see org.apache.wicket.request.resource.ResourceReference#getResource() */ @Override public PackageResource getResource() { final String extension = getExtension(); final PackageResource resource; RequestCycle requestCycle = RequestCycle.get(); UrlAttributes urlAttributes = null; if (requestCycle != null) { //resource attributes (locale, style, variation) might be encoded in the URL final Url url = requestCycle.getRequest().getUrl(); urlAttributes = ResourceUtil.decodeResourceReferenceAttributes(url); } final String currentVariation = getCurrentVariation(urlAttributes); final String currentStyle = getCurrentStyle(urlAttributes); final Locale currentLocale = getCurrentLocale(urlAttributes); final Class<?> scope = getScope(); final String name = getName(); if (CSS_EXTENSION.equals(extension)) { resource = new CssPackageResource(scope, name, currentLocale, currentStyle, currentVariation); } else if (JAVASCRIPT_EXTENSION.equals(extension)) { resource = new JavaScriptPackageResource(scope, name, currentLocale, currentStyle, currentVariation); } else { resource = new PackageResource(scope, name, currentLocale, currentStyle, currentVariation); } resource.readBuffered(readBuffered); removeCompressFlagIfUnnecessary(resource); return resource; } /** * Method allowing to remove the compress flag if the resource has been detected as a minified * one (i.e. ending with .min.EXT) This method is to be called by subclasses overriding * <code>getResource</code> if they want to rely on default minification detection handling * * see WICKET-5250 for further explanation * * @param resource * resource to check */ protected final void removeCompressFlagIfUnnecessary(final PackageResource resource) { String minifiedName = getName(); if (minifiedName != null && minifiedName.contains(MIN_POSTFIX_DEFAULT_AS_EXTENSION)) { resource.setCompress(false); } } private ResourceReference.UrlAttributes getUrlAttributes(Locale locale, String style, String variation) { IResourceStreamLocator locator = Application.get() .getResourceSettings() .getResourceStreamLocator(); String absolutePath = Packages.absolutePath(getScope(), getName()); IResourceStream stream = locator.locate(getScope(), absolutePath, style, variation, locale, null, false); if (stream == null) return new ResourceReference.UrlAttributes(null, null, null); return new ResourceReference.UrlAttributes(stream.getLocale(), stream.getStyle(), stream.getVariation()); } private Locale getCurrentLocale(UrlAttributes attributes) { Locale currentLocale = getCurrentLocale(); return currentLocale != null ? currentLocale : attributes != null ? attributes.getLocale() : null; } private Locale getCurrentLocale() { final Locale locale = getLocale(); if (locale != null) { return locale; } if (Session.exists()) { return Session.get().getLocale(); } return locale; } private String getCurrentStyle(UrlAttributes attributes) { String currentStyle = getCurrentStyle(); return currentStyle != null ? currentStyle : attributes != null ? attributes.getStyle() : null; } private String getCurrentStyle() { final String style = getStyle(); if (style != null) { return style; } if (Session.exists()) { return Session.get().getStyle(); } return style; } private String getCurrentVariation(UrlAttributes attributes) { final String variation = getVariation(); return variation != null ? variation : attributes != null ? attributes.getVariation() : null; } /** * @return How the minified file should be named. */ protected String getMinifiedName() { String name = super.getName(); return ResourceUtils.getMinifiedName(name, ResourceUtils.MIN_POSTFIX_DEFAULT); } @Override public ResourceReference.UrlAttributes getUrlAttributes() { Locale locale = getCurrentLocale(); String style = getCurrentStyle(); String variation = getVariation(); ResourceReference.UrlAttributes key = new ResourceReference.UrlAttributes(locale, style, variation); if (urlAttributesCacheMap == null) { urlAttributesCacheMap = Generics.newConcurrentHashMap(); } ResourceReference.UrlAttributes value = urlAttributesCacheMap.get(key); if (value == null) { value = getUrlAttributes(locale, style, variation); UrlAttributes tmpValue = urlAttributesCacheMap.putIfAbsent(key, value); if (tmpValue != null) { value = tmpValue; } } return value; } /** * If the package resource should be read buffered.<br> * <br> * WARNING - if the stream is not read buffered compressors will not work, because they require the * whole content to be read into memory.<br> * ({@link org.apache.wicket.javascript.IJavaScriptCompressor}, <br> * {@link org.apache.wicket.css.ICssCompressor}, <br> * {@link org.apache.wicket.resource.IScopeAwareTextResourceProcessor}) * * @param readBuffered * if the package resource should be read buffered * @return the current package resource */ public PackageResourceReference readBuffered(boolean readBuffered) { this.readBuffered = readBuffered; return this; } }