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 */ 019package org.apache.shiro.lang.io; 020 021import org.apache.shiro.lang.util.ClassUtils; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025import java.io.FileInputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.net.URL; 029 030/** 031 * Static helper methods for loading {@code Stream}-backed resources. 032 * 033 * @see #getInputStreamForPath(String) 034 * @since 0.2 035 */ 036public final class ResourceUtils { 037 038 /** 039 * Resource path prefix that specifies to load from a classpath location, value is <b>{@code classpath:}</b> 040 */ 041 public static final String CLASSPATH_PREFIX = "classpath:"; 042 /** 043 * Resource path prefix that specifies to load from a url location, value is <b>{@code url:}</b> 044 */ 045 public static final String URL_PREFIX = "url:"; 046 /** 047 * Resource path prefix that specifies to load from a file location, value is <b>{@code file:}</b> 048 */ 049 public static final String FILE_PREFIX = "file:"; 050 051 /** 052 * Private internal log instance. 053 */ 054 private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtils.class); 055 056 /** 057 * Prevent instantiation. 058 */ 059 private ResourceUtils() { 060 } 061 062 /** 063 * Returns {@code true} if the resource path is not null and starts with one of the recognized 064 * resource prefixes ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX}, 065 * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}), {@code false} otherwise. 066 * 067 * @param resourcePath the resource path to check 068 * @return {@code true} if the resource path is not null and starts with one of the recognized 069 * resource prefixes, {@code false} otherwise. 070 * @since 0.9 071 */ 072 @SuppressWarnings({"UnusedDeclaration"}) 073 public static boolean hasResourcePrefix(String resourcePath) { 074 return resourcePath != null 075 && (resourcePath.startsWith(CLASSPATH_PREFIX) 076 || resourcePath.startsWith(URL_PREFIX) || resourcePath.startsWith(FILE_PREFIX)); 077 } 078 079 /** 080 * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise. This 081 * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}. 082 * 083 * @param resourcePath the path of the resource to check. 084 * @return {@code true} if the resource at the specified path exists, {@code false} otherwise. 085 * @since 0.9 086 */ 087 public static boolean resourceExists(String resourcePath) { 088 InputStream stream = null; 089 boolean exists = false; 090 091 try { 092 stream = getInputStreamForPath(resourcePath); 093 exists = true; 094 } catch (IOException e) { 095 stream = null; 096 } finally { 097 if (stream != null) { 098 try { 099 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}