Регистрация и использование собственного протокола java.net.URL
Я пытался вызвать custom url
из моей программы Java, следовательно, я использовал что-то вроде этого:
URL myURL;
try {
myURL = new URL("CustomURI:");
URLConnection myURLConnection = myURL.openConnection();
myURLConnection.connect();
} catch (Exception e) {
e.printStackTrace();
}
Я получил следующее исключение:
java.net.MalformedURLException: неизвестный протокол: CustomURI на java.net.URL.(неизвестный источник) на java.net.URL.(неизвестный источник) на java.net.URL.(неизвестный источник) на com.demo.TestDemo. Основной (TestDemo.java:14)
Если я вызову URI
из браузера, то он работает как положено, но если я пытаюсь вызвать его из Java Program
тогда я получаю вышеуказанное исключение.
РЕДАКТИРОВАТЬ:
Ниже приведены шаги, которые я попробовал (я что-то упустил, пожалуйста, дайте мне знать об этом):
Шаг 1. Добавление пользовательского URI вjava.protocol.handler.pkgs
Шаг 2: Запуск пользовательского URI из URL
Код:
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"));
}
}
Когда я запускаю этот код, я получаю CustomURI:
напечатано в моей консоли (из метода add), но затем я получаю это исключение, когда URL
инициализируется с CustomURI:
как конструктор:
Exception in thread "main" java.lang.StackruError
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)
Пожалуйста, совет, как сделать эту работу.
3 ответа
Создать кастом
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()
метод.В качестве альтернативы, если вам не нравится Закон Деметры, бросьте все это вместе в анонимные классы для минимизации кода:
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
Функциональный интерфейс лямбда для дальнейшей минимизации: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 connection = new URL("CustomURI:blabla").openConnection();
connection.connect();
// ...
Или с протоколом в нижнем регистре согласно спецификации:
URLConnection connection = new URL("customuri:blabla").openConnection();
connection.connect();
// ...
Если вы не хотите брать на себя единственный-единственный URLStreamHandlerFactory, вы можете использовать отвратительное, но эффективное соглашение об именах, чтобы войти в реализацию по умолчанию.
Вы должны назвать свой URLStreamHandler
учебный класс Handler
и протокол, к которому он привязан, является последним сегментом пакета этого класса.
Так, com.foo.myproto.Handler
->myproto:urls
при условии, что вы добавите свой пакет com.foo
в список "пакетов источника потока url" для поиска по неизвестному протоколу. Вы делаете это через системное свойство "java.protocol.handler.pkgs"
(который представляет собой разделенный | список имен пакетов для поиска).
Вот абстрактный класс, который выполняет то, что вам нужно: StringTo<Out1<String>>
или же 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);
}
}
Тогда вот фактический класс, реализующий абстрактный обработчик (для 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);
}
}
Вы сделали рекурсию / бесконечный цикл.
Classloader ищет класс по-разному.
Трассировка стека (URLClassPath) выглядит следующим образом:
- Загрузите ресурс.
- Я загрузил каждый протокол? Нет!
- Загрузите все обработчики protocoll, я не могу найти файл
«your java.protocol.handler.pkgs-package».CustomURI.Handler
, - Класс это ресурс! Я загрузил каждый протокол? Нет!
- Загрузите все обработчики protocoll, я не могу найти файл
«your java.protocol.handler.pkgs-package».CustomURI.Handler
, - Класс это ресурс! Я загрузил каждый протокол? Нет!
- Загрузите все обработчики protocoll, я не могу найти файл
«your java.protocol.handler.pkgs-package».CustomURI.Handler
, - Класс это ресурс! Я загрузил каждый протокол? Нет!
- Загрузите все обработчики protocoll, я не могу найти файл
«your java.protocol.handler.pkgs-package».CustomURI.Handler
, - Класс это ресурс! Я загрузил каждый протокол? Нет!
Загрузите все обработчики protocoll, я не могу найти файл
«your java.protocol.handler.pkgs-package».CustomURI.Handler
,...... StackruException!!!