2012-09-02 3 views
3

서비스 용 플러그인을 개발 중입니다. 서비스가 작동하려면 서비스에서 제공하지 않는 데이터가 필요합니다.이미로드 된 클래스 프로파일하기

플러그인에는 엄격한로드/언로드 사양이 있습니다. bare plugin은 다음과 같습니다.

public class Plugin extends JavaPlugin 
{  
    @Override 
    public void onEnable() {} //Plugin enters here. Comparable to main(String[] args) 

    @Override 
    public void onDisable() {} //Plugin exits here, when service shuts down. 
} 

org.service.aClass라는 패키지가 있습니다. 그 안에는 방법이 있습니다. 방법은 다음과 같습니다 :

public boolean aMethod(boolean bool) { 
return bool; 
} 

단순한 시나리오이지만 작동합니다. 플러그인은 aMethod가 호출 될 때마다 bool의 값을 알아야합니다. 이것은 내 프로그램에서 절대적으로 중요합니다. 나는 그 값을 얻는 다른 방법이 없다.

나는 aMethod를 조언 할 것이지만 서비스 이후에 내 플러그인이로드되기 때문에 이것은 작동하지 않을 것이다. 로드 시간은 내가 이해 한 것에서부터,로드 된 이후로 여기에 적합하지 않습니다. 지금 내 옆에 작업 책에서 개방과 :

public aspect LogAspect { 

    pointcut publicMethodExecuted(): execution(* org.service.aClass.aMethod(..)); 

    after(): publicMethodExecuted(){ 
    cat(String.format("Entered method: %s.", 
     thisJoinPoint.getSignature())); 

    List<Object> arguments = Arrays.asList(thisJoinPoint.getArgs()); 
    List<Object> argTypes = new ArrayList<Object>(); 
    for (Object o: arguments) { 
     argTypes.add(o.getClass().toString()); 

    } 

    cat(String.format("With argument types: %s and values: %s.", 
      argTypes, arguments)); 


    cat(String.format("Exited method: %s.", thisJoinPoint.getSignature())); 
    } 

public void cat(Object dog) { 
    System.out.println("[TEST] " + dog); 
    } 

} 

내가 AspectJ를 가지고 : 그것은 작동하지 않습니다에도 불구하고

, 여기에 내가 사용하던 측면은 경우에는 유용 ​​할 수있다 모든로드 타임 위빙 예제에서 프로그램은 -javaagent 플래그로 시작되어야한다고 언급합니다. 내 proram이 플러그인이기 때문에 이런 일이 발생할 수있는 방법이 없습니다.

나는 또한 ASM을 들여다 보았다. 프로파일 러 구축에 대한 훌륭한 튜토리얼을 발견했습니다 (기본적으로 내가하고 싶은 부분) here.

문제는 다시 시작할 때 -javaagent 플래그와 public static premain을 사용하므로 부적절하므로 onEnable 및 onDisable 만 있기 때문입니다.

그런 다음 Java의 Attach API에 대해 알았습니다. 클래스를로드 한 후에 내 에이전트 인 프로필러를 첨부 할 수 있습니다. 그것은 완벽 해 보이지만 30 시간 동안 검색 한 후에 나는 이해할 수있는 좋은 예를 찾을 수 없었습니다.

아무도 도와 줄 수 있습니까? 이것은 2 대 1 질문입니다. AspectJ를이 용도로 사용할 수 있습니까? 그렇다면 어떻게? 또한, 그럴 수 없다면 누군가가 Attach API를 ASM 프로파일 러와 함께 사용하는 올바른 방향으로 나를 가리킬 수 있습니까?

미리 감사드립니다.

+0

왜 플러그인을로드하기 전에 서비스를 만들 수 없습니까? (나는 당신이 당신이 서비스 항아리를 제어 할 수 있다고 지시하기 때문에 당신이 전체 jvm을 제어한다고 가정하고있다). – jtahlborn

+0

@jtahlborn 서비스 자체가 이와 같이 작동합니다. Loader는로드 할 서비스를 지정하는 yml 파일을 읽습니다. 내가하고있는 일은 서비스 사본을 만들고 필요로하는 클래스를 변경 한 다음 yml 파일을 수정하여 새 서비스를 가리키는 것입니다. modded 서비스를 시작할 때 바닐라 서비스를 삭제합니다. 내 플러그인은 서비스가로드 된 후 서비스에 의해로드됩니다. – Xyene

+0

아마도 귀하의 의견을 이해할 수 없지만 LTW가 귀하에게 적합하지 않아도되는 이유는 없습니다. 그것은 당신이하고 싶은 것을 위해 발명되었습니다. 즉,로드 될 때 클래스를 짜십시오 - 따라서 LTW 이름. – kriegaex

답변

2

내가 해냈어!

here 프로파일 러에 대한 런타임 첨부 파일을 만들었습니다. 기본적으로 premain은 'agentmain'으로 이름이 바뀌 었습니다.

다른 유용한 기능과 함께 attacher가있는 Util 클래스를 만들었습니다. attachher는 에이전트와 함께 jar 파일을 만들고 프로파일 링 할 수 있음을 명시하는 매니페스트로 작업합니다. Util 클래스는 다음과 같습니다.

public class Util { 

    /** 
    * Gets the current JVM PID 
    * @return 
    * Returns the PID 
    * @throws Exception 
    */ 

    public static String getPidFromRuntimeMBean() { 
    String jvm = ManagementFactory.getRuntimeMXBean().getName(); 
    String pid = jvm.substring(0, jvm.indexOf('@')); 
    return pid; 
    } 

    /** 
    * Attaches given agent classes to JVM 
    * 
    * @param agentClasses 
    * A Class<?>[] of classes to be included in agent 
    * @param JVMPid 
    * The PID of the JVM to attach to 
    */ 

    public static void attachAgentToJVM(Class<?>[] agentClasses, String JVMPid) { 

    try { 


    File jarFile = File.createTempFile("agent", ".jar"); 
    jarFile.deleteOnExit(); 

    Manifest manifest = new Manifest(); 
    Attributes mainAttributes = manifest.getMainAttributes(); 
    mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); 
    mainAttributes.put(new Attributes.Name("Agent-Class"), 
     Agent.class.getName()); 
    mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), 
     "true"); 
    mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true"); 

    JarOutputStream jos = new JarOutputStream(new FileOutputStream(
     jarFile), manifest); 


    for(Class<?> clazz: agentClasses) {   
     JarEntry agent = new JarEntry(clazz.getName().replace('.', 
      '/') 
      + ".class"); 
     jos.putNextEntry(agent); 

    jos.write(getBytesFromIS(clazz.getClassLoader() 
     .getResourceAsStream(
      clazz.getName().replace('.', '/') + ".class"))); 
    jos.closeEntry(); 
    } 

    jos.close(); 
    VirtualMachine vm = VirtualMachine.attach(JVMPid); 
    vm.loadAgent(jarFile.getAbsolutePath()); 
    vm.detach(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

    } 

    /** 
    * Gets bytes from InputStream 
    * 
    * @param stream 
    * The InputStream 
    * @return 
    * Returns a byte[] representation of given stream 
    */ 

    public static byte[] getBytesFromIS(InputStream stream) { 

    ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
    try { 
     int nRead; 
     byte[] data = new byte[16384]; 

     while ((nRead = stream.read(data, 0, data.length)) != -1) { 
     buffer.write(data, 0, nRead); 
     } 

     buffer.flush(); 
    } catch (Exception e) { 
     System.err.println("Failed to convert IS to byte[]!"); 
     e.printStackTrace(); 
    } 

    return buffer.toByteArray(); 

    } 

    /** 
    * Gets bytes from class 
    * 
    * @param clazz  
    * The class 
    * @return 
    * Returns a byte[] representation of given class 
    */ 

    public static byte[] getBytesFromClass(Class<?> clazz) {    
    return getBytesFromIS(clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class")); 
    } 

} 

명확성을 위해 JavaDoc 주석이 포함되었습니다.

이 될 것이다 사용하는 예 :

Util.attachAgentToJVM(new Class<?>[] { Agent.class, Util.class, 
     Profile.class, ProfileClassAdapter.class, 
     ProfileMethodAdapter.class }, Util.getPidFromRuntimeMBean()); 

가 삽입 기가 Agent.class이 주체가되고 싶어 것을 기억하십시오. 이것을 쉽게 변경할 수 있습니다. 클래스 []의 나머지 부분은 임시 agent.jar에 포함될 클래스입니다.

IDE가 "UnsatisfiedLinkError"에 대해 불만을 표시하는 것은 첨부 파일 (dll | so)이 JDK 그냥 % JAVA_PATH %/jre/lib에 복사하여 붙여 넣으십시오. 또한 모든 com.sun 가져 오기가 포함되어 있으므로 JDK의 tools.jar에 대한 참조를 추가하십시오.

EDIT : 나는이 github 예제가 유용하다고 생각할 수 있습니다. 그것의 here.

관련 문제