web-dev-qa-db-ja.com

JavaのクラスパスからリソースをロードするURL

Javaでは、同じAPIを使用し、URLプロトコルが異なるすべての種類のリソースをロードできます。

file:///tmp.txt
http://127.0.0.1:8080/a.properties
jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

これにより、リソースを必要とするアプリケーションからリソースの実際のロードがうまく切り離されます。URLは単なる文字列であるため、リソースのロードも非常に簡単に構成できます。

現在のクラスローダーを使用してリソースをロードするプロトコルはありますか?これは、Jarプロトコルに似ていますが、リソースがどのjarファイルまたはクラスフォルダーから来ているかを知る必要がない点が異なります。

もちろん、 Class.getResourceAsStream("a.xml") を使用してそれを行うことができますが、別のAPIを使用する必要があるため、既存のコードを変更する必要があります。プロパティファイルを更新するだけで、リソースのURLを既に指定できるすべての場所でこれを使用できるようにしたいと考えています。

189
Thilo
URL url = getClass().getClassLoader().getResource("someresource.xxx");

それはそれを行う必要があります。

92
Rasin

これは独自の答えの価値があると思います-Springを使用している場合は、すでにこれを持っています

Resource firstResource =
    context.getResource("http://www.google.fi/");
Resource anotherResource =
    context.getResource("classpath:some/resource/path/myTemplate.txt");

spring documentation で説明され、skaffmanのコメントで指摘されているように。

12
eis

起動時にプログラムでプロパティを設定することもできます。

final String key = "Java.protocol.handler.pkgs";
String newValue = "org.my.protocols";
if (System.getProperty(key) != null) {
    final String previousValue = System.getProperty(key);
    newValue += "|" + previousValue;
}
System.setProperty(key, newValue);

このクラスの使用:

package org.my.protocols.classpath;

import Java.io.IOException;
import Java.net.URL;
import Java.net.URLConnection;
import Java.net.URLStreamHandler;

public class Handler extends URLStreamHandler {

    @Override
    protected URLConnection openConnection(final URL u) throws IOException {
        final URL resourceUrl = ClassLoader.getSystemClassLoader().getResource(u.getPath());
        return resourceUrl.openConnection();
    }
}

したがって、これを行うには最も邪魔にならない方法を使用します。 :) Java.net.URLは常にシステムプロパティの現在の値を使用します。

10
subes

カスタムハンドラーのセットアップ時のエラーを減らし、システムプロパティを利用するクラスを作成したので、最初にメソッドを呼び出したり、適切なコンテナー内にいなくても問題はありません。物事が間違っている場合は、例外クラスもあります。

CustomURLScheme.Java:
/*
 * The CustomURLScheme class has a static method for adding cutom protocol
 * handlers without getting bogged down with other class loaders and having to
 * call setURLStreamHandlerFactory before the next guy...
 */
package com.cybernostics.lib.net.customurl;

import Java.net.URLStreamHandler;
import Java.util.regex.Matcher;
import Java.util.regex.Pattern;

/**
 * Allows you to add your own URL handler without running into problems
 * of race conditions with setURLStream handler.
 * 
 * To add your custom protocol eg myprot://blahblah:
 * 
 * 1) Create a new protocol package which ends in myprot eg com.myfirm.protocols.myprot
 * 2) Create a subclass of URLStreamHandler called Handler in this package
 * 3) Before you use the protocol, call CustomURLScheme.add(com.myfirm.protocols.myprot.Handler.class);
 * @author jasonw
 */
public class CustomURLScheme
{

    // this is the package name required to implelent a Handler class
    private static Pattern packagePattern = Pattern.compile( "(.+\\.protocols)\\.[^\\.]+" );

    /**
     * Call this method with your handlerclass
     * @param handlerClass
     * @throws Exception 
     */
    public static void add( Class<? extends URLStreamHandler> handlerClass ) throws Exception
    {
        if ( handlerClass.getSimpleName().equals( "Handler" ) )
        {
            String pkgName = handlerClass.getPackage().getName();
            Matcher m = packagePattern.matcher( pkgName );

            if ( m.matches() )
            {
                String protocolPackage = m.group( 1 );
                add( protocolPackage );
            }
            else
            {
                throw new CustomURLHandlerException( "Your Handler class package must end in 'protocols.yourprotocolname' eg com.somefirm.blah.protocols.yourprotocol" );
            }

        }
        else
        {
            throw new CustomURLHandlerException( "Your handler class must be called 'Handler'" );
        }
    }

    private static void add( String handlerPackage )
    {
        // this property controls where Java looks for
        // stream handlers - always uses current value.
        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 );
    }
}


CustomURLHandlerException.Java:
/*
 * Exception if you get things mixed up creating a custom url protocol
 */
package com.cybernostics.lib.net.customurl;

/**
 *
 * @author jasonw
 */
public class CustomURLHandlerException extends Exception
{

    public CustomURLHandlerException(String msg )
    {
        super( msg );
    }

}
5
Jason Wraxall

Azderの答え に似ていますが、わずかに異なるタクトです。)

クラスパスのコンテンツ用の定義済みプロトコルハンドラーがあるとは思わない。 (いわゆるclasspath:プロトコル)。

ただし、Javaでは独自のプロトコルを追加できます。これは、具体的な実装 Java.net.URLStreamHandler および Java.net.URLConnection を提供することにより行われます。

この記事では、カスタムストリームハンドラーの実装方法について説明します。 http://Java.Sun.com/developer/onlineTraining/protocolhandlers/

5
Dilum Ranatunga

@Stephenに触発 https://stackoverflow.com/a/1769454/980442 および http://docstore.mik.ua/orelly/Java/exp/ch09_06.htm =

使用するには

new URL("classpath:org/my/package/resource.extension").openConnection()

このクラスをSun.net.www.protocol.classpathパッケージに作成し、それをOracle JVM実装で実行するだけで、チャームのように機能します。

package Sun.net.www.protocol.classpath;

import Java.io.IOException;
import Java.net.URL;
import Java.net.URLConnection;
import Java.net.URLStreamHandler;

public class Handler extends URLStreamHandler {

    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        return Thread.currentThread().getContextClassLoader().getResource(u.getPath()).openConnection();
    }
}

別のJVM実装を使用している場合は、Java.protocol.handler.pkgs=Sun.net.www.protocolシステムプロパティを設定します。

参考: http://docs.Oracle.com/javase/7/docs/api/Java/net/URL.html#URL(Java.lang.String、%20Java.lang.String、%20int、 %20Java.lang.String)

4
Daniel De León

もちろん、URLStreamHandlersを登録するソリューションは最も正確ですが、場合によっては最も簡単なソリューションが必要になります。そのため、次の方法を使用します。

/**
 * Opens a local file or remote resource represented by given path.
 * Supports protocols:
 * <ul>
 * <li>"file": file:///path/to/file/in/filesystem</li>
 * <li>"http" or "https": http://Host/path/to/resource - gzipped resources are supported also</li>
 * <li>"classpath": classpath:path/to/resource</li>
 * </ul>
 *
 * @param path An URI-formatted path that points to resource to be loaded
 * @return Appropriate implementation of {@link InputStream}
 * @throws IOException in any case is stream cannot be opened
 */
public static InputStream getInputStreamFromPath(String path) throws IOException {
    InputStream is;
    String protocol = path.replaceFirst("^(\\w+):.+$", "$1").toLowerCase();
    switch (protocol) {
        case "http":
        case "https":
            HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();
            int code = connection.getResponseCode();
            if (code >= 400) throw new IOException("Server returned error code #" + code);
            is = connection.getInputStream();
            String contentEncoding = connection.getContentEncoding();
            if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip"))
                is = new GZIPInputStream(is);
            break;
        case "file":
            is = new URL(path).openStream();
            break;
        case "classpath":
            is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path.replaceFirst("^\\w+:", ""));
            break;
        default:
            throw new IOException("Missed or unsupported protocol in path '" + path + "'");
    }
    return is;
}
3
domax

Java 9以降では、新しいURLStreamHandlerProviderを定義できます。 URLクラスは、サービスローダーフレームワークを使用して実行時にロードします。

プロバイダーを作成します。

package org.example;

import Java.io.IOException;
import Java.net.URL;
import Java.net.URLConnection;
import Java.net.URLStreamHandler;
import Java.net.spi.URLStreamHandlerProvider;

public class ClasspathURLStreamHandlerProvider extends URLStreamHandlerProvider {

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        if ("classpath".equals(protocol)) {
            return new URLStreamHandler() {
                @Override
                protected URLConnection openConnection(URL u) throws IOException {
                    return ClassLoader.getSystemClassLoader().getResource(u.getPath()).openConnection();
                }
            };
        }
        return null;
    }

}

Java.net.spi.URLStreamHandlerProviderという名前のファイルをMETA-INF/servicesディレクトリに作成し、その内容を次のようにします。

org.example.ClasspathURLStreamHandlerProvider

これで、URLクラスは次のようなものを見たときにプロバイダーを使用します。

URL url = new URL("classpath:myfile.txt");
2
mhvelplund

既に存在するかどうかはわかりませんが、自分で簡単に作成できます。

その異なるプロトコルの例は、ファサードパターンのように見えます。ケースごとに異なる実装がある場合、共通のインターフェースがあります。

同じ原則を使用して、プロパティファイルから文字列を取得し、私たちのカスタムプロトコルをチェックするResourceLoaderクラスを作成できます

myprotocol:a.xml
myprotocol:file:///tmp.txt
myprotocol:http://127.0.0.1:8080/a.properties
myprotocol:jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

myprotocol:を文字列の先頭から削除し、リソースをロードする方法を決定して、リソースを提供します。

2
Azder

Dilumsの答え の拡張:

コードを変更せずに、おそらくDilumが推奨するURL関連インターフェースのカスタム実装を追求する必要があります。あなたのために物事を簡単にするために、私は Spring Frameworkのリソース のソースを見ることをお勧めします。コードはストリームハンドラーの形式ではありませんが、目的の処理を正確に実行するように設計されており、ASL 2.0ライセンスの下にあります。

2
DavidValeri

クラスパスにTomcatがある場合、次のように簡単です。

TomcatURLStreamHandlerFactory.register();

これにより、「war」および「classpath」プロトコルのハンドラーが登録されます。

1

URLクラスを避けて、代わりにURIに依存しようとします。したがって、Springを使用しないルックアップのようなSpring Resourceを実行したいURLを必要とするものについては、次のようにします:

public static URL toURL(URI u, ClassLoader loader) throws MalformedURLException {
    if ("classpath".equals(u.getScheme())) {
        String path = u.getPath();
        if (path.startsWith("/")){
            path = path.substring("/".length());
        }
        return loader.getResource(path);
    }
    else if (u.getScheme() == null && u.getPath() != null) {
        //Assume that its a file.
        return new File(u.getPath()).toURI().toURL();
    }
    else {
        return u.toURL();
    }
}

URIを作成するには、URI.create(..)を使用できます。この方法は、リソース検索を行うClassLoaderを制御するため、より優れています。

スキームを検出するためにURLを文字列として解析しようとする他の回答に気付きました。 URIを渡し、代わりに解析に使用する方が良いと思います。

Spring Sourceは、他のすべてのSpringのものが不要になるように、coreからリソースコードを分離するように頼んで、実際に問題を提出しました。

0
Adam Gent

Spring Bootアプリでは、次を使用してファイルURLを取得し、

Thread.currentThread().getContextClassLoader().getResource("PromotionalOfferIdServiceV2.wsdl")
0
Sathesh