Is there such a functionality in JAXB to perform operations on a class after it is unmarshalled i.e. after it is constructed by JAXB? If not, how could I achieve this?
You can use JAXB Unmarshal Event Callbacks which are defined in your JAXB class e.g:
// This method is called after all the properties (except IDREF) are unmarshalled for this object,
// but before this object is set to the parent object.
void afterUnmarshal( Unmarshaller u, Object parent )
{
System.out.println( "After unmarshal: " + this.state );
}
Though the the demanded functionality seems not to be present in JAXB, I managed to
achieve something which goes into the right direction:
I'm using JSR-305's #PostConstruct annotation
(it's just a nacked annotation, no functionality is provided by the JSR)
I add an unmasrshaller-listener to the unmarshaller, which gets invoked by JAXB every time an object was unmarshalled.
I inspect this object using Java reflection and search for the #PostConstruct annotation on a method
I execute the method
Tested. Works.
Here is the code. Sorry, I'm using some external reflection API to get all methods, but I think the idea is understandable:
Implementation
JAXBContext context = // create the context with desired classes
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setListener(new Unmarshaller.Listener() {
#Override
public void afterUnmarshal(Object object, Object arg1) {
System.out.println("unmarshalling finished on: " + object);
Class<?> type = object.getClass();
Method postConstructMethod = null;
for (Method m : ReflectionUtils.getAllMethods(type)) {
if (m.getAnnotation(PostConstruct.class) != null) {
if (postConstructMethod != null) {
throw new IllegalStateException(
"#PostConstruct used multiple times");
}
postConstructMethod = m;
}
}
if (postConstructMethod != null) {
System.out.println("invoking post construct: "
+ postConstructMethod.getName() + "()");
if (!Modifier.isFinal(postConstructMethod.getModifiers())) {
throw new IllegalArgumentException("post construct method ["
+ postConstructMethod.getName() + "] must be final");
}
try {
postConstructMethod.setAccessible(true); // thanks to skaffman
postConstructMethod.invoke(object);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
}
});
EDIT
Added a check for #PostConstruct-annotated method, to ensure it is final.
Do you think it's a useful restriction?
Usage
Here is how the concept might be used.
#XmlAccessorType(XmlAccessType.NONE)
public abstract class AbstractKeywordWithProps
extends KeywordCommand {
#XmlAnyElement
protected final List<Element> allElements = new LinkedList<Element>();
public AbstractKeywordWithProps() {
}
#PostConstruct
public final void postConstruct() {
// now, that "allElements" were successfully initialized,
// do something very important with them ;)
}
}
// further classes can be derived from this one. postConstruct still works!
Filed a feature request
https://jaxb.dev.java.net/issues/show_bug.cgi?id=698
It's not a 100% solution, but you can always register a XmlAdapter using #XmlJavaTypeAdapter annotation
for this type.
The downside would be that you have to serialize the class yourself (?). I am not aware of any simple way of accessing and calling the default serialization mechanism. But with custom [XmlAdapter] you can control how is the type serialized and what happens before/after it.
Related
I have a Java application using JAXB for XML parsing. I need to parse one of the subtags to different JAXB classes based on the value of another tag (think a "version" tag, resulting in slightly different XML formats).
Here is code for Request JAXB class which I am parsing:
#XmlRootElement(name = "request")
#XmlAccessorType(XmlAccessType.FIELD)
public class Request {
#XmlElement(name = "fields1")
private List<Field1> fields1;
#XmlElement(name = "problemTag")
#XmlJavaTypeAdapter(value = JavaBeanAdapter.class)
private List<BaseClass> problemTag;
...
Here are some details of what I've got so far:
In my code I have an IXmlProcessor interface. Its implementation contains JAXB related logic (marshalling and unmarshalling).
I wrote an XmlAdapter for my problem tag. It adds extra complexity of using two additional IXmlProcessor instances with different JAXB contexts.
I declared this XmlAdapter as a Spring bean.
I inject this XmlAdapter bean inside my main IXmlProcessor implementation, when creating javax.xml.bind.Unmarshaller.
BaseClass is JAXB class for 'older' version of XML and ExtendingClass with additional fields for a newer version; ExtendingClass of course extends BaseClass
So, the flow in my test implementation is like this:
call IXmlProcessor#read(String xml, Class<Z> clazz)
inside of javax.xml.bind.Unmarshaller#unmarshal my custom XmlAdapter will be called.
In turn, XmlAdapter will call it's own instances of IXmlProcessor to parse String to BaseClass or ExtendingClass.
Here is code example:
public class JavaBeanAdapter extends XmlAdapter<Object, BaseClass> {
private IXmlProcessor xmlOld;
private IXmlProcessor xmlNew;
//IXmlProcessor, holding JAXBContext with BaseClass
public void setXmlOld(IXmlProcessor xml) {
this.xmlOld = xml;
}
//IXmlProcessor, holding JAXBContext with ExtendingClass
public void setXmlNew(IXmlProcessor xml) {
this.xmlNew = xml;
}
//First, I parse org.w3c.dom.Element back to string.
//Than I can inspect raw XML.
//This is just test solution, obviously too sloppy to be used in prod, just to get an insight to what i want to achieve
#Override
public BaseClass unmarshal(Object v) {
if (v == null) return null;
String rawXmlTag = xmlNew.asStringNonRoot(v, Object.class, ((Element) v).getTagName());
BaseClass req;
if (rawXmlTag.contains("someSPecificTagForNewVersionOfXml")) {
req = xmlNew.read(rawXmlTag, ExtendingClass.class);
} else {
req = xmlOld.read(rawXmlTag, BaseClass.class);
}
return req;
}
#Override
public Object marshal(BaseClass v) throws Exception {
if (v == null) return null;
else if (v instanceof ExtendingClass) return xmlNew.asStringNonRoot((ExtendingClass) v,
ExtendingClass.class, "BaseClass");
else return xmlOld.asStringNonRoot(v, BaseClass.class, "BaseClass");
}
}
Then I manually add this adapter to a unmarshaller inside my main IXmlProcessor implementation.
Of course, I am not satisfied by rawXmlTag.contains(...).
Part of the IXmlProcessor implementation code (for understanding of what going there):
public <Z> Z read(String xml, Class<Z> clazz) {
try {
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xml));
return genUnmarshaller(clazz).unmarshal(xmlStreamReader, clazz).getValue();
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
}
}
#Override
public <Z> String asStringNonRoot(Z xmlObj, Class<Z> clazz, String rootName) {
try {
StringWriter strWriter = new StringWriter();
Marshaller marshaller = genMarshaller(xmlObj);
QName qName = new QName(null, rootName);
JAXBElement<Z> root = new JAXBElement<Z>(qName, clazz, xmlObj);
marshaller.marshal(root, strWriter);
return strWriter.toString();
} catch (Throwable e) {
log.error("Error serializing object to XML string: {}", ExceptionUtils.getStackTrace(e));
throw new IllegalStateException("cannot serialize object to XML");
}
}
#Override
public <Z> Z read(org.w3c.dom.Document xml, Class<Z> clazz) {
try {
return genUnmarshaller(clazz).unmarshal(xml, clazz).getValue();
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
}
}
My questions are following:
Is there any alternative solution to address this issue without adding additional IXmlProcessor instances with different JAXB contexts? How can I improve and simplify this implementation?
Maybe I should just check rootObject.version value after unmarshalling, and manually set correct version of BaseClass/ExtendingClass. How should I get raw Xml precisely for BaseClass/ExtendingClass then?
Any help will be appreciated.
I used XmlAdapter to manually figure out xml format. It solves the problem, but results in hard to understand, dubious code.
What we are doing - Annotation driven null and empty values check for Object.
How we are doing - Creating one annotation and putting that annotation on the variable declaration.
I am not sure what design pattern i need to use to make it work best
. Please suggest.
Annotation class -
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
/**
* Message.
*
* #return the string
*/
public String message();
}
How we are declaring variable -
#Test(message="five message")
private String five;
How i am calling annotation processor -
Class<?> annotationClass = annotationTestinClass.getClass();
Field[] decalaredFieldsArray = annotationClass.getDeclaredFields();
List<String> lstString = new ArrayList<>();
parseFields(decalaredFieldsArray,annotationTestinClass,lstString);
How i am processing object -
public static List<String> parseFields(Field[] decalaredFieldsArray,Object obj,List<String> lstString){
Arrays.stream(decalaredFieldsArray).forEach(field ->{
field.setAccessible(true);
Test test = field.getDeclaredAnnotation(Test.class);
if(field.getType().isPrimitive() || field.getType().getName().equals("java.lang.String")){
if(field.isAnnotationPresent(Test.class)){
try {
System.out.println("field value is :"+field.get(obj));
System.out.println("field Name is :"+field.getName());
if(field.get(obj)== null || !StringUtils.isNoneBlank(field.get(obj).toString())){
lstString.add(test.message());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}else{
Field[] objectFields =null;
Object objValue = null;
try {
if(field.isAnnotationPresent(Test.class)){
objValue = field.get(obj);
if(objValue!=null){
objectFields = objValue.getClass().getDeclaredFields();
parseFields(objectFields, objValue, lstString);
}else{
System.out.println("Object value is -"+field.get(obj));
System.out.println("Messsage value is -"+test.message());
lstString.add(test.message());
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
return lstString;
}
Here Test.Class is repersenting my Test annotation.
Annotations don't work that way.
You can create custom annotations that get processed at compile time. But at compile time, very often, you might not be able to check that
#NotNull
private Whatever foo = someBar();
really leads to "not null". In other words: at compile time, no code behind #NotNull can really decide in all cases if the annotated variable will be really not null.
And at runtime, annotations ... are just that: Meta-Information!
Meaning: if you want annotations to have an effect at runtime, you need code that checks for annotations when doing things.
Example: you create #Persist.
And then you have a framework that "processes objects". And whenever that framework processes some object, it can check if that annotation is present, and if so "persist" that object.
In other words:
foo = bar;
will just assign bar to foo; and there is "no framework" in place that could check if foo is #NotNull annotated, and do something about that assignment if bar is null.
So, in order for you to be helpful, you need
That new annotation
Some sort of "processor" that works on objects; and checks if they are annotated; and if they are annotated, special things happen.
I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.
Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
Getting an error at line 7.
NoSuchMethodException:
com.myapp.impl.service.Customer.cypherCBC(java.lang.String)
that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!
Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.
As solution, you can implement a non-throwing lookup yourself, like
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
Using that helper method, you can rewrite your code to:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
Or, with the idea from hunter in mind; a much more "OO like" version:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
And your client code boils down to:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]
And yes, an alternative implementation of the lookup method could just go like
private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages
if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
But to write a better application,you should use two version baselines.
even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.
Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.
interface CBCCypherable {
public String cypherCBC(String pass);
}
You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.
P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.
How can I iterate over the attributes of an object, with the attribute names provided in a list/array - NOT all attributes, like using reflection & getDeclaredFields().
public class MyClass
{
public type1 att1;
public type2 att2;
public type3 att3;
public MyClass(
att1="helo";
att2="bye";
att3="morning";
);
...
public void function()
{
String myStrings[];
myStrings = new String[] { "att2", "att3" };
MyClass myobject = new MyClass();
for(var in myStrings)
{
System.out.println(var);
System.out.println(myobject.var);
System.out.println();
}
}
}
Your question is somewhat ambiguous about using reflection. If you are OK with reflection, but want specific fields only without iterating over getDeclaredFields(), then the following code should work for you:
for (String var : myStrings) {
Field field = MyClass.class.getDeclaredField(var);
field.setAccessible(true);
System.out.println(var);
System.out.println(field.get(myObject));
System.out.println();
}
Note that this code works for private fields, too. Also, keep in mind that you'll have to handle exception associated with the reflection calls.
UPDATE: Exceptions thrown in this code.
MyClass.class.getDeclaredField(var) declares a checked NoSuchFieldException. You must handle it because obviously there is no mechanism to make sure that the fields in myString match an actual implementation of MyClass.
field.get(myObject) throws a checked IllegalAccessException if the field is inaccessible. Which it should not be because of field.setAccessible(true), but you still have to catch or re-throw the exception.
There are also unchecked exceptions you may want to handle. See the javadoc for details
java.lang.Class.getDeclaredField(String)
java.lang.reflect.AccessibleObject.setAccessible(boolean) inherited by java.lang.reflect.Field
java.lang.reflect.Field.get(Object)
You probably want to use some technology that builds on top of JavaBeans / BeanInfo. Apache Commons / BeanUtils is a good starting point here.
Please refer to this previous answer of mine for more info:
https://stackoverflow.com/a/5856982/342852
But if you just want to use fields, not bean properties, here's a Java 8 method to do so:
public static Map<String, Object> getFieldProperties(Object o, Collection<String> fields) {
Class<?> type = o.getClass();
return fields.stream().map(n -> {
try {
return type.getDeclaredField(n);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}).collect(Collectors
.toMap(
(Function<Field, String>) Field::getName,
(Function<Field, Object>) field -> {
try {
field.setAccessible(true);
return field.get(o);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}));
}
Unfortunately the checked exceptions make this more verbose than it would need to be.
I have a series of classes, which are unrelated. Each classs has one property with and #PrimaryKey (with getter and setter) that could be of any type. How can I use reflection to find which property of an instance of any class has the #PrimaryKey annotation - so I can get its value as a string.
The code doesn't know which type of class its being passed - it will just be of type "Object"
You can do something like this:
for (Field field : YourClass.class.getDeclaredFields()) {
try {
Annotation annotation = field.getAnnotation(PrimaryKey.class);
// what you want to do with the field
} catch (NoSuchFieldException e) {
// ...
}
}
If you are working with the instance of your class then you can do this to get its class object:
Class<?> clazz = instance.getClass();
so the first line becomes something like this:
instance.getClass().getDeclaredFields()
If you are in trouble you can always check out the official documentation. I believe it is quite good.
You can get all fields of a class and then iterate and find which field has your annotation:
Field[] fields = YourClass.class.getDeclaredFields();
for (Field field : fields) {
Annotation annot = field.getAnnotation(PrimaryKey.class);
if (annot != null) {
System.out.println("Found! " + field);
}
}
First of all you need to find all classes that may have the annotation in their members. This can be accomplished using Spring Framework ClassUtils:
public static void traverse(String classSearchPattern, TypeFilter typeFilter) {
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(classLoader);
Resource[] resources = null;
try {
resources = resourceResolver.getResources(classSearchPattern);
} catch (IOException e) {
throw new FindException(
"An I/O problem occurs when trying to resolve resources matching the pattern: "
+ classSearchPattern, e);
}
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
for (Resource resource : resources) {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (typeFilter.match(metadataReader, metadataReaderFactory)) {
String className = metadataReader.getClassMetadata().getClassName();
Class<?> annotatedClass = classLoader.loadClass(className);
// CHECK IF THE CLASS HAS PROPERLY ANNOTATED FIELDS AND
// DO SOMETHING WITH THE CLASS FOUND... E.G., PUT IT IN SOME REGISTRY
}
} catch (Exception e) {
throw new FindException("Failed to analyze annotation for resource: " + resource, e);
}
}
}