注册和使用自定义 java.net.URL 协议
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26363573/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Registering and using a custom java.net.URL protocol
提问by user182944
I was trying to invoke a custom url
from my java program, hence I used something like this:
我试图custom url
从我的 java 程序中调用 a ,因此我使用了这样的东西:
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)
java.net.MalformedURLException: 未知协议:CustomURI at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at com.demo.TestDemo。主要(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.
如果我URI
从浏览器触发,那么它会按预期工作,但如果我尝试从浏览器调用它,Java Program
则会出现上述异常。
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
第 1 步:在中添加自定义 URI java.protocol.handler.pkgs
Step 2: Triggering the Custom URI from URL
第 2 步:从 URL 触发自定义 URI
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:
当我运行这段代码时,我CustomURI:
在我的控制台中打印出来(来自 add 方法)但是当我作为构造函数URL
初始化时我得到这个异常CustomURI:
:
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.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.
请建议如何使这项工作。
采纳答案by BalusC
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 alwayslowercase.
Finally register it during application's startup via
URL#setURLStreamHandlerFactory()
URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
Note that the Javadocexplicitly 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);
创建
URLConnection
在connect()
方法中执行作业的自定义实现。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!"); } }
不要忘记相应地覆盖和实现其他方法
getInputStream()
。由于问题中缺少此信息,因此无法提供更多详细信息。创建一个自定义
URLStreamHandler
实现,在openConnection()
.public class CustomURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { return new CustomURLConnection(url); } }
如有必要,不要忘记覆盖和实现其他方法。
创建一个自定义
URLStreamHandlerFactory
,根据协议创建并返回它。public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("customuri".equals(protocol)) { return new CustomURLStreamHandler(); } return null; } }
请注意,协议始终是小写的。
最后在应用程序启动期间注册它
URL#setURLStreamHandlerFactory()
URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
请注意,Javadoc明确指出您最多可以设置一次。因此,如果您打算在同一应用程序中支持多个自定义协议,则需要泛化自定义
URLStreamHandlerFactory
实现以将它们全部包含在createURLStreamHandler()
方法中。或者,如果您不喜欢 Demeter 法则,请将其全部放在匿名类中以进行代码精简:
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; } });
如果您已经使用 Java 8,请用
URLStreamHandlerFactory
lambda替换功能接口以进一步缩小: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();
// ...
回答by Grim
You made a recursion/endless-loop.
你做了一个递归/无限循环。
The Classloader searching for the class in different ways.
类加载器以不同的方式搜索类。
The stacktrace (URLClassPath) is like this:
堆栈跟踪(URLClassPath)是这样的:
- 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!!!
- 加载资源。
- 我是否加载了每个协议?不!
- 加载所有协议处理程序,我找不到文件
?your java.protocol.handler.pkgs-package?.CustomURI.Handler
。 - 课堂是一种资源!我是否加载了每个协议?不!
- 加载所有协议处理程序,我找不到文件
?your java.protocol.handler.pkgs-package?.CustomURI.Handler
。 - 课堂是一种资源!我是否加载了每个协议?不!
- 加载所有协议处理程序,我找不到文件
?your java.protocol.handler.pkgs-package?.CustomURI.Handler
。 - 课堂是一种资源!我是否加载了每个协议?不!
- 加载所有协议处理程序,我找不到文件
?your java.protocol.handler.pkgs-package?.CustomURI.Handler
。 - 课堂是一种资源!我是否加载了每个协议?不!
加载所有协议处理程序,我找不到文件
?your java.protocol.handler.pkgs-package?.CustomURI.Handler
。...... StackOverflowException!!!
回答by Ajax
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.
如果您不想接管唯一的 URLStreamHandlerFactory,您实际上可以使用一个可怕但有效的命名约定来获得默认实现。
You mustname your URLStreamHandler
class Handler
, and the protocol it maps to is the last segment of that class' package.
您必须命名您的URLStreamHandler
class Handler
,它映射到的协议是该类包的最后一段。
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).
因此,com.foo.myproto.Handler
-> myproto:urls
,前提是您将包添加com.foo
到“url 流源包”列表中以查找未知协议。您可以通过系统属性"java.protocol.handler.pkgs"
(这是一个 | 分隔的要搜索的包名称列表)执行此操作。
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)
这是一个执行您需要的抽象类:(不要介意缺少的StringTo<Out1<String>>
or StringURLConnection
,它们按照其名称进行操作,您可以使用任何您喜欢的抽象)
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:
然后,这是实现抽象处理程序的实际类(对于dynamic:
url:
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);
}
}