Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

URL.setURLStreamHandlerFactory

I am developing a web application using an executable jar with embedded Jetty.
My jar contains a dependent jar.(jar in jar)
I referenced to the JarRsrcLoader and RsrcURLStreamHandlerFactory that developed by Eclipse.
JarRsrcLoader is using URL#setURLStreamHandlerFactory(RsrcURLStreamHandlerFactory) to resolve rsrc protocol.
Thereby it can resolve class path for jar.
But it becomes impossible to solve the usual protocol as side effects.
for example file:xxxx or jar:xxxx.
RsrcURLStreamHandlerFactory has setURLStreamHandlerFactory method.
Maybe I think I should set the default implement to this method.
I don't know what set this method.

like image 313
user1160137 Avatar asked Jan 20 '12 07:01

user1160137


2 Answers

There is only one instance of a URLStreamHandlerFactory implementation registered into the Java runtime, so this implementation must be aware of all supported protocols.

The default Oracle/Sun behavior is not implemented that way but directly in the java.net.URL class. So you cannot simply inject a default implementation as a chained factory in RsrcURLStreamHandlerFactory. First part of the answer.

The java.net.URL getURLStreamHandler method loads an implementation for a protocol X according to the naming policy of its class name, by default as sun.net.www.protocol.X.Handler

If you look at jre/lib/rt.jar, you will find:

sun/net/www/protocol/ftp/Handler.class
sun/net/www/protocol/gopher/Handler.class
sun/net/www/protocol/mailto/Handler.class
sun/net/www/protocol/netdoc/Handler.class
sun/net/www/protocol/http/Handler.class
sun/net/www/protocol/jar/Handler.class
sun/net/www/protocol/file/Handler.class

The list of base packages used for the protocol URLStreamHandler selection comes from the java.protocol.handler.pkgs Java system property. I invite you to read the full source code of the java/net/URL.java from the JDK src.zip to understand details.

  • So the right way to do it (whatever IBM/Eclipse did) is to leave the default mechanism in place and set for instance -Djava.protocol.handler.pkgs="com.company.product.protocol" on the command-line (if you have permission/accreditation to do so). With a URLStreamHandler implementation named com.company.product.protocol.rsrc.Handler that uses JarRsrcLoader, you get the job done.

  • The alternate option is to write a URLStreamHandlerFactory implementation as a chained factory in RsrcURLStreamHandlerFactory inspired from URL.getURLStreamHandler source code. As an example, you can read this old JBoss code. It relies on URL internal handlers cache by preloading other known (or used) protocols before registering the factory. In my opinion, just ugly.

Warning: RsrcURLStreamHandler has replaced the original 180-lines-of-code of URLStreamHandler.parseURL by its own 10-lines "version" without calling super.parseURL. For sure it does not respect URL concatenation specifications ! Take care, you may face a bug depending on the way such URLs are used.

like image 137
Yves Martin Avatar answered Sep 23 '22 03:09

Yves Martin


As of Java 9 you can use SPI to provide handlers:

URL(String, String, int, String) constructor JavaDoc describing search algorithm:

  1. If the application has previously set up an instance of URLStreamHandlerFactory as the stream handler factory, then the createURLStreamHandler method of that instance is called with the protocol string as an argument to create the stream protocol handler.

  2. If no URLStreamHandlerFactory has yet been set up, or if the factory's createURLStreamHandler method returns null, then the ServiceLoader mechanism is used to locate URLStreamHandlerProvider implementations using the system class loader. The order that providers are located is implementation specific, and an implementation is free to cache the located providers. A ServiceConfigurationError, Error or RuntimeException thrown from the createURLStreamHandler, if encountered, will be propagated to the calling thread. The createURLStreamHandler method of each provider, if instantiated, is invoked, with the protocol string, until a provider returns non-null, or all providers have been exhausted.

  3. If the previous step fails to find a protocol handler, the constructor reads the value of the system property: java.protocol.handler.pkgs
  4. If the previous step fails to find a protocol handler, then the constructor tries to load a built-in protocol handler. If this class does not exist, or if the class exists but it is not a subclass of URLStreamHandler, then a MalformedURLException is thrown.

Protocol handlers for the following protocols are guaranteed to exist on the search path : http, https, file, and jar

like image 29
MeTTeO Avatar answered Sep 23 '22 03:09

MeTTeO