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 java.io.IOException;
020import java.io.InputStream;
021import java.io.Serializable;
022
023import org.apache.wicket.WicketRuntimeException;
024import org.apache.wicket.core.util.resource.WebExternalResourceStream;
025import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
026import org.apache.wicket.util.io.IOUtils;
027import org.apache.wicket.util.io.Streams;
028import org.apache.wicket.util.lang.Args;
029import org.apache.wicket.util.resource.IResourceStream;
030import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Resource served from a file relative to the context root.
036 * 
037 * @author almaw
038 */
039public class ContextRelativeResource extends AbstractResource implements IStaticCacheableResource
040{
041        private static final String CACHE_PREFIX = "context-relative:/";
042        private static final long serialVersionUID = 1L;
043
044        private static final Logger log = LoggerFactory.getLogger(ContextRelativeResource.class);
045
046        private final String path;
047        private boolean cachingEnabled;
048
049        /**
050         * Construct.
051         * 
052         * @param pathRelativeToContextRoot
053         */
054        public ContextRelativeResource(String pathRelativeToContextRoot)
055        {
056                Args.notNull(pathRelativeToContextRoot, "pathRelativeToContextRoot");
057
058                // Make sure there is a leading '/'.
059                if (!pathRelativeToContextRoot.startsWith("/"))
060                {
061                        pathRelativeToContextRoot = "/" + pathRelativeToContextRoot;
062                }
063                this.path = pathRelativeToContextRoot;
064                this.cachingEnabled = true;
065        }
066
067        @Override
068        public boolean isCachingEnabled()
069        {
070                return cachingEnabled;
071        }
072
073        public void setCachingEnabled(final boolean enabled)
074        {
075                this.cachingEnabled = enabled;
076        }
077
078        @Override
079        public Serializable getCacheKey()
080        {
081                return CACHE_PREFIX + path;
082        }
083
084        @Override
085        public IResourceStream getResourceStream()
086        {
087                return new WebExternalResourceStream(path);
088        }
089        
090        @Override
091        protected ResourceResponse newResourceResponse(final Attributes attributes)
092        {
093                final ResourceResponse resourceResponse = new ResourceResponse();
094
095                final WebExternalResourceStream webExternalResourceStream =
096                        new WebExternalResourceStream(path);
097                resourceResponse.setContentType(webExternalResourceStream.getContentType());
098                resourceResponse.setLastModified(webExternalResourceStream.lastModifiedTime());
099                resourceResponse.setFileName(path);
100                resourceResponse.setWriteCallback(new WriteCallback()
101                {
102                        @Override
103                        public void writeData(final Attributes attributes) throws IOException
104                        {
105                                try
106                                {
107                                        InputStream inputStream = webExternalResourceStream.getInputStream();
108                                        try
109                                        {
110                                                Streams.copy(inputStream, attributes.getResponse().getOutputStream());
111                                        }
112                                        finally {
113                                                IOUtils.closeQuietly(inputStream);
114                                        }
115                                }
116                                catch (ResourceStreamNotFoundException rsnfx)
117                                {
118                                        throw new WicketRuntimeException(rsnfx);
119                                }
120                        }
121                });
122
123                return resourceResponse;
124        }
125
126        @Override
127        public int hashCode()
128        {
129                final int prime = 31;
130                int result = 1;
131                result = prime * result + ((path == null) ? 0 : path.hashCode());
132                return result;
133        }
134
135        @Override
136        public boolean equals(Object obj)
137        {
138                if (this == obj)
139                        return true;
140                if (obj == null)
141                        return false;
142                if (getClass() != obj.getClass())
143                        return false;
144                ContextRelativeResource other = (ContextRelativeResource)obj;
145                if (path == null)
146                {
147                        if (other.path != null)
148                                return false;
149                }
150                else if (!path.equals(other.path))
151                        return false;
152                return true;
153        }
154}