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}