2012-03-15 4 views
2

메신저 플러그인 개발에 익숙하며 한 가지 도움이 필요합니다.클래스를 Maven2 플러그인에 전달하십시오.

복잡한 객체를 메이븐 플러그인에 전달하는 것은 매우 쉽지만 클래스를 전달하는 것은 어떨까요? 메신저에 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()); 
    } 
} 

그래서 플러그인은 확장 된 메시지 클래스의 이름을 기대하고 초기화 dynamicaly하려고합니다.

실제 Maven-Project의 확장 Message 클래스는 다음과 같습니다.

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> 

는하지만 : 테스트 나는 왜 그렇게 java.lang.ClassNotFoundException: message.RealMessage

를 얻을?

+0

클래스에 명시 적으로 사용하지 않으면 클래스 이름으로 해석해야하는 문자열이어야하며이를 수행 할 수있는 유일한 방법은 내 지식에 맞는 플러그인을 작성하는 것입니다. – Neil

답변

1

플러그인의 클래스 로더는 프로젝트의 클래스에 액세스 할 수 없습니다. 자체 클래스와 그 의존성에만 액세스 할 수 있습니다.

두 가지 옵션이 있습니다.

옵션 하나는 현재 프로젝트와 그 종속성에서 클래스를로드하는 새로운 클래스 로더를 만드는 것입니다. surefire plugin은 이것을 수행하는 플러그인의 예입니다. 컴파일 된 유닛 테스트 클래스를로드하고 fork 모드가 아닌 경우이를 실행할 수 있습니다.

의존성에서 새로운 클래스 로더를 만드는 것은 완전히 쉽지는 않지만 @requiresDependencyResolution test을 Mojo에 넣고 프로젝트의 종속성을 얻고 싶다면 (프로젝트의 빌드 출력 디렉토리와 함께) 검색에 프로젝트의 클래스를 포함 시키려면),이 JAR과 디렉토리로부터 URLClassLoader를 생성하십시오. Surefire의 generateTestClasspath() method이 출발점으로 도움이 될 수 있습니다.

다른 옵션은 별도의 프로젝트에서 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 클래스를 포함하는 경우 :이 경우

빌드 섹션은 같을 것입니다. 이것은 별도의 프로젝트를 기꺼이 사용하려는 경우 첫 번째 옵션보다 훨씬 간단해질 수 있습니다.

+0

젠장,이 코멘트에 igonre –

1

새로운 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으로 정확하게 "프로젝트의 종속성을 얻으려면"어떻게해야합니까?

편집 :

이 좋아 내가 그것을 가지고 생각합니다. 플러그인의 ClassLoader 계층 구조는 다음과 같습니다.

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

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

Class.forName(clazz)

그의 부모 ExtClassLoaderAppClassLoader 대표가 함께 시작하여 clazz에를 찾습니다. 그러나 나는 새로운 클래스 경로를 URLClassLoader에 추가했기 때문에 apperantly 다른 ClassLoader 계층 구조에 있는데 나는 ClassNotFoundException이됩니다.

스레드의 ContextClassLoaderURLClassLoader을 연결했기 때문에 해결책은 Class.forName(clazz) 대신 Thread.currentThread().getContextClassLoader().loadClass(clazz)을 사용하지 않아도됩니다.