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 }