Abstractions for Low-Level Resources


Spring contains a useful abstraction for describing various types of simple resources, as found on the filesystem or classpath, or accessed via a URL. This is the Resource interface, with a number of actual implementations available:

 public interface Resource extends InputStreamSource {        boolean exists();        boolean isOpen();        URL getURL() throws IOException;        File getFile() throws IOException;        Resource createRelative(String relativePath) throws IOException;        String getFilename();        String getDescription(); }     public interface InputStreamSource {        InputStream getInputStream() throws IOException; } 

Given a Resource, you may obtain an InputStream from it, see if it actually exists, try to get a URL or File handle from it, and do some name-related operations. It may not be obvious why this interface is really needed, with a normal java.net.URL potentially being directly used instead. You may in fact think of a Resource as a somewhat more capable version of a URL, able to handle extra types of resources that a URL cannot uniquely describe (such as those on a classpath, or those relative to a ServletContext), with methods that behave appropriately for the actual resource type. For example, for a ClasspathResource, getInputStream() will ask a ClassLoader for the stream; for a FilesystemResource, the underlying File will produce the stream; and for a UrlResource, the underlying URL will open the stream.

The Application Context as a ResourceLoader

Every application context implements the ResourceLoader interface:

 public interface ResourceLoader {   Resource getResource(String location); } 

There's not very much to it, obviously. You can ask the ResourceLoader (the context in this case) for a resource as a String location and get back an object implementing the Resource interface. What is interesting (and useful) though is that the actual type of Resource you get back will depend on a combination of an optional prefix that the location string has, and the actual type of application context. If you ask for a location starting with a normal URL prefix (for a URL type known to the Java Virtual Machine, or JVM):

 Resource mailMessage = ctx.getResource("file:/data/emails/error.email");     Resource mailMessage = ctx.getResource("http://mydomain.com/emails/error.email"); 

you will get back an instance of UrlResource, a Resource type backed by a real URL, which will be used for actual operations such as obtaining a stream. If the location string has the special Spring- recognized classpath: prefix:

 Resource mailMessage = ctx.getResource("classpath:error.email"); 

you will get back an instance of ClasspathResource, a Resource that accesses resources from a class-path, in this case the classpath associated with the ClassLoader of the application context. Finally, if the location string has no prefix, as shown here,

 Resource mailMessage = ctx.getResource("data/emails/error.email"); 

the actual Resource implementation returned is dependent on the actual application context implementation being used (and is appropriate to that context). From a ClasspathXmlApplicationContext, the location would be interpreted as a classpath location, and you would get back a ClasspathResource. From a FilesystemXmlApplicationContext, the location would be interpreted as a relative filesystem location, and you would get back a FilesystemResource. From an XmlWebApplicationContext, the location would be interpreted as being relative to the web-app root, and you would get back a ServletContextResource.

Note 

As an aside, note that depending on the servlet engine implementation, the actual physical resource file for the latter could be on the filesystem, in a packed WAR file, or conceivably even in a database, but handling is transparent.

A bean that wants to use the application context as a resource loader can just implement the ResourceLoaderAware interface, and will automatically be called back by the application context at initialization time with a reference to the application context as the ResourceLoader. In this example, an HTML page generator class obtains Resources to use as headers and footers for the page:

 public class HtmlPageGenerator implements ResourceLoaderAware {        private ResourceLoader resourceLoader;   private String headerLocation;   private String footerLocation;   private Resource header;   private Resource footer;        public void setResourceLoader(ResourceLoader resourceLoader) {     this.resourceLoader = resourceLoader;     // properties are set before this callback so are safe to use     header = resourceLoader.getResource(headerLocation);     footer = resourceLoader.getResource(footerLocation);   }   public void setHeaderLocation(String headerLocation) {     this.headerLocation = headerLocation;   }   public void setFooterLocation(String footerLocation) {     this.footerLocation = footerLocation;   }        public String generate(String bodyData) {     //   } } 

Of course, because ApplicationContext is a sub-interface of ResourceLoader, an application class that already has an instance of the context (which it got by implementing ApplicationContextAware, or by any other means) can just use the context as a ResourceLoader directly. However, for a new class that needs to do resource loading but doesn't need to programmatically manipulate the context in other ways, it's cleaner to implement ResourceLoaderAware rather than ApplicationContextAware, as the former is the more specific interface. Among other benefits, this looser coupling makes it easier to mock for testing.

When the Resources to be loaded are going to be dynamically selected at runtime (consider for example an emailing service object, which loads email templates as Resources), then using the context as a ResourceLoader is convenient. However, if there are only a few Resources and their locations are known from the beginning, it would be nice to get rid of the dependency on ResourceLoader or ApplicationContext, as well as reduce the amount of code, by just being able to set the Resources directly as properties.

In fact, this is quite possible, and happens transparently, as demonstrated in this variant of the page generator and an application context configuration that populates an instance of it:

public class HtmlPageGenerator {       private Resource header;   private Resource footer;       public void setFooter(Resource footer) {     this.footer = footer;   }   public void setHeader(Resource header) {     this.header = header;   }       public String wrapPage(String pageData) {     return null;//    } }     <bean  >   <property name="header">     <value>webcontent/templates/footer.txt</value>   </property>   <property name="footer">     <value>webcontent/templates/footer.txt</value>   </property> </bean> 

You are able to specify properties of type Resource as string values because the application context automatically self-registers a JavaBean PropertyEditor called ResourceEditor. The application context itself is set as the ResourceLoader to be used by this property editor to produce the output Resource objects from the input string locations. The net effect is exactly the same as would be obtained by manually calling getResource() on the context with the same location strings.

This ability to directly set Resource properties on a bean as text string values when wiring the bean in an application context, along with the general flexibility and usefulness of the Resource abstraction, makes it very convenient to use the Resource type for all general references to low-level resources in application code.

While using the Resource interface and implementations is indeed coupling the application code to a Spring interface, keep in mind that this is not coupling it to the container in any way, just to a very small set of utility classes in Spring, which can be easily separated from the rest of the Spring library if there is a need, and put in its own JAR.

The Application Context as a Message Source

Applications often need to obtain string-form messages, as defined in an external file. These are typically used for UI elements and logging, and often the ability to handle internationalization concerns, with resolution of messages appropriate to a specific locale, is a necessity.

In Spring, the MessageSource interface, which is implemented by all application contexts, exists to serve this need:

 public interface MessageSource {   String getMessage(String code, Object[] args, String defaultMessage,                     Locale locale);   String getMessage(String code, Object[] args, Locale locale)           throws NoSuchMessageException;   String getMessage(MessageSourceResolvable resolvable, Locale locale)           throws NoSuchMessageException; } 

When requesting a message via the first two getMessage() variants, the message to retrieve is identified by a string code, along with a locale. If the message is not found, the default supplied is returned(first variant) or an exception is thrown (second variant). In standard Java java.text.MessageFormat fashion (please see the JavaDocs for the latter class for more info), the message string that is resolved from the code may have placeholders in the {n} format, which get replaced with values supplied by the client requesting the message, before the message is returned. Following is an example where a default string is supplied:

 String msg = messageSource.getMessage("error.duplicate.username",         new Object[] {username},         "Invalid username, as it already exists: {0}", locale); 

The third getMessage() variant() simply passes in the message code, array of arguments, and default message string as an object implementing the MessageSourceResolvable interface. This variant is mostly used in concert with other parts of Spring. For example, the validation portion of the framework will return a FieldError class that implements this interface to describe a validation error. You can then pass this FieldError instance directly to getMessage() to get an appropriate error message.

A bean that wishes to use the application context as a message source may implement the MessageSourceAware interface, as shown here:

 public interface MessageSourceAware {    void setMessageSource(MessageSource messageSource); } 

Then, the bean will automatically be called back by the application context at initialization time with a reference to the application context as the MessageSource. Of course, because ApplicationContext is a sub-interface of MessageSource, an application class that already has an instance of the context(which it got by implementing ApplicationContextAware, or by any other means) can just use the context as a MessageSource directly. However, for a new class that needs to load messages, but doesn't need to programmatically manipulate the context in other ways, it's cleaner to implement MessageSource Aware rather than ApplicationContextAware, as the former is the more specific interface. Among other benefits, this makes it easier to mock for testing.

The actual implementation of MessageSource functionality is pluggable, as the context just delegates MessageSource method calls to an internal provider. Configuration of this provider is very simple; the context on startup will look for an application-deployed bean within it, which must have the special name messageSource and must implement the MessageSource interface, and will treat this bean as the MessageSource provider:

 <bean        >   <property name="basename"><value>applicationResources</value></property> </bean> 

Note that MessageSources can be hierarchical. A message source in an application context that is the child of another context will be the child of the message source in the parent context. When the message source cannot resolve a message code, it will ask its parent, which will ask its parent on failure, and so on. Because it sometimes makes sense to split up contexts into a hierarchy, this feature allows messages to be associated with their related context, but still be resolved by a request from a child. Note that this also means a message resolved in a child will override any message resolved from the same code in a parent. A child context does not have to actually define a message source for delegation to happen toa parent.

One implementation of MessageSource, which is included with Spring, is ResourceBundleMessageSource, shown in the preceding example. This is essentially a wrapper around a normal java.util.ResourceBundle. The basename property specifies the base name of the bundle. Please see the ResourceBundle JavaDoc for full details for how resource bundles work, but the bundle will normally look for messages in Properties files with names that are variations of the specified base name depending on the locale; for example, for the base name in the example,an optional applicationResources_en.properties would match an English locale, applicationResources_fr.properties a French one, and so on. The bundle would fall back to applicationResources.properties as a default, so at a minimum the latter file would have to exist. Instead of using the basename property, the basenames property may be used to specify multiple base names. In this case, message resolution happens against multiple resource bundles, in a sequential fashion. This allows, for example, message files to be split up by functionality.

Resource bundles are normally cached permanently by the resource bundle handling code in Java. Especially while doing development, it's sometimes desirable to be able to pick up changes to the underlying message Properties files without restarting the application. For this reason, an alternative message source implementation exists, ReloadableResourceBundleMessageSource. This message source works in a similar fashion to ResourceBundleMessageSource, but can be configured to cache values for a specific period of time, instead of permanently like the latter. Additionally, it allows more flexible resolution of message Properties files, not just from the classpath. The JavaDocs for this class should be consulted for more information.

A note on deploying and accessing the message source: As mentioned, the message source is just another bean in the context, deployed under the special name messageSource. If you do not wish to tie application code to the MessageSourceAware interface, there is nothing stopping you from just setting the message source as another property of your application bean(s), with the value being a reference to the messageSource bean. Unless you use autowiring for the message source property (which makes sense since there should only ever be one message source), this is more verbose because you'll have to explicitly do this for any bean that needs it, but has no other disadvantages. All message source functionality will still work, including participation in any hierarchy with message sources in parent contexts.

Users interested in exploiting message sources should also look at the MessageSourceAccessor class, which can make working with message sources a bit simpler in terms of default locale handling. See the related JavaDoc for more info.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net