|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 5
The combination of methods overridden in ClassLoaderResourceHandler and its nested connection class are enough to make URL.openStream() and related methods work by connecting the URL input stream to the classloader resource input stream. (They are still not
enough to make URL.getContent() work, but I leave that as an exercise for you.) For simplicity, I use the current thread's context classloader for all resource
lookup (see comments in code for other strategies).
As a demo of how this works, let's add a single line:
clsloader = ClassLoaderResourceHandler
to a file "url.properties" that will be packaged along with the URLFactory classes and try the new URL syntax with this simple Swing application that displays a javax.swing.ImageIcon:
public class URLDemo extends JPanel
{
public static void main (final String[] args)
{
if (args.length == 0)
{
System.out.println ("usage: URLDemo url");
System.exit (1);
}
final String iconURL = args [0];
JFrame frame = new JFrame ("URL demo");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
URLDemo newContentPane = new URLDemo (iconURL);
newContentPane.setOpaque (true);
frame.setContentPane (newContentPane);
frame.pack ();
frame.setVisible (true);
}
public URLDemo (final String iconURL)
{
super (new GridLayout(1, 1));
ImageIcon icon = createImageIcon (iconURL, "JavaWorld logo");
JLabel label = new JLabel ("image loaded from " + iconURL,
icon, JLabel.CENTER);
add (label);
}
private static ImageIcon createImageIcon (final String url,
final String description)
{
try
{
return new ImageIcon (URLFactory.newURL (url), description);
}
catch (IOException ioe)
{
System.err.println ("couldn't load icon URL: " + url);
ioe.printStackTrace ();
return null;
}
}
} // End of class
This demo is a reimplemented LabelDemo from Sun Microsystems' Swing tutorial. It has been modified to take the icon image URL as a command-line parameter. It then
passes the URL string into a modified version of createImageIcon() that uses URLFactory. Assuming all classes and resources have been packaged in urldemo.jar, the following all work equally well:
java -cp urldemo.jar URLDemo clsloader:images/jwlogo.gif
java -cp urldemo.jar URLDemo jar:file:urldemo.jar!/images/jwlogo.gif
java -cp urldemo.jar URLDemo http://www.javaworld.com/images/top_jwlogo.gif
(However, only the first option is truly disk position-independent and requires no connection to an HTTP server.)
If for some reason you need to go back to using files, it can be done without any code modifications. Assuming you extract
all images from urldemo.jar into a local directory files, this will work too:
java -cp urldemo.jar URLDemo file:files/jwlogo.gif
(You can experiment with these options using this article's download.)
This kind of resource strategy flexibility is impossible in the original LabelDemo, which is caught in a dilemma: the javax.swing.ImageIcon constructor takes either a filename string or a URL. The first option is disk position-dependent. The second is not, but
such a URL cannot be constructed by feeding a single String to a URL constructor to make that URL reference a classloader resource. This forces the original LabelDemo to hardcode just one strategy in its version of createImageIcon():
protected static ImageIcon createImageIcon(String path,
String description) {
java.net.URL imgURL = LabelDemo.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
Before moving on, I'll mention another good design strategy. It is somewhat underappreciated that java.net.URL constructors that take a URL context parameter essentially allow you to build URLs by combining two parts, a base URL and a relative URL. The following
URL base = URLFactory.newURL ("clsloader:images/");
URL url = URLFactory.newURL (base, "jwlogo.gif");
uses a two-step process to build a URL equivalent to URLFactory.newURL("clsloader:images/jwlogo.gif"). However, imagine that base is constructed in a special bootstrap part of the application and can be set to URLFactory.newURL("clsloader:images_en_US/") or URLFactory.newURL("clsloader:images_fr_CH/") based on external requirements. You have just gained an ability to localize all image resource URLs in the application with
very little work while keeping the locale selection decision to a single point in the Java code. You should get a lot of other
ideas at this point.
The opportunities here are truly limitless. Because file: URLs always accept forward slashes as platform-independent separators (and that's what I also happen to use in my clsloader: custom scheme), I can even transparently alternate the base URLs between those two schemes! In one fell swoop I can change
the resource loading strategy from disk files to classpath resources with almost no code changes.
To support URL recombination in your own schemes, you need to be prepared to handle the non-null context parameter passed into the parseURL() method and have a way of combining it with the spec string. This is what I do in the ClassLoaderResourceHandler.combineResourceNames() private method. The rules are similar for combining HTTP and file: URLs (names ending in "/" are treated as directories). You can even add processing for "." and ".." in resource names—the
result feels just like filesystem browsing of the classpath without ever directly touching the disk!
I now come to the second idea promised at the end of "Smartly Load Your Properties." I think of it as a zero code change pluggability pattern.
Hidden in URLFactory.loadHandlerList()'s implementation is a small Java design gem: retrieving a list of all identically named ("url.properties" in this case) classloader resources via ClassLoader.getResources(). This allows me to merge handler mapping data from as many url.properties resources as are deployed in the classpath (without knowing where they are exactly). This idea is borrowed from JNDI internals
and is a rare Java situation where duplicate classpath entries would be there by design.
To show this off, let me pretend that long after deploying my URLDemo application I decide to extend it by adding a plug-in that implements a new custom URL scheme. This will feel quite artificial,
but just as an illustration I create a urljoin: protocol that joins data streams from several nested URLs. That is, a URL
Archived Discussions (Read only)