/* * Copyright 2007 T-Rank AS * * Licensed 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 no.trank.openpipe.step; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collections; import java.util.Formatter; import java.util.Map; import javax.xml.stream.XMLStreamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import no.trank.openpipe.api.BasePipelineStep; import no.trank.openpipe.api.PipelineException; import no.trank.openpipe.api.PipelineStepStatus; import no.trank.openpipe.api.document.Document; import no.trank.openpipe.config.annotation.NotEmpty; import no.trank.openpipe.config.annotation.NotNull; import no.trank.openpipe.step.xml.DocumentWriter; import no.trank.openpipe.step.xml.XMLStreamDocWriter; /** * Uses a <tt>DocumentWriter</tt> to write documents to a set of XML-files. * * @version $Revision$ */ public class WriteXML extends BasePipelineStep { private static final Logger log = LoggerFactory.getLogger(WriteXML.class); private boolean failOnXMLError; private int maxDocsPerFile = -1; @NotEmpty private String rootElementName; @NotEmpty private String docElementName; @NotNull private File directory; @NotNull private Map<String, String> fieldToAttributes = Collections.emptyMap(); @NotNull private Map<String, String> fieldToElements = Collections.emptyMap(); private DocumentWriter writer; private int docCount; private int fileCount; @Override public void prepare() throws PipelineException { super.prepare(); if (fieldToAttributes.isEmpty() && fieldToElements.isEmpty()) { throw new PipelineException("No fields are configured!", getName()); } if (directory.isFile()) { throw new PipelineException(directory + " is a file", getName()); } else if (!directory.exists() && !directory.mkdir()) { throw new PipelineException("Could not create directory " + directory, getName()); } else if (!directory.canWrite()) { throw new PipelineException("Directory " + directory + " is not writable", getName()); } if (writer == null) { writer = new XMLStreamDocWriter(); } setupWriter(); } @Override public void finish(boolean success) throws PipelineException { closeWriter(); } @Override public PipelineStepStatus execute(Document doc) throws PipelineException { ensureWriter(); writeDocument(doc); return PipelineStepStatus.DEFAULT; } private void writeDocument(Document doc) throws PipelineException { try { writer.startElement(docElementName); addAttributes(doc); addElements(doc); writer.endElement(); } catch (XMLStreamException e) { if (failOnXMLError) { throw new PipelineException("Could not write document", e, getName()); } log.error("Could not write document", e); } } private void addElements(Document doc) throws XMLStreamException { for (Map.Entry<String, String> e : fieldToElements.entrySet()) { writer.addElement(e.getValue(), doc.getFieldValues(e.getKey())); } } private void addAttributes(Document doc) throws XMLStreamException { for (Map.Entry<String, String> e : fieldToAttributes.entrySet()) { writer.addAttribute(e.getValue(), doc.getFieldValues(e.getKey())); } } private void ensureWriter() throws PipelineException { if (maxDocsPerFile > 0 && docCount++ > maxDocsPerFile) { docCount = 0; closeWriter(); setupWriter(); } } private void closeWriter() throws PipelineException { if (writer != null) { try { writer.endDocument(); try { writer.close(); } catch (IOException e) { log.error("Unable to close writer", e); } } catch (XMLStreamException e) { throw new PipelineException("Could not close writer", e, getName()); } } } private void setupWriter() throws PipelineException { try { writer.reset(new File(directory, createFileName())); writer.startDocument(rootElementName); } catch (XMLStreamException e) { throw new PipelineException("Could not setup writer", e, getName()); } catch (FileNotFoundException e) { throw new PipelineException(e, getName()); } } private String createFileName() { return new Formatter().format("%1$04d.xml", fileCount++).toString(); } /** * Sets whether an error in writing will cause a <tt>PipelineException</tt> to be thrown or not. * * @param failOnXMLError <tt>true</tt> if a <tt>PipelineException</tt> should be thrown. */ public void setFailOnXMLError(boolean failOnXMLError) { this.failOnXMLError = failOnXMLError; } /** * Sets the name of the root element of the XML-file(s). * * @param rootElementName name of the root element of the XML-file(s). */ public void setRootElementName(String rootElementName) { this.rootElementName = rootElementName; } /** * Sets the name of the document element of the XML-file(s). * * @param rootElementName name of the document element of the XML-file(s). */ public void setDocElementName(String docElementName) { this.docElementName = docElementName; } /** * Sets the directory where the XML-file(s) will be written. * <br/> * <tt>directory</tt> or <tt>directory.getParent()</tt> must be writable. * * @param directory the directory where the XML-file(s) will be written. */ public void setDirectory(File directory) { this.directory = directory; } /** * Sets the maximum number of documents in an XML-file. * <br/> * Default value is <tt>-1</tt>. * * @param maxDocsPerFile the maximum number of documents in an XML-file. <tt><= 0</tt> specifies no limit. */ public void setMaxDocsPerFile(int maxDocsPerFile) { this.maxDocsPerFile = maxDocsPerFile; } /** * Sets which fields will be rendered as attributes. * <br/> * The <tt>keys</tt> of the map gives the field-names. The <tt>value</tt> of the <tt>key</tt> gives the * attribute-name. * * @param fieldToAttributes the field-name to attribute-name map. */ public void setFieldToAttributes(Map<String, String> fieldToAttributes) { this.fieldToAttributes = fieldToAttributes; } /** * Sets which fields will be rendered as elements. * <br/> * The <tt>keys</tt> of the map gives the field-names. The <tt>value</tt> of the <tt>key</tt> gives the element-name. * * @param fieldToElements the field-name to element-name map. */ public void setFieldToElements(Map<String, String> fieldToElements) { this.fieldToElements = fieldToElements; } /** * Sets the <tt>DocumentWriter</tt> that writes the acctual XML-file(s). * <br/> * If no writer is specified a {@link no.trank.openpipe.step.xml.XMLStreamDocWriter} is used. * * @param writer the field-name to element-name map. */ public void setWriter(DocumentWriter writer) { this.writer = writer; } @Override public String getRevision() { return "$Revision$"; } }