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.io;
020
021import org.apache.shiro.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 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 log = 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) ||
077                        resourcePath.startsWith(FILE_PREFIX));
078    }
079
080    /**
081     * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise.  This
082     * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}.
083     *
084     * @param resourcePath the path of the resource to check.
085     * @return {@code true} if the resource at the specified path exists, {@code false} otherwise.
086     * @since 0.9
087     */
088    public static boolean resourceExists(String resourcePath) {
089        InputStream stream = null;
090        boolean exists = false;
091
092        try {
093            stream = getInputStreamForPath(resourcePath);
094            exists = true;
095        } catch (IOException e) {
096            stream = null;
097        } finally {
098            if (stream != null) {
099                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 InputStraem 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}