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.lang.invoke.MethodHandles;
32  import java.net.MalformedURLException;
33  import java.net.URL;
34  import java.net.URLConnection;
35  import java.net.URLStreamHandler;
36  
37  /*
38   * Was copied from MyFaces-Impl, because there is no JSF base class.
39   */
40  public final class Resource {
41  
42    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
43  
44    private Resource() {
45    }
46  
47    /**
48     * Get an URL of an internal resource. First, {@link javax.faces.context.ExternalContext#getResource(String)} is
49     * 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
50     * for a packed war), a URL with a special URL handler is constructed, which can be used for <em>opening</em> a
51     * serlvet resource later. Internally, this special URL handler will call
52     * {@link javax.servlet.ServletContext#getResourceAsStream(String)} when an inputstream is requested.
53     * This works even on Weblogic 8.1
54     *
55     * @param ctx  the faces context from which to retrieve the resource
56     * @param path an URL path
57     * @return an url representing the URL and on which getInputStream() can be called to get the resource
58     */
59    public static URL getResourceUrl(final FacesContext ctx, final String path) throws MalformedURLException {
60      final ExternalContext externalContext = ctx.getExternalContext();
61      URL url = externalContext.getResource(path);
62      if (LOG.isTraceEnabled()) {
63        LOG.trace("Resource-Url from external context: " + url);
64      }
65      if (url == null) {
66        // This might happen on Servlet container which doesnot return
67        // anything
68        // for getResource() (like weblogic 8.1 for packaged wars) we
69        // are trying
70        // to use an own URL protocol in order to use
71        // ServletContext.getResourceAsStream()
72        // when opening the url
73        if (resourceExist(externalContext, path)) {
74          url = getUrlForResourceAsStream(externalContext, path);
75        }
76      }
77      return url;
78    }
79  
80    // This method could be used above to provide a 'fail fast' if a
81    // resource
82    // doesnt exist. Otherwise, the URL will fail on the first access.
83    private static boolean resourceExist(final ExternalContext externalContext, final String path) {
84      if ("/".equals(path)) {
85        // The root context exists always
86        return true;
87      }
88      final Object ctx = externalContext.getContext();
89      if (ctx instanceof ServletContext) {
90        final ServletContext servletContext = (ServletContext) ctx;
91        try (final InputStream stream = servletContext.getResourceAsStream(path)) {
92          return stream != null;
93        } catch (final IOException e) {
94          // Ignore here, since we donnot wanted to read from this
95          // resource anyway
96        }
97      }
98      return false;
99    }
100 
101   // Construct URL with special URLStreamHandler for proxying
102   // ServletContext.getResourceAsStream()
103   private static URL getUrlForResourceAsStream(final ExternalContext externalContext, final String path)
104       throws MalformedURLException {
105     final URLStreamHandler handler = new URLStreamHandler() {
106       @Override
107       protected URLConnection openConnection(final URL u) throws IOException {
108         final String file = u.getFile();
109         return new URLConnection(u) {
110           @Override
111           public void connect() throws IOException {
112           }
113 
114           @Override
115           public InputStream getInputStream() throws IOException {
116             if (LOG.isTraceEnabled()) {
117               LOG.trace("Opening internal url to " + file);
118             }
119             final Object ctx = externalContext.getContext();
120             // Or maybe fetch the external context afresh ?
121             // Object ctx =
122             // FacesContext.getCurrentInstance().getExternalContext().getContext();
123 
124             if (ctx instanceof ServletContext) {
125               final ServletContext servletContext = (ServletContext) ctx;
126               final InputStream stream = servletContext.getResourceAsStream(file);
127               if (stream == null) {
128                 throw new FileNotFoundException("Cannot open resource " + file);
129               }
130               return stream;
131             } else {
132               throw new IOException("Cannot open resource for an context of "
133                   + (ctx != null ? ctx.getClass() : null));
134             }
135           }
136         };
137       }
138     };
139     return new URL("internal", null, 0, path, handler);
140   }
141 }