Make Java annotation act differently depending on field annotated - java

In Java, is there a way to change the behaviour of an annotation depending on the type of the annotated field?
I know that annotation presence is supposed to be tested by code. Not the opposite. But the case is rather particular: this is a Jackson 2.0 « inside » annotation which gather a list of annotations. We use it to define the field name (#JsonProperty) and the field serializing policies (#JsonSerialize).
The serialisation policies must be adapted to the annotated field. And, because we are talking of a framework, one unique annotation is far better than two separate ones.
#Retention(RUNTIME)
#JacksonAnnotationsInside.
#JsonProperty("_id")
#JsonSerialize(using=IdSerializer.class)
#JsonDeserialize(using=IdDeserializer.class)
public #interface Id {}
Some cases need to turn the serializers down, that's the point. In the following example, the String must be processed by the de/serializers, ObjectId don't. Both need to be renamed _id by the #JsonProperty.
public class Car {
#Id String id
}
public class Bus {
#Id ObjectId id
}
Any clues?

Related

Field mapping with MapStruct by JsonProperty annotation

In my current project the names of the model class fields are German. The fields are all annotated with #JsonProperty for the English translation of the names. E.g. #JsonProperty(value = "operation"). Is there a way in the configuration that the mapping of the fields is done using the JsonProperty annotation?
Example:
public class Auftrag {
#JsonProperty(value = "orderType")
private String auftragsart;
...
}
public class OrderDto {
private String orderType;
}
MapStruct uses the Java Bean convention to detect the properties. This means that it looks in the getters and setters.
Out-of-the-box you cannot use the #JsonProperty. However, you can create your own AccessorNamingStrategy that will provide the properties based on #JsonProperty. The AccessorNamingStrategy gives you access to the Abstract syntax tree, which means you can look for fields in types, check their annotations and check their values.
Keep in mind that MapStruct will only ask to get the property for a method, so you would need to get the property name, then find the field in the type, then look for the #JsonProperty annotation and its value.
You can read more about the AccessorNamingStrategy here in the documentation.

using javax.validation to validate bean with choices

I'm implementing an in-memory API gateway to a SOAP service utilizing JAXB. One of the schema elements is a "choice", and there are several elements in the choice block.
I'm attempting to mirror the generated JAXB classes in the client namespace, so for this "choice" scenario I have a bean class with several properties, only one of which will be non-null.
I'm attempting to use the #NotNull annotation from javax.validation, along with the ValidatorFactory and Validator. However, a "choice" scenario makes this a little more complicated. I'm guessing this would call for a custom ConstraintValidator, along with a custom annotation to refer to the custom ConstraintValidator.
For instance, here's some fake code that resembles a part of my structure:
public class InquireRandomInformationRequest {
#NotNull(message ="subscriberSelector cannot be null")
#Valid
private SubscriberSelector subscriberSelector; // required
private SelectorMode mode; // optional
...
}
public class SubscriberSelector {
// Choice 1
private String billingAccountNumber; // \d{8,9,12,13}; required
private MarketInfo billingMarket; // optional
// Choice 2
private String subscriberNumber; // \d{10}; required
private ValidationCriteria validationCriteria; // optional
private BillingAccountInformation billingAccountInformation; // optional
private MemoProductType memoProductType; // optional
// Choice 3
private String unifiedBillingAccountNumber; // [0-9A-Za-z]{13}; required
...
}
I understand that I need the #Valid annotation on the "subscriberSelector" property for the validator to validate the sub-object. Past that, I'm not quite sure what I need to do to handle the choices problem.
To fit my example, I will need exactly one of "billingAccountNumber", "subscriberNumber", or "unifiedBillingAccountNumber" to be non-null (although I could compromise on simply taking the first non-null one in a particular sequence). In each "choice group", the other properties are optional, but it's possible that another property could be "required" if that particular choice group is selected (the selector property is non-null, in other words).
I've looked through the Hibernate Validator documentation, but I'm not sure exactly how to apply that for this situation.
If I define a custom annotation and a custom ConstraintValidator, where is the annotation referenced? On the class (like "SubscriberSelector") or on the "subscriberSelector" property (the former seems more logical to me).
You can define constraints on the class or on the property depending on your requirements.
Usually, the constraints are placed on the property but, in the case you mention, as multiple properties are concerned and interdependent, you should place the constraint at the class level.
See https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-declaring-bean-constraints in our documentation.

#Transient not working in hibernate

I am using hibernate 4.1.9.
My code is
#Transient
private String ldapIdTemp;
package is
import javax.persistence.Transient;
Still in hibernate query, it is not working and putting the attribute in the query.
part of query snippet (assetasset0_.ldapIdTemp as ldapIdTemp16_0_, )
I am not sure what I am doing wrong.
Can you try creating setter and getter for the field and annotate the get method with #Transient, as follows:
private String ldapIdTemp;
#Transient
public String getLdapIdTemp() {
return ldapIdTemp;
}
public void setLdapIdTemp(String ldapIdTemp) {
this.ldapIdTemp = ldapIdTemp;
}
Much depends on how you "integrated" this field in your Entity or class hierarchy. Moreover, field vs. property-access could cause an issue for your setting. See this post for a detailed explanation.
In your case, I could imagine that you either:
mixed field and property-access in your entity inheritance strategy
use XML-based configuration for Hibernate in your application.
In both cases the JPA 2.0/2.1 specification clearly states in Section 2.3.1:
It is an error if a default access type cannot be determined and an access type is not explicitly specified
by means of annotations or the XML descriptor. The behavior of applications that mix the placement of
annotations on fields and properties within an entity hierarchy without explicitly specifying the
Access annotation is undefined.
Please check that your persistent Entity classes have either field OR property-based annotations.
Check the #Transient annotation fully qualified name.
It can be from either,
org.springframework.data.annotation.Transient or javax.persistence.Transient.
Try to use javax.persistence.Transient.

Jackson equivalent to #XmlSeeAlso

I am writing a RESTful web service using Java and Jersey, where the service will accept either XML or JSON inputs. Jackson is used as the JSON deserializer, and integrated into the Jersey config.
One of the endpoints is a POST request to a URL, where the content can be one of several different Java classes, and there is a common base class. These classes - with XML annotations - are:
#XmlRootElement(name = "action")
#XmlAccessorType(XmlAccessType.NONE)
#XmlSeeAlso({ FirstAction.class, SecondAction.class, ThirdAction.class })
public abstract class BaseAction {
}
#XmlRootElement(name = "first-action")
#XmlAccessorType(XmlAccessType.NONE)
public class FirstAction extends BaseAction implements Serializable {
}
// Likewise for SecondAction, ThirdAction
In my resource I can declare a method like:
#POST
#Path("/{id}/action")
public Response invokeAction(#PathParam("id") String id, BaseAction action) {...}
Then I can POST an XML fragment that looks like <firstAction/> and my method will be invoked with a FirstAction instance. So far so good.
Where I'm struggling is getting the JSON deserialization to work as seamlessly as the XML deserialization. Where the #XmlSeeAlso annotation was critical to get the XML deserialization working properly, it seemed that the equivalent for JSON was #JsonSubTypes. So I annotated the classes like this:
// XML annotations removed for brevity, but they are present as in the previous code snippet
#JsonSubTypes({ #JsonSubTypes.Type(name = "first-action", value = FirstAction.class),
#JsonSubTypes.Type(name = "second-action", value = SecondAction.class),
#JsonSubTypes.Type(name = "third-action", value = ThirdAction.class) })
public abstract class BaseAction {
}
#JsonRootName("first-action")
public class FirstAction extends BaseAction implements Serializable {
}
// Likewise for SecondAction, ThirdAction
I then feed it my test input: { "first-action": null } but all I can get is:
"org.codehaus.jackson.map.JsonMappingException: Root name 'first-action' does not match expected ('action') for type [simple type, class com.alu.openstack.domain.compute.server.actions.BaseAction]"
Unfortunately since I'm trying to be compatible with someone else's API I can't change my sample input - { "first-action": null } has to work, and deliver to my method an object of class FirstAction. (The action doesn't have any fields, which is why null shouldn't be a problem - it's the type of the class that's important).
What's the correct way to have the JSON deserialization work in the same way as the XML deserialization already is?
If you are using Jackson, you are looking for #JsonTypeInfo and #Type . Please see here for more information
JSON does not work the way XML does, so the solution is not identical.
What you need to use is (like the other answer said), #JsonTypeInfo. That only triggers inclusion and use of the type identifier. If so, then '#JsonSubTypes` will be of use in deserialization.
The reason this indicator must be used is simple: if you have more than one alternative type to deserialize to, there must be something to differentiate.
Note, too, that this does NOT have to be a property -- while most users choose "As.PROPERTY" inclusion, it is not (IMO) the best way. "WRAPPER_OBJECT" may be what you are looking for, as it adds an extra intermediate JSON property, which is somewhat similar to what XML does.
I investigated the use of #JsonTypeInfo but ran into problems because I could not alter the input format. The parser absolutely had to be able to handle input { "first-action":null }. This ruled out the possibility of adding an #type or #class property. Using a wrapper object may have worked, but it choked on the null payload.
A crucial point was that I was using the UNWRAP_ROOT_PROPERTY configuration option. Jackson was absolutely insisting on finding an action property and I could not get it to consider anything else. So, I had to selectively disable UNWRAP_ROOT_PROPERTY for certain domain objects, so that Jackson would be open to parsing alternatives. I modified the project's ContextResolver.getContext(...) implementation to check for a #JsonRootName annotation - since this only has meaning if wrapping is enabled, I used the presence of this annotation to determine whether to return an object mapper configured with root property wrapping on, or off.
At this stage, I might have been able to use #JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...), except for the issue with the null payload mentioned above (this is used to indicate that the child object has no properties - if the spec I was working from had given an empty object {} instead then there would not be a problem). So to proceed I needed a custom type resolver.
I created a new class that extended org.codehaus.jackson.map.TypeDeserializer, with the purpose that whenever Jackson is called to deserialize a BaseAction instance, it will call this custom deserializer. The deserializer will be given a subtypes array, which for BaseAction maps first-action, second-action, etc. to FirstAction.class, etc. The deserializer reads the input stream for the field name, then matches the name to a class. If the next token is an object, then it finds and delegates to the appropriate deserializer for that class, or if it is null it finds the no-args constructor and invokes it to get an object.
A class that implements org.codehaus.jackson.map.jsontype.TypeResolverBuilder is needed that can build an instance of this previous class, and then the TypeResolverBuilder is given as a #JsonTypeResolver annotation on the BaseAction class.

Exclude field in JPA Entity Listener

I have an entity class in my Enterprise Java application that has an entity listener attached to it:
#Entity
#EntityListeners(ChangeListener.class)
public class MyEntity {
#Id
private long id;
private String name;
private Integer result;
private Boolean dirty;
...
}
However, I would like it so that the entity listener got triggered for all fields except the boolean one. Is there any way exclude a field from triggering the entity listener without making it transient?
I'm using Java EE 5 with Hibernate.
However, it is possible if you implement your own solution. I've had the same need for audit log business requirement, so designed my own AuditField annotation, and applied to the fields to be audit-logged.
Here's the example in one entity bean - Site.
#AuditField(exclude={EntityActionType.DELETE})
#Column(name = "site_code", nullable = false)
private String siteCode;
So, the example indicates the 'siteCode' is a field to audit log, except DELETE action. (EntityActionType is an enum and it contains CRUD operations.)
Also, the EntityListenerhas this part of code.
#PostPersist
public void created(Site pEntity) {
log(pEntity, EntityActionType.CREATE);
}
#PreUpdate
public void updated(Site pEntity) {
log(pEntity, EntityActionType.UPDATE);
}
#PreRemove
public void deleted(Site pEntity) {
log(pEntity, EntityActionType.DELETE);
}
Now what it has to do in log() is, to figure what fields are to audit log and what custom actions are involved optionally.
However, there's another to consider.
If you put the annotation at another entity variable, what fields of the entity have to be logged? (i.e. chained logging)
It's your choice whether what are annotated with #AuditField only in the entity or some other ways. For my case, we decided to log only the entity ID, which is a PK of a DB table. However, I wanted to make it flexible assuming the business can change. So, all the entites must implement auditValue() method, which is coming from a base entity class, and the default implementation (that's overridable) is to return its ID.
There is some kind of mixing of concepts here. EntityListeners are not notified about changes in attribute values - not for single attribute, neither for all attributes.
For reason they are called lifecycle callbacks. They are triggered by following lifecycle events of entity:
persist (pre/post)
load (post)
update(pre/post)
remove (pre/post)
For each one of them there is matching annotation. So answer is that it is not possible to limit this functionality by type of persistent attributes.

Categories

Resources