JaxB reference resolving - jaxb

For the follwing example XML input:
<Participants course="someCourse">
<workers>
<Worker ref="p3">
<Worker ref="p2">
</workers>
<Trainer ref="p1"/>
</Participants>
<Group id="group1" name="some mixed Person group">
<trainers>
<Trainer id="p1" name="John Doe">
</trainers>
<workers>
<Worker id="p2" name="Jim Scott">
<Worker id="p3" name="Walter Peace">
</workers>
</Group>
I am trying to make sure that the PersonList in Participants points to the Persons read from group1. (see code snipptes below for the JaxB annotations used). This is just an example for the more generic
approach I am seeking. I need to be generally able to follow id="" and ref="" attributes in a way
that the list elements are correctly unmarshalled as references.
With an UnmarshalListener and Unmarshalling twice I get around the problem of the references from the ref attribute to the id attribute. In the first phase the lookup Map is filled from the id attributes. In the second phase the refs are looked up. Unfortunately this solution will create copies instead of references. I could use the parent object to fix this but I am looking for a more generic solution. What would be a good way to achieve the proper dereferencing using ref/id attributes in the manner shown?
/**
* intercept the unmarshalling
*/
public static class ModelElementMarshallerListener extends javax.xml.bind.Unmarshaller.Listener {
public Map<String,Person> lookup=new HashMap<String,Person>();
#Override
public void afterUnmarshal(java.lang.Object target, java.lang.Object parent) {
if (target instanceof Person) {
person=(Person) target;
if (person.getId()!=null) {
lookup.put(person.getId(), person);
}
if (person.getRef()!=null) {
if (lookup.containsKey(person.getRef())) {
Person personRef=lookup.get(person.getRef());
person.copyFrom(personRef);
person.setRef(null);
}
}
}
}
}
#XmlRootElement(name="Participants")
public class Participants {
private List<Worker> workers;
/**
* getter for List<Worker> workers
* #return workers
*/
#XmlElementWrapper(name="workers")
#XmlElement(name="Worker", type=Worker.class)
public List<Worker> getWorkers() {
return workers;
}
...
}
#XmlRootElement(name="Group")
public class Group {
private List<Worker> workers;
/**
* getter for List<Worker> workers
* #return workers
*/
#XmlElementWrapper(name="workers")
#XmlElement(name="Worker", type=Worker.class)
public List<Worker> getWorkers() {
return workers;
}
...
}
#XmlRootElement(name="Trainer")
public class Trainer extends Person {}
#XmlRootElement(name="Worker")
public class Worker extends Person {}
#XmlRootElement(name="Person")
public class Person {
private String name;
/**
* getter for xsd:string/String name
* #return name
*/
#XmlAttribute(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name=name;
}
private String ref;
/**
* getter for xsd:string/String id
* #return id
*/
#XmlAttribute(name="ref")
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref=ref;
}
private String id;
/**
* getter for xsd:string/String id
* #return id
*/
#XmlAttribute(name="id")
#XmlID
public String getId() {
this.id;
}
/**
* setter for xsd:string/String id
* #param pid - new value for id
*/
public void setId(String pid) {
this.id=pid;
}
}

To better illustrate the point I have modified the question to fit his answer. There is now a generic base class Person and I am trying to use it as per Can generic XmlAdapter be written
I solved the issue of being able to actually make sure the Adapters are used by writing specific derived Classes and using them with #XmlJavaTypeAdapter. I preregister the adapters using:
JAXBContext context = JAXBContext.newInstance(type);
Unmarshaller u = context.createUnmarshaller();
u.setAdapter(Worker.WorkerAdapter.class,new Worker.WorkerAdapter());
u.setAdapter(Trainer.TrainerAdapter.class,new Trainer.TrainerAdapter());
and then unmarshalling twice. The debug shows that the Adapter instance for both passes is the same. Still the lookup somehow seemed to fail ... The reason was the way the #XmlJavaTypeAdapter annotation works see:
What package-info do I annotate with XmlJavaTypeAdapters?
There seem to be multiple modes for #XmlJavaTypeAdapter:
it can be an annotation for a class
it can be an annotation for a field (getter)
it can be used in a package-info.java file to annotate a whole package
At this point I am using all three annotations and now have to debug which ones are necessary. I assume the global annotations (class,package) are not working as expected. The reason might be the type= usage in the #XmlElementWrapper which explicitly calls for a type. Personally I do not understand what is going on yet. At least things are now working as expected.
the local field annotation is now e.g.:
#XmlElementWrapper(name="workers")
#XmlElement(name="Worker", type=Worker.class)
#XmlJavaTypeAdapter(WorkerAdapter.class)
the package-info.java annotation is:
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(value=WorkerAdapter.class,type=Worker.class),
#XmlJavaTypeAdapter(value=TrainerAdapter.class,type=Trainer.class),
})
package com.bitplan.jaxb.refidtest;
import javax.xml.bind.annotation.adapters.*;
the class annotation is:
#XmlJavaTypeAdapter(Worker.WorkerAdapter.class)
public class Worker extends Person {
...
/**
* Worker Adapter
* #author wf
*
*/
public static class WorkerAdapter extends PersonAdapter<Worker>{
#Override
public Worker marshal(Worker me)
throws Exception {
return super.marshal(me);
}
#Override
public Worker unmarshal(Worker me) throws Exception {
return super.unmarshal(me);
}
}
/**
* https://stackoverflow.com/questions/7587095/can-jaxb-marshal-by-containment-at-first-then-marshal-by-xmlidref-for-subsequen/7587727#7587727
* #author wf
*
*/
public class PersonAdapter<T extends Person> extends XmlAdapter<T, T>{
public boolean debug=true;
/**
* keep track of the elements already seen
*/
public Map<String,T> lookup=new HashMap<String,T>();
#Override
public T marshal(T me)
throws Exception {
return me;
}
/**
* show debug information
* #param title
* #param key
* #param me
* #param found
*/
public void showDebug(String title,String key,T me, T found) {
String deref="?";
if (found!=null)
deref="->"+found.getId()+"("+found.getClass().getSimpleName()+")";
if (debug)
System.err.println(title+": "+key+"("+me.getClass().getSimpleName()+")"+deref+" - "+this);
}
#Override
public T unmarshal(T me) throws Exception {
if (me.getId()!=null) {
showDebug("id",me.getId(),me,null);
lookup.put(me.getId(), me);
return me;
}
if (me.getRef()!=null) {
if (lookup.containsKey(me.getRef())) {
T meRef=lookup.get(me.getRef());
showDebug("ref",me.getRef(),me,meRef);
me.setRef(null);
return meRef;
} else {
if (debug)
showDebug("ref",me.getRef(),me,null);
}
}
return me;
}
}

Related

spring integration adviceChain on ServiceActivator is not executing the beforeReceive of an advice

I defined my poller with a service activator with adviceChain like this:
#ServiceActivator(inputChannel = EVENT_CHANNEL,
adviceChain = {"globalLockAdvice"},
poller = #Poller(
maxMessagesPerPoll = "${event.poller.maxMessagesPerPoll:1}",
fixedDelay = "${event.poller.fixedDelay:1000}",
receiveTimeout = "${event.poller.receiveTimeout:1000}"))
public void handleEventMessage(Message<String> message) throws MessagingException {
...
}
#Component
public class GlobalLockAdvice implements ReceiveMessageAdvice {
private final LockConfiguration lockConfiguration;
public GlobalLockAdvice(LockConfiguration lockConfiguration) {
this.lockConfiguration = lockConfiguration;
}
#Override
public boolean beforeReceive(Object source) {
return lockConfiguration.isLeader();
}
#Override
public Message<?> afterReceive(Message<?> result, Object source) {
return result;
}
}
But beforeReceive is not called.
When debugging I see 'target' is a ServiceActivator class, resulting in skipping calling the beforeReceive:
#FunctionalInterface
public interface ReceiveMessageAdvice extends MethodInterceptor {
#Override
#Nullable
default Object invoke(MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
if (!(target instanceof MessageSource) && !(target instanceof PollableChannel)) {
return invocation.proceed();
}
...
What am I doing wrong?
The advice chain on the service activator advises the message handler, not the channel polled by the poller.
To add an advice chain to the poller, you must define the poller as a PollerMetadata bean instead of using the #Poller annotation attributes. #Poller("metadata").
See the javadocs for #Poller.
/**
* Provides the {#link org.springframework.integration.scheduling.PollerMetadata} options
* for the Messaging annotations for polled endpoints. It is an analogue of the XML
* {#code <poller/>} element, but provides only simple attributes. If the
* {#link org.springframework.integration.scheduling.PollerMetadata} requires more options
* (e.g. Transactional and other Advices) or {#code initialDelay} etc, the
* {#link org.springframework.integration.scheduling.PollerMetadata} should be configured
* as a generic bean and its bean name can be specified as the {#code value} attribute of
* this annotation. In that case, the other attributes are not allowed.
* <p>
* Non-reference attributes support Property Placeholder resolutions.

JAX-RS Get request 404 not found

I have been struggling for a while now trying to find the solution to my problem.
The following things I have:
#ApplicationPath("/api/v1")
public class App extends javax.ws.rs.core.Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(MoocAPI.class);
classes.add(Authentication.class);
return classes;
}
}
And the MoocAPI.class is:
#Path("moocs")
public class MoocAPI {
/**
* Retrieves moocs from the database
* #return the list of moocs as a JSON.
*/
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getMoocs() {
List<Mooc> moocs = MoocMapper.getMoocs();
return Response.ok(moocs).build();
}
}
So the problem is, once I start the server, it will open the index at localhost:8080/webapp which works fine. Then when I goto /webapp/api/v1/moocs it should return a list. But instead I get a 404 not found. I can not figure out why this is doing it.

Groovy Property Annotation for Public-Read Protected-Write

In groovy, when I want to create a property with public-read but protected-write access, I need to write the following code (as per the Groovy Docs):
class SomeClass {
/**
* Public property
*/
String someProperty
/**
* Make it protected-write
*/
protected void setSomeProperty(String someProperty) { this.someProperty = someProperty }
}
Is there a short-hand for this? Something like:
class SomeClass {
#Protected String someProperty
}
There should also be a #Private annotation along the same lines.
Note
These annotations would only affect the setter method of the property. This should allow any required access-level combination i.e.:
/** An entirely protected property */
protected String someProperty
/** An entirely private property */
private String someProperty
/** A read-only property */
final String someProperty
/** Private-Setter, Protected-Getter */
#Private protected String someProperty
There is not any shorthand way to express that, at least not in default Groovy. You could write an AST transformation which imposes rules like those you describe but Groovy does not do anything like that by default.
I hope that helps.

How can I add JsonTypeInfo (annotation) on java.util.ArrayList

My POJO:
public class Album{
private String title;
private Object tracks; // I can not change the type, beyond my control..
/** setter **/
....
/** getter **/
....
}
public class Track{
private String title;
private String singer;
/** setter **/
....
/** getter **/
....
}
Main method:
public static void main(String[] args)
{
Album album = new Album ();
album.setTitle("Thriller");
Track track = new Track();
track.setTitle("Beat It");
track.setSinger("M.J");
List<Track> trackLst = new ArrayList<Track>();
trackLst.add(track);
Album.setTracks(trackLst);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, As.PROPERTY);
m.writeValue(System.out, album);
}
/** console printed **/
{
"title": "Thriller";
"tracks":[
"java.util.ArrayList",
[
{
"#class":"com.hs.Track",
"title":"Beat It",
"singer":"M.J"
}
]
]
}
As you can see, tracks serialized to a jsonArray, one element is the type(ArrayList), the other is the real jsonArray. Is there any solution that just keep the real jsonArray? like this:
{
"title": "Thriller";
"tracks":
[
{
"#class":"com.hs.Track",
"title":"Beat It",
"singer":"M.J"
}
]
}
The problem is type declaration private Object tracks, which causes property to match definition and force use of type information for the list as well. If it was declared as List<?>, this would not occur.
One thing you may be able to do is to use "mix-in annotations", to associate annotation like:
public class MixIn {
#JsonDeserialize(as=List.class)
#JsonSerialize(as=List.class)
private Object tracks;
}
and register mix-in to apply to class Album. This should indicate that intended type is List, and avoid inclusion of type information

Multiple threads adding to child collection results in StaleObjectStateException

I have a situation where multiple threads load the same entity Job and then each thread adds to its child collection Set<JobError>. I can understand this exception if the parent itself was updated, but the only 'change' to the parent is the addition to the collection, even then?
Parent Entity:
#Entity
#Table(name = "JOB")
public class Job extends BaseEntity {
private Set<JobError> jobErrors = new HashSet<JobError>();
/**
* #return the jobErrors
*/
#OneToMany(mappedBy = "job", cascade = { CascadeType.PERSIST,
CascadeType.MERGE, CascadeType.REMOVE })
public Set<JobError> getJobErrors() {
return jobErrors;
}
/**
* #param jobErrors
* the jobErrors to set
*/
public void setJobErrors(Set<JobError> jobErrors) {
this.jobErrors = jobErrors;
}
/**
* Helper to take care of both sides of the association
* #param message
* #param currentProfileId
*/
public void addError(String message, Long currentProfileId,
String firstName, String lastName) {
JobError er = new JobError(message, currentProfileId, firstName,
lastName, this);
jobErrors.add(er);
}
}
Child Entity:
#Entity
#Table(name = "JOB_ERROR")
public class JobError extends BaseEntity {
private Job job;
public JobError(String description, Long profileId, String firstName,
String lastName, Job job) {
this.description = description;
this.profileId = profileId;
this.firstName = firstName;
this.lastName = lastName;
this.job = job;
}
/**
*
*/
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "JOB_ID", nullable = false)
public Job getJob() {
return job;
}
/**
* #param jobErrors
* the jobErrors to set
*/
public void setJob(Job job) {
this.job = job;
}
}
Service Code, this runs in multiple concurrent threads:
job = jobDao.findById(er.getJobId(), false);
for (Long profileId : er.getProfileIds()) {
// do stuff
try {
sendEmail(emailTemplateDto, user);
} catch (RuntimeException re) {
job.addError(re.getLocalizedMessage(), currentProfileId, profile.getPersonalData().getFirstName(), profile.getPersonalData().getLastName());
}
Once the service method returns which is annotated as #Transactional(propagation = Propagation.REQUIRED) the StaleObjectStateException is thrown:
2013-03-28 13:22:52,578 ERROR org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(324): - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.test.project.domain.Job#2]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1950)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2594)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2494)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2821)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy162.processSendEmail(Unknown Source)
at com.test.project.service.messaging.EmailRequestMessageListener.onMessage(EmailRequestMessageListener.java:57)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:560)
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:498)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:467)
at org.springframework.jms.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:439)
at org.springframework.jms.listener.SimpleMessageListenerContainer.processMessage(SimpleMessageListenerContainer.java:311)
at org.springframework.jms.listener.SimpleMessageListenerContainer$2.onMessage(SimpleMessageListenerContainer.java:287)
at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1321)
at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131)
at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202)
at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:129)
at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:47)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
I can think of nothing else except for trying to save JobError directly. Currently I first load Job, add to the collection of JobError and then merge Job and hope the cascade.merge will take care of saving the child collection.
Any pointers will be appreciated.
I don't know if this is the cause of the exception in question, but if not it is going to cause problems down the line: HashSet is not a thread-safe collection, which means that if two threads call addError at the same time then one of the errors might not make it into the set. You'll either need to add the "synchronized" keyword to the addError method, or else you'll need to replace the HashSet with a thread-safe alternative, e.g. a ConcurrentLinkedQueue or ConcurrentHashMap

Resources