1

I need to know the name of my .jar inside the aspect so I can create a string field with it via @DeclareParents.

I know I can pass things to the ajc compiler, but is it actually possible to use the passed arguments from the aspect? The end result should be classes with an additional field containing as value the name of my .jar.

UPDATE: Test of suggestion. Gson.jar is a .jar on the classpath

    InputStream r = Gson.class.getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
    String x = getString(r);
    System.out.println(x);

Output:

Manifest-Version: 1.0

Name: org/aspectj/lang/
Specification-Title: AspectJ Runtime Classes 
Specification-Version: 1.7
Specification-Vendor: aspectj.org
Implementation-Title: org.aspectj.tools
Implementation-Version: 1.7.3
Implementation-Vendor: aspectj.org
Bundle-Name: AspectJ Runtime
Bundle-Version: 1.7.3
Bundle-Copyright: (C) Copyright 1999-2001 Xerox Corporation, 2002 Palo 
  Alto Research Center, Incorporated (PARC), 2003-2009 Contributors. 
  All Rights Reserved.

Seems like there can only be one MANIFEST.MF resource at the same time and AspectJ.jar happens to be first on the class path.

Blub
  • 13,014
  • 18
  • 75
  • 102

1 Answers1

2

I do not know any way to do what you want. Even if it was possible, the string values would still be the same if you unpacked the JAR and loaded the modified classes from the file system or repackaged/renamed the JAR. The solution would be static, not dynamic.

Anyway, how about putting information like that into a configuration file packaged into the JAR or maybe even right into the manifest file? Maven has capabilities to add information to the manifest and Java can read them during runtime. I have not thought it through, not to speak of trying to implement something like that, but maybe this is a way you can go.

Feel free to ask follow-up questions.


Update: You can avoid the manifest approach and try to directly determine the JAR file for each loaded class, see also this answer.

Utility class:

package de.scrum_master.util;

import java.net.URL;

public class ClassFileHelper {
    public static URL getClassURL(Class<?> clazz) {
        return clazz.getResource('/' + clazz.getName().replace('.', '/') + ".class");
    }

    public static String getJARFromURL(URL url) {
        if (!url.getProtocol().equals("jar"))
            return null;
        String fileName = url.getFile();
        fileName = fileName.substring(0, fileName.lastIndexOf('!'));
        fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
        return fileName;
    }
}

Usage:

I tried this in a Java + AspectJ project in which the aspects are wrapped in a JAR file and the Java files are stored in a file system directory. I just added the following advice to an aspect within the JAR:

before() : execution(public static void main(..)) {
    try {
        Class<?>[] classes = { String.class, this.getClass(), Class.forName("com.BadReader") };
        for (Class<?> clazz : classes) {
            System.out.println(clazz);
            URL classURL = ClassFileHelper.getClassURL(clazz);
            System.out.println(classURL);
            System.out.println(ClassFileHelper.getJARFromURL(classURL));
            System.out.println();
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

As you can see, the array contains

  • a JRE bootstrap class (String),
  • the aspect class itself (this.getClass()),
  • a class from the Java project outside the JAR (Class.forName("com.BadReader")).

Console output:

class java.lang.String
jar:file:/C:/Programme/Java/jre7/lib/rt.jar!/java/lang/String.class
rt.jar

class com.SafeReaderAspect
jar:file:/C:/Users/Alexander/Documents/java-src/SO_AJ_ITD2StepCompilation_AspectJ/aspectLib.jar!/com/SafeReaderAspect.class
aspectLib.jar

class com.BadReader
file:/C:/Users/Alexander/Documents/java-src/SO_AJ_ITD2StepCompilation_Java/bin/com/BadReader.class
null

Is this what you want?

Community
  • 1
  • 1
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • kriegaex, I need that information *per type* !. So I need to know at runtime whether type "MyType" came from "MyJar1.jar" or "MyJar2.jar". I dont see any other possible way. I guess I could have Aspectj dynamically write a properties file into the jar which then includes all types + the jar name, but my approach would be much more beautiful and I dont know how to accomplish the 2nd approach either. In fact, wouldn't i need the jarname in aspectj either way? – Blub Nov 06 '14 at 13:20
  • it would also require much more performance to keep a hashmap of the jar-class mapping and then keep finding the relevant one over and over again instead of just reading a compiled in field. – Blub Nov 06 '14 at 13:29
  • No, I think it is much easier. I think you can first determine the classloader for each class and the manifest file from the classloader. That way you would always find the right information for each set of classes within one JAR. – kriegaex Nov 06 '14 at 15:22
  • But each .jar doesn't seem to have its own classloader? I tried it and it didn't work, see my update. – Blub Nov 07 '14 at 06:17
  • Check out my update, I think I found a dynamic way of determining the JAR for each loaded class. No need to fiddle with manifests. :-) – kriegaex Nov 07 '14 at 11:18
  • it does look good. Yes, I think that may even be the best solution if it really works (comment for me: i can then name the .jars like I name my .dlls), I can only test it in a few weeks. If you edit your first few lines to "That is not possible but [...]" then I can/should accept it as answer, seeing as my question was fairly specific to somehing else. – Blub Nov 11 '14 at 11:13