I have a few lambdas working together through SNS. One lambda receives a request and send data to SNS. Another lambda is subscribed to SNS. It was easy to do in JavaScript as the incoming message just an JS object. Now I am rewriting the lambda to Java. I am looking for the type to use in the handler.
Here's what the lambda looks like. SNSMessage is the placeholder for the type.
public class ArchiveRequestHandler implements RequestHandler<SNSMessage?, Void> {
#Override public Void handleRequest(SNSMessage? input, Context context) {
// do something with the message
return null;
}
}
This is how an example message looks like:
{
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:EXAMPLE",
"EventSource": "aws:sns",
"Sns": {
"SignatureVersion": "1",
"Timestamp": "1970-01-01T00:00:00.000Z",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"MessageId": "1234567-ee98-5cb9-9903-4c221d41eb5e",
"Message": "Hello from SNS!",
"MessageAttributes": {
"Test": {
"Type": "String",
"Value": "TestString"
},
"TestBinary": {
"Type": "Binary",
"Value": "TestBinary"
}
},
"Type": "Notification",
"UnsubscribeUrl": "EXAMPLE",
"TopicArn": "arn:aws:sns:EXAMPLE",
"Subject": "TestInvoke"
}
}
]
}
Now I am sure I can create my own type to parse this, but I was hoping there is a more standard way of doing this. However I haven't found anything in the lambda nor SNS SDK dependencies that looks like this object.
Thanks to the comments by #dnault and #notionquest I found that this object is part of the aws-lambda-java-events library. I added the dependency:
compile 'com.amazonaws:aws-lambda-java-events:1.3.0'
And now I can do:
public class ArchiveRequestHandler implements RequestHandler<SNSEvent, Void> {
#Override public Void handleRequest(SNSEvent input, Context context) {
// do something with the message
return null;
}
}
The following Lambda code processes the SNS input message/s:
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class LogEvent implements RequestHandler<SNSEvent, Object> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
public Object handleRequest(SNSEvent request, Context context){
String timeStamp = sdf.format(new Date());
LambdaLogger logger = context.getLogger();
logger.log("-------------------------Invocation started: --------------" + timeStamp);
List<SNSEvent.SNSRecord> snsRecordList = request.getRecords();
if ( snsRecordList != null ){
SNSEvent.SNS recordSNS = null;
for ( SNSEvent.SNSRecord snsRecord : snsRecordList ) {
recordSNS = snsRecord.getSNS();
logger.log(
"Subject:[" + recordSNS.getSubject() + "]" +
"Arn:[" + recordSNS.getTopicArn() + "]" +
"attribs:[" + recordSNS.getMessageAttributes() + "]" +
"message:[" + recordSNS.getMessage() + "]" );
}//for
}
timeStamp = sdf.format(new Date());
logger.log("-------------------------Invocation completed: -------------" + timeStamp);
return null;
}
}
Related
I have created a minimal application in order to debug the following problem:
The application is creating a Java object which includes a GeoJsonPoint. When the object gets validated against an automatically created json schema, I receive different results when starting the application multiple times. For example, I am starting the application for 5 times and the result is "Json not valid!". When I start the application another time, I receive the result "Json successfully validated".
The error message when the json is not valid tells me:
/geoPosition/coordinates: instance type (array) does not match any allowed primitive type (allowed: [object])
The following line is randomly returning a different json schema:
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass());
I do not understand that this happens randomly. Has someone seen this behavior before?
To exclude dependency problems during runtime, I have created a jar with all dependencies (jar-with-dependencies)
Below are my files:
pom.xml dependencies:
<dependencies>
<dependency>
<groupId>com.github.reinert</groupId>
<artifactId>jjschema</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-schema-validator</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
App.java:
package de.s2.json.test;
import java.util.ArrayList;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
public class App
{
public static void main( String[] args )
{
Address address = new Address();
address.setCountry("Deutschland");
GeoJsonPoint geoPoint = new GeoJsonPoint(12, 23);
address.setGeoPosition(geoPoint);
ArrayList<String> ret = null;
try {
ret = Toolbox.validateJson(address);
} catch (ProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(ret != null) {
System.out.println("Json not valid!");
for (int i = 0; i < ret.size(); i++) {
System.out.println(ret.get(i));
}
} else {
System.out.println("Json successfully validated");
}
}
}
Toolbox.java:
package de.s2.json.test;
import java.util.ArrayList;
import java.util.Iterator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.reinert.jjschema.v1.JsonSchemaV4Factory;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
public class Toolbox {
public static <T> ArrayList<String> validateJson(T inputObj) throws ProcessingException {
com.github.reinert.jjschema.v1.JsonSchemaFactory schemaFactory = new JsonSchemaV4Factory();
schemaFactory.setAutoPutDollarSchema(true);
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass()); // <= here I get different results
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonSchema schema = factory.getJsonSchema(fstabSchema);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode baseReceiptJson = objectMapper.convertValue(inputObj, JsonNode.class);
ProcessingReport report;
report = schema.validate(baseReceiptJson);
ArrayList<String> validationErrorDetails = new ArrayList<String>();
if (!report.isSuccess()) {
StringBuilder builder = new StringBuilder();
builder.append("Not all required fields are filled with data");
builder.append(System.getProperty("line.separator"));
for (Iterator<ProcessingMessage> i = report.iterator(); i.hasNext();) {
ProcessingMessage msg = i.next();
builder.append(msg.asJson().findValue("instance").findValue("pointer").toString());
builder.append(": ");
builder.append(msg.getMessage());
String detail = msg.asJson().findValue("instance").findValue("pointer").toString() + ": " + msg.getMessage();
detail = detail.replace("\"", "");
validationErrorDetails.add(detail);
builder.append("\n");
}
return validationErrorDetails;
}
return null;
}
}
Address.java:
package de.s2.json.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.reinert.jjschema.Attributes;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Address
{
#JsonProperty("country")
private String country = "";
#JsonProperty("geoPosition")
#Attributes(required=false, description="longitude and latitude (it is initialized with 0,0)")
private GeoJsonPoint geoPosition = new GeoJsonPoint(0, 0);
}
Thank you very much for your support!
Update 1:
Answer to Hiran Chaudhuri
If the validation fails, then the schema looks like this:
{
"type": "object",
"properties": {
"country": {
"type": "string"
},
"geoPosition": {
"type": "object",
"properties": {
"coordinates": {
"type": "object"
},
"TYPE": {
"type": "string"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "longitude and latitude (it is initialized with 0,0 which is inside the ocean)"
}
},
"$schema": "http://json-schema.org/draft-04/schema#"
}
If it is successful, then it looks like this:
{
"type": "object",
"properties": {
"country": {
"type": "string"
},
"geoPosition": {
"type": "object",
"properties": {
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
},
"TYPE": {
"type": "string"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "longitude and latitude (it is initialized with 0,0 which is inside the ocean)"
}
},
"$schema": "http://json-schema.org/draft-04/schema#"
}
As already in the error message indicated, one time the coordinates are an 'object' the other time an 'array'
Update 2
As this issue is blocking me to continue with my project, I have added an "ugly hack" to check in the schema if it contains a GeoJsonPoint. If it is included, it will patch it with the correct values.
package de.s2.json.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.reinert.jjschema.v1.JsonSchemaV4Factory;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
public class Toolbox {
public static <T> ArrayList<String> validateJson(T inputObj) throws ProcessingException {
com.github.reinert.jjschema.v1.JsonSchemaFactory schemaFactory = new JsonSchemaV4Factory();
schemaFactory.setAutoPutDollarSchema(true);
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass());
ArrayList<String> validationErrorDetails = new ArrayList<String>();
// *****************************************************************************************
// /!\ this is an ugly hack /!\
// Randomly the function createSchema() returns an 'array' or 'object' type for coordinates
// of the geoJsonPoint. The correct value should be array. The following code checks if a
// geoJsonPoint is inside the schema and overwrites it with the correct value.
// TODO: fix this issue correctly
// *****************************************************************************************
try {
String jsonCoordinate = "{\"type\": \"array\",\"items\": {\"type\": \"number\"}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNodeCoordinate = mapper.readTree(jsonCoordinate);
JsonNode coordJsonNode = fstabSchema.findValue("geoPosition").get("properties");
ObjectNode coordObjNode = (ObjectNode) coordJsonNode;
coordObjNode.set("coordinates", jsonNodeCoordinate);
} catch (JsonProcessingException e) {
validationErrorDetails.add("Could not patch geoPosition");
return validationErrorDetails;
} catch (IOException e) {
validationErrorDetails.add("Could not patch geoPosition");
return validationErrorDetails;
} catch (NullPointerException e) {
// this means that geoPosition could not be found
// we do nothing ...
}
// ******************
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonSchema schema = factory.getJsonSchema(fstabSchema);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode baseReceiptJson = objectMapper.convertValue(inputObj, JsonNode.class);
ProcessingReport report;
report = schema.validate(baseReceiptJson);
if (!report.isSuccess()) {
StringBuilder builder = new StringBuilder();
builder.append("Not all required fields are filled with data");
builder.append(System.getProperty("line.separator"));
for (Iterator<ProcessingMessage> i = report.iterator(); i.hasNext();) {
ProcessingMessage msg = i.next();
builder.append(msg.asJson().findValue("instance").findValue("pointer").toString());
builder.append(": ");
builder.append(msg.getMessage());
String detail = msg.asJson().findValue("instance").findValue("pointer").toString() + ": " + msg.getMessage();
detail = detail.replace("\"", "");
validationErrorDetails.add(detail);
builder.append("\n");
}
return validationErrorDetails;
}
return null;
}
}
By serializing the JSON Schema you generated we are able now to compare whether they are the same or how much they actually differ. Take into account that the method to generate a schema requires some input parameter.
I was never a friend of generating a schema based on actual data.
Why do you not strictly define the schema you want and let your code measure whether you really meet that structure? This way you can use the schema as a contract between application components since it is documented and not generated on the fly.
This is still no answer to the question why this happens randomly. But it is maybe a better practice to follow for you and others reading along.
I'm using SignalR on ASP.NET Core 5 web server for Android device management. I can send messages from device (D2C), and receive messages with String parameters (C2D). But I can't receive messages with custom object parameters, the handler receives all object members as null. I develop an WPF client and it receives this object well.
I'm following ASP.NET Core SignalR Java client documentation. It explains how to use custom objects in Passing Class information in Java section.
In build.gradle file:
dependencies {
...
implementation 'com.microsoft.signalr:signalr:5.0.5'
implementation 'org.slf4j:slf4j-jdk14:1.7.25'
}
This is my custom class in Android project:
package com.mycompany.mayapp.signalr.models;
public class CustomClass
{
public String Param1;
public String Param2;
}
If this can help, this is my custom class in ASP.NET Core project (if I use fields instead of properties WPF client doesn't work, I don't know why):
namespace MyWebWithSignalRCore.SignalR.Models
{
public class CustomClass
{
public string Param1 { get; set; }
public string Param2 { get; set; }
}
}
And this is my android client class:
package com.mycompany.mayapp.signalr;
import android.util.Log;
import com.fagorelectronica.fagorconnectservice.signalr.models.UpdateAppParams;
import com.microsoft.signalr.HubConnection;
import com.microsoft.signalr.HubConnectionBuilder;
import com.microsoft.signalr.OnClosedCallback;
import com.microsoft.signalr.TypeReference;
import java.lang.reflect.Type;
public class SignalRClient
{
private static final String TAG = SignalRClient.class.getSimpleName();
HubConnection hubConnection;
public SignalRClient(String url)
{
this.hubConnection = HubConnectionBuilder.create(url).build();
this.handleIncomingMethods();
}
private void handleIncomingMethods()
{
this.hubConnection.on("ReceiveMessage", (user, message) -> { // OK
Log.d(TAG, user + ": " + message);
}, String.class, String.class);
this.hubConnection.on("Reset", () -> { // OK
Log.d(TAG, "Reset device");
});
Type customClassType = new TypeReference<CustomClass>() { }.getType();
this.hubConnection.<CustomClass>on("Custom", (params) -> { // NOK!!
Log.d(TAG, params.Param1 + ": " + params.Param2);
}, customClassType);
}
public void start()
{
this.hubConnection.start().blockingAwait();
this.hubConnection.send("Hello", "My device ID"); // OK
}
public void stop()
{
this.hubConnection.stop().blockingAwait();
}
}
This is the output I get in each handler:
D/SignalRClient: User: message
D/SignalRClient: Reset device
D/SignalRClient: null: null
Do you know what I'm doing wrong?
It seems that in the java client the custom object field names should be in lowercase. So changing the field names solves the problem.
Custom class in Android project:
package com.mycompany.mayapp.signalr.models;
public class CustomClass
{
public String param1;
public String param2;
}
Handling method:
private void handleIncomingMethods()
{
// ... other methods ...
Type customClassType = new TypeReference<CustomClass>() { }.getType();
this.hubConnection.<CustomClass>on("Custom", (params) -> { // OK!!
Log.d(TAG, params.param1 + ": " + params.param2);
}, customClassType);
}
I have some trouble using flink's SerializationSchema.
Here is my main code :
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DeserializationSchema<Row> sourceDeserializer = new JsonRowDeserializationSchema.Builder( /*Extract TypeInformation<Row> from an avsc schema file*/ ).build();
DataStream<Row> myDataStream = env.addSource( new MyCustomSource(sourceDeserializer) ) ;
final SinkFunction<Row> sink = new MyCustomSink(new JsonRowSerializationSchema.Builder(myDataStream.getType()).build());
myDataStream.addSink(sink).name("MyCustomSink");
env.execute("MyJob");
Here is my custom Sink Function :
import org.apache.flink.api.common.serialization.SerializationSchema;
import org.apache.flink.streaming.api.functions.sink.SinkFunction;
import org.apache.flink.types.Row;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#SuppressWarnings("serial")
public class MyCustomSink implements SinkFunction<Row> {
private static final Logger LOGGER = LoggerFactory.getLogger(MyCustomSink.class);
private final boolean print;
private final SerializationSchema<Row> serializationSchema;
public MyCustomSink(final SerializationSchema<Row> serializationSchema) {
this.serializationSchema = serializationSchema;
}
#Override
public void invoke(final Row value, final Context context) throws Exception {
try {
LOGGER.info("MyCustomSink- invoke : [{}]", new String(serializationSchema.serialize(value)));
}catch (Exception e){
LOGGER.error("MyCustomSink- Error while sending data : " + e);
}
}
}
And here is my custom Source Function (not sure it is useful for the problem I have) :
import org.apache.flink.api.common.serialization.DeserializationSchema;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.typeutils.ResultTypeQueryable;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.shaded.guava18.com.google.common.io.ByteStreams;
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyCustomSource<T> extends RichSourceFunction<T> implements ResultTypeQueryable<T> {
/** logger */
private static final Logger LOGGER = LoggerFactory.getLogger(MyCustomSource.class);
/** the JSON deserializer */
private final DeserializationSchema<T> deserializationSchema;
public MyCustomSource(final DeserializationSchema<T> deserializer) {
this.deserializationSchema = deserializer;
}
#Override
public void open(final Configuration parameters) {
...
}
#Override
public void run(final SourceContext<T> ctx) throws Exception {
LOGGER.info("run");
InputStream data = ...; // Retrieve the input json data
final T row = deserializationSchema
.deserialize(ByteStreams.toByteArray(data));
ctx.collect(row);
}
#Override
public void cancel() {
...
}
#Override
public TypeInformation<T> getProducedType() {
return deserializationSchema.getProducedType();
}
}
Now I run my code and I send some data sequentially to my pipeline :
==>
{
"id": "sensor1",
"data":{
"rotation": 250
}
}
Here, the data is correctly printed by my sink : MyCustomSink- invoke : [{"id":"sensor1","data":{"rotation":250}}]
==>
{
"id": "sensor1"
}
Here, the data is correctly printed by my sink : MyCustomSink- invoke : [{"id":"sensor1","data":null}]
==>
{
"id": "sensor1",
"data":{
"rotation": 250
}
}
Here, there is an error on serialization. The error log printed is :
MyCustomSink- Error while sending data : java.lang.RuntimeException: Could not serialize row 'sensor1,250'. Make sure that the schema matches the input.
I do not understand at all why I have this behavior. Someone have an idea ?
Notes:
Using Flink 1.9.2
-- EDIT --
I added the CustomSource part
-- EDIT 2 --
After more investigations, it looks like this behavior is caused by the private transient ObjectNode node of the JsonRowSerializationSchema. If I understand correctly, this is used for optimization, but seems to be the cause of my problem.
Is it the normal behavior, and if it is, what would be the correct use of this class in my case ? (Else, is there any way to bypass this problem ?)
This is a JsonRowSerializationSchema bug which has been fixed in most recent Flink versions - I believe, this PR addresses the issue above.
I'm a little clueless on this subject, but I'm trying to parse some Json into variables using GSON.
Here's an example of part of the Json I'm trying to parse. The goal is to get an array of objects containing the .ogg path(i.e. "minecraft/sounds/mob/stray/death2.ogg") as well as its corresponding hash variable.
{
"objects": {
"minecraft/sounds/mob/stray/death2.ogg": {
"hash": "d48940aeab2d4068bd157e6810406c882503a813",
"size": 18817
},
"minecraft/sounds/mob/husk/step4.ogg": {
"hash": "70a1c99c314a134027988106a3b61b15389d5f2f",
"size": 9398
}
}
Any help or suggestions on how to get that sort of result with GSON would be appreciated.
Xeyeler , the following is a solution that will work. A better way is to look at how GSON can convert this to an object directly. I have not tried it though
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class MainProgram {
public static void main(String[] args) {
String mineCraft = "{ \"objects\": { \"minecraft/sounds/mob/stray/death2.ogg\": { \"hash\": \"d48940aeab2d4068bd157e6810406c882503a813\", \"size\": 18817 }, \"minecraft/sounds/mob/husk/step4.ogg\": { \"hash\": \"70a1c99c314a134027988106a3b61b15389d5f2f\", \"size\": 9398 }}}";
JsonParser parser = new JsonParser();
JsonObject objects = parser.parse(mineCraft).getAsJsonObject().get("objects").getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : objects.entrySet()) {
String oggFileName = entry.getKey();
JsonElement attributes = entry.getValue();
System.out.println(
"Key is " + oggFileName + " and the hash value is " + attributes.getAsJsonObject().get("hash"));
}
}
}
I have been trying to generate Java spring code like the following.
#RequestMapping(method=RequestMethod.DELETE, value="/message/{id}")
public void reject(#PathVariable final Long id) {
return;
}
I have provided the following code in the xtend file.
members += event.toMethod(event.action.name, typeRef(void)) [
var dataType = map.get(method.action.name)
parameters += event.toParameter(method.type.parameter.name, dataType.javaType) => [
annotations += annotationRef("org.springframework.web.bind.annotation.PathVariable");
]
annotations += annotationRef("org.springframework.web.bind.annotation.RequestMapping", "method=RequestMethod.DELETE", "/message/{id}");
body = '''
return;
'''
]
and the output I am getting is
#RequestMapping({ "method=RequestMethod.DELETE", "/message/{id}" })
public void reject(#PathVariable final Long id) {
return;
}
I am confused how to provide the xtend code so that I could get the RequestMapping format as #RequestMapping(method=RequestMethod.DELETE, value="/message/{id}")
the api has no convenience for that usecase so you have to do it yourself
package org.eclipse.xtext.example.domainmodel.jvmmodel
import com.google.inject.Inject
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.common.types.JvmAnnotationReference
import org.eclipse.xtext.common.types.JvmAnnotationType
import org.eclipse.xtext.common.types.JvmType
import org.eclipse.xtext.common.types.TypesFactory
import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.example.domainmodel.domainmodel.Entity
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmAnnotationReferenceBuilder
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.eclipse.xtext.common.types.JvmStringAnnotationValue
class DomainmodelJvmModelInferrer extends AbstractModelInferrer {
#Inject extension JvmTypesBuilder
#Inject extension IQualifiedNameProvider
def dispatch infer(Entity entity, extension IJvmDeclaredTypeAcceptor acceptor, boolean prelinkingPhase) {
accept(entity.toClass(entity.fullyQualifiedName)) [
documentation = entity.documentation
members += entity.toMethod("demo", Void.TYPE.typeRef) [
annotations +=
entity.toAnnotationRef("org.eclipse.xtext.example.domainmodel.lib.Demo", "a" -> "Hallo",
"b" -> "Welt", "c" -> "!")
body = ''''''
]
]
}
#Inject
private TypesFactory typesFactory;
#Inject
private TypeReferences references;
def toAnnotationRef(EObject context, String annotationTypeName, Pair<String, String> ... values) {
val JvmAnnotationReference result = typesFactory.createJvmAnnotationReference();
val JvmType jvmType = references.findDeclaredType(annotationTypeName, context);
if (jvmType == null) {
throw new IllegalArgumentException("The type " + annotationTypeName + " is not on the classpath.");
}
if (!(jvmType instanceof JvmAnnotationType)) {
throw new IllegalArgumentException("The given class " + annotationTypeName + " is not an annotation type.");
}
val jvmAnnotationType = jvmType as JvmAnnotationType
result.setAnnotation(jvmAnnotationType);
for (value : values) {
val JvmStringAnnotationValue annoValue = typesFactory.createJvmStringAnnotationValue
annoValue.values += value.value
annoValue.operation = jvmAnnotationType.declaredOperations.findFirst[simpleName == value.key]
result.explicitValues.add(annoValue)
}
return result
}
}