XmlUtils.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.protocol.http.restv2.utils;
import org.waarp.common.exception.InvalidArgumentException;
import org.waarp.common.file.FileUtils;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
import org.waarp.openr66.protocol.http.restv2.errors.RestErrors;
import javax.ws.rs.InternalServerErrorException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import static javax.xml.transform.OutputKeys.*;
/**
* A series of utility methods for serializing and deserializing XML.
*/
public final class XmlUtils {
/**
* Prevents the default constructor from being called.
*/
private XmlUtils() throws InstantiationException {
throw new InstantiationException(
getClass().getName() + " cannot be instantiated.");
}
// ######################### PUBLIC METHODS #################################
/**
* Converts a serializable Java object into XML format as a String.
*
* @param object the object to convert to XML
*
* @return the object's representation in XML
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static String objectToXml(final XmlSerializable object) {
try {
final StringWriter writer = new StringWriter();
final JAXBContext context = JAXBContext.newInstance(object.getClass());
final Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.marshal(object, writer);
return writer.toString();
} catch (final JAXBException e) {
throw new InternalServerErrorException(e);
}
}
/**
* Converts an XML String into a serializable Java object.
*
* @param xml the string to convert into an object
* @param clazz the class of the serializable object
*
* @return the deserialized Java object
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static <T extends XmlSerializable> T xmlToObject(final String xml,
final Class<T> clazz) {
try {
ParametersChecker.checkSanityString(xml);
} catch (final InvalidArgumentException e) {
throw new InternalServerErrorException(e);
}
try {
final StringReader reader = new StringReader(xml);
final StreamSource source = new StreamSource(reader);
final JAXBContext context = JAXBContext.newInstance(clazz);
final Unmarshaller unmarshaller = context.createUnmarshaller();
return unmarshaller.unmarshal(source, clazz).getValue();
} catch (final JAXBException e) {
throw new InternalServerErrorException(e);
}
}
/**
* Saves an XML String to a file at the given location.
*
* @param xml the XML String
* @param filePath the path where to save the XML file
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static void saveXML(final String xml, final String filePath) {
try {
ParametersChecker.checkSanityString(xml);
} catch (final InvalidArgumentException e) {
throw new InternalServerErrorException(e);
}
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(filePath, false);
final String formattedXML = pretty(xml);
fileWriter.write(formattedXML);
fileWriter.flush();
} catch (final IOException e) {
throw new InternalServerErrorException(e);
} finally {
FileUtils.close(fileWriter);
}
}
/**
* Loads an XML file into a String.
*
* @param filePath the path of the XML file to load
*
* @return the content of the XML file
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static String loadXML(final String filePath) {
FileReader fr = null;
BufferedReader buff = null;
try {
fr = new FileReader(filePath);
buff = new BufferedReader(fr);
final StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = buff.readLine()) != null) {
stringBuilder.append(line.trim());
}
return stringBuilder.toString();
} catch (final FileNotFoundException e) {
throw new RestErrorException(RestErrors.FILE_NOT_FOUND(filePath));
} catch (final IOException e) {
throw new InternalServerErrorException(e);
} finally {
FileUtils.close(buff);
FileUtils.close(fr);
}
}
/**
* Saves a serializable Java object to an XML file at the given location.
*
* @param object the object to save as XML
* @param filePath the path where to save the XML file
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static void saveObject(final XmlSerializable object,
final String filePath) {
final String xml = objectToXml(object);
saveXML(xml, filePath);
}
/**
* Loads the given XML file into a corresponding serializable Java object.
*
* @param filePath path of the file to load
* @param clazz class of the target Java object
*
* @return the deserialized XML object
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
public static <T extends XmlSerializable> T loadObject(final String filePath,
final Class<T> clazz) {
final String xml = loadXML(filePath);
return xmlToObject(xml, clazz);
}
// ######################### PRIVATE METHODS #################################
/**
* Formats an unformatted XML String into a human readable one.
*
* @param input The unformatted XML String.
*
* @return The XML String in human readable format.
*
* @throws InternalServerErrorException if an unexpected error
* occurred
*/
private static String pretty(final String input) {
if (ParametersChecker.isEmpty(input)) {
throw new InternalServerErrorException("Input empty but should not");
}
try {
final Source xmlInput = new StreamSource(new StringReader(input));
final StringWriter stringWriter = new StringWriter();
final StreamResult xmlOutput = new StreamResult(stringWriter);
final TransformerFactory factory =//NOSONAR
TransformerFactory.newInstance();//NOSONAR
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setAttribute("indent-number", 2);
final Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(INDENT, "yes");
transformer.setOutputProperty(OMIT_XML_DECLARATION, "yes");
transformer.transform(xmlInput, xmlOutput);
return xmlOutput.getWriter().toString();
} catch (final TransformerConfigurationException e) {
throw new InternalServerErrorException(e);
} catch (final TransformerException e) {
throw new InternalServerErrorException(e);
}
}
}