web-dev-qa-db-ja.com

Java 9の実行時にクラスパス/モジュールパスをスキャンしています

実行可能なすべてのクラス(インターフェース、注釈など)のスキャンが、Spring、Reflections、およびその他の多くのフレームワークとライブラリが現在実行している方法で、ジグソーに関連する変更に直面してまだ可能かどうかについての情報を見つけることができないようですクラスのロード方法。

[〜#〜] edit [〜#〜]:この質問はスキャンクラスを探す実際の物理ファイルパス。 他の質問 は、動的にloadingクラスとリソースについてです。それは関連していますが、非常に重複ではありません

[〜#〜] update [〜#〜]:Jettyプロジェクトが JEPの提案 これのための標準化されたAPI。この実現に役立つ方法がある場合は、実行してください。それ以外の場合は、お待ちください。

UPDATE 2:見つかりました this 関連するサウンド投稿。後世のコードスニペットを引用:

本当にブートレイヤーのモジュール(起動時に解決されるモジュール)のコンテンツを取得するだけの場合は、次のようにします。

  ModuleLayer.boot().configuration().modules().stream()
         .map(ResolvedModule::reference)
         .forEach(mref -> {
             System.out.println(mref.descriptor().name());
             try (ModuleReader reader = mref.open()) {
                 reader.list().forEach(System.out::println);
            } catch (IOException ioe) {
                 throw new UncheckedIOException(ioe);
             }
         });
18
kaqqao

次のコードは、Java 9+(Jigsaw/JPMS)でモジュールパススキャンを実行します。これは、コールスタック上のすべてのクラスを検索し、各クラス参照に対してclassRef.getModule().getLayer().getConfiguration().modules()を呼び出します。 _List<ResolvedModule>_ではなく_List<Module>_を返します(ResolvedModuleはモジュールリソースへのアクセスを提供しますが、Moduleはアクセスしません。)各モジュールのResolvedModule参照を指定すると、.reference()メソッドは、モジュールのModuleReferenceを取得します。ModuleReference#open()ModuleReaderを提供します。これにより、ModuleReader#list()を使用してモジュール内のリソースを一覧表示したり、 Optional<InputStream> ModuleReader#open(resourcePath)またはOptional<ByteBuffer> ModuleReader#read(resourcePath)。モジュールが完了したら、ModuleReaderを閉じます。これは、私が見たどこにも文書化されていません。これらすべてを理解するのは非常に困難でした。しかし、他の誰かがこれから利益を得ることを期待して、ここにコードがあります。

JDK9 +でも、モジュールパス要素と一緒に従来のクラスパス要素を利用できるため、完全なモジュールパス+クラスパススキャンを行うには、おそらく ClassGraph などの適切なクラスパススキャンソリューションを使用する必要があります。以下のメカニズムを使用してモジュールのスキャンをサポートします(免責事項、私は著者です)。次のコードのリフレクションベースのバージョン here を見つけることができます。

また、JDK 9以降のいくつかのJDKリリースのStackWalkerに bug があったため、回避する必要があることに注意してください。詳細については、上記のリフレクションベースのコードを参照してください。

_package main;

import Java.lang.StackWalker;
import Java.lang.StackWalker.Option;
import Java.lang.StackWalker.StackFrame;
import Java.lang.module.ModuleReader;
import Java.lang.module.ModuleReference;
import Java.lang.module.ResolvedModule;
import Java.net.URI;
import Java.security.AccessController;
import Java.security.PrivilegedAction;
import Java.util.AbstractMap.SimpleEntry;
import Java.util.ArrayDeque;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Deque;
import Java.util.HashSet;
import Java.util.List;
import Java.util.Map.Entry;
import Java.util.Optional;
import Java.util.Set;
import Java.util.stream.Stream;

public class Java9Scanner {

    /** Recursively find the topological sort order of ancestral layers. */
    private static void findLayerOrder(ModuleLayer layer,
            Set<ModuleLayer> visited, Deque<ModuleLayer> layersOut) {
        if (visited.add(layer)) {
            List<ModuleLayer> parents = layer.parents();
            for (int i = 0; i < parents.size(); i++) {
                findLayerOrder(parents.get(i), visited, layersOut);
            }
            layersOut.Push(layer);
        }
    }

    /** Get ModuleReferences from a Class reference. */
    private static List<Entry<ModuleReference, ModuleLayer>> findModuleRefs(
            Class<?>[] callStack) {
        Deque<ModuleLayer> layerOrder = new ArrayDeque<>();
        Set<ModuleLayer> visited = new HashSet<>();
        for (int i = 0; i < callStack.length; i++) {
            ModuleLayer layer = callStack[i].getModule().getLayer();
            findLayerOrder(layer, visited, layerOrder);
        }
        Set<ModuleReference> addedModules = new HashSet<>();
        List<Entry<ModuleReference, ModuleLayer>> moduleRefs = new ArrayList<>();
        for (ModuleLayer layer : layerOrder) {
            Set<ResolvedModule> modulesInLayerSet = layer.configuration()
                    .modules();
            final List<Entry<ModuleReference, ModuleLayer>> modulesInLayer =
                    new ArrayList<>();
            for (ResolvedModule module : modulesInLayerSet) {
                modulesInLayer
                        .add(new SimpleEntry<>(module.reference(), layer));
            }
            // Sort modules in layer by name for consistency
            Collections.sort(modulesInLayer,
                    (e1, e2) -> e1.getKey().descriptor().name()
                            .compareTo(e2.getKey().descriptor().name()));
            // To be safe, dedup ModuleReferences, in case a module occurs in multiple
            // layers and reuses its ModuleReference (no idea if this can happen)
            for (Entry<ModuleReference, ModuleLayer> m : modulesInLayer) {
                if (addedModules.add(m.getKey())) {
                    moduleRefs.add(m);
                }
            }
        }
        return moduleRefs;
    }

    /** Get the classes in the call stack. */
    private static Class<?>[] getCallStack() {
        // Try StackWalker (JDK 9+)
        PrivilegedAction<Class<?>[]> stackWalkerAction =
                (PrivilegedAction<Class<?>[]>) () ->
                    StackWalker.getInstance(
                            Option.RETAIN_CLASS_REFERENCE)
                    .walk(s -> s.map(
                            StackFrame::getDeclaringClass)
                            .toArray(Class[]::new));
        try {
            // Try with doPrivileged()
            return AccessController
                    .doPrivileged(stackWalkerAction);
        } catch (Exception e) {
        }
        try {
            // Try without doPrivileged()
            return stackWalkerAction.run();
        } catch (Exception e) {
        }

        // Try SecurityManager
        PrivilegedAction<Class<?>[]> callerResolverAction = 
                (PrivilegedAction<Class<?>[]>) () ->
                    new SecurityManager() {
                        @Override
                        public Class<?>[] getClassContext() {
                            return super.getClassContext();
                        }
                    }.getClassContext();
        try {
            // Try with doPrivileged()
            return AccessController
                    .doPrivileged(callerResolverAction);
        } catch (Exception e) {
        }
        try {
            // Try without doPrivileged()
            return callerResolverAction.run();
        } catch (Exception e) {
        }

        // As a fallback, use getStackTrace() to try to get the call stack
        try {
            throw new Exception();
        } catch (final Exception e) {
            final List<Class<?>> classes = new ArrayList<>();
            for (final StackTraceElement elt : e.getStackTrace()) {
                try {
                    classes.add(Class.forName(elt.getClassName()));
                } catch (final Throwable e2) {
                    // Ignore
                }
            }
            if (classes.size() > 0) {
                return classes.toArray(new Class<?>[0]);
            } else {
                // Last-ditch effort -- include just this class
                return new Class<?>[] { Java9Scanner.class };
            }
        }
    }

    /**
     * Return true if the given module name is a system module.
     * There can be system modules in layers above the boot layer.
     */
    private static boolean isSystemModule(
            final ModuleReference moduleReference) {
        String name = moduleReference.descriptor().name();
        if (name == null) {
            return false;
        }
        return name.startsWith("Java.") || name.startsWith("jdk.")
            || name.startsWith("javafx.") || name.startsWith("Oracle.");
    }

    public static void main(String[] args) throws Exception {
        // Get ModuleReferences for modules of all classes in call stack,
        List<Entry<ModuleReference, ModuleLayer>> systemModuleRefs = new ArrayList<>();
        List<Entry<ModuleReference, ModuleLayer>> nonSystemModuleRefs = new ArrayList<>();

        Class<?>[] callStack = getCallStack();
        List<Entry<ModuleReference, ModuleLayer>> moduleRefs = findModuleRefs(
                callStack);
        // Split module refs into system and non-system modules based on module name
        for (Entry<ModuleReference, ModuleLayer> m : moduleRefs) {
            (isSystemModule(m.getKey()) ? systemModuleRefs
                    : nonSystemModuleRefs).add(m);
        }

        // List system modules
        System.out.println("\nSYSTEM MODULES:\n");
        for (Entry<ModuleReference, ModuleLayer> e : systemModuleRefs) {
            ModuleReference ref = e.getKey();
            System.out.println("  " + ref.descriptor().name());
        }

        // Show info for non-system modules
        System.out.println("\nNON-SYSTEM MODULES:");
        for (Entry<ModuleReference, ModuleLayer> e : nonSystemModuleRefs) {
            ModuleReference ref = e.getKey();
            ModuleLayer layer = e.getValue();
            System.out.println("\n  " + ref.descriptor().name());
            System.out.println(
                    "    Version: " + ref.descriptor().toNameAndVersion());
            System.out.println(
                    "    Packages: " + ref.descriptor().packages());
            System.out.println("    ClassLoader: "
                    + layer.findLoader(ref.descriptor().name()));
            Optional<URI> location = ref.location();
            if (location.isPresent()) {
                System.out.println("    Location: " + location.get());
            }
            try (ModuleReader moduleReader = ref.open()) {
                Stream<String> stream = moduleReader.list();
                stream.forEach(s -> System.out.println("      File: " + s));
            }
        }
    }
}
_
21
Luke Hutchison

ここでの実際の問題は、クラスパス上のすべてのjarおよびフォルダーへのパスを見つけることです。一度入手したら、スキャンできます。

私がしたことは次のとおりです:

  • 現在のクラスの現在のモジュール記述子を取得します
  • すべてのrequiresモジュールを取得する
  • そのようなモジュールごとにMANIFEST.MFのオープンリソース
  • リソースURLからMANIFEST.MFパスを削除します
  • 残っているのはモジュールのクラスパス、つまりjarまたはフォルダーです。

現在のコードのクラスパスを取得するために、現在のモジュールでも同じことを行います。

このようにして、現在機能しているモジュールと必要なすべてのモジュールのクラスパスを収集します(1ステップ先)。それは私にとってはうまくいきました-そして私のJava8スキャナーはまだ仕事をすることができていました。このアプローチでは、追加のVMフラグなどは必要ありません。

このアプローチを拡張してall必要なモジュールを(最初のレベルだけでなく)簡単に取得できますが、今のところは必要ありません。

コード

2
igr