This project has retired. For details please refer to its Attic page.
URLApplicationResource xref
View Javadoc

1   /*
2    * $Id$
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.tiles.request.locale;
23  
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileNotFoundException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.JarURLConnection;
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  import java.net.URL;
33  import java.net.URLConnection;
34  import java.util.HashSet;
35  import java.util.Locale;
36  import java.util.Set;
37  
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import static java.lang.System.getProperty;
42  import static java.util.Collections.unmodifiableSet;
43  
44  /**
45   * A {@link PostfixedApplicationResource} that can be accessed through a URL.
46   *
47   * @version $Rev$ $Date$
48   */
49  
50  public class URLApplicationResource extends PostfixedApplicationResource {
51      /**
52       * System parameter to specify additional remote protocols. If a url has a remote protocol, then any
53       * {@link IOException} will be thrown directly. If a url has a local protocol, then any {@link IOException}
54       * will be caught and transformed into a {@link FileNotFoundException}.
55       */
56      static final String REMOTE_PROTOCOLS_PROPERTY = "tiles.remoteProtocols";
57      private static final Logger LOG = LoggerFactory.getLogger(URLApplicationResource.class);
58      private static final Set<String> REMOTE_PROTOCOLS;
59  
60      static {
61          REMOTE_PROTOCOLS = initRemoteProtocols();
62      }
63  
64      /**
65       * Creates an unmodifiable set of <em>remote</em> protocols which are used in {@link URL} objects, see {@link URL#getProtocol()}.
66       * A url with a remote protocol establishes a network connection when its {@link URL#openConnection()} is being called.
67       * The set will always contain the built-in remote protocols below:
68       * <ul>
69       *  <li><a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/ftp">ftp</a></li>
70       *  <li><a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/http">http</a></li>
71       *  <li><a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/https">https</a></li>
72       *  <li><a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/mailto">mailto</a></li>
73       *  <li><a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/netdoc">netdoc</a></li>
74       * </ul>
75       * It's possible, that your environment provides additional remote protocols because of following reasons:
76       * <ul>
77       *     <li>your application server adds more remote protocols, see its documentation for further details.</li>
78       *     <li>your application supplies custom remote protocols trough its own {@link java.net.URLStreamHandlerFactory}
79       *     (see following excellent <a href="https://stackoverflow.com/questions/26363573/registering-and-using-a-custom-java-net-url-protocol">explanation</a>
80       *     for getting an idea how to do this)</li>
81       * </ul>
82       * If you need to use such extra remote protocols in Tiles, you may enhance the set via system property {@code tiles.remoteProtocols}. Suppose
83       * you need to add your custom remote protocols "foo" and "bar". To do so, add following parameter to the command line (use ";" as separator):
84       * <pre>
85       *     -Dtiles.remoteProtocols=foo;bar
86       * </pre>
87       * The resulting set will then contain the built-in protocols plus "foo" and "bar".
88       *
89       * @return Unmodifiable set of remote protocols, never {@code null}
90       */
91      static Set<String> initRemoteProtocols() {
92          Set<String> remoteProtocols = new HashSet<String>();
93          remoteProtocols.add("ftp");
94          remoteProtocols.add("http");
95          remoteProtocols.add("https");
96          remoteProtocols.add("mailto");
97          remoteProtocols.add("netdoc");
98  
99          String protocolsProp = getProperty(REMOTE_PROTOCOLS_PROPERTY);
100         if (protocolsProp != null) {
101             for (String protocol : protocolsProp.split(";")) {
102                 remoteProtocols.add(protocol.trim());
103             }
104         }
105         return unmodifiableSet(remoteProtocols);
106     }
107 
108     private static boolean isLocal(URL url) {
109         return !REMOTE_PROTOCOLS.contains(url.getProtocol());
110     }
111 
112     /** the URL where the contents can be found. */
113     private final URL url;
114     /** if the URL matches a file, this is the file. */
115     private File file;
116     /** if the URL points to a local resource */
117     private final boolean local;
118 
119     /**
120      * Creates a URLApplicationResource for the specified path that can be accessed through the specified URL.
121      *
122      * @param localePath the path including localization.
123      * @param url the URL where the contents can be found.
124      */
125     public URLApplicationResource(String localePath, URL url) {
126         super(localePath);
127         this.url = url;
128         if ("file".equals(url.getProtocol())) {
129             file = getFile(url);
130         }
131         local = isLocal(url);
132     }
133 
134     /**
135      * Creates a URLApplicationResource for the specified path that can be accessed through the specified URL.
136      *
137      * @param path the path excluding localization.
138      * @param locale the Locale.
139      * @param url the URL where the contents can be found.
140      */
141     public URLApplicationResource(String path, Locale locale, URL url) {
142         super(path, locale);
143         this.url = url;
144         if ("file".equals(url.getProtocol())) {
145             file = getFile(url);
146         }
147         local = isLocal(url);
148     }
149 
150     private URLConnection openConnection() throws IOException {
151         try {
152             return url.openConnection();
153         } catch (IOException e) {
154             // If the url points to a local resource but it cannot be
155             // opened, then the resource actually does not exist. In this
156             // case throw a FileNotFoundException
157             if (local) {
158                 FileNotFoundException fne = new FileNotFoundException(url.toString());
159                 fne.initCause(e);
160                 throw fne;
161             }
162             throw e;
163         }
164     }
165 
166     private static File getFile(URL url) {
167         try {
168             return new File(new URI(url.toExternalForm()).getSchemeSpecificPart());
169         } catch (URISyntaxException e) {
170             LOG.debug("Cannot translate URL to file name, expect a performance impact", e);
171             return null;
172         }
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public InputStream getInputStream() throws IOException {
178         if (file != null) {
179             return new FileInputStream(file);
180         } else {
181             return openConnection().getInputStream();
182         }
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public long getLastModified() throws IOException {
188         if (file != null) {
189             return file.lastModified();
190         } else {
191             URLConnection connection = openConnection();
192             if (connection instanceof JarURLConnection) {
193                 return ((JarURLConnection) connection).getJarEntry().getTime();
194             } else {
195                 long result = connection.getLastModified();
196                 return result;
197             }
198         }
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public String toString() {
204         return "Resource " + getLocalePath() + " at " + url.toString();
205     }
206 
207     protected URL getURL(){
208         return url;
209     }
210 
211     protected File getFile(){
212         return file;
213     }
214 }