Регистрация и использование пользовательского протокола 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.main(TestDemo.java:14)
Если я запускаю URI
из браузера, то он работает так, как ожидалось, но если я попытаюсь вызвать его из Java Program
, то я получаю исключение выше.
EDIT:
Ниже приведены шаги, которые я пробовал (я что-то пропустил точно, сообщите мне об этом):
Шаг 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.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)
Пожалуйста, советую, как это сделать.
Ответы
Ответ 1
-
Создайте пользовательскую 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();
// ...
Ответ 2
Вы сделали рекурсию/бесконечный цикл.
Класс Loader ищет класс по-разному.
Элемент stacktrace (URLClassPath) выглядит следующим образом:
- Загрузите ресурс.
- Я загрузил каждый протокол? Нет!
- Загрузите все обработчики протоколов, я не могу найти файл
«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!!!
Ответ 3
Если вы не хотите использовать одностраничный 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);
}
}
Ответ 4
Если все, что вы пытаетесь сделать, это вызвать схему URI, которая уже зарегистрирована в системе, и вы не против использовать код для Windows, чтобы сделать это (вы упомянули реестр, поэтому я предполагаю, что ваш код только для Windows), вы можете сделать это, если CMD "start" builtin вызовет ShellExecute для вас. Это не связывает вашу Java-программу с URL-адресом, но если вы просто хотите вызвать обработчик URI, тогда он отлично работает:
public static void main (String[] args)
{
Runtime run = Runtime.getRuntime();
try {
run.exec("cmd /c CustomURI:");
} catch (Exception ex) {
System.err.println(ex.toString());
}
}
Это полный взлом, конечно, и имеет накладные расходы на выполнение вызова CMD.EXE, но он может работать для вашего дела. Несколько более элегантный подход заключается в вызове ShellExecuteW (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx) через JNI, но это справедливо. Код, который вы вызываете (C/С++),
ShellExecuteW(NULL, NULL, L"CustomURI:", NULL, NULL, 1 /* SW_SHOWNORMAL */);