JAXB and different versions of the class - java

I wonder if there is any support for different versions of class in JAXB.
My thoughts about it: xml is kind of persistence object of class (serialized value) but class object can be changed due to development (e.g. extending the functionality). Is there any way to know which version of class is stored in persistence (read: xml) using only JAXB API?
For me it seems convenient to store class version - like it's done in standard java serialization mechanism (I mean serialVersionUID) and provide functionality to map xml on class even in case of different version (e.g. adding in XmlAdapter information about version of class stored in xml). By default, if version of class in xml and in runtime differs - throw InvalidClassException.
Example:
We have class Test as follows:
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class Test {
private Long time;
private Test () {};
}
assuming that time is UNIX time in millis.
This code was released on production and this class was persisted in the database as xml.
Next release shows that it was not good choice to use Long as time representation and it was changed to Date:
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class Test {
private Date time;
private Test () {};
}
Now there are two ways - either to migrate persisted data or to write xml adapter which will handle Long time and apply it to the Date time.
If we choose the second way it would be great if JAXB API provides class version which were stored in xml (assuming if no class version is specified in class = 0 version) and we add new version of class explicitly (either by annotation or by static field):
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
#XmlClassVersion(value = 1)
public class Test {
private Date time;
private Test () {};
}
or
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class Test {
private static final long xmlSerialVersionUID = 1;
private Date time;
private Test () {};
}
and JAXB will provide XmlAdapter as follows:
public abstract class XmlAdapter<ValueType,BoundType> {
protected XmlAdapter() {}
public abstract BoundType unmarshal(ValueType v, long classVersion) throws Exception;
public abstract ValueType marshal(BoundType v, long classVersion) throws Exception;
}
In order to support multiple versions, of course, we need to implement such adapter and handle different versions explicitly.
Clearly, then, JAXB will add special information about class in xml and will generate xml on the latest class version.
NOTE: the point of the example above that we have 2 different representations of class in the persistence simultaneously but still can map them to the version of class available in the runtime.

JAXB stands for "Java Architecture for XML Binding", so, it's about binding objects fields values to XML elements/attributes, and not about serializing java objects. In you read for example this article, you see that it says
javax.xml.bind, contains classes and interfaces for performing
operations such as unmarshalling, marshalling, and validation
Java object serialization is another thing, and it's related to the serialVersionUID that you mention in your question.

If you are using JAXB you would be persisting xml and not the java object. If you were persisting java object it would be serialization. Jaxb transformation tries to bind the xml values to the class attributes. As long as the transformation prerequisites work, there shouldn't be a problem. Other libraries like JIBX, try to access bytecode directly. The class version may be an issue in such a case.

Related

Is there a way to completely swap out the way serialization is handled with Apache Beam?

I'm using Kotlin with Apache Beam and I have a set of DTOs that reference each other and all serialize great for any encoder with Kotlinx Serialization. When I try to use them with Beam I end up having issues because it's looking for all objects, type parameters and nested objects to implement the Java Serializable interface. Problem is, I'm not in control of that with all object types because some come from 3rd-party libraries.
I've implemented my own CustomCoder<T> type that uses Kotlinx Serialization but then I run into issues with my custom coder not being serializable, particularly due to the Kotlinx Serialization plugin-generated Companion object serializer not serializing. Since it's compile-time generated code I don't really have control over that and I can't flag it as #Transient. I tried implementing Externalizable on the coder and it fails as soon as I pass a type argument for T that doesn't implement Serializable or has a nested type argument that doesn't.
Also, Kotlinx Serialization is nice because it doesn't use reflection. It would make a lot of my current headaches disappear if I could just swap out the serialization mechanism somehow and not have to rely on standard Java serialization methods at all or somehow implement Externalizable in a way that just calls out to my own serialization mechanism and ignores the type parameter. Are there any solutions? I don't care how hacky it is, even if the solution involves messing with stuff in the Gradle build config to override something. I'm just not sure how to go about it so any pointers would be a great help!
Alternatively, if I abandon Kotlinx Serialization, are there any simple solutions to make any arbitrarily complex data type serialization just work with Java, even using reflection, without a lot of custom, manual work to handle encoding and decoding? I feel like maybe I'm just missing something obvious. This is my first project with Apache Beam but so far the google is little help.
Mybe late, I develop an annotation processor called beanknife recently, it support generate DTO from any class. You need config by annotation. But you don't need change the original class. This library support configuring on a separate class. Of course you can choose which property you want and which you not need. And you can add new property by the static method in the config class. The most power feature of this library is it support automatically convert a object property to the DTO version. for example
class Pojo1 {
String a;
Pojo b; // circular reference to Pojo2
}
class Pojo2 {
Pojo1 a;
List<Pojo1> b;
Map<List<Pojo1>>[] c;
}
// remove the circular reference in the DTO
#ViewOf(value = Pojo1.class, includePattern = ".*", excludes={Pojo1Meta.b})
class ConfigureOfPojo2 {}
// use the no circular reference versioned dto replace the Pojo1
#ViewOf(value = Pojo2.class, includePattern = ".*")
class ConfigureOfPojo2 {
// convert b to dto version
#OverrideViewProperty(Pojo2Meta.b)
private List<Pojo1View> b;
// convert c to dto version
#OverrideViewProperty(Pojo2Meta.c)
private Map<List<Pojo1View>>[] c;
}
will generate
// meta class, you can use it to reference the property name in a safe way.
class Pojo1Meta {
public final String a = "a";
public final String b = "b";
}
// generated DTO class. The actual one will be more complicate, there are many other method.
class Pojo1View {
private String a;
public Pojo1View read(Pojo1 source) { ... }
... getters and setters ...
}
class Pojo2Meta {
public final String a = "a";
public final String b = "b";
public final String c = "c";
}
class Pojo2View {
private String a;
private List<Pojo1View> b;
private Map<List<Pojo1View>>[] c;
public Pojo1View read(Pojo2 source) { ... }
... getters and setters ...
}
The interest things here is you can safely use the class not exist yet in the source. Although the compiler may complain, all will be ok after compiled. Because all the extra class will be automatically generated just before compiled.
A better approach may be to compile step by step, first add #ViewOf annotations, and then compile, so that all the classes that need to be used later are generated. Compile again after the configuration is complete. The advantage of this is that the IDE will not have grammatical error prompts, and can make better use of the IDE's auto-complete function.
With the support of using generated DTO in the configure class. You can define a Dto without circular reference just like the example. Furthermore, you can define another dto for Pojo2, and remove all property reference the Pojo1 and use it to replace the property b in Pojo1.

Is it possible to use Mirror API to recursively check for annotations during annotation processing

Suppose I have a class structure like :
#MyAnnotationOne
class A {
private String id;
private B b;
public static class B {
private C c;
#MyAnnoationOne
public static class C {
#MyAnnotationTwo
private String annotatedString;
}
}
}
I am using annotation processing to generate code. If I'm processing #MyAnnotationOne, then using the Mirror API I can get all the fields in class A and class C.
I want to know if there is any way I could find if any of the fields in class A, going down the hierarchy contain the annotation #MyAnnotationOne or #MyAnnotationTwo. Finding any one would be enough.
I tried looking for a solution but I found some saying that since the annotation processing happens in a pre-compilation stage, the information might not be available. Please let me know if there's any solution that you might know. It'd be a great help.
You can configure whether annotations are retained into runtime or not. You annotate your own annotation type with the java.lang.annotation.Retention meta-annotation, specifying one of the values from the enum java.lang.annotation.RetentionPolicy.
RUNTIME Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
CLASS Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
SOURCE Annotations are to be discarded by the compiler.
EDIT
If you are building an annotation processor:
your code is running during that (pre) compilation phase, so all annotations should be present.
Mirror API is deprecated, see javax.lang.model... and javax.annotation.processing
If you override javax.annotation.processing.AbstractProcessor.process() you receive a RoundEnvironment with methods for getting model Elements, including sets filtered by specific annotation types. And given an Element you can getEnclosedElements() and on those getAnnotation(annotationType).

Force the usage of a JPA AttributeConverter for enums

We're trying to figure out a robust way of persisting enums using JPA. The common approach of using #Enumerated is not desirable, because it's too easy to break the mappings when refactoring. Each enum should have a separate database value that can be different than the enum name/order, so that you can safely change the name or internal ordering (e.g. the ordinal values) of the enum without breaking anything. E.g. this blog post has an example on how to achieve this, but we feel the suggested solution adds too much clutter to the code. We'd like to achieve a similar result by using the new AttributeConverter mechanism introduced in JPA 2.1. We have an interface that each enum should implement that defines a method for getting the value that is used to store the enum in the database. Example:
public interface PersistableEnum {
String getDatabaseValue();
}
...
public enum SomeEnum implements PersistableEnum {
FOO("foo"), BAR("bar");
private String databaseValue;
private SomeEnum(String databaseValue) {
this.databaseValue = databaseValue;
}
public void getDatabaseValue() {
return databaseValue;
}
}
We also have a base converter that has the logic for converting enums to Strings and vice versa, and separate concrete converter classes for each enum type (AFAIK, a fully generic enum converter is not possible to implement, this is also noted in this SO answer). The concrete converters then simply call the base class that does the conversion, like this:
public abstract class EnumConverter<E extends PersistableEnum> {
protected String toDatabaseValue(E value) {
// Do the conversion...
}
protected E toEntityAttribute(Class<E> enumClass, String value) {
// Do the conversion...
}
}
...
#Converter(autoApply = true)
public class SomeEnumConverter extends EnumConverter<SomeEnum>
implements AttributeConverter<SomeEnum, String> {
public String convertToDatabaseColumn(SomeEnum attribute) {
return toDatabaseValue(attribute);
}
public SomeEnum convertToEntityAttribute(String dbData) {
return toEntityAttribute(SomeEnum.class, dbData);
}
}
However, while this approach works very nicely in a technical sense, there's still a pretty nasty pitfall: Whenever someone creates a new enum class whose values need to be stored to the database, that person also needs to remember to make the new enum implement the PersistableEnum interface and write a converter class for it. Without this, the enum will get persisted without a problem, but the conversion will default to using #Enumerated(EnumType.ORDINAL), which is exactly what we want to avoid. How could we prevent this? Is there a way to make JPA (in our case, Hibernate) NOT default to any mapping, but e.g. throw an exception if no #Enumerated is defined on a field and no converter can be found for the type? Or could we create a "catch all" converter that is called for all enums that don't have their own specific converter class and always throw an exception from there? Or do we just have to suck it up and try to remember the additional steps each time?
You want to ensure that all Enums are instances of PersistableEnum.
You need to set a Default Entity Listener (an entity listener whose callbacks apply to all entities in the persistence unit).
In the Default Entity Listener class implement the #PrePersist method and make sure all the Enums are instances of PersistableEnum.

Problems with GWT and Enum

I have an enum in the client part of a GWT application and I am getting an exception when I try to run it that is related to serialization problems. Am I doing anything wrong? I read that enums are supported by GWT and I am using the last version.
The enum:
public enum AnEnum implements Serializable {
ITEM_A("Item a description"), ITEM_B("Item b description");
private String description;
private AnEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The exception:
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeWithCustomSerializer(ServerSerializationStreamWriter.java:742)
... 47 more
Caused by: com.google.gwt.user.client.rpc.SerializationException: Type '(...).client.(...).AnEnum' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = ITEM_A
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:610)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:129)
at com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase.serialize(Collection_CustomFieldSerializerBase.java:43)
at com.google.gwt.user.client.rpc.core.java.util.LinkedList_CustomFieldSerializer.serialize(LinkedList_CustomFieldSerializer.java:36)
... 52 more
Add IsSerializable interface, a default scoped no-arg constructor, and make sure its in one of the paths listed in the source tags in your gwt.xml file. <source path="client">
I really think the third suggestion is the issue; I remember having this issue before and it was because I had a dto outside the source paths.
You can have multiple source tags.
<source path="common" />
<source path="client" />
One pattern is to put persisted objects directly under com.mysite.common, and mashups of persisted items that get transferred over the wire in com.mysite.common.dto, and of course the client gui code is in client.
package com.mysite.client;
import java.io.Serializable;
import com.google.gwt.user.client.rpc.IsSerializable;
public enum AnEnum implements Serializable, IsSerializable {
ITEM_A("Item a description"), ITEM_B("Item b description");
private String description;
AnEnum() {
}
AnEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
You can try this check list:
Verify that the class has a default constructor (without arguments)
Verify that the class implements Serializable or IsSerializable or
implements an Interface that extends Serializable or extends a class
that implement Serializable
Verify that the class is in a client.* package or …
Verify, if the class is not in client.* package, that is compiled in
your GWT xml module definition. By default
is present. If your class is in another package you have to add it
to source. For example if your class is under domain.* you should
add it to xml as . Be aware that the class
cannot belong to server package! More details on GWT page: http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideModuleXml
If you are including the class from another GWT project you have to
add the inherits to your xml module definition. For example if your
class Foo is in the package com.dummy.domain you have to add
to the module definition.
More details here: http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideInheritingModules
If you are including the class from another GWT project released as
a jar verify that the jar contains also the source code because GWT
recompile also the Java source for the classes passed to the Client.
Font: http://isolasoftware.it/2011/03/22/gwt-serialization-policy-error/
i think you need a no arg constructor.
I been studying above to solve some GWT code written in 2008, when upgraded to GWT SDK 2.4.0 (with latest gxt*.jar) gives me:
[WARN] adempiereService: An IncompatibleRemoteServiceException was thrown while processing this call.
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: com.google.gwt.user.client.rpc.SerializationException: Type 'org.idempiere.ui.gwt.client.util.AdempiereGXTUtil$LoginStage' was not included in the set of types which can be deserialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be deserialized.
at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:315)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
...
Caused by: com.google.gwt.user.client.rpc.SerializationException: com.google.gwt.user.client.rpc.SerializationException: Type 'org.idempiere.ui.gwt.client.util.AdempiereGXTUtil$LoginStage' was not included in the set of types which can be deserialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be deserialized.
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:581)
That notorious class is as follows (edited to follow ideas given in this thread):
public class AdempiereGXTUtil {
public enum LoginStage implements IsSerializable, Serializable {
LOGOUT,
LOGIN,
ISLOGGEDIN,
ROLES,
WRONGUSER,
WRONGROLE;
LoginStage(){
}
};
}
Thinking about Andrej's answer to add type to white-list but enum is not some new myType, right? Anyway here is some reference in the codebase (non-relevant fields removed):
public interface AdempiereService extends RemoteService {
public static final String SERVICE_URI = "adempiereService";
public static class Util {
public static AdempiereServiceAsync getInstance() {
AdempiereServiceAsync instance = (AdempiereServiceAsync) GWT
.create(AdempiereService.class);
return instance;
}
}
...
public LoginStage getLoginStage();
with:
public interface AdempiereServiceAsync {
...
public void getLoginStage(AsyncCallback<LoginStage> callback);
Originally the AdempiereGXTUtil did not implement IsSerializable, Serializable nor has empty constructor but putting them in above, and cleaning out project in Eclipse does not change the same errors. Eclipse version used is Indigo on Java 1.6 in a Mac Lion environment. Hoping to get more from this thread, which by the way is amazing in its technical depth.
Only names of enumeration constants are serialized by GWT's RPC. Field values are not serialized.
GWT:Server Communication:Serializable Types
In this case, Enum cannot be in the class. You have to create an external Enum.
In Gwt 2.9, I also had a "was not included in the set of types which can be serialized" error on a class that contained a field of type java.util.EnumSet of a custom enum class.
It turned out the issue was not my custom enum, but the EnumSet itself. After replacing the EnumSet with a HashSet or LinkedHashSet, serialization worked.
Maybe this is related to this issue: https://github.com/gwtproject/gwt/issues/3319
See http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes:
A type is serializable and can be used in a service interface if one
of the following is true:
The type is primitive, such as char, byte, short, int, long, boolean,
float, or double. The type an instance of the String, Date, or a
primitive wrapper such as Character, Byte, Short, Integer, Long,
Boolean, Float, or Double. The type is an enumeration. Enumeration
constants are serialized as a name only; none of the field values are
serialized. The type is an array of serializable types (including
other serializable arrays). The type is a serializable user-defined
class. The type has at least one serializable subclass. The type has a
Custom Field Serializer
In your case the type is an enumeration, so it is meant to be serializable.
These days GWT has 2 serialisation policies: LegacySerializationPolicy and StandardSerializationPolicy.
The LegacySerializationPolicy has an issue serialising enums unless they implement IsSerializable.
Normally StandardSerializationPolicy will be used unless the SerializationPolicyProvider cannot find the *.gwt.rpc file for some reason. If it cannot find the *.gwt.rpc file then it will write a warning that you will most likely get serialisation issues so I would suggest that you start looking in the error log to see if it had issues finding the *.gwt.rpc file and if so fix that.
Also on a side note you should not be mutating Enums, I recall that Joshua Bloch writes about that in his book Effective Java.
a) You definitely need a no-op constructor for serialization.
b) You can either extend GWT' IsSerializable class or, if you want to use Java's Serialization interface you must setup a policy to allow this. There's a post related to this at How do I add a type to GWT's Serialization Policy whitelist?. Also, check the GWT sight for more information on IsSerializable vs. Serializable.

Marshalling polymorphic objects in JAX-WS

I'm creating a JAX-WS type webservice, with operations that return an object WebServiceReply. The class WebServiceReply itself contains a field of type Object. The individual operations would populate that field with a few different data-types, depending on the operation.
Publishing the WSDL (I'm using Netbeans 6.7), and getting a ASP.NET application to retrieve and parse the WSDL was fine, but when I tried to call an operation, I would receive the following exception:
javax.xml.ws.WebServiceException: javax.xml.bind.MarshalException
- with linked exception:
[javax.xml.bind.JAXBException: class [LDataObject.Patient; nor any of its super class is known to this context.]
How do I mark the annotations in the DataObject.Patient class, as well as the WebServiceReply class to get it to work? I haven't been able to fine a definitive resource on marshalling based upon annotations within the target classes either, so it would be great if anybody could point me to that too.
WebServiceReply.java
#XmlRootElement(name="WebServiceReply")
public class WebServiceReply {
private Object returnedObject;
private String returnedType;
private String message;
private String errorMessage;
.......... // Getters and setters follow
}
DataObject.Patient.java
#XmlRootElement(name="Patient")
public class Patient {
private int uid;
private Date versionDateTime;
private String name;
private String identityNumber;
private List<Address> addressList;
private List<ContactNumber> contactNumberList;
private List<Appointment> appointmentList;
private List<Case> caseList;
}
Solution
(Thanks to Gregory Mostizky for his answer)
I edited the WebServiceReply class so that all the possible return objects extend from a new class ReturnValueBase, and added the annotations using #XmlSeeAlso to ReturnValueBase. JAXB worked properly after that!
Nonetheless, I'm still learning about JAXB marshalling in JAX-WS, so it would be great if anyone can still post any tutorial on this.
Gregory: you might want to add-on to your answer that the return objects need to sub-class from ReturnValueBase. Thanks a lot for your help! I had been going bonkers over this problem for so long!
You need to use #XmlSeeAlso so that your JAXB implementation will now to include additional classes as well.
In your case it would go something like this:
#XmlRootElement
#XmlSeeAlso({Patient.class, ....})
public class ReturnValueBase {
}
And also change returnedObject property to be of type ReturnValueBase.

Categories

Resources