java string expression parser - java

I was asked to include math expression inside a string
say: "price: ${price}, tax: ${price}*${tax)"
the string is given at run-time and a Map values too
I used Velocity for this:
maven:
<properties>
<velocity.version>1.6.2</velocity.version>
<velocity.tools.version>2.0</velocity.tools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-tools</artifactId>
<version>${velocity.tools.version}</version>
</dependency>
</dependencies>
java:
public class VelocityUtils {
public static String mergeTemplateIntoString(String template, Map<String,String> model)
{
try
{
final VelocityEngine ve = new VelocityEngine();
ve.init();
final VelocityContext context = new VelocityContext();
context.put("math", new MathTool());
context.put("number", new NumberTool());
for (final Map.Entry<String, String> entry : model.entrySet())
{
final String macroName = entry.getKey();
context.put(macroName, entry.getValue());
}
final StringWriter wr = new StringWriter();
final String logStr = "";
ve.evaluate(context, wr, logStr,template);
return wr.toString();
} catch(Exception e)
{
return "";
}
}
}
test class:
public class VelocityUtilsTest
{
#Test
public void testMergeTemplateIntoString() throws Exception
{
Map<String,String> model = new HashMap<>();
model.put("price","100");
model.put("tax","22");
String parsedString = VelocityUtils.mergeTemplateIntoString("price: ${price} tax: ${tax}",model);
assertEquals("price: 100 tax: 22",parsedString);
String parsedStringWithMath = VelocityUtils.mergeTemplateIntoString("price: $number.integer($math.div($price,2))",model);
assertEquals("price: 50",parsedStringWithMath);
}
}
would it be better to use SPel instead?

I agree that this is kind of off topic, but I think it merits an answer nonetheless.
The whole idea of using a templating engine is that you need to have access to the templates at runtime. If that is the case, then sure, Velocity is a good choice. Then you could provide new versions of the HTML, and assuming you didn't change the variables that were used, you would not have to provide a new version of the application itself (recompiled).
However, if you are just using Velocity to save yourself time, it's not saving much here: you could do this with the StringTokenizer in only a few more lines of code.

Related

How to deserialize/serialize Azure Service Bus using spring.cloud in Java

I am using spring.cloud to connect to Azure Service Bus in Java. Here is maven dependency I am using:
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-integration-servicebus</artifactId>
<version>4.5.0</version>
</dependency>
I am able to consume the message from the queue as byte array and it converts the message to string. Here is my main code after receiving a message from queue:
#ServiceActivator(inputChannel = INPUT_CHANNEL)
public void messageReceiver(byte[] payload, #Header(AzureHeaders.CHECKPOINTER) Checkpointer checkpointer) {
String message = new String(payload);
LOGGER.info("New message received: '{}'", message);
checkpointer.success()
.doOnSuccess(s -> LOGGER.info("Message '{}' successfully checkpointed", message))
.doOnError(e -> LOGGER.error("Error found", e))
.block();
}
And here is my example data in JSON as short version:
{
"serverId": 123,
"message": "{some message}"
}
What I would like to do is to create a Java object like this:
public class ExampleMessage {
private final Integer serverId;
private final String message;
and when a message from queue is consumed, it will convert the message to my Java object. I am used to using DataTypeProvider in order to use custom Java object for AMQP message consumption which will convert and validate the conversion behind the scene. Does spring.cloud.azure has built-in method/functionality for deserialization? or Do I manually deserialize and do validation for a consumed message?
Here I was able to convert the Json object to java object using Gson class.
I am just reading a message from the service bus and converting it to java object.
my pom.xml (dependencies)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-servicebus</artifactId>
<version>7.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.11.RELEASE</version>
</dependency>
My test class :
public class TestClass {
public String name ;
public int version;
TestClass(String n , int v)
{
this.name = n ;
this.version = v ;
}
}
The main class :
#SpringBootApplication
public class ServicebustestApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(ServicebustestApplication.class, args);
String conn = " Endpoint=sb://v-mganorkarjsonobject.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=HglLVGlgMsYZGQMOtUfp4g2oka1CpCbVR0YEHgly7jA= ";
CountDownLatch countdownLatch1 = new CountDownLatch(1);
ServiceBusProcessorClient processorClient = new ServiceBusClientBuilder()
.connectionString("<Your COnnection String >")
.processor()
.queueName("test")
.processMessage(ServicebustestApplication::processMessage)
.processError(context -> processError(context,countdownLatch1))
.buildProcessorClient();
processorClient.start();
TimeUnit.SECONDS.sleep(10);
processorClient.close();
}
private static void processMessage(ServiceBusReceivedMessageContext context) {
ServiceBusReceivedMessage message = context.getMessage();
System.out.printf("Processing message. Session: %s, Sequence #: %s. Contents: %s%n", message.getMessageId(),
message.getSequenceNumber(), message.getBody());
Gson gson = new Gson();
TestClass testobject = gson.fromJson(String.valueOf(message.getBody()),TestClass.class);
System.out.println("name: "+testobject.name +" version: "+ testobject.version+"");
}
private static void processError(ServiceBusErrorContext context, CountDownLatch countdownLatch) {
}
}
Here the callback to process message will process the message and then we can use the GSON to convert the json string to java object.
Gson gson = new Gson();
TestClass testobject = gson.fromJson(String.valueOf(message.getBody()),TestClass.class);
output of the code :

Unexpected behaviour of Apache Commons CollectionUtils addAll(Collection collection, Object[] elements)

Brief description:
Basically, inside my code i want to add a List <String> to every <key> inside a ListValuedMap <String, List<String>> as a <value>. I did some testing on the created ListValuedMap spreadNormal with
System.out.println(spreadNormal.keySet());
System.out.println(spreadNormal.values());
and it showed me, that the keys are inside the map (unsorted), but the corresponding values are empty. I am deleting the inserted List <String> after using Collections.addAll(String, List<String>) with list.clear() after each loop.
I would have expected, that indeed a copy of these values stay in my ListValuedMap but my results are:
[Agios Pharmaceuticals Inc., Vicor Corp., EDP Renov�veis S.A., Envista Holdings Corp., JENOPTIK AG,...]
[[], [], [], [], [], ...]
My expected result is more like this:
[Agios Pharmaceuticals Inc., ...] =
[["US00847X1046, "30,60", "30,80", "0,65"], ....]
Can you provide some explanations on that? Is the default behaviour of the Collections.addAll method to copy the reference to an object, instead of the object itself?
The corresponding code section is highlighted with // ++++++++++
Full code example (Eclipse):
import java.io.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.util.*;
import org.apache.commons.collections4.ListValuedMap;
import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
public class Webdata {
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
parseData();
System.out.println(secondsElapsed(start)+" seconds processing time");
};
private static void parseData() throws IOException
{
List <String> subdirectories = new ArrayList<>();
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String errorMessage1 = "String formatting problem";
String errorMessage2 = "String object non existent";
for (int i=0; i< chars.length(); i++)
{
subdirectories.add("https://www.tradegate.de/indizes.php?buchstabe="+chars.charAt(i));
}
List <String> stockMetadata = new ArrayList<>();
ListValuedMap <String, List<String>> nonError = new ArrayListValuedHashMap<>();
ListValuedMap <String, List<String>> numFormatError = new ArrayListValuedHashMap<>();
ListValuedMap <String, List<String>> nullPointerError = new ArrayListValuedHashMap<>();
ListValuedMap <String, List<String>> spreadTooHigh = new ArrayListValuedHashMap<>();
ListValuedMap <String, List<String>> spreadNormal = new ArrayListValuedHashMap<>();
int cap1 = 44;
int cap2 = 56;
for (int suffix = 0; suffix <chars.length(); suffix++)
{
Document doc = Jsoup.connect(subdirectories.get(suffix).toString()).get();
Elements htmlTableRows = doc.getElementById("kursliste_abc").select("tr");
htmlTableRows.forEach( tr->
{
String stockName = tr.child(0).text();
String bid_price = tr.child(1).text();
String ask_price = tr.child(2).text();
String isin = tr.child(0).selectFirst("a").absUrl("href").substring(cap1,cap2);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
try
{
if (calcSpread(bid_price, ask_price) < 5)
{
Collections.addAll(stockMetadata, isin, bid_price, ask_price, calcSpread(bid_price, ask_price).toString());
spreadNormal.put(stockName,stockMetadata);
}
else if (calcSpread(bid_price, ask_price) > 5)
{
Collections.addAll(stockMetadata, isin, bid_price, ask_price, calcSpread(bid_price, ask_price).toString());
spreadTooHigh.put(stockName,stockMetadata);
}
stockMetadata.clear();
}
catch (NumberFormatException e)
{
Collections.addAll(stockMetadata, e.getMessage());
numFormatError.put(stockName, stockMetadata);
stockMetadata.clear();
}
catch (NullPointerException Ne)
{
Collections.addAll(stockMetadata, Ne.getMessage());
nullPointerError.put(stockName, stockMetadata);
stockMetadata.clear();
} //end of try-catch
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}); //end of for-each loop htmlTableRows
} //end of JSOUP method
System.out.println(spreadNormal.keySet());
System.out.println(spreadNormal.values());
} //end of parseData()
public static Float calcSpread (String arg1, String arg2)
{
try
{
Float bid = Float.parseFloat(arg1.replace("," , "."));
Float ask = Float.parseFloat(arg2.replace("," , "."));
Float spread = ((ask-bid)/ask)*100;
return spread;
}
catch (NumberFormatException e)
{
return null;
}
}
public static Long secondsElapsed(Long start) {
Long startTime = start;
Long endTime = System.currentTimeMillis();
Long timeDifference = endTime - startTime;
return timeDifference / 1000;
}
} //end of class
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>TEST</groupId>
<artifactId>TEST</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>18</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
There is nothing wrong with Collections.addAll.
I believe you expect all of your maps to be ListValuedMap<String, String> which is basically a Map<String, List<String>>. As such, your ListValuedMap<String, List<String>> is a Map<String, List<List<String>>>.
Just update each of your maps to be like below so each key is mapped to a List<String>:
ListValuedMap<String, String> nonError = new ArrayListValuedHashMap<>();
ListValuedMap<String, String> numFormatError = new ArrayListValuedHashMap<>();
ListValuedMap<String, String> nullPointerError = new ArrayListValuedHashMap<>();
ListValuedMap<String, String> spreadTooHigh = new ArrayListValuedHashMap<>();
ListValuedMap<String, String> spreadNormal = new ArrayListValuedHashMap<>();
And then, instead of using ListValuedMap.put(K key, V value) you have to use ListValuedMap.putAll(K key, Iterable<? extends V> values) like this:
spreadNormal.putAll(stockName, stockMetadata);
spreadTooHigh.putAll(stockName, stockMetadata);
numFormatError.putAll(stockName, stockMetadata);
nullPointerError.putAll(stockName, stockMetadata);
The putAll method will iterate over the stockMetadata iterable and add the elements one by one into the map's underlying list.

AuroraRDS Serverless with data-api library in Java does not work

I want to access a database via the data-api which is AWS providing since the start of 2020.
This is my Maven code (only aws dependency shown):
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.11.790</version>
</dependency>
<dependency>
<groupId>software.amazon.rdsdata</groupId>
<artifactId>rds-data-api-client-library-java</artifactId>
<version>1.0.4</version>
</dependency>
This is my Java code
public class Opstarten {
public static final String RESOURCE_ARN = "arn:aws:rds:eu-central <number - name >";
public static final String SECRET_ARN = "arn:aws:secretsmanager:eu-central-1:<secret>";
public static final String DATABASE = "dbmulesoft";
public static void main(String[] args) {
// TODO Auto-generated method stub
new Opstarten().testme();
}
public void testme( ) {
var account1 = new Account(1, "John"); //plain POJO conform AWS manual hello world example
var account2 = new Account(2, "Mary");
RdsDataClient client = RdsDataClient.builder().database(DATABASE)
.resourceArn(RESOURCE_ARN)
.secretArn(SECRET_ARN).build();
client.forSql("INSERT INTO accounts(accountId, name) VALUES(:accountId, :name)").
withParameter(account1).withParameter(account2).execute();
}
}
Error I am having:
Exception in thread "main" java.lang.NullPointerException
at com.amazon.rdsdata.client.RdsDataClient.executeStatement(RdsDataClient.java:134)
at com.amazon.rdsdata.client.Executor.executeAsSingle(Executor.java:92)
at com.amazon.rdsdata.client.Executor.execute(Executor.java:77)
at nl.bpittens.aws.rds.worker.Opstarten.testme(Opstarten.java:47)
at nl.bpittens.aws.rds.worker.Opstarten.main(Opstarten.java:29)
When I debug it I see that the client object is nog null but the rdsDataService is null as a method or parameter of the client object.
I have checked AWS side for Java RDS Data API but nothing is mentioned there.
Any idea whats wrong ?
Looks like you aren't passing RDS data service, you need to do as follows:
AWSRDSData awsrdsData = AWSRDSDataClient.builder().build();
RdsDataClient client = RdsDataClient.builder()
.rdsDataService(awsrdsData)
.database(DATABASE)
.resourceArn(RESOURCE_ARN)
.secretArn(SECRET_ARN)
.build();
You can also configure mapping options as follows:
MappingOptions mappingOptions = MappingOptions.builder()
.ignoreMissingSetters(true)
.useLabelForMapping(true)
.build();
AWSRDSData awsrdsData = AWSRDSDataClient.builder().build();
RdsDataClient client = RdsDataClient.builder()
.rdsDataService(awsrdsData)
.database(DATABASE)
.resourceArn(RESOURCE_ARN)
.secretArn(SECRET_ARN)
.mappingOptions(mappingOptions)
.build();

How can I deserialize JSON with u' unicode markup in Java?

I have this HTTP response body to de-serialize:
String response = "result : {'url': u'https://somedomain/', 'fields': {'policy':
u'eyJjb25kaXRpb25zIjogW1siYfgfhudGVudC1sZjMyWiJ9', 'AWSAccessKeyId':
u'ASIccccccNA', 'x-amz-security-token': 'FQofgF', 'key': u'bbb.file',
'signature': u'rm9gdflkjfs='}}"
I am using the jackson.core (2.9.0) java package and lib (have also tried GSON) and get this error:
Exception in thread "main" com.fasterxml.jackson.core.JsonParseException:
Unrecognized token 'u': was expecting ('true', 'false' or 'null')
Deserialization code:
MyResponse deserializedResponse = new ObjectMapper()
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true)
.readValue(response, MyResponse.class);
I have considered something like this but it feels like there should be a better / safer way:
String sanitizedResponse = response.replaceAll("u'", "'");
--
Using Java 1.8.
Any help appreciated.
As python caused this problem I think the best solution is to let python fix it ;-). Fortunately with jython you can stick with a pure java implementation.
First you need to add the jython standalone dependency in your pom.xml:
<dependencies>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
(As you can see I also used apache commons io for my example so I added it as well)
I put your (invalid) json string into the text file "c:/temp/json.txt" which has the following content:
{'url': u'https://somedomain/', 'fields': {'policy':
u'eyJjb25kaXRpb25zIjogW1siYfgfhudGVudC1sZjMyWiJ9', 'AWSAccessKeyId':
u'ASIccccccNA', 'x-amz-security-token': 'FQofgF', 'key': u'bbb.file',
'signature': u'rm9gdflkjfs='}}
Now here is the code to read the json file, set up the Python Interpreter and handover the json to clean it up:
String content = FileUtils.readFileToString(new File("c:/temp/json.txt"), "UTF-8");
PythonInterpreter pi = new PythonInterpreter();
pi.exec("import json");
pi.exec("jsondata = " + content);
pi.exec("jsonCleaned = json.dumps(jsondata)");
PyObject jsonCleaned = (PyObject) pi.get("jsonCleaned");
System.out.println(jsonCleaned.asString());
pi.close();
The output is:
{"url": "https://somedomain/", "fields": {"signature": "rm9gdflkjfs=", "AWSAccessKeyId": "ASIccccccNA", "x-amz-security-token": "FQofgF", "key": "bbb.file", "policy": "eyJjb25kaXRpb25zIjogW1siYfgfhudGVudC1sZjMyWiJ9"}}
When you put that in a json validator (https://jsonlint.com/) you can see that it is a valid json now.
I can't tell if the performance is good enough for your use case so you have to test that out.
Remark:
In Eclipse there seems to be a bug with that jython version. It shows the following error:
console: Failed to install '': java.nio.charset.UnsupportedCharsetException: cp0.
Although it works nevertheless you can get rid of it by adding the following VM-Argument to your Run-Configuration:
-Dpython.console.encoding=UTF-8
Remark2: For the sake of completeness and to fully answer that question - here is how you can deserialize the cleaned JSON:
Add GSON Dependency to your pom.xml:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
Create representing classes:
Info class
public class Info {
private String url;
private Fields fields;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Fields getFields() {
return fields;
}
public void setFields(Fields fields) {
this.fields = fields;
}
}
Fields class
import com.google.gson.annotations.SerializedName;
public class Fields {
private String signature;
private String AWSAccessKeyId;
#SerializedName("x-amz-security-token")
private String x_amz_security_token;
private String key;
private String policy;
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public String getAWSAccessKeyId() {
return AWSAccessKeyId;
}
public void setAWSAccessKeyId(String aWSAccessKeyId) {
AWSAccessKeyId = aWSAccessKeyId;
}
public String getX_amz_security_token() {
return x_amz_security_token;
}
public void setX_amz_security_token(String x_amz_security_token) {
this.x_amz_security_token = x_amz_security_token;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getPolicy() {
return policy;
}
public void setPolicy(String policy) {
this.policy = policy;
}
}
Finally add the following code after you get your cleaned JSON:
Gson gson = new Gson();
Info info = gson.fromJson(jsonCleaned.asString(), Info.class);
You need to use the following regex that replaces u' from beginning of word boundaries to replace with '
String regexPattern = "(\\bu')";

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

Categories

Resources