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     * @author Jeremy Haile
034     * @author Les Hazlewood
035     * @see #getInputStreamForPath(String)
036     * @since 0.2
037     */
038    public class ResourceUtils {
039    
040        /**
041         * Resource path prefix that specifies to load from a classpath location, value is <b>{@code classpath:}</b>
042         */
043        public static final String CLASSPATH_PREFIX = "classpath:";
044        /**
045         * Resource path prefix that specifies to load from a url location, value is <b>{@code url:}</b>
046         */
047        public static final String URL_PREFIX = "url:";
048        /**
049         * Resource path prefix that specifies to load from a file location, value is <b>{@code file:}</b>
050         */
051        public static final String FILE_PREFIX = "file:";
052    
053        /**
054         * Private internal log instance.
055         */
056        private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
057    
058        /**
059         * Prevent instantiation.
060         */
061        private ResourceUtils() {
062        }
063    
064        /**
065         * Returns {@code true} if the resource path is not null and starts with one of the recognized
066         * resource prefixes ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
067         * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}), {@code false} otherwise.
068         *
069         * @param resourcePath the resource path to check
070         * @return {@code true} if the resource path is not null and starts with one of the recognized
071         *         resource prefixes, {@code false} otherwise.
072         * @since 0.9
073         */
074        @SuppressWarnings({"UnusedDeclaration"})
075        public static boolean hasResourcePrefix(String resourcePath) {
076            return resourcePath != null &&
077                    (resourcePath.startsWith(CLASSPATH_PREFIX) ||
078                            resourcePath.startsWith(URL_PREFIX) ||
079                            resourcePath.startsWith(FILE_PREFIX));
080        }
081    
082        /**
083         * Returns {@code true} if the resource at the specified path exists, {@code false} otherwise.  This
084         * method supports scheme prefixes on the path as defined in {@link #getInputStreamForPath(String)}.
085         *
086         * @param resourcePath the path of the resource to check.
087         * @return {@code true} if the resource at the specified path exists, {@code false} otherwise.
088         * @since 0.9
089         */
090        public static boolean resourceExists(String resourcePath) {
091            InputStream stream = null;
092            boolean exists = false;
093    
094            try {
095                stream = getInputStreamForPath(resourcePath);
096                exists = true;
097            } catch (IOException e) {
098                stream = null;
099            } finally {
100                if (stream != null) {
101                    try {
102                        stream.close();
103                    } catch (IOException ignored) {
104                    }
105                }
106            }
107    
108            return exists;
109        }
110    
111    
112        /**
113         * Returns the InputStream for the resource represented by the specified path, supporting scheme
114         * prefixes that direct how to acquire the input stream
115         * ({@link #CLASSPATH_PREFIX CLASSPATH_PREFIX},
116         * {@link #URL_PREFIX URL_PREFIX}, or {@link #FILE_PREFIX FILE_PREFIX}).  If the path is not prefixed by one
117         * of these schemes, the path is assumed to be a file-based path that can be loaded with a
118         * {@link FileInputStream FileInputStream}.
119         *
120         * @param resourcePath the String path representing the resource to obtain.
121         * @return the InputStraem for the specified resource.
122         * @throws IOException if there is a problem acquiring the resource at the specified path.
123         */
124        public static InputStream getInputStreamForPath(String resourcePath) throws IOException {
125    
126            InputStream is;
127            if (resourcePath.startsWith(CLASSPATH_PREFIX)) {
128                is = loadFromClassPath(stripPrefix(resourcePath));
129    
130            } else if (resourcePath.startsWith(URL_PREFIX)) {
131                is = loadFromUrl(stripPrefix(resourcePath));
132    
133            } else if (resourcePath.startsWith(FILE_PREFIX)) {
134                is = loadFromFile(stripPrefix(resourcePath));
135    
136            } else {
137                is = loadFromFile(resourcePath);
138            }
139    
140            if (is == null) {
141                throw new IOException("Resource [" + resourcePath + "] could not be found.");
142            }
143    
144            return is;
145        }
146    
147        private static InputStream loadFromFile(String path) throws IOException {
148            if (log.isDebugEnabled()) {
149                log.debug("Opening file [" + path + "]...");
150            }
151            return new FileInputStream(path);
152        }
153    
154        private static InputStream loadFromUrl(String urlPath) throws IOException {
155            log.debug("Opening url {}", urlPath);
156            URL url = new URL(urlPath);
157            return url.openStream();
158        }
159    
160        private static InputStream loadFromClassPath(String path) {
161            log.debug("Opening resource from class path [{}]", path);
162            return ClassUtils.getResourceAsStream(path);
163        }
164    
165        private static String stripPrefix(String resourcePath) {
166            return resourcePath.substring(resourcePath.indexOf(":") + 1);
167        }
168    
169        /**
170         * Convenience method that closes the specified {@link InputStream InputStream}, logging any
171         * {@link IOException IOException} that might occur. If the {@code InputStream}
172         * argument is {@code null}, this method does nothing.  It returns quietly in all cases.
173         *
174         * @param is the {@code InputStream} to close, logging any {@code IOException} that might occur.
175         */
176        public static void close(InputStream is) {
177            if (is != null) {
178                try {
179                    is.close();
180                } catch (IOException e) {
181                    log.warn("Error closing input stream.", e);
182                }
183            }
184        }
185    }