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}