View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.facelets;
21  
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import javax.faces.context.ExternalContext;
26  import javax.faces.context.FacesContext;
27  import javax.servlet.ServletContext;
28  import java.io.FileNotFoundException;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.net.MalformedURLException;
32  import java.net.URL;
33  import java.net.URLConnection;
34  import java.net.URLStreamHandler;
35  
36  /*
37   * Was copied from MyFaces-Impl, because there is no JSF base class.
38   */
39  public final class Resource {
40  
41    private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
42  
43    private Resource() {
44    }
45  
46    /**
47     * Get an URL of an internal resource. First, {@link javax.faces.context.ExternalContext#getResource(String)} is
48     * checked for an non-null URL return value. In the case of a null return value (as it is the case for Weblogic 8.1
49     * for a packed war), a URL with a special URL handler is constructed, which can be used for <em>opening</em> a
50     * serlvet resource later. Internally, this special URL handler will call
51     * {@link javax.servlet.ServletContext#getResourceAsStream(String)} when an inputstream is requested.
52     * This works even on Weblogic 8.1
53     *
54     * @param ctx  the faces context from which to retrieve the resource
55     * @param path an URL path
56     * @return an url representing the URL and on which getInputStream() can be called to get the resource
57     */
58    public static URL getResourceUrl(final FacesContext ctx, final String path) throws MalformedURLException {
59      final ExternalContext externalContext = ctx.getExternalContext();
60      URL url = externalContext.getResource(path);
61      if (LOG.isTraceEnabled()) {
62        LOG.trace("Resource-Url from external context: " + url);
63      }
64      if (url == null) {
65        // This might happen on Servlet container which doesnot return
66        // anything
67        // for getResource() (like weblogic 8.1 for packaged wars) we
68        // are trying
69        // to use an own URL protocol in order to use
70        // ServletContext.getResourceAsStream()
71        // when opening the url
72        if (resourceExist(externalContext, path)) {
73          url = getUrlForResourceAsStream(externalContext, path);
74        }
75      }
76      return url;
77    }
78  
79    // This method could be used above to provide a 'fail fast' if a
80    // resource
81    // doesnt exist. Otherwise, the URL will fail on the first access.
82    private static boolean resourceExist(final ExternalContext externalContext, final String path) {
83      if ("/".equals(path)) {
84        // The root context exists always
85        return true;
86      }
87      final Object ctx = externalContext.getContext();
88      if (ctx instanceof ServletContext) {
89        final ServletContext servletContext = (ServletContext) ctx;
90        final InputStream stream = servletContext.getResourceAsStream(path);
91        if (stream != null) {
92          try {
93            stream.close();
94          } catch (final IOException e) {
95            // Ignore here, since we donnot wanted to read from this
96            // resource anyway
97          }
98          return true;
99        }
100     }
101     return false;
102   }
103 
104   // Construct URL with special URLStreamHandler for proxying
105   // ServletContext.getResourceAsStream()
106   private static URL getUrlForResourceAsStream(final ExternalContext externalContext, final String path)
107       throws MalformedURLException {
108     final URLStreamHandler handler = new URLStreamHandler() {
109       @Override
110       protected URLConnection openConnection(final URL u) throws IOException {
111         final String file = u.getFile();
112         return new URLConnection(u) {
113           @Override
114           public void connect() throws IOException {
115           }
116 
117           @Override
118           public InputStream getInputStream() throws IOException {
119             if (LOG.isTraceEnabled()) {
120               LOG.trace("Opening internal url to " + file);
121             }
122             final Object ctx = externalContext.getContext();
123             // Or maybe fetch the external context afresh ?
124             // Object ctx =
125             // FacesContext.getCurrentInstance().getExternalContext().getContext();
126 
127             if (ctx instanceof ServletContext) {
128               final ServletContext servletContext = (ServletContext) ctx;
129               final InputStream stream = servletContext.getResourceAsStream(file);
130               if (stream == null) {
131                 throw new FileNotFoundException("Cannot open resource " + file);
132               }
133               return stream;
134             } else {
135               throw new IOException("Cannot open resource for an context of "
136                   + (ctx != null ? ctx.getClass() : null));
137             }
138           }
139         };
140       }
141     };
142     return new URL("internal", null, 0, path, handler);
143   }
144 }