I was trying to invoke a custom url
from my java program, hence I used something like this:
URL myURL;
try {
myURL = new URL("CustomURI:");
URLConnection myURLConnection = myURL.openConnection();
myURLConnection.connect();
} catch (Exception e) {
e.printStackTrace();
}
I got the below exception:
java.net.MalformedURLException: unknown protocol: CustomURI at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at com.demo.TestDemo.main(TestDemo.java:14)
If I trigger the URI
from a browser then it works as expected but if I try to invoke it from the Java Program
then I am getting the above exception.
EDIT:
Below are the steps I tried (I am missing something for sure, please let me know on that):
Step 1: Adding the Custom URI in java.protocol.handler.pkgs
Step 2: Triggering the Custom URI from URL
Code:
public class CustomURI {
public static void main(String[] args) {
try {
add("CustomURI:");
URL uri = new URL("CustomURI:");
URLConnection uc = uri.openConnection();
uc.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void add( String handlerPackage ){
final String key = "java.protocol.handler.pkgs";
String newValue = handlerPackage;
if ( System.getProperty( key ) != null )
{
final String previousValue = System.getProperty( key );
newValue += "|" + previousValue;
}
System.setProperty( key, newValue );
System.out.println(System.getProperty("java.protocol.handler.pkgs"));
}
}
When I run this code, I am getting the CustomURI:
printed in my console (from the add method) but then I am getting this exception when the URL
is initialized with CustomURI:
as a constructor:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)
Please advice how to make this work.
Create a custom URLConnection
implementation which performs the job in connect()
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 in openConnection()
.
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 the createURLStreamHandler()
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();
// ...