Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there something like PostConstruct for JAXB-annnotated classes?

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?

like image 289
java.is.for.desktop Avatar asked Dec 17 '25 00:12

java.is.for.desktop


2 Answers

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 );
}
like image 51
Oded Peer Avatar answered Dec 19 '25 12:12

Oded Peer


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

like image 36
java.is.for.desktop Avatar answered Dec 19 '25 13:12

java.is.for.desktop



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!