2017-02-05 13 views
3

ジャワ9は、反射ベースのJavaコンポーネントを介してアクセスモジュール化排除です。これにより、classpathとjava.library.pathをプログラムで無効にする拡張方法のほとんどがレンダリングされます。どのようにそれを正しく行うには?そしてそれをjava.sql.DriverManagerとjavax.activationとの互換性を持たせる方法は?ジャワ9クラスパスとライブラリパス拡張

+1

「ディスカッション・スターター」オーバーフローがどのように機能するか、スタック、質問などしてくださいフレーズあなたの問題を、その後、あなたのソリューションを持つ**答え**ではありません。 –

+0

あなたの要件を満たしているので、アンサーを元に戻してください。 – user579013

+0

私はそれを元に戻すために司会者の注意を喚起しました。 –

答えて

4

以下は、プログラムで反射やパブリックでないメソッドやフィールドにアクセスしようとせずに、「許可」方式でクラスパスやjava.library.pathにどちらかを拡張する方法の研究の時間の結果です。また、JDBCドライバが呼び出し側クラスのClassLoaderとは異なるClassLoaderで作成された場合、java.sql.DriverManagerを "is class authorized"チェックとしてバイパスする方法を示します。これはJava 8とJava 9でテストされており、両方の環境で動作しています(URLClassLoaderコードは1.1に戻って動作するはずです)。

これは、アプリケーション全体で使用される基本クラスローダです:メインで

public class MiscTools 
{ 
    private static class SpclClassLoader extends URLClassLoader 
    { 
     static 
     { 
      ClassLoader.registerAsParallelCapable(); 
     } 

     private final Set<Path> userLibPaths = new CopyOnWriteArraySet<>(); 

     private SpclClassLoader() 
     { 
      super(new URL[0]); 
     } 

     @Override 
     protected void addURL(URL url) 
     { 
      super.addURL(url); 
     } 

     protected void addLibPath(String newpath) 
     { 
      userLibPaths.add(Paths.get(newpath).toAbsolutePath()); 
     } 

     @Override 
     protected String findLibrary(String libname) 
     { 
      String nativeName = System.mapLibraryName(libname); 
      return userLibPaths.stream().map(tpath -> tpath.resolve(nativeName)).filter(Files::exists).map(Path::toString).findFirst().orElse(super.findLibrary(libname));   } 
    } 
    private final static SpclClassLoader ucl = new SpclClassLoader(); 

    /** 
    * Adds a jar file or directory to the classpath. From Utils4J. 
    * 
    * @param newpaths JAR filename(s) or directory(s) to add 
    * @return URLClassLoader after newpaths added if newpaths != null 
    */ 
    public static ClassLoader addToClasspath(String... newpaths) 
    { 
     if (newpaths != null) 
      try 
      { 
       for (String newpath : newpaths) 
        if (newpath != null && !newpath.trim().isEmpty()) 
         ucl.addURL(Paths.get(newpath.trim()).toUri().toURL()); 
      } 
      catch (IllegalArgumentException | MalformedURLException e) 
      { 
       RuntimeException re = new RuntimeException(e); 
       re.setStackTrace(e.getStackTrace()); 
       throw re; 
      } 
     return ucl; 
    } 

    /** 
    * Adds to library path in ClassLoader returned by addToClassPath 
    * 
    * @param newpaths Path(s) to directory(s) holding OS library files 
    */ 
    public static void addToLibraryPath(String... newpaths) 
    { 
     for (String newpath : Objects.requireNonNull(newpaths)) 
      ucl.addLibPath(newpath); 
    } 
} 

早期()持つjavax.activationのようなものを処理するために、次のコードを配置します。

Thread.currentThread().setContextClassLoader(MiscTools.addToClasspath()); 

すべてのスレッド(java.util.concurrent.Executorsによって作成されたものを含む)は、コンテキストClassLoaderを継承します。拡張されたクラスパスからロードされたクラスについて

、次のコードを使用しDriverManager.getDriverの呼び出しクラス(かどうかを確認するためにチェックするのjava.sql.DriverManagerをバイパスする方法、最後

try 
{ 
    Class.forName(classname, true, MiscTools.addToClasspath(cptoadd); 
} 
catch (ClassNotFoundException IllegalArgumentException | SecurityException e) 
{ 
    classlogger.log(Level.WARNING, "Error loading ".concat(props.getProperty("Class")), e); 
} 

)をClassLoaderは、JDBCドライバをロードするために使用されるClassLoaderと同じです(呼び出し元のクラスがアプリケーションClassLoaderによってロードされても、ドライバがSpclClassLoaderを使用してロードされている場合は表示されません)。

private final static CopyOnWriteArraySet<Driver> loadedDrivers = new CopyOnWriteArraySet<>(); 

private static Driver isLoaded(String drivername, String... classpath) throws ClassNotFoundException 
{ 
    Driver tdriver = loadedDrivers.stream().filter(d -> d.getClass().getName().equals(drivername)).findFirst().orElseGet(() -> 
    { 
     try 
     { 
      Driver itdriver = (Driver) Class.forName(drivername, true, addToClasspath(classpath)).newInstance(); 
      loadedDrivers.add(itdriver); 
      return itdriver; 
     } 
     catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) 
     { 
      return null; 
     } 
    }); 
    if (tdriver == null) 
     throw new java.lang.ClassNotFoundException(drivername + " not found."); 
    return tdriver; 
} 

isLoaderは、要求されたドライバを提供しながら、私たちは余分なオーバーヘッドのすべてと同じドライバの束をロードしない保証します。欠点は、DriverManagerが行うURL検索だけでなく、Class.forName関数を実行しなくても起動時にJDBCクラスがロードされることをJDBCクラスに要求することです(誰でもこれを公開します)。

これは、私が書いたアプリケーションでこのアプローチを改良するのに大量の時間を費やさないように助けてくれることを願っています。これは多くのプラットフォームや、多くの構成で使用されています。プロパティファイルを開き、library.pathを拡張して、デフォルトのlibrary.pathにないネイティブライブラリを使用するようにします(プロパティファイルでも記述されています)。

+0

誰かがClassLoader.findLibrary()のデフォルトの動作を変更してnull以外を返す場合に備えて、findLibrary()を更新してsuper.findLibrary()を呼び出すようにしました。 – user579013

+0

ディレクトリパスの代わりにネイティブライブラリへの絶対パスを返すようにfindLibrary()を修正しました。 – user579013