martin
martin

Reputation: 980

Generate aspect from a Java class

i'm looking for a way to generate an aspectJ aspect out of a Java class during my build process. The goal ist to generate an inter-type declaration aspect that contains a String constant for each attribute of the java class.

Java class:

public class CarDTO {

    private String vendor;
    private String name;


    public String getVendor() {}
    public String getName() {}
    [..]    
}

This is the aspect which should be generated:

aspect CarAspect 
{
     public static final String CarDTO.VENDOR = "vendor";
     public static final String CarDTO.NAME = "name";
}

Does any obne know a tool or a plugin for maven etc with which i can achieve this behaviour?

Thanks

martin

Upvotes: 2

Views: 1131

Answers (4)

Ralph
Ralph

Reputation: 120771

AspectJ Version 1.8.2 now supports Annotation Processing. You can use this feature to generate ApspectJ Files during the build process, triggered by some annotations.

See this blog post for an example: http://andrewclement.blogspot.de/2014/08/annotation-processing-in-ajdt.html

Upvotes: 0

martin
martin

Reputation: 980

Well, finally i got a solution but still get stuck at one point. The hint with the apt was a success.

I managed to create a AnnotationProcessor which generates an aspect as String. And here is the problem. Is it bad to create a new File object and paste the String into it to create the aspect file for each annotated class? Thats the only way i currently can imageing.

thanks

martin

Solution:

I created an AnnotationProcessor (JDK1.6) that creates my aspects. The method generateAspect creates a file in the default source output folder for each aspect.

@SupportedAnnotationTypes( { "my.own.annotation.GenerateDTOConstants" } )
@SupportedSourceVersion( SourceVersion.RELEASE_6 )
public class DTOConstantAnnotationProcessor extends AbstractProcessor {

    private static final Logger LOG = LoggerFactory
            .getLogger( DTOConstantAnnotationProcessor.class );

    private static final String ASPECT_POSTFIX = ".aj";


    @Override
    public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv ) {

        DTOConstantElementVisitor visitor = new DTOConstantElementVisitor();
        for( TypeElement element : annotations ) {

            Set<? extends Element> annotatedClasses = roundEnv.getElementsAnnotatedWith( element );

            for( Element dto : annotatedClasses ) {
                generateAspect( visitor, dto );
            }
        }

        return true;

    }

    /**
     * @param visitor
     * @param dto
     */
    private void generateAspect( DTOConstantElementVisitor visitor, Element dto ) {
        dto.accept( visitor, null );
        LOG.info( "Generating aspect for " + dto.getSimpleName() );

        Filer filer = this.processingEnv.getFiler();
        try {

            String fileName = visitor.getFileName() + ASPECT_POSTFIX;
            String pkg = visitor.getPkg();

            FileObject aspectFile = filer.createResource( StandardLocation.SOURCE_OUTPUT, pkg,
                    fileName );

            Writer writer = aspectFile.openWriter();
            LOG.info( "writing aspect content into file" );
            writer.write( visitor.getFileContent() );

            writer.close();
            LOG.info( "Aspect generated for " + visitor.getFileName() );
        }
        catch( IOException e ) {
            e.printStackTrace();
            throw new java.lang.RuntimeException( e );
        }
    }
}

Ans here is the visitor i used (just a snippet):

public class DTOConstantElementVisitor extends AbstractElementVisitor6<Void, String> {

    private static final String FIELD_PREFIX = "public static final String ";

    private String fileName = null;

    private String clazzName;
    private String pkg;

    private StringBuffer fileContentBuff;

    @Override
    public Void visitPackage( PackageElement e, String p ) {
        System.out.println( "visitPackage" + e );

        return null;
    }

    @Override
    public Void visitType( TypeElement e, String p ) {
        System.out.println( "visitTypeElement" + e );

        try {
            Class<?> clazz = Class.forName( e.getQualifiedName().toString() );
            this.clazzName = clazz.getSimpleName();
            createFileName( clazz );

            this.pkg = clazz.getPackage().getName();
            this.fileContentBuff = new StringBuffer();

            fileContentBuff.append( "package " + this.pkg + ";\n" );
            fileContentBuff.append( "public aspect " + this.fileName + " {\n" );

            for( Field field : clazz.getDeclaredFields() ) {
                if( Modifier.isPrivate( field.getModifiers() ) ) {
                    String fieldName = field.getName();

                    if( shouldGenerateField( fieldName ) ) {
                        fileContentBuff.append( FIELD_PREFIX + clazzName + "."
                                + fieldName.toUpperCase() + " = \"" + fieldName + "\";\n" );
                    }

                }
            }
            fileContentBuff.append( "}\n" );

            System.out.println( fileContentBuff.toString() );

        }
        catch( ClassNotFoundException e1 ) {
            throw new java.lang.RuntimeException( e1 );
        }

        return null;
    }
    private boolean shouldGenerateField( String fieldName ) {
        if( "serialVersionUID".equals( fieldName ) ) {
            return false;
        }
        return true;
    }

    private void createFileName( Class clazz ) {
        this.fileName = clazzName + "Aspect";
    }
}

Additonally you have to create config file in

META-INF/services

called

javax.annotation.processing.Processor

that contains the package and the name of the AnnotationProcessor

my.package.annotation.processor.DTOConstantAnnotationProcessor

And finally, the include in the maven build process:

   <build>
    <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <version>2.0.0</version>
        <executions>
            <execution>
            <id>aspectprocessing</id>
            <phase>compile</phase>
            <goals>
                    <goal>process</goal>
            </goals>
            </execution>
        </executions>
    </plugin>
   </build>

For single call of the goal use

mvn processor:process

Thats all =)

Upvotes: 1

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28737

Perhaps you can try annotation processing. See apt:

http://download.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html

Eclipse and AspectJ recognize annotation processing in both full and incremental builds.

Upvotes: 1

Blake Niemyjski
Blake Niemyjski

Reputation: 3577

You could generate this code with CodeSmith Generator. If you are using JScript (Microsoft) inside of Visual Studio you could use our GenerateOnBuild or MSBuild (see this document aswell) support. Otherwise you could shell the CodeSmith Generator executable from within your build process and have it generate code that way too.

A custom template would need to be built to parse the file and generate the code. Out of the box we support Parsing Visual Basic or CSharp Code Files and generating off of Visual Basic or CSharp (this isn't helpful for you but it shows you that it has been done and is supported). Here is some documentation on creating a custom template.

Also, I do know that you can take a compiled jar file and convert it to a .NET assembly. From here you could use reflection in a template and generate your Java code.

This might not be the best alternative as you don't have Eclipse integration (depending on your editor, but it is an alternative solution that could solve this issue easily. Also you can write your template in JScript, CSharp or Visual Basic)

Thanks -Blake Niemyjski (CodeSmith Employee)

Upvotes: 1

Related Questions