čtvrtek 21. února 2013

JAXB marshal element without @XmlRootElement anotation

When you are trying marshal MyClass, that is not anotated as @XmlRootElement element, you can obtain:

unable to marshal type "org.company.MyClass" as an element
because it is missing an @XmlRootElement annotation

Possible workaround is marshal your object in this way:

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(new JAXBElement<MyClass>(new QName("NameSpaceUri","ElementName"), MyClass.class, myClassInstance), System.out);
view raw gistfile1.java hosted with ❤ by GitHub

I have found, that more details can be found here:
http://weblogs.java.net/blog/2006/03/03/why-does-jaxb-put-xmlrootelement-sometimes-not-always
http://stackoverflow.com/questions/819720/no-xmlrootelement-generated-by-jaxb

středa 13. února 2013

Jax-WS - SchemaValidation zkouší číst stream 2x

JAX-WS, Metro, hází "Stream already closed" Při použití streamovaného dataSource

@StreamingAttachment(parseEagerly = true, memoryThreshold = 4000000L)
@WebService(name = "SomeService", targetNamespace = "http://www.comnapy.com/Some/Namespace", serviceName = "SomeService")
@MTOM
@SchemaValidation
view raw gistfile1.java hosted with ❤ by GitHub
Stane se to, že když je zapnutá Schema Validation, tak při validaci se přečte celý stream (ten attachment). Takže validace ho celý přečte a když dojde na skutečné spracování requestu a chtějí se přečíst data, tak to hodí "Stream already closed". A je po srandě.

Neznám žádné rozumné řešení tohoto problému. U nás jsme to pořešili tím, že jsme udělali vlastní DataSource. Která obalí skutečný stream a při prvním zavolání metody getInputStream() - což je z validace nevrátí skutečný stream, ale jen "new ByteArrayInputStream(new byte[]{})"
A až při druhém zavolání metody getInputStream() vrátí skutečný stream.
Tím ho validace nezničí a vše funguje. Jak říkám, jestli ví někdo jak to řešit lépe - sem s tím.

Takový hrozný DataSource pak vypadá zhruba takhle:

import javax.activation.DataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Vzniklo to, protoze servisa, ktera ma zapnute @SchemaValidation
* Udela to, ze pri validaci zavola getInputStream a cely ho precte
* Potom se pri vyplnovani responsu znova zavola getInputStream a chce ho napsat do response, jenomze
* to umre, protoze ho uz precetla a zavrela validace.
*
* Reseni: na prvni volani to nevrati pravy inputStream
*/
public class InputStreamDataSourceWithHackForSchemaValidation implements DataSource {
private InputStream inputStream;
private int count = 0;
public InputStreamDataSourceWithHackForSchemaValidation(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public InputStream getInputStream() throws IOException {
if (count == 0) {
//First access to data stream is for @SchemaValidation (and we do not need read stream for validation)
count++;
return new ByteArrayInputStream(new byte[]{});
}
if (count != 1) {
throw new IllegalStateException("getInputStream was expected to be called exactly twice");
}
//second access is for writing data to response
count++;
return inputStream;
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public String getContentType() {
return "*/*";
}
@Override
public String getName() {
return "InputStreamDataSource";
}
}
view raw gistfile1.java hosted with ❤ by GitHub