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.caching.version;
018
019import java.io.Serializable;
020import java.util.Map;
021import java.util.regex.Pattern;
022
023import org.apache.wicket.MetaDataKey;
024import org.apache.wicket.ThreadContext;
025import org.apache.wicket.request.cycle.RequestCycle;
026import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
027import org.apache.wicket.util.lang.Args;
028import org.apache.wicket.util.lang.Generics;
029
030/**
031 * Caches the results of a delegating {@link IResourceVersion} instance
032 * for the lifetime of the current http request.
033 *
034 * @author Peter Ertl
035 *
036 * @since 1.5
037 */
038public class RequestCycleCachedResourceVersion implements IResourceVersion
039{
040        private static final MetaDataKey<Map<Serializable, String>> CACHE_KEY =
041                new MetaDataKey<>()
042                {
043                        private static final long serialVersionUID = 1L;
044                };
045
046        /**
047         * resource version provider which will actually do 
048         * the hard work and retrieve the version
049         */
050        private final IResourceVersion delegate;
051
052        /**
053         * create request-scoped resource provider cache
054         * 
055         * @param delegate
056         *           resource version provider to cache
057         */
058        public RequestCycleCachedResourceVersion(IResourceVersion delegate)
059        {
060                this.delegate = Args.notNull(delegate, "delegate");
061        }
062
063        @Override
064        public String getVersion(IStaticCacheableResource resource)
065        {
066                // get current request cycle
067                final RequestCycle requestCycle = ThreadContext.getRequestCycle();
068
069                // cache instance
070                Map<Serializable, String> cache = null;
071
072                // cache key
073                Serializable key = null;
074
075                // is request cycle available?
076                if (requestCycle != null)
077                {
078                        // retrieve cache from current request cycle
079                        cache = requestCycle.getMetaData(CACHE_KEY);
080
081                        // create caching key
082                        key = resource.getCacheKey();
083
084                        // does cache exist within current request cycle?
085                        if (cache == null)
086                        {
087                                // no, so create it
088                                requestCycle.setMetaData(CACHE_KEY, cache = Generics.newHashMap());
089                        }
090                        else if (cache.containsKey(key))
091                        {
092                                // lookup timestamp from cache (may contain NULL values which are valid)
093                                return cache.get(key);
094                        }
095                }
096                
097                // no cache entry found, query version from delegate
098                final String version = delegate.getVersion(resource);
099
100                // store value in cache (if it is available)
101                if (cache != null && key != null)
102                {
103                        cache.put(key, version);
104                }
105                
106                return version;
107        }
108
109        @Override
110        public Pattern getVersionPattern()
111        {
112                return delegate.getVersionPattern();
113        }
114}