001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.io;
020
021 import org.apache.shiro.util.ClassUtils;
022 import org.slf4j.Logger;
023 import org.slf4j.LoggerFactory;
024
025 import java.io.FileInputStream;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.net.URL;
029
030 /**
031 * Static helper methods for loading {@code Stream}-backed resources.
032 *
033 * @author Jeremy Haile
034 * @author Les Hazlewood
035 * @see #getInputStreamForPath(String)
036 * @since 0.2
037 */
038 public class ResourceUtils {
039
040 /**
041 * Resource path prefix that specifies to load from a classpath location, value is <b>{@code classpath:}</b>
042 */
043 public static final String CLASSPATH_PREFIX = "classpath:";
044 /**
045 * Resource path prefix that specifies to load from a url location, value is <b>{@code url:}</b>
046 */
047 public static final String URL_PREFIX = "url:";
048 /**
049 * Resource path prefix that specifies to load from a file location, value is <b>{@code file:}</b>
050 */
051 public static final String FILE_PREFIX = "file:";
052
053 /**
054 * Private internal log instance.
055 */
056 private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
057
058 /**
059 * Prevent instantiation.
060 */
061 private ResourceUtils() {
062 }
063
064 /**
065 * Returns {@code true} if the resource path is not null and starts with one of the recognized
066 * resource prefixes ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
067 * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}), {@code false} otherwise.
068 *
069 * @param resourcePath the resource path to check
070 * @return {@code true} if the resource path is not null and starts with one of the recognized
071 * resource prefixes, {@code false} otherwise.
072 * @since 0.9
073 */
074 @SuppressWarnings({"UnusedDeclaration"})
075 public static boolean hasResourcePrefix(String resourcePath) {
076 return resourcePath != null &&
077 (resourcePath.startsWith(CLASSPATH_PREFIX) ||
078 resourcePath.startsWith(URL_PREFIX) ||
079 resourcePath.startsWith(FILE_PREFIX));
080 }
081
082 /**
083 * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise. This
084 * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}.
085 *
086 * @param resourcePath the path of the resource to check.
087 * @return {@code true} if the resource at the specified path exists, {@code false} otherwise.
088 * @since 0.9
089 */
090 public static boolean resourceExists(String resourcePath) {
091 InputStream stream = null;
092 boolean exists = false;
093
094 try {
095 stream = getInputStreamForPath(resourcePath);
096 exists = true;
097 } catch (IOException e) {
098 stream = null;
099 } finally {
100 if (stream != null) {
101 try {
102 stream.close();
103 } catch (IOException ignored) {
104 }
105 }
106 }
107
108 return exists;
109 }
110
111
112 /**
113 * Returns the InputStream for the resource represented by the specified path, supporting scheme
114 * prefixes that direct how to acquire the input stream
115 * ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
116 * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}). If the path is not prefixed by one
117 * of these schemes, the path is assumed to be a file-based path that can be loaded with a
118 * {@link FileInputStream FileInputStream}.
119 *
120 * @param resourcePath the String path representing the resource to obtain.
121 * @return the InputStraem for the specified resource.
122 * @throws IOException if there is a problem acquiring the resource at the specified path.
123 */
124 public static InputStream getInputStreamForPath(String resourcePath) throws IOException {
125
126 InputStream is;
127 if (resourcePath.startsWith(CLASSPATH_PREFIX)) {
128 is = loadFromClassPath(stripPrefix(resourcePath));
129
130 } else if (resourcePath.startsWith(URL_PREFIX)) {
131 is = loadFromUrl(stripPrefix(resourcePath));
132
133 } else if (resourcePath.startsWith(FILE_PREFIX)) {
134 is = loadFromFile(stripPrefix(resourcePath));
135
136 } else {
137 is = loadFromFile(resourcePath);
138 }
139
140 if (is == null) {
141 throw new IOException("Resource [" + resourcePath + "] could not be found.");
142 }
143
144 return is;
145 }
146
147 private static InputStream loadFromFile(String path) throws IOException {
148 if (log.isDebugEnabled()) {
149 log.debug("Opening file [" + path + "]...");
150 }
151 return new FileInputStream(path);
152 }
153
154 private static InputStream loadFromUrl(String urlPath) throws IOException {
155 log.debug("Opening url {}", urlPath);
156 URL url = new URL(urlPath);
157 return url.openStream();
158 }
159
160 private static InputStream loadFromClassPath(String path) {
161 log.debug("Opening resource from class path [{}]", path);
162 return ClassUtils.getResourceAsStream(path);
163 }
164
165 private static String stripPrefix(String resourcePath) {
166 return resourcePath.substring(resourcePath.indexOf(":") + 1);
167 }
168
169 /**
170 * Convenience method that closes the specified {@link InputStream InputStream}, logging any
171 * {@link IOException IOException} that might occur. If the {@code InputStream}
172 * argument is {@code null}, this method does nothing. It returns quietly in all cases.
173 *
174 * @param is the {@code InputStream} to close, logging any {@code IOException} that might occur.
175 */
176 public static void close(InputStream is) {
177 if (is != null) {
178 try {
179 is.close();
180 } catch (IOException e) {
181 log.warn("Error closing input stream.", e);
182 }
183 }
184 }
185 }