Registering and using a custom java.net.URL protocol
Create a custom
URLConnection
implementation which performs the job inconnect()
method.public class CustomURLConnection extends URLConnection { protected CustomURLConnection(URL url) { super(url); } @Override public void connect() throws IOException { // Do your job here. As of now it merely prints "Connected!". System.out.println("Connected!"); } }
Don't forget to override and implement other methods like
getInputStream()
accordingly. More detail on that cannot be given as this information is missing in the question.Create a custom
URLStreamHandler
implementation which returns it inopenConnection()
.public class CustomURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { return new CustomURLConnection(url); } }
Don't forget to override and implement other methods if necessary.
Create a custom
URLStreamHandlerFactory
which creates and returns it based on the protocol.public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("customuri".equals(protocol)) { return new CustomURLStreamHandler(); } return null; } }
Note that protocols are always lowercase.
Finally register it during application's startup via
URL#setURLStreamHandlerFactory()
URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
Note that the Javadoc explicitly says that you can set it at most once. So if you intend to support multiple custom protocols in the same application, you'd need to generify the custom
URLStreamHandlerFactory
implementation to cover them all inside thecreateURLStreamHandler()
method.Alternatively, if you dislike the Law of Demeter, throw it all together in anonymous classes for code minification:
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { public URLStreamHandler createURLStreamHandler(String protocol) { return "customuri".equals(protocol) ? new URLStreamHandler() { protected URLConnection openConnection(URL url) throws IOException { return new URLConnection(url) { public void connect() throws IOException { System.out.println("Connected!"); } }; } } : null; } });
If you're on Java 8 already, replace the
URLStreamHandlerFactory
functional interface by a lambda for further minification:URL.setURLStreamHandlerFactory(protocol -> "customuri".equals(protocol) ? new URLStreamHandler() { protected URLConnection openConnection(URL url) throws IOException { return new URLConnection(url) { public void connect() throws IOException { System.out.println("Connected!"); } }; } } : null);
Now you can use it as follows:
URLConnection connection = new URL("CustomURI:blabla").openConnection();
connection.connect();
// ...
Or with lowercased protocol as per the spec:
URLConnection connection = new URL("customuri:blabla").openConnection();
connection.connect();
// ...
If you don't want to take over the one-and-only URLStreamHandlerFactory, you can actually use a hideous, but effective naming convention to get in on the default implementation.
You must name your URLStreamHandler
class Handler
, and the protocol it maps to is the last segment of that class' package.
So, com.foo.myproto.Handler
->myproto:urls
,
provided you add your package com.foo
to the list of "url stream source packages" for lookup on unknown protocol. You do this via system property "java.protocol.handler.pkgs"
(which is a | delimited list of package names to search).
Here is an abstract class that performs what you need: (don't mind the missing StringTo<Out1<String>>
or StringURLConnection
, these do what their names suggest and you can use whatever abstractions you prefer)
public abstract class AbstractURLStreamHandler extends URLStreamHandler {
protected abstract StringTo<Out1<String>> dynamicFiles();
protected static void addMyPackage(Class<? extends URLStreamHandler> handlerClass) {
// Ensure that we are registered as a url protocol handler for JavaFxCss:/path css files.
String was = System.getProperty("java.protocol.handler.pkgs", "");
String pkg = handlerClass.getPackage().getName();
int ind = pkg.lastIndexOf('.');
assert ind != -1 : "You can't add url handlers in the base package";
assert "Handler".equals(handlerClass.getSimpleName()) : "A URLStreamHandler must be in a class named Handler; not " + handlerClass.getSimpleName();
System.setProperty("java.protocol.handler.pkgs", handlerClass.getPackage().getName().substring(0, ind) +
(was.isEmpty() ? "" : "|" + was ));
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
final String path = u.getPath();
final Out1<String> file = dynamicFiles().get(path);
return new StringURLConnection(u, file);
}
}
Then, here is the actual class implementing the abstract handler (for dynamic:
urls:
package xapi.dev.api.dynamic;
// imports elided for brevity
public class Handler extends AbstractURLStreamHandler {
private static final StringTo<Out1<String>> dynamicFiles = X_Collect.newStringMap(Out1.class,
CollectionOptions.asConcurrent(true)
.mutable(true)
.insertionOrdered(false)
.build());
static {
addMyPackage(Handler.class);
}
@Override
protected StringTo<Out1<String>> dynamicFiles() {
return dynamicFiles;
}
public static String registerDynamicUrl(String path, Out1<String> contents) {
dynamicFiles.put(path, contents);
return path;
}
public static void clearDynamicUrl(String path) {
dynamicFiles.remove(path);
}
}
You made a recursion/endless-loop.
The Classloader searching for the class in different ways.
The stacktrace (URLClassPath) is like this:
- Load a resource.
- Did i load every protocol? No!
- Load all protocoll-Handlers, i cant find the File
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - The class is a resource! Did i load every protocol? No!
- Load all protocoll-Handlers, i cant find the File
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - The class is a resource! Did i load every protocol? No!
- Load all protocoll-Handlers, i cant find the File
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - The class is a resource! Did i load every protocol? No!
- Load all protocoll-Handlers, i cant find the File
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - The class is a resource! Did i load every protocol? No!
Load all protocoll-Handlers, i cant find the File
«your java.protocol.handler.pkgs-package».CustomURI.Handler
....... StackOverflowException!!!