Пройдите класс к плагину Maven2 - PullRequest
2 голосов
/ 15 марта 2012

Я знакомлюсь с разработкой плагинов для Maven, и мне нужна помощь с одним вопросом.

Как я узнал, очень просто передать сложный объект плагину maven, но как насчет передачи класса? Допустим, я пишу свой собственный плагин, который содержит только 2 класса.

Message.java

package abstractmessage;
public abstract class Message {
    public abstract String getMessage();
}

MessageMojo.java

/**
 * @goal message
 */
public class MessageMojo extends AbstractMojo {
    /**
     * @parameter expression="${message.messageClass}"
     */
    private String messageClass ;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        Message message = null;

        try {
            Class<?> clazz = Class.forName(messageClass);
            message = (Message) clazz.newInstance();
        } catch (Exception e) {
            getLog().error(e);
        }

        getLog().info(message.getMessage());
    }
}

Таким образом, плагин ожидает имя расширенного класса Message и пытается динамически инициализировать его.

Расширенный класс Message в реальном Maven-проекте выглядит следующим образом.

package message;
import abstractmessage.Message;

public class RealMessage extends Message {
    @Override
    public String getMessage() {
        return "Hello plugin. From MavenProject.";
    }
}

И pom.xml использует плагин.

<build>
    <plugins>
        <plugin>
            <artifactId>message-plugin</artifactId>
            <groupId>message-plugin</groupId>
            <version>1.0</version>
            <executions>
                <execution>
                    <phase>test</phase>
                    <goals>
                        <goal>message</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <messageClass>message.RealMessage</messageClass>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <artifactId>message-plugin</artifactId>
        <groupId>message-plugin</groupId>
        <version>1.0</version>
    </dependency>
</dependencies>

Но когда я выполняю maven: test, я получаю java.lang.ClassNotFoundException: message.RealMessage

Почему так?

Ответы [ 2 ]

1 голос
/ 16 марта 2012

Ну, я попытался создать новый URLClassLoader и связать его с текущим ClassLoader.

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
    //a direct absolute path just for testning
    chainClassLoader(new File("C:\\Users\\admin\\.m2\\repository\\message-plugin\\real-message\\1.0\\real-message-1.0.jar"));
    Message message = null;

    try {
        Class<?> clazz = Class.forName(messageClass);
        message = (Message) clazz.newInstance();
    } catch (Exception e) {
        getLog().error(e);
    }

    getLog().info(message.getMessage());
}

private void chainClassLoader(File file) throws Exception {
        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{file.toURI().toURL()}, currentClassLoader);
        Thread.currentThread().setContextClassLoader(urlClassLoader);
}

Но все же я получаю ClassNotFoundException.

Кроме того, как именно я могу «захватить зависимости проекта» с помощью @requiresDependencyResolution?

EDIT:

Хорошо, я думаю, что понял. Иерархия ClassLoader в плагине выглядит следующим образом.

null
  sun.misc.Launcher$ExtClassLoader
    sun.misc.Launcher$AppClassLoader

null
  org.codehaus.classworlds.RealmClassLoader
    java.net.URLClassLoader // my chained classloader

Class.forName(clazz) пытается найти клац, начиная с AppClassLoader, который делегирует его родителю ExtClassLoader. Но так как я добавил новый classpath к URLClassLoader, который, по-видимому, находится в другой иерархии ClassLoader, я получаю ClassNotFoundException.

Решение заключается не в использовании Class.forName(clazz), а в Thread.currentThread().getContextClassLoader().loadClass(clazz), потому что я приковал URLClassLoader к ContextClassLoader Нити.

1 голос
/ 16 марта 2012

Загрузчик классов вашего плагина не имеет доступа к классам проекта. Он имеет доступ только к своим собственным классам и своим зависимостям.

У вас есть два варианта.

Первый вариант - создать новый загрузчик классов, который загружает классы из текущего проекта и его зависимостей. Плагин surefire является примером плагина, который делает это - он может загружать скомпилированные классы модульных тестов и запускать их, если он не находится в режиме форка.

Создание нового загрузчика классов из зависимостей не совсем тривиально, но для начала вы захотите поместить @requiresDependencyResolution test в свой Mojo и захватить зависимости проекта (вместе с выходным каталогом сборки проекта, если вы хотите включить классы проекта в поиск) и создать URLClassLoader из этих JAR-файлов и каталогов. В качестве отправной точки может помочь метод Surefire generateTestClasspath () .

Другой вариант - скомпилировать ваш класс RealMessage в отдельном проекте, создать отдельный JAR и включить его в качестве зависимости от плагина сообщений.

Ваш раздел сборки в этом случае будет выглядеть примерно так:

<build>
    <plugins>
        <plugin>
            <artifactId>message-plugin</artifactId>
            <groupId>message-plugin</groupId>
            <version>1.0</version>
            <executions>
                <execution>
                    <phase>test</phase>
                    <goals>
                        <goal>message</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <messageClass>message.RealMessage</messageClass>
            </configuration>
            <dependencies>
                <groupId>message-plugin-additions</groupId>
                <artifactId>message-plugin-additions</artifactId>
                <version>1.0</version>
            </dependencies>
        </plugin>
    </plugins>
</build>

где проект message-plugin-additions содержит класс message.RealMessage. Скорее всего, это окажется намного проще, чем первый вариант, если вы захотите использовать отдельный проект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...