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: */
017: package org.apache.wicket.util.resource;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.net.HttpURLConnection;
023: import java.net.JarURLConnection;
024: import java.net.URI;
025: import java.net.URL;
026: import java.net.URLConnection;
027:
028: import org.apache.wicket.Application;
029: import org.apache.wicket.protocol.http.WebApplication;
030: import org.apache.wicket.util.time.Time;
031: import org.slf4j.Logger;
032: import org.slf4j.LoggerFactory;
033:
034: /**
035: * UrlResourceStream implements IResource for URLs.
036: *
037: * @see org.apache.wicket.util.resource.IResourceStream
038: * @see org.apache.wicket.util.watch.IModifiable
039: * @author Jonathan Locke
040: */
041: public class UrlResourceStream extends AbstractResourceStream implements
042: IFixedLocationResourceStream {
043: private static final long serialVersionUID = 1L;
044:
045: /** Logging. */
046: private static final Logger log = LoggerFactory
047: .getLogger(UrlResourceStream.class);
048:
049: /** Resource stream. */
050: private transient InputStream inputStream;
051:
052: /** The URL to this resource. */
053: private final URL url;
054:
055: /**
056: * the handle to the file if it is a file resource
057: */
058: private File file;
059:
060: /** Length of stream. */
061: private int contentLength;
062:
063: /** Content type for stream. */
064: private String contentType;
065:
066: /** Last known time the stream was last modified. */
067: private long lastModified;
068:
069: /**
070: * Construct.
071: *
072: * @param url
073: * URL of resource
074: */
075: public UrlResourceStream(final URL url) {
076: // Save URL
077: this .url = url;
078: URLConnection connection = null;
079: try {
080: connection = url.openConnection();
081: contentLength = connection.getContentLength();
082: contentType = connection.getContentType();
083: lastModified = connection.getLastModified();
084: try {
085: file = new File(new URI(url.toExternalForm()));
086: } catch (Exception ex) {
087: log
088: .debug("cannot convert url: "
089: + url
090: + " to file ("
091: + ex.getMessage()
092: + "), falling back to the inputstream for polling");
093: }
094: if (file != null && !file.exists()) {
095: file = null;
096: }
097: } catch (IOException ex) {
098: // It should be impossible to get here or the original URL
099: // couldn't have been constructed. But we re-throw with details
100: // anyway.
101: final IllegalArgumentException illegalArgumentException = new IllegalArgumentException(
102: "Invalid URL parameter " + url);
103: illegalArgumentException.initCause(ex);
104: throw illegalArgumentException;
105: } finally {
106: // if applicable, disconnect
107: if (connection != null) {
108: if (connection instanceof HttpURLConnection) {
109: ((HttpURLConnection) connection).disconnect();
110: } else {
111: try {
112: connection.getInputStream().close();
113: } catch (Exception ex) {
114: // ignore
115: }
116: }
117: }
118: }
119: }
120:
121: /**
122: * Closes this resource.
123: *
124: * @throws IOException
125: */
126: public void close() throws IOException {
127: if (inputStream != null) {
128: inputStream.close();
129: inputStream = null;
130: }
131: }
132:
133: /**
134: * @return The content type of this resource, such as "image/jpeg" or
135: * "text/html"
136: */
137: public String getContentType() {
138: testContentType();
139: return contentType;
140: }
141:
142: /**
143: * Method to test the content type on null or unknown. if this is the case
144: * the content type is tried to be resolved throw the servlet context
145: */
146: private void testContentType() {
147: if (contentType == null || contentType.indexOf("unknown") != -1) {
148: Application application = Application.get();
149: if (application instanceof WebApplication) {
150: // TODO Post 1.2: General: For non webapplication another method
151: // should be implemented (getMimeType on application?)
152: contentType = ((WebApplication) application)
153: .getServletContext().getMimeType(url.getFile());
154: if (contentType == null) {
155: contentType = URLConnection.getFileNameMap()
156: .getContentTypeFor(url.getFile());
157: }
158: } else {
159: contentType = URLConnection.getFileNameMap()
160: .getContentTypeFor(url.getFile());
161: }
162: }
163: }
164:
165: /**
166: * @return A readable input stream for this resource.
167: * @throws ResourceStreamNotFoundException
168: */
169: public InputStream getInputStream()
170: throws ResourceStreamNotFoundException {
171: if (inputStream == null) {
172: try {
173: inputStream = url.openStream();
174: } catch (IOException e) {
175: throw new ResourceStreamNotFoundException("Resource "
176: + url + " could not be opened", e);
177: }
178: }
179:
180: return inputStream;
181: }
182:
183: /**
184: * @return The URL to this resource (if any)
185: */
186: public URL getURL() {
187: return url;
188: }
189:
190: /**
191: * @see org.apache.wicket.util.watch.IModifiable#lastModifiedTime()
192: * @return The last time this resource was modified
193: */
194: public Time lastModifiedTime() {
195: if (file != null) {
196: long lastModified = file.lastModified();
197: if (lastModified != this .lastModified) {
198: this .lastModified = lastModified;
199: this .contentLength = (int) file.length();
200: }
201: } else {
202: URLConnection urlConnection = null;
203: boolean close = false;
204: try {
205: urlConnection = url.openConnection();
206: long lastModified = this .lastModified;
207: if (urlConnection instanceof JarURLConnection) {
208: JarURLConnection jarUrlConnection = (JarURLConnection) urlConnection;
209: URL jarFileUrl = jarUrlConnection.getJarFileURL();
210: URLConnection jarFileConnection = jarFileUrl
211: .openConnection();
212: try {
213: lastModified = jarFileConnection
214: .getLastModified();
215: } finally {
216: jarFileConnection.getInputStream().close();
217: }
218: } else {
219: close = true;
220: lastModified = urlConnection.getLastModified();
221: }
222: // update the last modified time.
223: if (lastModified != this .lastModified) {
224: this .lastModified = lastModified;
225: close = true;
226: this .contentLength = urlConnection
227: .getContentLength();
228: }
229: } catch (IOException e) {
230: log.error("getLastModified for " + url + " failed: "
231: + e.getMessage());
232: } finally {
233: // if applicable, disconnect
234: if (urlConnection != null) {
235: if (urlConnection instanceof HttpURLConnection) {
236: ((HttpURLConnection) urlConnection)
237: .disconnect();
238: } else if (close) {
239: try {
240: urlConnection.getInputStream().close();
241: } catch (Exception ex) {
242: // ignore
243: }
244: }
245: }
246: }
247: }
248: return Time.milliseconds(lastModified);
249: }
250:
251: /**
252: * @see java.lang.Object#toString()
253: */
254: public String toString() {
255: return url.toString();
256: }
257:
258: /**
259: * @see org.apache.wicket.util.resource.IResourceStream#length()
260: */
261: public long length() {
262: return contentLength;
263: }
264:
265: /**
266: * @see org.apache.wicket.util.resource.IFixedLocationResourceStream#locationAsString()
267: */
268: public String locationAsString() {
269: return url.toExternalForm();
270: }
271: }
|