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.io; 20 21 import org.apache.shiro.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 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 log = 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) || 77 resourcePath.startsWith(FILE_PREFIX)); 78 } 79 80 /** 81 * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise. This 82 * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}. 83 * 84 * @param resourcePath the path of the resource to check. 85 * @return {@code true} if the resource at the specified path exists, {@code false} otherwise. 86 * @since 0.9 87 */ 88 public static boolean resourceExists(String resourcePath) { 89 InputStream stream = null; 90 boolean exists = false; 91 92 try { 93 stream = getInputStreamForPath(resourcePath); 94 exists = true; 95 } catch (IOException e) { 96 stream = null; 97 } finally { 98 if (stream != null) { 99 try { 100 stream.close(); 101 } catch (IOException ignored) { 102 } 103 } 104 } 105 106 return exists; 107 } 108 109 110 /** 111 * Returns the InputStream for the resource represented by the specified path, supporting scheme 112 * prefixes that direct how to acquire the input stream 113 * ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX}, 114 * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}). If the path is not prefixed by one 115 * of these schemes, the path is assumed to be a file-based path that can be loaded with a 116 * {@link FileInputStream FileInputStream}. 117 * 118 * @param resourcePath the String path representing the resource to obtain. 119 * @return the InputStream for the specified resource. 120 * @throws IOException if there is a problem acquiring the resource at the specified path. 121 */ 122 public static InputStream getInputStreamForPath(String resourcePath) throws IOException { 123 124 InputStream is; 125 if (resourcePath.startsWith(CLASSPATH_PREFIX)) { 126 is = loadFromClassPath(stripPrefix(resourcePath)); 127 128 } else if (resourcePath.startsWith(URL_PREFIX)) { 129 is = loadFromUrl(stripPrefix(resourcePath)); 130 131 } else if (resourcePath.startsWith(FILE_PREFIX)) { 132 is = loadFromFile(stripPrefix(resourcePath)); 133 134 } else { 135 is = loadFromFile(resourcePath); 136 } 137 138 if (is == null) { 139 throw new IOException("Resource [" + resourcePath + "] could not be found."); 140 } 141 142 return is; 143 } 144 145 private static InputStream loadFromFile(String path) throws IOException { 146 if (log.isDebugEnabled()) { 147 log.debug("Opening file [" + path + "]..."); 148 } 149 return new FileInputStream(path); 150 } 151 152 private static InputStream loadFromUrl(String urlPath) throws IOException { 153 log.debug("Opening url {}", urlPath); 154 URL url = new URL(urlPath); 155 return url.openStream(); 156 } 157 158 private static InputStream loadFromClassPath(String path) { 159 log.debug("Opening resource from class path [{}]", path); 160 return ClassUtils.getResourceAsStream(path); 161 } 162 163 private static String stripPrefix(String resourcePath) { 164 return resourcePath.substring(resourcePath.indexOf(":") + 1); 165 } 166 167 /** 168 * Convenience method that closes the specified {@link InputStream InputStream}, logging any 169 * {@link IOException IOException} that might occur. If the {@code InputStream} 170 * argument is {@code null}, this method does nothing. It returns quietly in all cases. 171 * 172 * @param is the {@code InputStream} to close, logging any {@code IOException} that might occur. 173 */ 174 public static void close(InputStream is) { 175 if (is != null) { 176 try { 177 is.close(); 178 } catch (IOException e) { 179 log.warn("Error closing input stream.", e); 180 } 181 } 182 } 183 }