Making jaxb lines generic to avoid writing them repeatedly - jaxb

Unmarshalling from a File:
JAXBContext jc = JAXBContext.newInstance( "com.acme.foo" );
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal( new File( "nosferatu.xml" ) );
Unmarshalling from an InputStream:
InputStream is = new FileInputStream( "nosferatu.xml" );
JAXBContext jc = JAXBContext.newInstance( "com.acme.foo" );
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal( is );

The JAXB (JSR-222) APIs are already quite generic.
JAXBContext
This object is thread safe so you create this once and create all your instances of Marshaller, Unmarshaller, etc from it.
Marshaller/Unmarshaller
These objects are not thread safe, so you need to ensure that they are not being used by more than one thread at the same time. Unless you are setting any properties on then you could always do:
JAXBContext jc = JAXBContext.newInstance("com.acme.foo");
Object o = jc.createUnmarshaller().unmarshal(new File("nosferatu.xml"));

Related

Unmarshalling the XML using JAXB

Actually I am very new at spring and currently due to some requirement, I am working with spring-integration, I have made few JAXB classes to convert the data into XML and have to send it through webservices but in response I am getting the XML back with some new element, I want to know how to unmarshall the new XML with same JAXB classes that I have made?
I use the following component to do that (java configuration):
#Bean
public Jaxb2Marshaller jaxb2Marshaller() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
/* Packages with root elements (#XmlRootElement). Your JAXB classes */
marshaller.setContextPaths("...");
return marshaller;
}
#Bean
#ServiceActivator(inputChannel = "toWebServiceChannel")
public MessageHandler wsGateway() throws Exception {
ConfigWebServiceURLProvider provider = new ConfigWebServiceURLProvider(isHttps, host, port, endpoint);
/* marshaller and unmarshaller could be the same */
MarshallingWebServiceOutboundGateway gw = new MarshallingWebServiceOutboundGateway(url, jaxb2Marshaller(), jaxb2Marshaller());
gw.setOutputChannelName( "fromWebServiceChannel" );
return gw;
}

Serializing with JAXB and the Any (without ElementNS)

I'm looking a best practices to marshal a XMLAnyElement that can handle String, Long etc... I found a Serializing with JAXB and the Any, but I need avoid ElementNS and resolve the case attached
Is DOMHandler the best way?
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Payload.class, Foo.class, ObjectFactory.class);
Payload payload = new Payload();
payload.any = new ArrayList<>();
payload.any.add(new Bar());
payload.any.add(new Foo());
payload.any.add("pepe");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(payload, System.out);
}
Resolved, see my PoC on github..
https://github.com/franciscophilip/jaxb-payload-poc/

org.apache.camel.InvalidPayloadException: No body available of type error thrown while unMarshalling Jaxb Object

I am sending JAXB Object to Rabbit MQ via Java.
JAXBContext jaxbContext = JAXBContext.newInstance(MyDTO.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
java.io.StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(deliveryrequest, sw);
ConnectionFactory factory = new ConnectionFactory() ;
//TODO change the hardcoding to the properties file
factory.setHost("rabbitmq.host.net");
factory.setUsername("user");
factory.setPassword("pass");
Channel channel ;
Connection connection;
Map<String, Object> args = new HashMap<String, Object>();
String haPolicyValue = "all";
args.put("x-ha-policy", haPolicyValue);
connection = factory.newConnection();
channel = connection.createChannel();
//TODO change the hardcoding to the properties file
channel.queueDeclare("upload.com.some.queue", true, false, false, args);
//TODO change the hardcoding to the properties file
channel.basicPublish("com.some.exchange", "someroutingKey",
new AMQP.BasicProperties.Builder().contentType("text/plain")
.deliveryMode(2).priority(1).userId("guest").build(),
sw.toString().getBytes());
I am using Camel in different application to read this.
<camel:route id="myRoute">
<camel:from uri="RabbitMQEndpoint" />
<camel:to uri="bean:MyHandler" />
</camel:route>
Handler is using the Jaxb object redone in camel side
#Handler
public void handleRequest(MyDTO dto) throws ParseException {
I am getting the error which I did not expect to get.
Caused by: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: byte[] to the required type: com.mycompany.MyDTO with value [B#1b8d3e42
at org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:181)
at org.apache.camel.impl.MessageSupport.getMandatoryBody(MessageSupport.java:99)
Solution is appreciated.
Your message body type from rabbit is a byte[], and you want to call a bean which as MyDTO type. You have a type mismatch. And Camel cannot find a type converter that can convert the message body from byte[] to your MyDTO type.
Is the byte[] data from Rabbit in XML format? And do you have JAXB annotations on MyDTO classes, so you could use JAXB to marshal that from xml to Java ?
It is Jaxb object. I was under impression that if i move the publisher too Camel. Things will be sorted out.
I changed the publisher to the following.
#EndpointInject(context = "myContext" , uri = "direct:myRoute")
private ProducerTemplate sendAMyContext;
And in method I called
#Override
public MyResponse putMyCall(
MyRequest myrequest) {
sendMyContext.sendBody(myrequest);
My camel route is simple one
<camel:camelContext id="MyContext" autoStartup="true" xmlns="http://camel.apache.org/schema/spring">
<camel:endpoint id="myQueue" uri="${my.queue.1}" />
<camel:route>
<camel:from uri="direct:myRoute"/>
<camel:to uri="bean:mySendHandler"/>
<camel:convertBodyTo type="String"></camel:convertBodyTo>
<camel:to uri="ref:myQueue" />
</camel:route>
</camel:camelContext>
I added the handler to convert the Jaxb object to string(because of error)
public class MySendHandler {
#Handler
public String myDelivery( MyRequest myRequest) throws ParseException, JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(MyRequest.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
java.io.StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(attributedDeliveryRequest, sw);
return sw.toString();
}
Still the same error
[SpringAMQPConsumer.SpringAMQPExecutor-1] WARN org.springframework.amqp.support.converter.JsonMessageConverter (JsonMessageConverter.java:111) - Could not convert incoming message with content-type [text/plain]
SpringAMQPConsumer.SpringAMQPExecutor-1] ERROR org.apache.camel.processor.DefaultErrorHandler (MarkerIgnoringBase.java:161) - Failed delivery for (MessageId: ID-Con ExchangeId: ID-65455-1380873539452-3-2).
Exhausted after delivery attempt: 1 caught: org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[Message: <XML>]
org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[Message: <xml>
.....
Caused by: org.apache.camel.InvalidPayloadException: No body available of type: MyDTO but has value: [B#7da255c of type: byte[] on: Message: <xml>
. Caused by: [org.apache.camel.NoTypeConversionAvailableException - No type converter available to convert from type: byte[] to the required type: MyDeliveryDTO with value [B#7da255c]
at org.apache.camel.impl.MessageSupport.getMandatoryBody(MessageSupport.java:101)
at org.apache.camel.builder.ExpressionBuilder$38.evaluate(ExpressionBuilder.java:934)
... 74 more
Caused by: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: byte[] to the required type: MyDTO with value [B#7da255c
at org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:181)
at org.apache.camel.impl.MessageSupport.getMandatoryBody(MessageSupport.java:99)
... 75 more
My receiving camel route is as below
<camel:route id="processVDelivery">
<camel:from uri="aVEndpoint" />
<camel:to uri="bean:myDataHandler" />
</camel:route>
I added
<camel:convertBodyTo type="String"></camel:convertBodyTo>
to it and it gave me error that it cannot convert it from string to the object
adding this
<camel:unmarshal></camel:unmarshal>
-------------------------
Answer to the question will be all your mashaller have to be part of classpath
---------------------------------
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>${jackson.version}</version>
</dependency>
------- Also you can the custom marshaller using dataformat
<camel:dataFormats>
<camel:jaxb id="name" contextPath="com.something"/>
</camel:dataFormats
make sure that you keep jaxb.index file in com.something package with the root level Jaxb object name
Using Camel Rest with Spring boot
rest("/authenticate")
.post()
.route()
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.process(exchange -> {
org.apache.camel.TypeConverter tc = exchange.getContext().getTypeConverter();
String str_value = tc.convertTo(String.class, exchange.getIn().getBody());
System.err.println(str_value);
exchange.getOut().setBody(str_value);
})
//.removeHeader(Exchange.HTTP_URI)
.to("http://localhost:8082/authenticate?bridgeEndpoint=true")
//.convertBodyTo(DTO.class)
.log("Boo!")
.end()
.endRest();

How to force a JAXBException when marshalling for a JUnit test

I haven't been able to find a way to force a JAXBException when marshalling for a JUnit test. Does anyone have any ideas?
Here is my marshalling code:
public String toXml() {
log.debug("Entered toXml method");
String result = null;
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Config.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
jaxbMarshaller.marshal(this, writer);
result = writer.toString();
} catch (JAXBException e) {
log.error(e);
}
log.debug("Exiting toXml method");
return result;
}
There are different ways to create a JAXBException during a marshal operation:
1 - Marshal an Invalid Object
You can generate a JAXBException during a marshal operation by marshalling an instance of a class that the JAXBContext isn't aware of (i.e. Take your example and use it to marshal an instance of Foo). This will produce the following exception.
Exception in thread "main" javax.xml.bind.JAXBException: class forum13389277.Foo nor any of its super class is known to this context.
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:594)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:482)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:315)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:244)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at forum13272288.Demo.main(Demo.java:27)
2 - Marshal to Invalid Output
If you try to marshal to an invalid output such as an OutputStream that has been closed:
FileOutputStream closedStream = new FileOutputStream("src/foo.xml");
closedStream.close();
jaxbMarshaller.marshal(this, closedStream);
Then you will get a MarshalException which is a subclass of JAXBException.
Exception in thread "main" javax.xml.bind.MarshalException
- with linked exception:
[java.io.IOException: Stream Closed]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:320)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:244)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at forum13272288.Demo.main(Demo.java:27)
Caused by: java.io.IOException: Stream Closed
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:318)
at com.sun.xml.bind.v2.runtime.output.UTF8XmlOutput.flushBuffer(UTF8XmlOutput.java:413)
at com.sun.xml.bind.v2.runtime.output.UTF8XmlOutput.endDocument(UTF8XmlOutput.java:137)
at com.sun.xml.bind.v2.runtime.output.IndentingUTF8XmlOutput.endDocument(IndentingUTF8XmlOutput.java:165)
at com.sun.xml.bind.v2.runtime.XMLSerializer.endDocument(XMLSerializer.java:852)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.postwrite(MarshallerImpl.java:369)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:316)
... 3 more

read .properties file in static code of a JSF web application

I would like to get DB connection parameters from a properties file in a static block. The properties file location is WEB-INF/classes/db.properties.
I will prefer to use getResourceAsStream() method. I have tried many ways, but they all returned null.
private static Properties prop = new Properties();
static{
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();
InputStream inputStream = servletContext.getResourceAsStream("/db.properties");
InputStream is = prop.getClass().getResourceAsStream("/db.properties");
if(inputStream!=null){//it is null
prop.load(inputStream);
}
if(is!=null){//it is null
prop.load(is);
}
} catch (Exception e) {
e.printStackTrace();
}
}
How is this caused and how can I solve it?
As Thufir wrote in a comment, there is a nice tutorial from reading properties from Java code: http://jaitechwriteups.blogspot.ca/2007/01/how-to-read-properties-file-in-web.html
/**
* Some Method
*
* #throws IOException
*
*/
public void doSomeOperation() throws IOException {
// Get the inputStream
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("myApp.properties");
Properties properties = new Properties();
System.out.println("InputStream is: " + inputStream);
// load the inputStream using the Properties
properties.load(inputStream);
// get the value of the property
String propValue = properties.getProperty("abc");
System.out.println("Property value is: " + propValue);
}
InputStream inputStream = servletContext.getResourceAsStream("/db.properties");
This attempt expects the file to be in /WebContent/db.properties.
InputStream is = prop.getClass().getResourceAsStream("/db.properties");
This attempt expects it to be in at least the same archive (JAR) as the java.util.Properties class.
Neither of those attempts reads the file which you've placed in /WEB-INF/classes/db.properties. You can fix this problem in basically 2 ways.
Move it directly in the /WEB-INF folder as /WEB-INF/db.properties and load it as follows:
InputStream input = externalContext.getResourceAsStream("/WEB-INF/db.properties");
(note that you don't need to haul the ServletContext from under the JSF's hoods; there's already a delegate method for that)
Load it relative to the class which is also present in /WEB-INF/classes, e.g. the current managed bean class.
InputStream input = Bean.class.getResourceAsStream("/db.properties");
Or just use the context classloader, it has access to everything.
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");
(note the lack of the / prefix)
See also:
Where to place and how to read configuration resource files in servlet based application?

Resources