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     * @see #getInputStreamForPath(String)
034     * @since 0.2
035     */
036    public 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    }