1

Below (at the end of this question) is a generic JAXB marshaller/unmarshaller that uses Java generics. It works. It can marshal/unmarshal JAXB objects with one line:

JAXBUtils jaxbUtils = new JAXBUtils(ObjectFactory.class);
SomeClass object = jaxbUtils.unmarshall("<some xml >...");
String xml  = jaxbUtils.marshall(object);

I also needed to clone some of these objects, so I used marshall and unmarshall to deep clone any JAXB object. This also works without any compiler errors (just some warnings suppressed with annotations).

SomeClass object = ... some JAXB object obtained from XML
SomeClass cloneObject = clone(object);

But in another environment (continuous integration) I am getting a compiler error in the clone method return line:

type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Object

return unmarshal(marshal(object));
                ^

I downgraded my local environment from 1.6.0_27 to 1.6.0_23, so I have exactly the same version used in CI server, as I found some bugs related to generic code were fixed between those versions:

java version "1.6.0_23" Java(TM) SE Runtime Environment (build 1.6.0_23-b05)

But the code is still working for me, no compiler errors, unit tests passed, everything works.

I tried changing clone method to:

public <T> T clone(T object) throws UnsupportedEncodingException, JAXBException {
    return this.<T>unmarshal(this.<T>marshal(object));
}

That also compiles for me and looks as it gives more hints to the compiler, but I'm not sure if it will solve the problem in the server since I am not able to reproduce the error.

Do you see any problem with the use of generic code in clone method? Is it a bug in the compiler or is my code incorrect? And if it is incorrect why it works for me !?

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;

import org.xml.sax.InputSource;

public class JAXBUtils {

    private Marshaller marshaller;
    private Unmarshaller unmarshaller;

    public JAXBUtils(Class... classesToBeBound) throws JAXBException {
        final JAXBContext context = JAXBContext.newInstance(classesToBeBound);
        unmarshaller = context.createUnmarshaller();
        marshaller = context.createMarshaller();
    }

    @SuppressWarnings("unchecked")
    public <T> String marshal(T object) throws UnsupportedEncodingException, JAXBException {
        return marshal(object, object.getClass().getSimpleName(), (Class<T>) object.getClass());
    }

    public <T> String marshal(T object, String name, Class<T> clazz) throws JAXBException {
        final JAXBElement<T> jaxbElement = new JAXBElement<T>(new QName(name), clazz, object);
        final OutputStream os = new ByteArrayOutputStream();
        marshaller.marshal(jaxbElement, os);
        return os.toString();
    }

    @SuppressWarnings("unchecked")
    public <T> T unmarshal(String xml) throws UnsupportedEncodingException, JAXBException {
        final InputSource is = new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8")));
        final Object object = unmarshaller.unmarshal(is);
        // JAXB sometimes returns directly the object and sometimes wraps it in a JAXBElement.
        // Not sure why just handling both cases.
        return object instanceof JAXBElement ? ((JAXBElement<T>) object).getValue() : (T) object;
    }

    public <T> T clone(T object) throws UnsupportedEncodingException, JAXBException {
        return unmarshal(marshal(object));
    }

}

UPDATE: the server where it's giving the compiler error is a RedHat Linux (5 or 6 not sure). As I said the java version is the same, so I guess the bug is fixed for windows but not for linux. I will try to access some linux machine to try.

UPDATE: the code compiles without any error or warning in debian/open-jdk and ubuntu/sun-jdk6u23. I haven't been able to test on RedHat/sun-jdk6u23.

Boris Lopez
  • 516
  • 8
  • 14
  • Instead of cloning the object by unmarshalling and marshalling it, you could demand that `T` implements some kind of copy method, like in http://stackoverflow.com/a/18531276/1809463 – mike Sep 05 '13 at 20:40
  • seems to be this issue: http://bugs.sun.com/view_bug.do?bug_id=6302954 – Katona Sep 05 '13 at 20:42
  • by the way, do you compile with an ide in your local environment? Try it with javac (ant etc.), I think eclipse for instance have it's own compiler. – Katona Sep 05 '13 at 20:46
  • @mike unfortunately I can't touch the classes I am working with, they are used by a lot of other systems, etc. – Boris Lopez Sep 05 '13 at 20:51
  • @Katona the compiler error is the same, but I'm not sure if the bug is the same, the code is kind of similar but not the same thing. – Boris Lopez Sep 05 '13 at 20:53
  • @Katona I don't get any errors from Eclipse IDE, also I run ant to compile and run unit tests: no errors either. – Boris Lopez Sep 05 '13 at 20:55
  • Yes, eclipse has its own compiler ECJ. – mike Sep 05 '13 at 21:09

0 Answers0