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.handler.resource;
018
019import org.apache.wicket.core.request.handler.logger.ResourceStreamLogData;
020import org.apache.wicket.request.ILoggableRequestHandler;
021import org.apache.wicket.request.IRequestCycle;
022import org.apache.wicket.request.IRequestHandler;
023import org.apache.wicket.request.resource.ContentDisposition;
024import org.apache.wicket.request.resource.IResource.Attributes;
025import org.apache.wicket.request.resource.ResourceStreamResource;
026import org.apache.wicket.util.lang.Args;
027import org.apache.wicket.util.resource.IResourceStream;
028import org.apache.wicket.util.string.Strings;
029import java.time.Duration;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Request target that responds by sending its resource stream.
035 * 
036 * @author Eelco Hillenius
037 */
038public class ResourceStreamRequestHandler implements IRequestHandler, ILoggableRequestHandler
039{
040        /** Logger */
041        private static final Logger log = LoggerFactory.getLogger(ResourceStreamRequestHandler.class);
042
043        /**
044         * Optional filename, used to set the content disposition header. Only meaningful when using
045         * with web requests.
046         */
047        private String fileName;
048        private ContentDisposition contentDisposition;
049
050        /**
051         * The duration fow which the resource will be cached by the browser.
052         * <p>
053         * By default is {@code null} and
054         * {@link org.apache.wicket.settings.ResourceSettings#getDefaultCacheDuration()} is used.
055         * </p>
056         */
057        private Duration cacheDuration;
058
059        /** the resource stream for the response. */
060        private final IResourceStream resourceStream;
061
062        private ResourceStreamLogData logData;
063
064        /**
065         * Construct.
066         * 
067         * @param resourceStream
068         *            the resource stream for the response
069         */
070        public ResourceStreamRequestHandler(IResourceStream resourceStream)
071        {
072                this(resourceStream, null);
073        }
074
075        /**
076         * Construct.
077         * 
078         * @param resourceStream
079         *            the resource stream for the response
080         * @param fileName
081         */
082        public ResourceStreamRequestHandler(IResourceStream resourceStream, String fileName)
083        {
084                Args.notNull(resourceStream, "resourceStream");
085
086                this.resourceStream = resourceStream;
087                this.fileName = fileName;
088        }
089
090        @Override
091        public void detach(IRequestCycle requestCycle)
092        {
093                if (logData == null)
094                        logData = getResourceStream() == null ? new ResourceStreamLogData(this)
095                                : new ResourceStreamLogData(this, getResourceStream());
096        }
097
098        /** {@inheritDoc} */
099        @Override
100        public ResourceStreamLogData getLogData()
101        {
102                return logData;
103        }
104
105        /**
106         * @return Optional filename, used to set the content disposition header. Only meaningful when
107         *         using with web requests.
108         */
109        public final String getFileName()
110        {
111                return fileName;
112        }
113
114        /**
115         * Gets the resource stream for the response.
116         * 
117         * @return the resource stream for the response
118         */
119        public final IResourceStream getResourceStream()
120        {
121                return resourceStream;
122        }
123
124        /**
125         * Responds by sending the contents of the resource stream.
126         * 
127         * @see org.apache.wicket.request.IRequestHandler#respond(org.apache.wicket.request.IRequestCycle)
128         */
129        @Override
130        public void respond(IRequestCycle requestCycle)
131        {
132                Attributes attributes = new Attributes(requestCycle.getRequest(),
133                        requestCycle.getResponse());
134
135                ResourceStreamResource resource = new ResourceStreamResource(resourceStream);
136
137                configure(resource);
138
139                resource.respond(attributes);
140        }
141
142        /**
143         * Configures the ResourceStreamResource used by this request handler
144         *
145         * @param resource
146         *          the resource to configure
147         */
148        protected void configure(ResourceStreamResource resource)
149        {
150                resource.setFileName(fileName);
151                if (contentDisposition != null)
152                {
153                        resource.setContentDisposition(contentDisposition);
154                }
155                else
156                {
157                        resource.setContentDisposition(Strings.isEmpty(fileName) ? ContentDisposition.INLINE
158                                        : ContentDisposition.ATTACHMENT);
159                }
160
161                final Duration cacheDuration = getCacheDuration();
162                if (cacheDuration != null)
163                {
164                        resource.setCacheDuration(cacheDuration);
165                }
166        }
167
168        @Override
169        public int hashCode()
170        {
171                final int prime = 31;
172                int result = 1;
173                result = prime * result +
174                        ((contentDisposition == null) ? 0 : contentDisposition.hashCode());
175                result = prime * result + ((fileName == null) ? 0 : fileName.hashCode());
176                result = prime * result + resourceStream.hashCode();
177                return result;
178        }
179
180        @Override
181        public boolean equals(Object obj)
182        {
183                if (this == obj)
184                        return true;
185                if (obj == null)
186                        return false;
187                if (getClass() != obj.getClass())
188                        return false;
189                ResourceStreamRequestHandler other = (ResourceStreamRequestHandler)obj;
190                if (contentDisposition != other.contentDisposition)
191                        return false;
192                if (fileName == null)
193                {
194                        if (other.fileName != null)
195                                return false;
196                }
197                else if (!fileName.equals(other.fileName))
198                        return false;
199                if (!resourceStream.equals(other.resourceStream))
200                        return false;
201                return true;
202        }
203
204        /**
205         * @param fileName
206         *            Optional filename, used to set the content disposition header. Only meaningful
207         *            when using with web requests.
208         * 
209         * @return The this.
210         */
211        public final ResourceStreamRequestHandler setFileName(String fileName)
212        {
213                this.fileName = fileName;
214                return this;
215        }
216
217        /**
218         * @see java.lang.Object#toString()
219         */
220        @Override
221        public String toString()
222        {
223                return "[ResourceStreamRequestTarget[resourceStream=" + resourceStream + ",fileName=" +
224                        fileName + ", contentDisposition=" + contentDisposition + "]";
225        }
226
227        /**
228         * @return ContentDisposition
229         */
230        public final ContentDisposition getContentDisposition()
231        {
232                return contentDisposition;
233        }
234
235        /**
236         * @param contentDisposition
237         * @return this
238         */
239        public final ResourceStreamRequestHandler setContentDisposition(
240                ContentDisposition contentDisposition)
241        {
242                this.contentDisposition = contentDisposition;
243                return this;
244        }
245
246        /**
247         * @return the duration for which the resource will be cached by the browser
248         */
249        public Duration getCacheDuration()
250        {
251                return cacheDuration;
252        }
253
254        /**
255         * @param cacheDuration
256         *            the duration for which the resource will be cached by the browser
257         * @return this component
258         */
259        public ResourceStreamRequestHandler setCacheDuration(Duration cacheDuration)
260        {
261                this.cacheDuration = cacheDuration;
262                return this;
263        }
264
265}