/*
* Copyright (c) 2014 Red Hat, Inc. and/or its affiliates.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Cheng Fang - Initial API and implementation
*/
package org.jberet.support.io;
import java.io.Serializable;
import java.util.List;
import javax.batch.api.BatchProperty;
import javax.batch.api.chunk.ItemWriter;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.xml.stream.XMLStreamWriter;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.io.OutputDecorator;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import org.jberet.support._private.SupportLogger;
import org.jberet.support._private.SupportMessages;
/**
* An implementation of {@code javax.batch.api.chunk.ItemWriter} that writes a list of same-typed objects to XML resource.
* Each object is written as a sub-element of the target XML resource. The XML root element is specified with the
* injected batch artifact properties {@link #rootElementName}, {@link #rootElementNamespaceURI}, and
* {@link #rootElementPrefix}.
*
* @see XmlItemReader
* @see XmlItemReaderWriterBase
* @since 1.0.2
*/
@Named
@Dependent
public class XmlItemWriter extends XmlItemReaderWriterBase implements ItemWriter {
/**
* Instructs this class, when the target XML resource already
* exists, whether to append to, or overwrite the existing resource, or fail. Valid values are {@code append},
* {@code overwrite}, and {@code failIfExists}. Optional property, and defaults to {@code append}.
*/
@Inject
@BatchProperty
protected String writeMode;
/**
* Whether use wrapper for indexed (List, array) properties or not, if there are no explicit annotations.
* Optional property, valid values are "true" and "false", and defaults to "true".
*
* @see "com.fasterxml.jackson.dataformat.xml.JacksonXmlModule#setDefaultUseWrapper(boolean)"
* @see "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper"
*/
@Inject
@BatchProperty
protected String defaultUseWrapper;
/**
* Local name of the output XML root element. Required property.
*
* @see "javax.xml.stream.XMLStreamWriter#writeStartElement"
*/
@Inject
@BatchProperty
protected String rootElementName;
/**
* The prefix of the XML root element tag. Optional property and defaults to null.
*
* @see "javax.xml.stream.XMLStreamWriter#writeStartElement(java.lang.String, java.lang.String, java.lang.String)"
*/
@Inject
@BatchProperty
protected String rootElementPrefix;
/**
* The namespaceURI of the prefix to use. Optional property and defaults to null.
* @see "javax.xml.stream.XMLStreamWriter#writeStartElement(java.lang.String, java.lang.String, java.lang.String)"
* @see "javax.xml.stream.XMLStreamWriter#writeStartElement(java.lang.String, java.lang.String)"
*/
@Inject
@BatchProperty
protected String rootElementNamespaceURI;
/**
* The fully-qualified name of a class implementing {@code com.fasterxml.jackson.core.PrettyPrinter}, which implements
* pretty printer functionality, such as indentation. Optional property and defaults to null (default pretty printer
* is used).
*
* @see "com.fasterxml.jackson.core.PrettyPrinter"
* @see "com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator#setPrettyPrinter(com.fasterxml.jackson.core.PrettyPrinter)"
*/
@Inject
@BatchProperty
protected Class prettyPrinter;
/**
* The fully-qualified name of a class implementing {@code com.fasterxml.jackson.core.io.OutputDecorator}, which
* can be used to decorate output destinations. Optional property and defaults to null.
*
* @see "com.fasterxml.jackson.core.JsonFactory#setOutputDecorator(com.fasterxml.jackson.core.io.OutputDecorator)"
* @see "com.fasterxml.jackson.core.io.OutputDecorator"
*/
@Inject
@BatchProperty
protected Class outputDecorator;
protected ToXmlGenerator toXmlGenerator;
private XMLStreamWriter staxWriter;
@Override
public void open(final Serializable checkpoint) throws Exception {
SupportLogger.LOGGER.tracef("Open XmlItemWriter with checkpoint %s, which is ignored for XmlItemWriter.%n", checkpoint);
super.initXmlFactory();
if (outputDecorator != null) {
xmlFactory.setOutputDecorator((OutputDecorator) outputDecorator.newInstance());
}
xmlMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
toXmlGenerator = xmlFactory.createGenerator(getOutputStream(writeMode));
SupportLogger.LOGGER.openingResource(resource, this.getClass());
if (prettyPrinter == null) {
toXmlGenerator.useDefaultPrettyPrinter();
} else {
toXmlGenerator.setPrettyPrinter((PrettyPrinter) prettyPrinter.newInstance());
}
staxWriter = toXmlGenerator.getStaxWriter();
if (!this.skipWritingHeader) {
staxWriter.writeStartDocument();
}
staxWriter.writeCharacters(NEW_LINE);
if (rootElementName == null || rootElementName.isEmpty()) {
throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, rootElementName, "rootElementName");
}
if (rootElementPrefix == null || rootElementPrefix.isEmpty()) {
if (rootElementNamespaceURI == null || rootElementNamespaceURI.isEmpty()) {
staxWriter.writeStartElement(rootElementName);
} else {
staxWriter.writeStartElement(rootElementNamespaceURI, rootElementName);
}
} else {
if (rootElementNamespaceURI == null || rootElementNamespaceURI.isEmpty()) {
throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, rootElementNamespaceURI, "rootElementNamespaceURI");
} else {
staxWriter.writeStartElement(rootElementPrefix, rootElementName, rootElementNamespaceURI);
}
}
}
@Override
public void writeItems(final List<Object> items) throws Exception {
for (final Object o : items) {
staxWriter.writeCharacters(NEW_LINE);
toXmlGenerator.writeObject(o);
}
toXmlGenerator.flush();
}
@Override
public Serializable checkpointInfo() throws Exception {
return null;
}
@Override
public void close() throws Exception {
if (toXmlGenerator != null) {
SupportLogger.LOGGER.closingResource(resource, this.getClass());
staxWriter.writeCharacters(NEW_LINE);
staxWriter.writeEndDocument();
toXmlGenerator.close();
toXmlGenerator = null;
}
}
@Override
protected void initXmlModule() {
if (defaultUseWrapper != null) {
if (defaultUseWrapper.equals("false")) {
xmlModule = new JacksonXmlModule();
xmlModule.setDefaultUseWrapper(false);
} else if (defaultUseWrapper.equals("true")) {
//default value is already true, so nothing to do
} else {
throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, defaultUseWrapper, "defaultUseWrapper");
}
}
}
}