Find classes that implement interfaces or being subclasses/superclasses in maven CLASSPATH?

廉价感情. 提交于 2021-01-01 07:33:31

问题


VisualVM OQL queries can't query for interfaces because current heap dump format doesn't preserve this info.

To workaround this issue it is possible to find classes that implements interface and further perform heap dump analysis.

I have an application managed by Maven. During build Maven know full application CLASSPATH.

Is it possible to query via mvn command which classes in which package implements selected interface?

Or even more - to find classes and packages in application build CLASSPATH which is subclasses or superclasses of selected class?

Are there exist plug-in suitable for my needs?

UPDATE Interesting suggestion to use IDE for getting list of known implementation.

I work with Emacs and NetBeans. NetBeans have limited ability (Find Usage dialog by Alt+ F7) to find know implementation but its scope is limited to only to open projects. For example I look for org.hibernate.cfg.NamingStrategy implementation and NetBeans doesn't help in my case.

Because I need that list for further scripting GUI tools are not relevant unless they provide clean text export.


回答1:


If you really need to achieve this via maven or scripting, here is how I got it working.
Based on the approach suggested by another answer on Stackoverflow, I implemented the following simple class:

package com.sample;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Scanner;

import org.clapper.util.classutil.ClassFilter;
import org.clapper.util.classutil.ClassFinder;
import org.clapper.util.classutil.ClassInfo;

public class MainScan {

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Missing options");
            System.exit(-1);
        }
        System.out.println("Filtering by: " + args[1]);

        ClassFinder finder = new ClassFinder();
        finder.addClassPath();
        loadClasspath(finder, args[0]);

        ClassFilter filter = new ImplementInterfaceFilter(args[1]);
        // you could also use as a filter: new
        // SubclassClassFilter(AbstractFileFilter.class);
        // or make a concatenation of filters using an AndClassFilter

        Collection<ClassInfo> foundClasses = new ArrayList<ClassInfo>();
        finder.findClasses(foundClasses, filter);

        if (foundClasses.size() > 0) {
            for (ClassInfo classInfo : foundClasses) {
                System.out.println("- " + classInfo.getClassName());
                // consider also using classInfo.getClassLocation() to get the
                // jar file providing it
            }
        } else {
            System.out.println("No matches found.");
        }
    }

    static void loadClasspath(ClassFinder finder, String file) throws IOException {
        Scanner s = new Scanner(new File(file));
        s.useDelimiter(File.pathSeparator);
        try {
            while (s.hasNext()) {
                finder.add(new File(s.next()));
            }
        } finally {
            s.close();
        }
    }

    static class ImplementInterfaceFilter implements ClassFilter {

        private String interfaceName;

        public <T> ImplementInterfaceFilter(String name) {
            this.interfaceName = name;
        }

        public boolean accept(ClassInfo info, ClassFinder finder) {
            for (String i : info.getInterfaces()) {
                if (i.endsWith(this.interfaceName)) {
                    return true;
                }
            }
            return false;
        }

    }
}

Note, the class is located in the com.sample package, but it can obviously be moved to some other package. The main method expects two options, a classpath file and an interface name, it will then add the classpath to the classpath finder and scan it looking for classes implementing the provided interface name (via a custom filter also provided above). Both options will be provided at runtime by Maven as following:

I used this library for the classpath scanning, hence as suggested on its official page, we need to add a custom repository to our POM:

<repositories>
    <repository>
        <releases>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
        </releases>
        <id>clapper-org-maven-repo</id>
        <name>org.clapper Maven Repo</name>
        <url>http://maven.clapper.org/</url>
        <layout>default</layout>
    </repository>
</repositories>

And the required dependency:

<dependencies>
    ...
    <dependency>
        <groupId>org.clapper</groupId>
        <artifactId>javautil</artifactId>
        <version>3.1.2</version>
    </dependency>
    ...
</dependencies>

Then we just need to configure the following in our Maven build:

<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.1</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>build-classpath</goal>
                    </goals>
                    <configuration>
                        <outputFile>${project.build.directory}/classpath.txt</outputFile>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.1</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>java</goal>
                    </goals>
                    <configuration>
                        <mainClass>com.sample.MainScan</mainClass>
                        <arguments>
                            <argument>${project.build.directory}/classpath.txt</argument>
                            <argument>${interfaceName}</argument>
                        </arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
</build>

We are basically configuring the Maven Dependency Plugin to write the full Maven build classpath to a file, then using the Exec Maven Plugin to execute our custom Java main, passing to it the classpath file and a parameter, ${interfaceName}. Both plugins executions are linked to the validate phase: we don't need to execute the full maven build, we will just invoke one of its first phases for this task.

As such, we can invoke the maven build as following:

mvn validate -DinterfaceName=Serializable -q

And have an output like the following:

Filtering by: Serializable
- org.apache.commons.io.ByteOrderMark
- org.apache.commons.io.comparator.CompositeFileComparator
- org.apache.commons.io.comparator.DefaultFileComparator
...

The Maven command will directly invoke our concerned phase, validate, using the -q option (quite) to skip any maven build log and just get the output interesting to us. Moreover, we can then dynamically pass the interface we want via the -DinterfaceName=<value_here> option. It will pass the value to the Exec Maven Plugin and as such to the Java main above.

According to further needs (scripting, output, format, etc.), the Java main can be easily adapted. Moreover, the plugins, dependency, repositories configuration could also be moved to a Maven profile to have it cleaner and better organized.

Last note: if you change the package of the Java main above, do not forget to change the Exec Maven Plugin configuration accordingly (the mainClass element).

So, back to your questions:

  • Is it possible to query via mvn command which classes in which package implements selected interface? Yes, applying the approach above.
  • Or even more - to find classes and packages in application build CLASSPATH which is subclasses or superclasses of selected class? Yes, look at the SubclassClassFilter from the same library, change the main above accordingly and you will get to it.
  • Are there exist plug-in suitable for my needs? I couldn't find any, but the code above could be easily converted into a new Maven plugin. Otherwise the approach described here is a mix of Java code and existing Maven plugins usage, which could suit your need anyway.


来源:https://stackoverflow.com/questions/34462891/find-classes-that-implement-interfaces-or-being-subclasses-superclasses-in-maven

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!