import java.io.Serializable; import java.util.*; import java.lang.annotation.*; import java.lang.reflect.*; import wwutil.model.AnnotationFieldHandler; import wwutil.model.ValidationException; import wwutil.model.annotation.DbType; import wwutil.model.annotation.Model; import wwutil.model.annotation.Key; import wwutil.jsoda.Jsoda; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; /** * Sample to illustrate adding custom annotation for data handlers and validations */ public class Sample4 { // Get AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from environment variables. // You can hardcode them here for testing but should remove them afterward. private static final String key = System.getenv("AWS_ACCESS_KEY_ID"); private static final String secret = System.getenv("AWS_SECRET_ACCESS_KEY"); @Retention(RetentionPolicy.RUNTIME) public static @interface MyDataConverter { } @Retention(RetentionPolicy.RUNTIME) public static @interface MyValidator { } @Retention(RetentionPolicy.RUNTIME) public static @interface MyDataJoiner { public String field1(); public String field2(); public String field3(); } @Model public static class SampleCustom implements Serializable { @Key public int id; @MyDataConverter // apply a custom data converter public String name; @MyValidator // apply a custom validator public String desc; @MyDataJoiner(field1="id", field2="name", field3="desc") // apply a custom stage 2 data converter public String upperText; public SampleCustom() {} public SampleCustom(int id, String name, String desc) { this.id = id; this.name = name; this.desc = desc; } public String toString() { return " Product [" + id + ", " + name + ", " + desc + ", " + upperText + "]"; } } public static void main(String[] args) throws Exception { Jsoda jsoda = new Jsoda(new BasicAWSCredentials(key, secret)); // Register a custom AnnotationFieldHandler that implements the functionality of MyDataConverter. jsoda.registerData1Handler(MyDataConverter.class, new AnnotationFieldHandler() { // checkModel() is called when a model class is registered to see if the annotated fields confirm to this annotation's requirement. public void checkModel(Annotation fieldAnnotation, Field field, Map<String, Field> allFieldMap) throws ValidationException { if (field.getType() != String.class) throw new ValidationException("The field must be String type. Field: " + field.getName()); } // handle() is called when a model object is stored public void handle(Annotation fieldAnnotation, Object object, Field field, Map<String, Field> allFieldMap) throws Exception { String value = (String)field.get(object); if (value != null) { String trimValue = (value.length() > 4 ? value.substring(0, 4) : value); field.set(object, trimValue); } } }); // Register a custom AnnotationFieldHandler that implements the functionality of MyValidator jsoda.registerValidationHandler(MyValidator.class, new AnnotationFieldHandler() { // checkModel() is called when a model class is registered to see if the annotated fields confirm to this annotation's requirement. public void checkModel(Annotation fieldAnnotation, Field field, Map<String, Field> allFieldMap) throws ValidationException { if (field.getType() != String.class) throw new ValidationException("The field must be String type. Field: " + field.getName()); } // handle() is called when a model object is stored public void handle(Annotation fieldAnnotation, Object object, Field field, Map<String, Field> allFieldMap) throws Exception { String value = (String)field.get(object); if (value != null) { if (value.startsWith("foobar")) throw new ValidationException("Field cannot start with foobar. Field: " + field.getName()); } } }); // Register a custom AnnotationFieldHandler that implements the functionality of MyDataJoiner // Note it's registered as stage 2 handler, to run after the stage 1 handlers. jsoda.registerData2Handler(MyDataJoiner.class, new AnnotationFieldHandler() { // checkModel() is called when a model class is registered to see if the annotated fields confirm to this annotation's requirement. public void checkModel(Annotation fieldAnnotation, Field field, Map<String, Field> allFieldMap) throws ValidationException { if (field.getType() != String.class) throw new ValidationException("The field must be String type. Field: " + field.getName()); } // handle() is called when a model object is stored public void handle(Annotation fieldAnnotation, Object object, Field field, Map<String, Field> allFieldMap) throws Exception { // Join the values from field1, field2, and field3, and convert it to upper case. MyDataJoiner ann = (MyDataJoiner)fieldAnnotation; Field field1 = allFieldMap.get(ann.field1()); Field field2 = allFieldMap.get(ann.field2()); Field field3 = allFieldMap.get(ann.field3()); String value1 = field1.get(object).toString(); String value2 = field2.get(object).toString(); String value3 = field3.get(object).toString(); String result = (value1 + ": " + value2 + "/" + value3).toUpperCase(); field.set(object, result); } }); jsoda.registerModel(SampleCustom.class, DbType.SimpleDB); // Sample object to show how the fields are transformed via the custom annotations try { SampleCustom obj = new SampleCustom(101, "this-long-long-name", "custom object"); jsoda.preStoreSteps(obj); System.out.println(obj); } catch(Exception e) { System.out.println(e); } // Sample object to show how the custom validation is triggered. try { SampleCustom obj = new SampleCustom(101, "this-long-long-name", "foobar custom object"); jsoda.preStoreSteps(obj); System.out.println(obj); } catch(Exception e) { System.out.println(e); } } }