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  package org.apache.shiro.lang.io;
20  
21  import org.apache.shiro.lang.util.ClassUtils;
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.net.URL;
29  
30  /**
31   * Static helper methods for loading {@code Stream}-backed resources.
32   *
33   * @see #getInputStreamForPath(String)
34   * @since 0.2
35   */
36  public final class ResourceUtils {
37  
38      /**
39       * Resource path prefix that specifies to load from a classpath location, value is <b>{@code classpath:}</b>
40       */
41      public static final String CLASSPATH_PREFIX = "classpath:";
42      /**
43       * Resource path prefix that specifies to load from a url location, value is <b>{@code url:}</b>
44       */
45      public static final String URL_PREFIX = "url:";
46      /**
47       * Resource path prefix that specifies to load from a file location, value is <b>{@code file:}</b>
48       */
49      public static final String FILE_PREFIX = "file:";
50  
51      /**
52       * Private internal log instance.
53       */
54      private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtils.class);
55  
56      /**
57       * Prevent instantiation.
58       */
59      private ResourceUtils() {
60      }
61  
62      /**
63       * Returns {@code true} if the resource path is not null and starts with one of the recognized
64       * resource prefixes ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
65       * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}), {@code false} otherwise.
66       *
67       * @param resourcePath the resource path to check
68       * @return {@code true} if the resource path is not null and starts with one of the recognized
69       * resource prefixes, {@code false} otherwise.
70       * @since 0.9
71       */
72      @SuppressWarnings({"UnusedDeclaration"})
73      public static boolean hasResourcePrefix(String resourcePath) {
74          return resourcePath != null
75                  && (resourcePath.startsWith(CLASSPATH_PREFIX)
76                  || resourcePath.startsWith(URL_PREFIX) || resourcePath.startsWith(FILE_PREFIX));
77      }
78  
79      /**
80       * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise.  This
81       * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}.
82       *
83       * @param resourcePath the path of the resource to check.
84       * @return {@code true} if the resource at the specified path exists, {@code false} otherwise.
85       * @since 0.9
86       */
87      public static boolean resourceExists(String resourcePath) {
88          InputStream stream = null;
89          boolean exists = false;
90  
91          try {
92              stream = getInputStreamForPath(resourcePath);
93              exists = true;
94          } catch (IOException e) {
95              stream = null;
96          } finally {
97              if (stream != null) {
98                  try {
99                      stream.close();
100                 } catch (IOException ignored) {
101                 }
102             }
103         }
104 
105         return exists;
106     }
107 
108 
109     /**
110      * Returns the InputStream for the resource represented by the specified path, supporting scheme
111      * prefixes that direct how to acquire the input stream
112      * ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
113      * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}).  If the path is not prefixed by one
114      * of these schemes, the path is assumed to be a file-based path that can be loaded with a
115      * {@link FileInputStream FileInputStream}.
116      *
117      * @param resourcePath the String path representing the resource to obtain.
118      * @return the InputStream for the specified resource.
119      * @throws IOException if there is a problem acquiring the resource at the specified path.
120      */
121     public static InputStream getInputStreamForPath(String resourcePath) throws IOException {
122 
123         InputStream is;
124         if (resourcePath.startsWith(CLASSPATH_PREFIX)) {
125             is = loadFromClassPath(stripPrefix(resourcePath));
126 
127         } else if (resourcePath.startsWith(URL_PREFIX)) {
128             is = loadFromUrl(stripPrefix(resourcePath));
129 
130         } else if (resourcePath.startsWith(FILE_PREFIX)) {
131             is = loadFromFile(stripPrefix(resourcePath));
132 
133         } else {
134             is = loadFromFile(resourcePath);
135         }
136 
137         if (is == null) {
138             throw new IOException("Resource [" + resourcePath + "] could not be found.");
139         }
140 
141         return is;
142     }
143 
144     private static InputStream loadFromFile(String path) throws IOException {
145         if (LOGGER.isDebugEnabled()) {
146             LOGGER.debug("Opening file [" + path + "]...");
147         }
148         return new FileInputStream(path);
149     }
150 
151     private static InputStream loadFromUrl(String urlPath) throws IOException {
152         LOGGER.debug("Opening url {}", urlPath);
153         URL url = new URL(urlPath);
154         return url.openStream();
155     }
156 
157     private static InputStream loadFromClassPath(String path) {
158         LOGGER.debug("Opening resource from class path [{}]", path);
159         return ClassUtils.getResourceAsStream(path);
160     }
161 
162     private static String stripPrefix(String resourcePath) {
163         return resourcePath.substring(resourcePath.indexOf(":") + 1);
164     }
165 
166     /**
167      * Convenience method that closes the specified {@link InputStream InputStream}, logging any
168      * {@link IOException IOException} that might occur. If the {@code InputStream}
169      * argument is {@code null}, this method does nothing.  It returns quietly in all cases.
170      *
171      * @param is the {@code InputStream} to close, logging any {@code IOException} that might occur.
172      */
173     public static void close(InputStream is) {
174         if (is != null) {
175             try {
176                 is.close();
177             } catch (IOException e) {
178                 LOGGER.warn("Error closing input stream.", e);
179             }
180         }
181     }
182 }