Search This Blog

Tuesday 27 November 2012

Resource Loading in Spring

We have seen the various capabilities provided by the ApplicationContext. These include I18N, property file loading and event publishing. In this post we shall look at another utility functionality provided by the ApplicationContext - access to low-level resources.
What is a resource ?
A resource is anything that can be represented by org.springframework.core.io.Resource interface.
From the code comments:
Interface for a resource descriptor that abstracts from the actual type of 
underlying resource, such as a file or class path resource.
The interface is implemented by:
  •  FileSystemResource - Represents a File
  • ClassPathResource - Represents a File loaded from the classpath
  • UrlResource - Represents a URL
  • ByteArrayResource - Represents a byte array
  • InputStreamResource - Represents an input stream
While these classes appear added work as they simply seem to wrapping standard Java API classes, they could be useful in certain scenarios. For example, in one of our projects we had a utility class that was used to load velocity marker files. The files resided in our jars. So loading the vm files from the classpath seemed to be the cleanest approach.
Java API does not have any direct functionality for the same. But Spring's ApplicationContext does provide us with the ability. In fact it uses this very same ability to load configuration files from the classpath.So our code became:
final Resource template = this.applicationContext.getResource("classpath:" + pathUrl); 
As can be seem here, we needed access to the ApplicationContext. Rather than bind all our code to Spring, we created a file utility which took the vm file name and returned a byte array of the contents. This utility class had ApplicationContext wired to it, thus restricting our Spring code to this class.
What is the getResource() method ?
ApplicationContext inherits this method from the ResourceLoader interface. From the documentation for the getResource() method:
Return a Resource handle for the specified resource. The handle should always 
be a reusable resource descriptor, allowing for multiple 
InputStreamSource.getInputStream() calls.
->Must support fully qualified URLs, e.g. "file:C:/test.dat".
->Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
->Should support relative file paths, e.g. "WEB-INF/test.dat". (This will be 
implementation-specific, typically provided by an ApplicationContext implementation.)
As it says, since we are loading from the classpath we need to provide a "classpath:" prefix. This is exactly the same way we specify class path configuration files. Similarly we can provide "file:" prefix too.
The method we implemented is something like below:
public byte[] getResourceAsArray(final String pathUrl) {
        byte[] data = null;
        final Resource template = this.applicationContext.getResource("classpath:" + pathUrl);
        if (template.exists()) {
            final InputStream inputStream = template.getInputStream();
            data = getBytes(inputStream); // utility to get byte array of contents
        }
        return data;
}
The exists() method is very important as a resource object is returned for a non existent resource, just like the file object. What if there is no ApplicationContext available ? If we are working with a BeanFactory, we can still have the ability to load resources. But it does not come directly via the Container.Instead we need to use the DefaultResourceLoader class.
public static void main(final String[] args) {
    final DefaultResourceLoader loader = new DefaultResourceLoader();
    System.out.println(loader.getResource("file:C:\\test.pdf").exists());
    System.out.println(loader.getResource("classpath:spring-locale.xml").exists());
}
The output of the above code prints
true
true
You don't event need a Spring Container to run the above code. It is a stand alone class and can be used as is. 
In the next post we shall look at getting access to a more specific interface for loading resources.

No comments:

Post a Comment