View Javadoc

1   /*
2    * ====================================================================
3    * 
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2003 Nick Lothian. All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer. 
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:  
22   *       "This product includes software developed by the 
23   *        developers of Classifier4J (http://classifier4j.sf.net/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The name "Classifier4J" must not be used to endorse or promote 
28   *    products derived from this software without prior written 
29   *    permission. For written permission, please contact   
30   *    http://sourceforge.net/users/nicklothian/.
31   *
32   * 5. Products derived from this software may not be called 
33   *    "Classifier4J", nor may "Classifier4J" appear in their names 
34   *    without prior written permission. For written permission, please 
35   *    contact http://sourceforge.net/users/nicklothian/.
36   *
37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48   * SUCH DAMAGE.
49   * ====================================================================
50   */
51  
52  package net.sf.classifier4J.util;
53  
54  import java.io.*;
55  import java.net.*;
56  import java.util.*;
57  
58  /***
59   * <p>A class to locate resources, retrieve their contents, and determine their
60   * last modified time. To find the resource the class searches the CLASSPATH
61   * first, then Resource.class.getResource("/" + name). If the Resource finds
62   * a "file:" URL, the file path will be treated as a file. Otherwise, the
63   * path is treated as a URL and has limited last modified info.</p>
64   * 
65   * <p>Heavily based on the example from http://www.onjava.com/pub/a/onjava/excerpt/jebp_3/index1.html?page=3#ex3-4</p>
66   * 
67   */
68  public class Resource implements Serializable {
69  
70      private String name;
71      private File file;
72      private URL url;
73  
74      public Resource(String name) throws IOException {
75          this.name = name;
76          SecurityException exception = null;
77  
78          try {
79              // Search using the CLASSPATH. If found, "file" is set and the call
80              // returns true.  A SecurityException might bubble up.
81              if (tryClasspath(name)) {
82                  return;
83              }
84          } catch (SecurityException e) {
85              exception = e; // Save for later.
86          }
87  
88          try {
89              // Search using the classloader getResource(  ). If found as a file,
90              // "file" is set; if found as a URL, "url" is set.
91              if (tryLoader(name)) {
92                  return;
93              }
94          } catch (SecurityException e) {
95              exception = e; // Save for later.
96          }
97  
98          // If you get here, something went wrong. Report the exception.
99          String msg = "";
100         if (exception != null) {
101             msg = ": " + exception;
102         }
103 
104         throw new IOException("Resource '" + name + "' could not be found in " + "the CLASSPATH (" + System.getProperty("java.class.path") + "), nor could it be located by the classloader responsible for the " + "web application (WEB-INF/classes)" + msg);
105     }
106 
107     /***
108      * Returns the resource name, as passed to the constructor
109      */
110     public String getName() {
111         return name;
112     }
113 
114     /***
115      * Returns an input stream to read the resource contents
116      */
117     public InputStream getInputStream() throws IOException {
118         if (file != null) {
119             return new BufferedInputStream(new FileInputStream(file));
120         } else if (url != null) {
121             return new BufferedInputStream(url.openStream());
122         }
123         return null;
124     }
125 
126     /***
127      * Returns when the resource was last modified. If the resource 
128      * was found using a URL, this method will work only if the URL 
129      * connection supports last modified information. If there's no 
130      * support, Long.MAX_VALUE is returned. Perhaps this should return 
131      * -1, but you should return MAX_VALUE on the assumption that if
132      * you can't determine the time, it's maximally new.
133      */
134     public long lastModified() {
135         if (file != null) {
136             return file.lastModified();
137         } else if (url != null) {
138             try {
139                 return url.openConnection().getLastModified(); // Hail Mary
140             } catch (IOException e) {
141                 return Long.MAX_VALUE;
142             }
143         }
144         return 0; // can't happen
145     }
146 
147     /***
148      * Returns the directory containing the resource, or null if the 
149      * resource isn't directly available on the filesystem. 
150      * This value can be used to locate the configuration file on disk,
151      * or to write files in the same directory.
152      */
153     public String getDirectory() {
154         if (file != null) {
155             return file.getParent();
156         } else if (url != null) {
157             return null;
158         }
159         return null;
160     }
161 
162     // Returns true if found
163     private boolean tryClasspath(String filename) {
164         String classpath = System.getProperty("java.class.path");
165         String[] paths = split(classpath, File.pathSeparator);
166         file = searchDirectories(paths, filename);
167         return (file != null);
168     }
169 
170     private static File searchDirectories(String[] paths, String filename) {
171         SecurityException exception = null;
172         for (int i = 0; i < paths.length; i++) {
173             try {
174                 File file = new File(paths[i], filename);
175                 if (file.exists() && !file.isDirectory()) {
176                     return file;
177                 }
178             } catch (SecurityException e) {
179                 // Security exceptions can usually be ignored, but if all attempts
180                 // to find the file fail, report the (last) security exception.
181                 exception = e;
182             }
183         }
184         // Couldn't find any match
185         if (exception != null) {
186             throw exception;
187         } else {
188             return null;
189         }
190     }
191 
192     // Splits a String into pieces according to a delimiter.
193     // Uses JDK 1.1 classes for backward compatibility.
194     // JDK 1.4 actually has a split(  ) method now.
195     private static String[] split(String str, String delim) {
196         // Use a Vector to hold the split strings.
197         Vector v = new Vector();
198 
199         // Use a StringTokenizer to do the splitting.
200         StringTokenizer tokenizer = new StringTokenizer(str, delim);
201         while (tokenizer.hasMoreTokens()) {
202             v.addElement(tokenizer.nextToken());
203         }
204 
205         String[] ret = new String[v.size()];
206         v.copyInto(ret);
207         return ret;
208     }
209 
210     // Returns true if found
211     private boolean tryLoader(String name) {
212         name = "/" + name;
213         URL res = Resource.class.getResource(name);
214         if (res == null) {
215             return false;
216         } else {
217             // Try converting from a URL to a File.
218             File resFile = urlToFile(res);
219             if (resFile != null) {
220                 file = resFile;
221             } else {
222                 url = res;
223             }
224             return true;
225         }
226 
227     }
228 
229     private static File urlToFile(URL res) {
230         String externalForm = res.toExternalForm();
231         if (externalForm.startsWith("file:")) {
232             return new File(externalForm.substring(5));
233         }
234         return null;
235     }
236 
237     public String toString() {
238         return "[Resource: File: " + file + " URL: " + url + "]";
239     }
240 }