Generate aspect from a Java class - java

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

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)

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.

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 =)

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

Related

Custom rule that says there can be no project versions or dependency versions that begin with the string "master-"

I am creating a custom rule by following the writing-a-custom-rule.
The intent is to use maven-enforcer-plugin to accomplish the following objective:
"Writing a custom rule that says there can be no project versions or
dependency versions that begin with the string 'master-'".
So I have the following java class-
package com.gridpoint.energy.customRule;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.enforcer.rule.api.EnforcerRule;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.RuntimeInformation;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
/**
* Custom rules to enforce for maven dependencies
*
*/
public class CustomRule implements EnforcerRule
{
/**
* Simple param. This rule will fail if the value is true.
*/
private boolean shouldIfail = false;
public void execute( EnforcerRuleHelper helper )
throws EnforcerRuleException
{
Log log = helper.getLog();
try
{
// get the various expressions out of the helper.
MavenProject project = (MavenProject) helper.evaluate( "${project}" );
MavenSession session = (MavenSession) helper.evaluate( "${session}" );
String target = (String) helper.evaluate( "${project.build.directory}" );
String artifactId = (String) helper.evaluate( "${project.artifactId}" );
String version = (String) helper.evaluate( "${project.version}" );
if(version != null && version.length() > 7 && version.substring(0, 7).equals("master-")) {
this.shouldIfail = true;
}
// retreive any component out of the session directly
ArtifactResolver resolver = (ArtifactResolver) helper.getComponent( ArtifactResolver.class );
RuntimeInformation rti = (RuntimeInformation) helper.getComponent( RuntimeInformation.class );
log.info( "Retrieved Target Folder: " + target );
log.info( "Retrieved ArtifactId: " +artifactId );
log.info( "Retrieved Project: " + project );
log.info( "Retrieved RuntimeInfo: " + rti );
log.info( "Retrieved Session: " + session );
log.info( "Retrieved Resolver: " + resolver );
if ( this.shouldIfail )
{
throw new EnforcerRuleException( "Failing because my param said so." );
}
}
catch ( ComponentLookupException e )
{
throw new EnforcerRuleException( "Unable to lookup a component " + e.getLocalizedMessage(), e );
}
catch ( ExpressionEvaluationException e )
{
throw new EnforcerRuleException( "Unable to lookup an expression " + e.getLocalizedMessage(), e );
}
}
public String getCacheId()
{
//no hash on boolean...only parameter so no hash is needed.
return ""+this.shouldIfail;
}
public boolean isCacheable()
{
return false;
}
public boolean isResultValid( EnforcerRule arg0 )
{
return false;
}
}
But that class only checks for project version. I want to get dependencies versions as well. How to get all the versions to check if they start from "master-" ?
You can get the artefacts via
MavenProject project = (MavenProject) helper.evaluate("${project}");
Set<Artifact> artifacts = project.getArtifacts();
And check for the artefacts with certain group id by:
boolean containsInvalid = artifacts.stream()
.filter(item -> item.getGroupId()
.startsWith("master-"))
.anyMatch(item -> isInvalid(item));
isInvalid() would be your own implementation.

NetBeans Platform: How to register hidden file types

I'm writing a NetBeans plugin and would like to register a file type. The file type is a hidden file (e.g. ".something") with mime-type text/plain and a settled name (".something"). The registration looks like this:
#MIMEResolver.ExtensionRegistration(
displayName = "#Label",
mimeType = "text/plain+something",
extension = {"something"}
)
#DataObject.Registration(
mimeType = "text/plain+something",
iconBase = "com/welovecoding/netbeans/plugin/logo.png",
displayName = "#Label",
position = 300
)
public class SomethingDataObject extends MultiDataObject {
public SomethingDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
super(pf, loader);
registerEditor("text/plain", true);
}
//...
}
The problem with this is NetBeans will only recognize the filetype if the name of the file has a name, a point and an extension (e.g. "name.something"). Just a point and an extension (e.g. ".something") is not recognized properly. Is there a solution for this kind of problem?
I solved the problem by implementing a custom non-declarative MIMEResolver. Here's the code:
#ServiceProvider(service = MIMEResolver.class, position = 3214328)
public class FilenameResolver extends MIMEResolver {
private static final String mimetype = "text/plain+something";
public FilenameResolver() {
super(mimetype);
}
#Override
public String findMIMEType(FileObject fo) {
String nameExt = fo.getNameExt();
if (".something".equalsIgnoreCase(nameExt)) {
return mimetype;
}
return null;
}
}
There's a declarative MIMEResolver too. Note that the declarative way seems to be preferred by NetBeans-Devs.

Modify the predefined ontology by using jena

I created my ontology by Protege. my ontology has some classes and instances. Now i'm going to add other classes and instances by jena that's why i write the below code to create a new class and one instance in this class. the name of new class is "person" and the name of new instance is "base". when i run this code in java it works without any error and create the class and instance. but when i back to protege i can not see the new class and also the new instance. do you have any idea to help me.
thanks
public void create_model(){
modelMem = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM);
ModelMaker modelMaker = ModelFactory.createFileModelMaker("Ontologies/VBnet.owl");
Model modeltmp = modelMaker.createDefaultModel();
modelMem = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM, modeltmp);
System.out.println("Model has been Successfully Built");
}
public void addFile() {
System.out.println("Loading from FOAF instance File");
InputStream inFoafInstance =FileManager.get().open("Ontologies/VBnet.owl");
modelMem.read(inFoafInstance, defaultNameSpace);
//inFoafInstance.close();
System.out.println(modelMem.toString());
}
public void adddata() {
OntClass person = modelMem.createClass(defaultNameSpace + "Person");
Individual l1 = modelMem.createIndividual( defaultNameSpace + "base", person );
for (Iterator i = l1.listRDFTypes(true); i.hasNext(); )
System.out.println( l1.getURI() + " is asserted in class " + i.next() );
}
public static void main(String[] args) {
AddInfo add=new AddInfo();
add.create_model();
add.addFile();
add.adddata();
}
You don't seem to have saved the altered model:
OutputStream out = new FileOutputStream("altered.rdf");
modelMem.write( out, "RDF/XML-ABBREV"); // readable rdf/xml
out.close();

How can a Maven plugin discover its own version during execution?

I'd like to be able to discover the version of my plugin during its execution; 0.0.1-SNAPSHOT, 0.0.1, 1.0-SNAPSHOT, etc.
Can this be done? The AbstractMojo class doesn't really give you much information about the plugin itself.
EDIT - I am using the following code as a workaround. It assumes that the MANIFEST for the plugin can be loaded from a resource URL built using the resource URL of the plugin itself. It's not nice but seems to work for MANIFEST located in either file or jar class loader:
String getPluginVersion() throws IOException {
Manifest mf = loadManifest(getClass().getClassLoader(), getClass());
return mf.getMainAttributes().getValue("Implementation-Version");
}
Manifest loadManifest(final ClassLoader cl, final Class c) throws IOException {
String resourceName = "/" + c.getName().replaceAll("\\.", "/") + ".class";
URL classResource = cl.getResource(resourceName);
String path = classResource.toString();
int idx = path.indexOf(resourceName);
if (idx < 0) {
return null;
}
String urlStr = classResource.toString().substring(0, idx) + "/META-INF/MANIFEST.MF";
URL url = new URL(urlStr);
InputStream in = null;
Manifest mf = null;
try {
in = url.openStream();
mf = new Manifest(in);
} finally {
if (null != in) {
in.close();
}
in = null;
}
return mf;
}
I don't think your "workaround" with the manifest file is such a bad idea. Since it's packed inside the .jar of your plugin you should always have access to it.
For this post to be an answer, here is another idea: Let maven do the dirty work for you during the build of your plugin: have a placeholder in your plugin source:
private final String myVersion = "[CURRENT-VERSION]";
use ant-plugin or something else to replace that placeholder with the current version before compilation.
First, add the following dependency to your plugin's POM:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
Then you can just do the following:
public class MyMojo extends AbstractMojo {
private static final String GROUP_ID = "your-group-id";
private static final String ARTIFACT_ID = "your-artifact-id";
/**
* #parameter default-value="${project}"
*/
MavenProject project;
public void execute() throws MojoExecutionException {
Set pluginArtifacts = project.getPluginArtifacts();
for (Iterator iterator = pluginArtifacts.iterator(); iterator.hasNext();) {
Artifact artifact = (Artifact) iterator.next();
String groupId = artifact.getGroupId();
String artifactId = artifact.getArtifactId();
if (groupId.equals(GROUP_ID) && artifactId.equals(ARTIFACT_ID)) {
System.out.println(artifact.getVersion());
break;
}
}
}

Getting all Classes from a Package

Lets say I have a java package commands which contains classes that all inherit from ICommand can I get all of those classes somehow? I'm locking for something among the lines of:
Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real
Is something like that possible?
Here's a basic example, assuming that classes are not JAR-packaged:
// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));
// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
});
// Find classes implementing ICommand.
for (File file : files) {
String className = file.getName().replaceAll(".class$", "");
Class<?> cls = Class.forName(packageName + "." + className);
if (ICommand.class.isAssignableFrom(cls)) {
commands.add((Class<ICommand>) cls);
}
}
Below is an implementation using the JSR-199 API, i.e. classes from javax.tools.*:
List<Class> commands = new ArrayList<>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
null, null, null);
StandardLocation location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;
Iterable<JavaFileObject> list = fileManager.list(location, packageName,
kinds, recurse);
for (JavaFileObject classFile : list) {
String name = classFile.getName().replaceAll(".*/|[.]class.*","");
commands.add(Class.forName(packageName + "." + name));
}
Works for all packages and classes on the class path, packaged in jar files or without. For classes not explicitly added to the class path, i.e. those loaded by the bootstrap class loader, try setting location to PLATFORM_CLASS_PATH instead.
Here is an utility method, using Spring.
Details about the pattern can be found here
public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
List<Class> classes = new LinkedList<Class>();
PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
Resource[] resources = scanner.getResources(matchPattern);
for (Resource resource : resources) {
Class<?> clazz = getClassFromResource(resource);
classes.add(clazz);
}
return classes;
}
public static Class getClassFromResource(Resource resource) {
try {
String resourceUri = resource.getURI().toString();
resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
// try printing the resourceUri before calling forName, to see if it is OK.
return Class.forName(resourceUri);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
If you do not want to use external depencies and you want to work on your IDE / on a JAR file, you can try this:
public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
final String pkgPath = pkgName.replace('.', '/');
final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();
Path root;
if (pkg.toString().startsWith("jar:")) {
try {
root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
} catch (final FileSystemNotFoundException e) {
root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
}
} else {
root = Paths.get(pkg);
}
final String extension = ".class";
try (final Stream<Path> allPaths = Files.walk(root)) {
allPaths.filter(Files::isRegularFile).forEach(file -> {
try {
final String path = file.toString().replace('/', '.');
final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
allClasses.add(Class.forName(name));
} catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
}
});
}
return allClasses;
}
From: Can you find all classes in a package using reflection?
Start with public Classloader.getResources(String name). Ask the classloader for a class corresponding to each name in the package you are interested. Repeat for all classloaders of relevance.
Yes but its not the easiest thing to do. There are lots of issues with this. Not all of the classes are easy to find. Some classes could be in a: Jar, as a class file, over the network etc.
Take a look at this thread.
To make sure they were the ICommand type then you would have to use reflection to check for the inheriting class.
This would be a very useful tool we need, and JDK should provide some support.
But it's probably better done during build. You know where all your class files are and you can inspect them statically and build a graph. At runtime you can query this graph to get all subtypes. This requires more work, but I believe it really belongs to the build process.
Using Johannes Link's ClasspathSuite, I was able to do it like this:
import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;
public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
return new ClasspathClassesFinder(new ClassTester() {
#Override public boolean searchInJars() { return true; }
#Override public boolean acceptInnerClass() { return false; }
#Override public boolean acceptClassName(String name) {
return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
}
#Override public boolean acceptClass(Class<?> c) { return true; }
}, System.getProperty("java.class.path")).find();
}
The ClasspathClassesFinder looks for class files and jars in the system classpath.
In your specific case, you could modify acceptClass like this:
#Override public boolean acceptClass(Class<?> c) {
return ICommand.class.isAssignableFrom(c);
}
One thing to note: be careful what you return in acceptClassName, as the next thing ClasspathClassesFinder does is to load the class and call acceptClass. If acceptClassName always return true, you'll end up loading every class in the classpath and that may cause an OutOfMemoryError.
You could use OpenPojo and do this:
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
Then you can go over the list and perform any functionality you desire.

Categories

Resources