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.net.URLConnection;
020import java.time.Instant;
021import javax.servlet.http.HttpServletResponse;
022import org.apache.wicket.request.Response;
023
024/**
025 * An abstract resource that can deliver static data - passed to the constructor, or dynamic -
026 * returned by {@link #getData(org.apache.wicket.request.resource.IResource.Attributes)}
027 *
028 * @param <T>
029 *            The type of the data this resource can deliver
030 */
031public abstract class BaseDataResource<T> extends AbstractResource
032{
033        private static final long serialVersionUID = 1L;
034
035        /** the content type */
036        private final String contentType;
037
038        /** the data to deliver */
039        private T data;
040
041        /** the time that this resource was last modified; same as construction time. */
042        private final Instant lastModified = Instant.now();
043
044        private final String filename;
045
046        /**
047         * Creates a {@link org.apache.wicket.request.resource.BaseDataResource} which will provide its
048         * data dynamically with
049         * {@link #getData(org.apache.wicket.request.resource.IResource.Attributes)}
050         *
051         * @param contentType
052         *            The Content type of the array.
053         */
054        public BaseDataResource(final String contentType)
055        {
056                this(contentType, null, null);
057        }
058
059        /**
060         * Creates a Resource from the given data with its content type
061         *
062         * @param contentType
063         *            The Content type of the array.
064         * @param data
065         *            The data
066         */
067        public BaseDataResource(final String contentType, final T data)
068        {
069                this(contentType, data, null);
070        }
071
072        /**
073         * Creates a Resource from the given data with its content type and filename
074         *
075         * @param contentType
076         *            The Content type of the array.
077         * @param data
078         *            The data
079         * @param filename
080         *            The filename that will be set as the Content-Disposition header.
081         */
082        public BaseDataResource(final String contentType, final T data, final String filename)
083        {
084                this.contentType = contentType;
085                this.data = data;
086                this.filename = filename;
087        }
088
089        /**
090         * Post-configures the given response, e.g. set/override response headers.
091         *
092         * @param response
093         *            The response to configure
094         * @param attributes
095         *            The request attributes (web request, web response, parameters)
096         */
097        protected void configureResponse(final ResourceResponse response, final Attributes attributes)
098        {
099        }
100
101        @Override
102        protected ResourceResponse newResourceResponse(final Attributes attributes)
103        {
104                final ResourceResponse response = new ResourceResponse();
105
106                String contentType = this.contentType;
107                String filename = getFilename();
108
109                if (contentType == null)
110                {
111                        if (filename != null)
112                        {
113                                contentType = URLConnection.getFileNameMap().getContentTypeFor(filename);
114                        }
115
116                        if (contentType == null)
117                        {
118                                contentType = "application/octet-stream";
119                        }
120                }
121
122                response.setContentType(contentType);
123                response.setLastModified(lastModified);
124
125                final T data = getData(attributes);
126                if (data == null)
127                {
128                        response.setError(HttpServletResponse.SC_NOT_FOUND);
129                }
130                else
131                {
132                        Long length = getLength(data);
133                        if (length != null)
134                        {
135                                response.setContentLength(length);
136                        }
137
138                        if (response.dataNeedsToBeWritten(attributes))
139                        {
140                                if (filename != null)
141                                {
142                                        response.setFileName(filename);
143                                        response.setContentDisposition(ContentDisposition.ATTACHMENT);
144                                }
145                                else
146                                {
147                                        response.setContentDisposition(ContentDisposition.INLINE);
148                                }
149
150                                response.setWriteCallback(new WriteCallback()
151                                {
152                                        @Override
153                                        public void writeData(final Attributes attributes)
154                                        {
155                                                BaseDataResource.this.writeData(attributes.getResponse(), data);
156                                        }
157                                });
158
159                                configureResponse(response, attributes);
160                        }
161                }
162
163                return response;
164        }
165
166        /**
167         * Writes the given data to the response
168         * 
169         * @param response
170         *            The response to write to
171         * @param data
172         *            The data to write
173         */
174        protected abstract void writeData(Response response, T data);
175
176        /**
177         * @param data
178         *            The data to be written
179         * @return The length of the data to be written. Used to set "Content-Length" response header
180         */
181        protected abstract Long getLength(T data);
182
183        /**
184         * Gets the data for this resource.
185         * 
186         * @param attributes
187         *            the context bringing the request, response and the parameters
188         * 
189         * @return The data for this resource
190         */
191        protected T getData(final Attributes attributes)
192        {
193                return data;
194        }
195
196        /**
197         * Returns the filename that will be set as the Content-Disposition header.
198         *
199         * @return the filename
200         */
201        protected String getFilename()
202        {
203                return filename;
204        }
205}