Java Custom Annotation and dynamic database content - java

i am trying to implement the Custom Java Annotation :
and wrote following so far:
#Target({ElementType.FIELD,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MappedField{
boolean isValueRequired() default false;
}
My Requirement is if a property/Method of a Class is isValueRequired=true then it should get the mapped value for this property from database:
For example in my db Salutations are like 01 for MR and 02 for MRS and so on.
And when this annotation is enabled i want property value automatically to be fetched from db:
From other questions i got hint that i need to parse the annotations later something like this:
public static void performAnnotationScanOnClass(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for ( Field field : fields ) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if ( annotation instanceof MappedField) {
MappedField mappedField= (MappedField) annotation;
// if ( field.get( ... ) == null )
// field.set( ... , value)
}
but my point is where exactly i need to write this code for parsing the annotations.
Any help would be highly appreciated.
Thanks

A suitable place. You could for example have a factory that creates the instances and fills the values from the database. Then you'll just need to be careful to create all the instances through the factory (or you don't need to, but then you must realize that the annotations won't be processed either).

Related

Map null values to default using builder with MapStruct

I want to map field from Source to Target class, and if the source value is null, I would like to convert it to default value based on the data type ("" for strings, 0 for numeric types etc.). For setting the values, I am not using regular setters, but builder (with protobuf, so the names of the methods is newBuilder() and build()).
class Source {
private final String value; // getter
}
class Target {
private final String value;
public static Builder newBuilder() {return new Builder()}
public static class Builder {
public static setValue() {/*Set the field*/}
public static Target build() {/*Return the constructed instance*/}
}
My mapper looks like this:
#Mapper(
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT
)
public interface TargetMapper {
Target map(Source source);
}
The generated mapper implementation with this code calls target.setValue(source.getValue()), instead of performing the null check and setting default value if source returns null. The interesting part is when I add the following annotation to the map method, the null check is present in the implementation.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
Is this a bug in MapStruct with builders, or am I missing some configuration to be ably to set the null mapping as a default policy, instead of duplicating it on all field mappings?
EDIT: For some reason, adding nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS to the class level #Mapper annotation adds the null check, but does not explicitly set the value, just skips the call to setValue. For protobuf, this is okay, since this functionality is in the library, but for other implementations the field would remain null.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
applies to update method (so methods that have the #MappingTarget annotated parameter
There's no real counterpart for regular methods:
1. NullValueMappingStragegy applies to the bean argument itself.
2. NullValueCheckStragegy does perform a check on bean properties, but does not return a default.
Naming is not really brilliant and it has a long history. We still have the intention to align this one day.
A solution would be to use an Object factory creating the builder target object and pre-populate it with default values and then let MapStuct override these one day.
Perhaps you could do something like this:
#Mapper(
// to perform a null check
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS
)
public interface TargetMapper {
Target map(Source source);
}
// to create a pre-defined object (defaults set a-priori). Not sure
// whether this works with builders.. just try
#ObjectFactory
default Target.Builder create() {
Target.Builder builder = Target.newBuilder();
builder.setValueX( "someDefaultValue" );
return builder;
}

Jackson: can json specify the target type?

This is what my class looks like -
public class A {
private Map<String, Object> objects = null;
....
}
My json would be like -
{
"f1" : {
"name" : "some name",
"val" : 3
},
"f2" : {
"arg": {
some field/value pairs
}
}
}
What I want is to specify in the JSON itself the type to which it can be deserialized to. So the value for f1 would be converted to an object of class B and f2 would get converted to object of C.
My code will look like this -
Object o = objects.get("f1");
if (o instanceof B) {
...
} else if (o instanceof C) {
...
}
Is there a way to do this? I want the json to control the deserialization.
Yes, Jackson can use a type identifier if JSON document has it. This is usually done by using annotation #JsonTypeInfo.
There are multiple ways to add/use type identifier, both regarding how it is included in JSON document, and as to what kind of id is being used (type name or Java class name?).
The easiest way to see how things match is to actually start with a POJO, add #JsonTypeInfo annotation, and serialize it to see kind of JSON produced. And once you understood how inclusion works you can modify, if necessary, structure of JSON and/or Java class definition.

Is there a way to get the declared value of constraint in hibernate validator in another class?

Using the hibernate validator i declare something like this
public class TestSomething{
#Length(max=30, message="Error Message.")
private String name;
getter and setter here
}
is it possible to get the maximum number of character in this case 30
something like
TestSomething ts = new TestSomething();
int maxValue = ts.getName.getThatMaximumNumberOrSomethng
will java reflection on this kind of situation?
You should use the Bean Validation metadata API. Provided you have a Validator instance you can get hold of a so called ConstraintDescriptor:
BeanDescriptor beanDescriptor = getBeanDescriptor( TestSomething.class );
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( "name" );
Set<ConstraintDescriptor<?>> constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
Once you have the right ConstraintDescriptor you can either call
constraintDescriptor.getAnnotation(); // to get the actual constraint annotation
constraintDescriptor.getAttributes().get("max"); // to retrieve the attribute from the attributes map provided by the descriptor as convenience

Getting annotated variables from a class

This question is a follow up to a question I found before
java: get all variable names in a class
What I want is to get variables from a class, but instead of getting them all, I want only the variables that have the annotation #isSearchable .
So basically I have 2 questions :
How do I create an annotation ?
How to filter my fields by only this annotation ?
And one more thing , if it is something I'm using frequently is it advisable (I'm guessing reflections should be slow).
Thank you
/** Annotation declaration */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface isSearchable{
//...
}
#isSearchable
public String anyField = "any value";
checking like:
//use MyClass.class.getDeclaredFields() if you want the fields only for this class.
//.getFields() returns the fields for all the class hierarchy
for(Field field : MyClass.class.getFields()){
isSearchable s = field.getAnnotation(isSearchable.class);
if (s != null) {
//field has the annotation isSearchable
} else {
//field has not the annotation
}
}
Here is an example
class Test {
#IsSearchable
String str1;
String str2;
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface IsSearchable {
}
public static void main(String[] args) throws Exception {
for (Field f : Test.class.getDeclaredFields()) {
if (f.getAnnotation(IsSearchable.class) != null) {
System.out.println(f);
}
}
}
}
prints
java.lang.String Test.str1
How to filter my fields by only this annotation ?
You can get by this simple snippet
Field field = ... //obtain field object
Annotation[] annotations = field.getDeclaredAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof IsSearchable){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
In the above snippet you are basically filtering only IsSearchable annotations.
Regarding your one more thing query
Yes reflection will be slow as discussed here, if its possible to avoid, would advise you to avoid.
Field.getDeclaredAnnotations() gives you the annotations for each field.
To answer your supplementary question, I would normally expect reflection to be slow. Having said that, I perhaps wouldn't worry about optimising until this becomes a problem for you.
Hint: Make sure you're checking the up-to-date Javadoc. Google tends to give me Java 1.4 Javadocs, and annotations didn't exist prior to Java 5.

JAXB Marshalling with null fields

This is a pretty simple request, but I just didn't find a way to do it.
I'm basically trying to set up a role in JAXB which says that whenever an null field is encountered, instead of ignoring it in the output, set it to an empty value. So for the class :
#XMLRootElement
Class Foo {
Integer num;
Date date;
….
}
When this has been marshalled into the XML file if the date field is null, my output does not have that element in it. What I want to do is include all the fields in the output; and if they are null, replace them with - say a blank. So the output should be :
<foo>
<num>123</num>
<date></date>
</foo>
Thanks,
Jalpesh.
Thanks guys for your answers.
Chris Dail - I tried your approach, and it didn't really do what I wanted. JAXB was still ignoring my null values, in spite of defining a default value for my fields.
I did stumble across the answer after somebody in the Jersey forums pointed me to documentation section 2.2.12.8 No Value.
Basically, all I had to do was to add the following to my fields :
#XmlElement(nillable = true)
Once I added that, JAXB would show up those fields when marshalling them to XML like this:
...
<num>5</num>
<date xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
....
But but but...a empty string is not a valid lexical representation for a date, so you can't do that. i.e., if you generated an XML document with an empty value for a date field, it won't validate properly.
In other words, if your date element has a minOccurs of 1 or more and not nillable, then you absolutely must have (1 or more) dates, which can't be null (or blanks, or other non-values).
As indicated in the other answer is invalid since it is not a valid date. I had a similar issue where I wanted to handle (same as ) specially. Since you cannot use null, you can use the default value mechanism in JAXB. The following will default the value if none is specified. You can through code detect this special date and handle this exception case.
#XmlElement(defaultValue="1970-01-01T00:00:00.0-00:00")
So it is possible to detected and empty date value but you just cannot use null to do it.
In MOXy u can specify how the jsonProvider must do its job for JAXB.
So when doing JAX-RS, add following code in your class derived from Application
I used this code on Tomcat 7 with good results. (eclipselink 2.4.1)
#ApplicationPath("/rest")
public class RestApplication extends Application
{
...
public Set< Object> getSingletons()
{
HashSet<Object> set = new HashSet<Object>(1);
set.add( newMoxyJsonProvider());
return set;
}
public static MOXyJsonProvider newMoxyJsonProvider()
{
MOXyJsonProvider result = new MOXyJsonProvider();
//result.setAttributePrefix("#");
result.setFormattedOutput( false);
result.setIncludeRoot( false);
result.setMarshalEmptyCollections( true);
//result.setValueWrapper("$");
return result;
}
On Glassfish 3.1.2 and WAS 8.5 however, newMoxyJsonProvider() is not needed, but then the JAXB provider gets configured by the server.
In the case of Glassfish, which comes with MOXy, i witnessed same problems with null values.
Did not check yet, but guess the answer is in configuring JAXB at application server level if possible at all.
Try this:
marshal.setListener(new MarshallerListener());
with
public class MarshallerListener extends Marshaller.Listener {
public static final String BLANK_CHAR = "";
#Override
public void beforeMarshal(Object source) {
super.beforeMarshal(source);
Field[] fields = source.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
if (f.getType() == String.class && f.get(source) == null) {
f.set(source, BLANK_CHAR);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

Categories

Resources