accept argument with object which has a specific annotation - java

PROBLEM
If I generate a jaxb object from an XML root element named 'Message' it produces the following class:
#XmlRootElement(name = "Message")
public class Message{
...
}
I have a method with the following definition:
public void doSomething(Object rootElement) {
...
}
This method is part of a framework used by 30+ developers. The method only accepts a class with the annotation #XmlRootElement. Developers sometimes miss this crucial javadoc and pass a non #XmlRootElement object to the method giving errors during runtime. As the generated class has no superclass, no interfaces, etc I have to accept an Object as argument.
QUESTION
Is it not possible to enforce this check compile time ?
ALREADY TRIED
I've looked into (via jaxb binding file) letting generated classes implement an interface and then accept this interface as argument, however it cannot (in an automated manner) be applied to only the root element class.
We have several hundred different XML Schema files from which we generated in an automated manner.

Related

How to process annotations with #Target(ElementType.TYPE_USE)?

I'm implementing an annotation processor to make sure that the elements marked with an annotation are instances of a class that implements a certain interface, or are uses of types that implement a certain interface:
#Documented
#Target(value = { ElementType.PARAMETER, ElementType.TYPE_USE })
#Retention(value = RetentionPolicy.RUNTIME)
public #interface AuditSubject {
}
public interface Auditable {
// methods that provide data for writing a log entry...
}
public class Report implements Auditable {
}
For the annotated elements, a log entry must be created after method execution (using AOP). Examples:
#CreateLogEntry
public Result persist(#AuditSubject Report newReport) {
// A log entry must be created based on the incoming 'newReport' instance.
}
#CreateLogEntry
public UpdateResult<#AuditSubject Report> update(Report update) {
// A log entry must be created based on the updated report, which is not the same instance as 'update' but an equivalent one.
}
#CreateLogEntry
public Result persistBatch(List<#AuditSubject Report> batch) {
// A log entry must be created for each element in 'batch' after this method's execution.
}
The log entries must be created provided that Report implements Auditable; if it does not, a runtime exception is thrown (Yikes, I forgot to implement the interface!). Thus the annotation processor helps to catch programmer mistakes at compile time. So far I've been successful in checking all uses in parameters, but not in type uses. The relevant code from the annotation processor is as follows:
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element annotated : roundEnv.getElementsAnnotatedWith(AuditSubject.class)) {
// Only prints elements with ElementKind.PARAMETER)!
this.messager.printMessage(Kind.NOTE, TextUtils.replaceParams("annotated: {} ; Kind : {} ; enclosing : {}", annotated, annotated.getKind(), annotated.getEnclosingElement()));
if (annotated.getKind() == ElementKind.PARAMETER) {
// Code here works as expected, raises errors for annotated parameters of classes that don't implement Auditable.
} else if (annotated.getKind() == ElementKind.WHAT_TO_USE) {
// What ElementKind do I need to use here?
}
}
return false;
}
Only annotated elements with kind ElementKind.PARAMETER are recognized (the first line in the loop of process() only prints a single line for 'newReport') How can I check that the annotated types implement Auditable? There's no "ElementKind.TYPE_USE" constant to use. I haven't been able to find any relevant information on this matter. Thanks for your attention.
The Java annotation processing API was designed when Java only supported annotations on declarations. The API only supports visiting declarations, such as fields, methods, and method parameters. It does not visit local variable declarations, nor other annotations within a method body, nor type annotations.
If you wish to process type annotations or annotations within method bodies, you will need to write your own code to recurse on types or to recurse examining the lines of code within a method.
An alternative to this is to use a tool like the Checker Framework. It implements its own visitors, and therefore an annotation processor built on it is invoked for every occurrence of a type annotation.
Why not use TYPE_PARAMETER?
javax.annotation.processing.Processor API document there is:
An annotation type is considered present if there is at least one annotation of that type present on an element enclosed within the root elements of a round. For this purpose, a type parameter is considered to be enclosed by its generic element. Annotations on type uses, as opposed to annotations on elements, are ignored when computing whether or not an annotation type is present.
javax.lang.model.element.TypeParameterElement
Element getGenericElement()
Returns the generic class, interface, method, or constructor that is parameterized by this type parameter.

Use Json root element only for some classes

I'm using dropwizard to create REST API. But I dont understand, how can I configure Jackson to exclude some classes from WRAP_ROOT_VALUE/UNWRAP_ROOT_VALUE features? Right now I get a post request with json body that doesn't include root element name:
{
"identification": "dummyuser",
"password":"dummypass"
}
This should map to java class LoginRequest:
public class LoginRequest {
public String identidication;
public String passwrd;
}
I also get requests for some types that include root element name:
{
"user":{
"id":12345,
"name":"John Doe"
}
}
This should be mapped to:
#JsonRootName("user")
public class User {
...
}
To get root element working I had to include:
environment.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
environment.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
but now it applies for all classes. This means that whenever login request comes in, server will throw an error because it expects to see root element name.
Use JsonTypeName with JsonTypeInfo instead of JsonRootName:
#JsonTypeName("user")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class User {
...
}
#JsonTypeName
Annotation used for binding logical name that the annotated class has. Used with JsonTypeInfo (and specifically its JsonTypeInfo.use() property) to establish relationship between type names and types.
#JsonTypeInfo
Annotation used for configuring details of if and how type information is used with JSON serialization and deserialization, to preserve information about actual class of Object instances. This is necessarily for polymorphic types, and may also be needed to link abstract declared types and matching concrete implementation.

Java annotations: pass value of annotation attribute to another annotation

I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?
What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.

Unmarshalling a date from an XML String to entity using JAXB

When I use JAXB, there is something wrong.
I convert entity to a xml String and everything is ok.
But when I convert xml String back to entity, some information is lost (All of them have the same type java.util.Date).
In entity:
public Date flightBaseDate;
In xml:
<flightBaseDate>2013-09-16T00:00:00 08:00</flightBaseDate>
after unmarshalling, getFlightBaseDate() returns null.
I googled.
Following one suggestion, I used # in my entity.
Then it is:
#XmlElement(name = "timestamp", required = true)
public Date flightBaseDate;
I'm sure it will be perfect,
but...throws Exception, like this:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "flightBaseDate"
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.getFlightBaseDate()
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.flightBaseDate
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
Why JAXB could not distinguish between the property and its getMethod?
How to solve it?
Platform:jdk7 win7 eclipse tomcat wtp
My Unmarshalling code is:
JAXBContext context = JAXBContext.newInstance(FlightServiceInfoVo.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
FlightServiceInfoVo flightServiceInfoVo =(FlightServiceInfoVo)unMarshaller.unmarshal(new StringReader(flightServiceInfoVoXml));
flightServiceInfoVoXml is a String.
You can configure JAXB in many different ways. You have chosen Annotations to define the binding (this is allright, do not worry).
I strongle recommend you read about that technique first as there are a lot of pitfalls. Here is a link to a good tutorial. Here is the part in the tutorial which explains why your binding does not work: XmlAccessorType part
As for your specific issue:
In general you have to tell JAXB what and how to bind the java object to it's XML representation. If you do not do anything, then by default all public members of your class are bound (as you can read here).
Additionally you have chosen to annotate the getter method of your public member, which then just pushes the same variable twice to your XML which later causes the exception you see.
To fix your error, either specify a different mapping strategy for your class by putting e.g. (#XmlAccessorType(XmlAccessType.NONE)) before your class declaration or move the annotation from the getter method to the property.
By the way: Having a getter method and a public member variable does not make sense at all. So making your member variable private will also fix your issue with JAXB and be a lot better for your class design.
the exception clearly says that the property name is duplicated, so check you class for a property 'flightBaseDae', it should be unique. remove the duplicate then unmarshall it

How do I debug what's wrong when the Endpoints framework stops generating the WEB-INF/*.api-file?

Given a Google Cloud Endpoints project in Eclipse with the servlet-class annotated with #Api(name="helloworld"), the Endpoints framework generates a file named war/WEB-INF/helloworld-v1.api when the project compiles successfully. Sometimes this file is not generated even if there are no compilation errors though - only what I will call "GAE Endpoints code convention errors".
Example - working:
public class TestEntity {
public String Text;
public TestEntity(String text){
Text = text;
}
}
#ApiMethod
public TestEntity getTestEntity(){
return new TestEntity("Hello world");
}
Example - NOT working:
// The TestEntity-class is unchanged
#ApiMethod
public TestEntity getTestEntity(String input){
return new TestEntity("Hello world");
}
The problem with the latter example is that I take a String parameter as input without annotating it with #Named. I know that in this example, but there might be other cases where this is not so obvious.
Is there anywhere where I can read some sort of error log on why the .api file is not generated?
Although I am a fan of code by convention, it really takes the programming efficiency a step back if I cannot get feedback on what I do wrong. Eclipse provides compiler error feedback. The Google Cloud Endpoints Framework should provide Code-By-Convention-Rule-Breaking feedback.
There isn't currently good logging or error messaging when code generation fails, though it's one of the (if not most) requested features. In the interim, here's a list of the common failure cases:
The return type is invalid. Return types must be objects conforming to JavaBean conventions, and types like Object, String, and Integer are not allowed.
One or more argument types are invalid. Methods may accept at most one object in the POST body, and this object should also conform to JavaBean conventions. Methods may accept zero or more arguments via the query string (using the #Named annotation) and these must be scalar types (e.g. String, Integer).
An API, method, or parameter has an invalid name. APIs, methods, and parameters should be named to match the following regular expression: [a-z]+[A-Za-z0-9]*. Convention also suggests using lowerCamelCase for naming (though alllowercase is allowed).

Categories