View Javadoc

1   package org.jmage.resource;
2   
3   import com.sun.media.jai.codec.ByteArraySeekableStream;
4   import org.apache.log4j.Logger;
5   import org.apache.log4j.Priority;
6   import org.jmage.ApplicationContext;
7   import org.jmage.mapper.InterceptorMapper;
8   
9   import javax.media.jai.JAI;
10  import javax.media.jai.PlanarImage;
11  import javax.servlet.ServletContext;
12  import java.io.ByteArrayOutputStream;
13  import java.io.File;
14  import java.io.IOException;
15  import java.io.InputStream;
16  import java.net.MalformedURLException;
17  import java.net.URI;
18  import java.net.URL;
19  import java.net.URLConnection;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Properties;
23  
24  /***
25   * DefaultImageFactory loads images as resources from system environment.
26   */
27  public class DefaultImageFactory implements ResourceFactory {
28      protected static DefaultImageFactory defaultImageFactory;
29  
30      private final static String PNG = "png";
31      private final static String GIF = "gif";
32      private final static String JPG = "jpg";
33      private final static String JPEG = "jpeg";
34      private final static String TIF = "tif";
35      private final static String TIFF = "tiff";
36      private final static String BMP = "bmp";
37  
38      private final static String HTTP = "http";
39      private final static String FILE = "file";
40  
41      protected static Logger log = Logger.getLogger(DefaultImageFactory.class.getName());
42      protected List imageTypes;
43      protected List schemeTypes;
44      protected ApplicationContext applicationContext;
45      protected ServletContext servletContext;
46  
47      private static final String resourcedir = "resourcedir";
48      private static final String IMAGES = "images";
49      private static final String SERVLET_CONTEXT = "SERVLET_CONTEXT";
50      private static final String URI_HANDLINGERROR = "unable to handle URI resource, cause: ";
51      private static final String FILE_RESOURCE_RETRIEVED = " retrieved file resource: ";
52      private static final String URL_RESOURCE_RETRIEVED = " retrieved URL resource: ";
53      private static final String SCHEME_ERROR = "unable to retrieve resource, could not handle scheme: ";
54      private static final String FILELOAD = "fileload";
55      private static final String FILE_LOADED = " loaded image from file: ";
56      private static final String FILE_LOADERROR = "unable to load image from file: ";
57      private static final String STREAM = "stream";
58      private static final String SERVLET_LOADERROR = "unable to load image from servlet container: ";
59      private static final String SERVLET_LOADED = " loaded image from servlet container: ";
60      private static final String URL_LOADERROR = "unable to load image from URL: ";
61      private static final String URL_LOADED = " loaded image from url: ";
62      private static final String CAUSE = ", cause: ";
63      private static final String TRUE = "TRUE";
64      private static final String HTTP_HEADER_ERROR = "error while retrieving http status header from server";
65      private static final String HTTP_400 = "40";
66      private static final String SLASH = "/";
67      private static final char SUFFIX_SEPARATOR = '.';
68      private static final String REGEX_BACKSLASH = "////";
69      private static final String CLASSPATH_LOADERROR = "unable to retrieve resource from classpath, cause: ";
70  
71      /***
72       * Create a DefaultimageFactory
73       */
74      public DefaultImageFactory() {
75          imageTypes = new ArrayList();
76          imageTypes.add(PNG);
77          imageTypes.add(GIF);
78          imageTypes.add(JPG);
79          imageTypes.add(JPEG);
80          imageTypes.add(TIF);
81          imageTypes.add(TIFF);
82          imageTypes.add(BMP);
83  
84          schemeTypes = new ArrayList();
85          schemeTypes.add(HTTP);
86          schemeTypes.add(FILE);
87      }
88  
89      /***
90       * Configures the ImageFactory with ApplicationContext
91       *
92       * @param context the ApplicationContext
93       */
94      public void configure(ApplicationContext context) {
95          this.applicationContext = context;
96          this.servletContext = (ServletContext) this.applicationContext.get(SERVLET_CONTEXT);
97      }
98  
99      public void configureRequestProperties(Properties properties) {
100         //nothing to do
101     }
102 
103     public void removeRequestProperties(Properties properties) {
104         //nothing to do
105     }
106 
107     /***
108      * Tests whether the ImageFactory can handle a particular resource URI.
109      *
110      * @param resource
111      * @return true | false
112      */
113     public boolean canHandle(URI resource) {
114         try {
115             String suffix = resource.getPath().substring(resource.getPath().lastIndexOf(SUFFIX_SEPARATOR) + 1).toLowerCase();
116             String scheme = resource.getScheme();
117             return imageTypes.contains(suffix) && schemeTypes.contains(scheme);
118         } catch (Exception e) {
119             if (log.isInfoEnabled()) log.info(URI_HANDLINGERROR + e.getMessage());
120             return false;
121         }
122     }
123 
124     /***
125      * Create an object resource from a resource URI
126      *
127      * @param resource the resource URI
128      * @return the object
129      * @throws ResourceException
130      */
131     public Object createFrom(URI resource) throws ResourceException {
132         String scheme = resource.getScheme().toLowerCase();
133         if (FILE.equals(scheme)) {
134             File file = new File(resource);
135             PlanarImage image = this.getFile(file);
136             if (log.isInfoEnabled()) log.info(FILE_RESOURCE_RETRIEVED + file.getName());
137             return image;
138         }
139         if (HTTP.equals(scheme)) {
140             URL url = null;
141             try {
142                 url = resource.toURL();
143             } catch (MalformedURLException e) {
144                 throw new ResourceException(e.getMessage());
145             }
146             PlanarImage image = this.getURL(url);
147             if (log.isInfoEnabled()) log.info(URL_RESOURCE_RETRIEVED + url.toString());
148             return image;
149         }
150         throw new ResourceException(SCHEME_ERROR + scheme);
151     }
152 
153     /***
154      * Get the image resource from a file.
155      *
156      * @param file the file
157      * @return the image
158      * @throws ResourceException
159      */
160     protected PlanarImage getFile(File file) throws ResourceException {
161         PlanarImage image = null;
162 
163         //1) try load absolute image
164         image = getAbsoluteFile(file);
165 
166         //2) if not try to load as resource from ServletContext
167         image = image == null ? this.getServletContainerResource(file) : image;
168 
169         //3) if not try relative path beginning in JMAGE.RESOURCE.DIR system property
170         image = image == null ? this.getJMAGEResourceDirFile(file) : image;
171 
172         //4) if not try on classpath as a resource
173         image = image == null ? this.getClassPathResource(file) : image;
174 
175         //5) if not try relative path beginning in current dir
176         image = image == null ? this.getCurrentDirFile(file) : image;
177 
178         //6) still null? throw resourceexception
179         if (image == null) {
180             if (log.isEnabledFor(Priority.ERROR)) log.error(FILE_LOADERROR + file);
181             throw new ResourceException(FILE_LOADERROR + file);
182         }
183         return image;
184     }
185 
186     /***
187      * Get the file from the classpath as a system resource
188      *
189      * @param file
190      * @return true | false
191      * @throws ResourceException
192      */
193     protected PlanarImage getClassPathResource(File file) throws ResourceException {
194         PlanarImage image = null;
195         String imagePath = file.getPath();
196         //crop trailing slash for classloader
197         if (imagePath.indexOf(File.separator) == 0) {
198             imagePath = imagePath.substring(1);
199         }
200         imagePath = imagePath.replaceAll(REGEX_BACKSLASH, SLASH);
201 
202         //try find a classloader that handles this
203         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
204         URL imageURL = this.locateOnClassPath(imagePath, classLoader);
205         if(imageURL==null) {
206             classLoader = this.getClass().getClassLoader();
207             imageURL = this.locateOnClassPath(imagePath, classLoader);
208         }
209 
210         if (imageURL != null) {
211             try {
212                 image = JAI.create(STREAM, new ByteArraySeekableStream(this.streamConvert(classLoader.getResourceAsStream(imagePath)).toByteArray()));
213             } catch (IOException e) {
214                 if (log.isEnabledFor(Priority.INFO)) log.info(CLASSPATH_LOADERROR + e.getMessage());
215             }
216         }
217         return image;
218     }
219 
220     protected URL locateOnClassPath(String path, ClassLoader classLoader) {
221         URL uRL = classLoader.getResource(path);
222         if (uRL == null) {
223             uRL = classLoader.getResource(SLASH + path);
224             if (uRL != null) {
225                 path = (SLASH + path);
226             }
227         }
228         if (uRL == null) {
229             uRL = classLoader.getResource(IMAGES + SLASH + path);
230             if (uRL != null) {
231                 path = (IMAGES + SLASH + path);
232             }
233         }
234         if (uRL == null) {
235             uRL = classLoader.getResource(SLASH + IMAGES + SLASH + path);
236             if (uRL != null) {
237                 path = (SLASH + IMAGES + SLASH + path);
238             }
239         }
240         return uRL;
241     }
242 
243     /***
244      * Get the file from the current application directory.
245      *
246      * @param file the file
247      * @return the image
248      */
249     protected PlanarImage getCurrentDirFile(File file) {
250         PlanarImage image = null;
251         if (!file.isAbsolute()) {
252             File current = new File(".");
253             File imageFile = new File(current, file.getPath());
254             if (imageFile.isFile() && imageFile.exists()) {
255                 try {
256                     image = JAI.create(FILELOAD, imageFile.getAbsolutePath());
257                     if (log.isDebugEnabled()) log.debug(FILE_LOADED + imageFile.getAbsolutePath());
258                 } catch (Exception e) {
259                     if (log.isEnabledFor(Priority.ERROR)) log.error(FILE_LOADERROR, e);
260                 }
261             }
262         }
263         return image;
264     }
265 
266     /***
267      * Get the file from the dir specified trough the resourcedir property
268      *
269      * @param file the file
270      * @return the image or null
271      */
272     protected PlanarImage getJMAGEResourceDirFile(File file) {
273         PlanarImage image = null;
274             String resourceDirName = this.applicationContext.getProperty(resourcedir);
275             //gets caught by caller
276             if (resourceDirName == null) {
277                 return null;
278             }
279 
280             File imageResourceDir = new File(resourceDirName, IMAGES);
281             if (imageResourceDir != null && imageResourceDir.isDirectory() && imageResourceDir.exists()) {
282                 File imageFile = new File(imageResourceDir, file.getPath());
283                 if (imageFile.isFile() && imageFile.exists()) {
284                     try {
285                         image = JAI.create(FILELOAD, imageFile.getAbsolutePath());
286                         if (log.isDebugEnabled()) log.debug(FILE_LOADED + imageFile.getAbsolutePath());
287                     } catch (Exception e) {
288                         if (log.isEnabledFor(Priority.ERROR)) log.error(FILE_LOADERROR, e);
289                     }
290                 }
291             }
292             //use "images' subdirectory if present
293             if(image!=null) return image;
294 
295             imageResourceDir = new File(resourceDirName);
296             if (imageResourceDir != null && imageResourceDir.isDirectory() && imageResourceDir.exists()) {
297                 File imageFile = new File(imageResourceDir, file.getPath());
298                 if (imageFile.isFile() && imageFile.exists()) {
299                     try {
300                         image = JAI.create(FILELOAD, imageFile.getAbsolutePath());
301                         if (log.isDebugEnabled()) log.debug(FILE_LOADED + imageFile.getAbsolutePath());
302                     } catch (Exception e) {
303                         if (log.isEnabledFor(Priority.ERROR)) log.error(FILE_LOADERROR, e);
304                     }
305                 }
306             }
307         return image;
308     }
309 
310     /***
311      * Get the file from the ServletContainer as a system resource.
312      *
313      * @param file the file
314      * @return the image
315      */
316     protected PlanarImage getServletContainerResource(File file) {
317         PlanarImage image = null;
318         //needs initialization if ApplicationContext hasn't been initialized by ServletMapper
319         //at the time this Factory was created.
320         if (servletContext == null) {
321             servletContext = (ServletContext) this.applicationContext.get(SERVLET_CONTEXT);
322             if (log.isEnabledFor(Priority.DEBUG)) log.debug("servletContext is " + servletContext);
323         }
324 
325         if (servletContext != null) {
326             try {
327                 InputStream is = servletContext.getResourceAsStream(file.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
328                 //prefix with "images" the default resource dir and try again. This allows
329                 //relative resources to be referred to the same way they are in resourcedir
330                 if (is == null) {
331                     File derived = new File(SLASH + IMAGES, file.getPath());
332                     is = servletContext.getResourceAsStream(derived.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
333                 }
334                 if (is == null) {
335                     File derived = new File(IMAGES, file.getPath());
336                     is = servletContext.getResourceAsStream(derived.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
337                 }
338                 if (is == null) {
339                     File derived = new File(SLASH, file.getPath());
340                     is = servletContext.getResourceAsStream(derived.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
341                 }
342                 //check without trailing slash for crazy servlet contexts
343                 if (is == null) {
344                     String derivedPath = file.getPath().replaceAll(REGEX_BACKSLASH, SLASH);
345                     File derived = new File(derivedPath.startsWith(SLASH) ? derivedPath.substring(1) : derivedPath);
346                     is = servletContext.getResourceAsStream(derived.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
347                 }
348                 //don't try to create null resource
349                 if (is == null) {
350                     if (log.isDebugEnabled()) log.debug(SERVLET_LOADERROR + file.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
351                     return null;
352                 }
353                 image = JAI.create(STREAM, new ByteArraySeekableStream((this.streamConvert(is).toByteArray())));
354                 if (log.isDebugEnabled()) log.debug(SERVLET_LOADED + file.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
355             } catch (Throwable e) {
356                 if (log.isDebugEnabled()) log.debug(SERVLET_LOADERROR + file.getPath().replaceAll(REGEX_BACKSLASH, SLASH));
357             }
358         }
359         return image;
360     }
361 
362     /***
363      * Get the absolute file from the file system
364      *
365      * @param file the file
366      * @return the image
367      */
368     protected PlanarImage getAbsoluteFile(File file) {
369         PlanarImage image = null;
370         if (file.isAbsolute() && file.exists()) {
371             try {
372                 image = JAI.create(FILELOAD, file.getAbsolutePath());
373                 if (log.isDebugEnabled()) log.debug(FILE_LOADED + file.getAbsolutePath());
374             } catch (Exception e) {
375                 if (log.isEnabledFor(Priority.ERROR)) log.error(FILE_LOADERROR, e);
376             }
377         }
378         return image;
379     }
380 
381     /***
382      * Get the image from an URL
383      *
384      * @param url the url
385      * @return the image
386      * @throws ResourceException
387      */
388     protected PlanarImage getURL(URL url) throws ResourceException {
389         PlanarImage image = null;
390         final String errorMessage = URL_LOADERROR + url.toString();
391 
392         try {
393             byte[] urlBytes = this.readFromUrl(url).toByteArray();
394             image = JAI.create(STREAM, new ByteArraySeekableStream(urlBytes));
395             if (log.isDebugEnabled()) log.debug(URL_LOADED + url.toString());
396         } catch (Exception e) {
397             if (log.isEnabledFor(Priority.ERROR)) log.error(errorMessage + CAUSE + e.getMessage());
398             throw new ResourceException(errorMessage);
399         }
400 
401         // null? throw resourceexception
402         if (image == null) {
403             if (log.isEnabledFor(Priority.ERROR)) log.error(errorMessage);
404             throw new ResourceException(errorMessage);
405         }
406         return image;
407     }
408 
409     /***
410      * Read an image from a URL using http
411      *
412      * @param url the url
413      * @return the image stream
414      * @throws IOException
415      */
416     protected ByteArrayOutputStream readFromUrl(URL url) throws IOException {
417         URLConnection connection = url.openConnection();
418         //this is to make sure that the servlet filter does not intercept
419         //any internal ResourceManager requests
420         connection.setDoOutput(true);
421         connection.setRequestProperty(InterceptorMapper.JMAGE_INTERNAL, TRUE);
422         connection.connect();
423 
424         //throw IOException if 400 found in here
425         String responseHeader = connection.getHeaderField(null);
426         assert(responseHeader != null) : HTTP_HEADER_ERROR;
427         if (responseHeader.indexOf(HTTP_400) > -1) {
428             throw new IOException(URL_LOADERROR + url + CAUSE + responseHeader);
429         }
430 
431         InputStream is = connection.getInputStream();
432         return streamConvert(is);
433     }
434 
435     private ByteArrayOutputStream streamConvert(InputStream inputStream) throws IOException {
436         ByteArrayOutputStream bos = new ByteArrayOutputStream();
437         int c = 0;
438         while ((c = inputStream.read()) > -1) {
439             bos.write(c);
440         }
441         return bos;
442     }
443 
444     public String toString() {
445         return "[" + this.getClass().getName() + "#" + this.hashCode() + "]";
446     }
447 }