Proper way to create a protocol handler

M

Michael Conner

The Javadoc for URL describes three different ways to add a protocol
for a URL:
1) Create a URLStreamHandlerFactory which creates a new
URLStreamHandler, then install it as the factory.
2) Create an implementation with name, <package>.<protocol>.Handler
and add that name to the system property:
java.protocol.handler.pkgs.
3) Create the handler as:
sun.net.www.protocol.<protocol>.Handler
and simply ensure that it is on the classpath.

Of the three, the last seems the least problematic. The first might
result in problems when dealing with a third party wich also wants to
install its own factory. The second has a similar problem in that some
other party may want to specify the system property. If so, it must
be done programatically, so that you can get the original version of
the property and add to it, or it must be specified on the command
line (or the java configuration properties altered, if that is
supported by the jre).

However, my concerns are:
a) security policies might be defined which do not allow loading of
sun.* from application code.
b) Its not typical practice to define code in some other package
structure. Nevertheless, I've seen #3 used in several places when I
did a web search.

Does someone know of a resource which describes the preferred way to
do this, and addresses the issue of whether #3 is acceptable?
 
A

Anton Spaans

Take this as an example:

package <package_name>;

public class URLProtocols
{
private static final String HANDLER_PKGS_PROP
="java.protocol.handler.pkgs";
private static final String HANDLER_PKGS_VAL =
URLProtocols.class.getPackage().getName();

public static void setProtocolHandlers()
{
setProtocolHandlers(URLProtocols.class);
}

public static void setProtocolHandlers(Class pClass)
{
String value = System.getProperty(HANDLER_PKGS_PROP);
boolean didSet = false;
if(value == null)
{
System.setProperty(HANDLER_PKGS_PROP, HANDLER_PKGS_VAL);
didSet = true;
}
else if(value.indexOf(HANDLER_PKGS_VAL) == -1)
{
value = value + "|" + HANDLER_PKGS_VAL;
System.setProperty(HANDLER_PKGS_PROP, value);
didSet = true;
}

if (didSet)
{
<package_name>.<protocol>.Handler.initialize(pClass);
}
}
...
}

and implement this class:

package <package_name>.<protocol>;
public class Handler extends URLStreamHandler
{
....
public static void initialize(Class pClass)
{ // prevents problems when multiple classloaders are in use
(web-containers, etc).
...
}
}

And then, in your system, upon startup, call this method:

URLProtocols.setProtocolHandlers();

Suppose your <protocol> is "myprot", then URLs such as
"myprot://server/path" can be handled and opened.

Done! :)
-- Anton.
 
M

Michael Conner

Anton,

So then you are advocating the second approach (using the
java.protocol.handler.pkgs system
property). The problems with this approach are:
1) All parties must propery do this. If, for example, an app server or
another application sets its own set of protocol handlers, it might clobber
your careful appending. (Granted, this seems unlikely, as the app server
would probably set the property before you tried to modify it.)
2) There is or may be a timing issue. The javadoc for the method does not
indicate if it re-reads the property each time it tries to find the
protocol. It may be that it only reads the property once. In which case,
if someone else tried to create a URL -- thus triggering a read of the
property -- before you had a chance to modify it, I imagine that it might be
that the modification might have no effect.
3) It requires additional code besides the protocol (albeit very little).

I guess the question I should be asking is, is approach 3 considered an
acceptable approach, putting aside the asthetics of defining classes in
packages which are clearly not your own?

Also. You said:
public static void initialize(Class pClass)
{ // prevents problems when multiple classloaders are in use
(web-containers, etc).
...

Could you elaborate a bit more on this. I understand the issue with multiple
class loaders (i.e. if the protocol was loaded in one class loader, it might
not be seen by another). However, I don't get what your initialize method
addresses.

Mike
 
A

Anton Spaans

Hi Michael

I'm not sure, but choosing method 2 over method 3 - or vice-versa - is a
question of taste. You presented the possible cons of the third approach
(coding classes in a package that is not yours, security issues ...). It
looks like the third approach is designed for JDK extensions.

I chose the second approach because the (location of the) new protocol
handler is only known to the container's classloader(s) and not to the
system-classloader.. just a nice separation.

Also, i assume that containers set the "java.protocol.handler.pkgs"-property
in a 'well-behaved' way, not overwriting any previous values, just
appending. But you're right, most (if not all) containers have already set
up this property before my code modifies it.

There is no real timing issue. The URL class caches protocol handlers, but
as soon as it sees a new one, it tries to resolve it. The only thing that
you have to worry about is that you call setProtocolHandlers() before you
create any URL with that particular protocol. E.g. in a web-server's servlet
I register it when the init() method is called.

About that initialize() method:
====================
Your Handler classes must be known to the system-classloader. Maybe JKD1.4
has fixed this (I haven't checked), but JDK1.3 (and earlier) implements the
URL class in such a way that user-defined protocol handler must be visible
to the system-classloader (i.e. in web-servers, your own Handler class must
be accessible through the server-bootstrap's class-path :-( )

The java.net.URL implementation uses this code to load your Handler, in its
static URLStreamHandler getURLStreamHandler(String protocol) method:
...
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
...
Because 'Class' is loaded by the system-classloader and 'cl' only will
reference the system-classloader... well, you get the picture.

So, if your protocol handler needs classes that are only known to the
container (e.g. a web-server), you have a problem. That's why i added the
'initialize(Class)' method. It stores the Class of the instance that called
the 'setProtocolHandlers(Class pClass)'. For example, the 'pClass' might be
the web-servlet. Then I can use this 'pClass' value to load classes that are
only known to the web-server: pClass.getClassLoader().loadClass(className).

-- Anton.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,141
Latest member
BlissKeto
Top