org.apache.camel.component.stax.StAXJAXBIteratorExpression Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.stax;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.support.ExpressionAdapter;
import org.apache.camel.support.LRUCacheFactory;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
import static org.apache.camel.component.stax.StAXUtil.getTagName;
/**
* {@link org.apache.camel.Expression} to walk a {@link org.apache.camel.Message} body using an {@link Iterator}, which
* uses StAX to walk in streaming mode. The elements returned is a POJO which is bound using JAXB annotations.
*
* The message body must be able to convert to {@link XMLEventReader} type which is used as stream to access the message
* body. And there must be a JAXB annotated class to use as binding.
*/
public class StAXJAXBIteratorExpression extends ExpressionAdapter {
private static final Map, JAXBContext> JAX_CONTEXTS = LRUCacheFactory.newLRUSoftCache(1000);
private final Class handled;
private final String handledName;
private final boolean isNamespaceAware;
/**
* Creates this expression.
*
* @param handled the class which has JAXB annotations to bind POJO.
*/
public StAXJAXBIteratorExpression(Class handled) {
this(handled, true);
}
/**
* Creates this expression.
*
* @param handled the class which has JAXB annotations to bind POJO.
* @param isNamespaceAware sets the namespace awareness of the xml reader
*/
public StAXJAXBIteratorExpression(Class handled, boolean isNamespaceAware) {
ObjectHelper.notNull(handled, "handled");
this.handled = handled;
this.handledName = null;
this.isNamespaceAware = isNamespaceAware;
}
/**
* Creates this expression.
*
* @param handledName the FQN name of the class which has JAXB annotations to bind POJO.
*/
public StAXJAXBIteratorExpression(String handledName) {
this(handledName, true);
}
/**
* Creates this expression.
*
* @param handledName the FQN name of the class which has JAXB annotations to bind POJO.
* @param isNamespaceAware sets the namespace awareness of the xml reader
*/
public StAXJAXBIteratorExpression(String handledName, boolean isNamespaceAware) {
ObjectHelper.notNull(handledName, "handledName");
this.handled = null;
this.handledName = handledName;
this.isNamespaceAware = isNamespaceAware;
}
private static JAXBContext jaxbContext(Class> handled) throws JAXBException {
if (JAX_CONTEXTS.containsKey(handled)) {
return JAX_CONTEXTS.get(handled);
}
JAXBContext context;
synchronized (JAX_CONTEXTS) {
context = JAXBContext.newInstance(handled);
JAX_CONTEXTS.put(handled, context);
}
return context;
}
@Override
@SuppressWarnings("unchecked")
public Object evaluate(Exchange exchange) {
try {
InputStream inputStream = null;
XMLEventReader reader = exchange.getContext().getTypeConverter().tryConvertTo(XMLEventReader.class, exchange,
exchange.getIn().getBody());
if (reader == null) {
inputStream = exchange.getIn().getMandatoryBody(InputStream.class);
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, isNamespaceAware);
xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
reader = xmlInputFactory.createXMLEventReader(inputStream);
}
Class clazz = handled;
if (clazz == null && handledName != null) {
clazz = (Class) exchange.getContext().getClassResolver().resolveMandatoryClass(handledName);
}
return createIterator(reader, clazz, inputStream);
} catch (InvalidPayloadException | JAXBException | ClassNotFoundException | XMLStreamException e) {
exchange.setException(e);
return null;
}
}
private Iterator createIterator(XMLEventReader reader, Class clazz, InputStream inputStream) throws JAXBException {
return new StAXJAXBIterator<>(clazz, reader, inputStream);
}
/**
* Iterator to walk the XML reader
*/
static class StAXJAXBIterator implements Iterator, Closeable {
private final XMLEventReader reader;
private final InputStream inputStream;
private final Class clazz;
private final String name;
private final Unmarshaller unmarshaller;
private T element;
StAXJAXBIterator(Class clazz, XMLEventReader reader, InputStream inputStream) throws JAXBException {
this.clazz = clazz;
this.reader = reader;
this.inputStream = inputStream;
name = getTagName(clazz);
JAXBContext jaxb = jaxbContext(clazz);
// unmarshaller is not thread safe so we need to create a new instance per iterator
unmarshaller = jaxb.createUnmarshaller();
}
@Override
public boolean hasNext() {
if (element == null) {
element = getNextElement();
}
return element != null;
}
@Override
public T next() {
if (element == null) {
element = getNextElement();
}
T answer = element;
element = null;
return answer;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
T getNextElement() {
XMLEvent xmlEvent;
boolean found = false;
while (!found && reader.hasNext()) {
try {
xmlEvent = reader.peek();
if (xmlEvent != null && xmlEvent.isStartElement()
&& name.equals(xmlEvent.asStartElement().getName().getLocalPart())) {
found = true;
} else {
reader.nextEvent();
}
} catch (XMLStreamException e) {
throw new RuntimeCamelException(e);
}
}
if (!found) {
return null;
}
try {
return unmarshaller.unmarshal(reader, clazz).getValue();
} catch (JAXBException e) {
throw new RuntimeCamelException(e);
}
}
@Override
public void close() throws IOException {
if (inputStream != null) {
IOHelper.close(inputStream);
}
try {
reader.close();
} catch (XMLStreamException e) {
throw new IOException(e);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy