JaxbUtils.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2026 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.xml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import org.openspcoop2.utils.resources.FileSystemUtilities;
/**
* Utility per la gestione di xml tramite Jaxb
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class JaxbUtils {
private JaxbUtils() {}
// Rimuove i caratteri non validi in XML 1.0 da un array di byte UTF-8.
// I caratteri validi in XML 1.0 sono: 0x09 (tab), 0x0A (LF), 0x0D (CR), 0x20-0xD7FF, 0xE000-0xFFFD, 0x10000-0x10FFFF
// In UTF-8 i caratteri 0x00-0x1F (esclusi 0x09, 0x0A, 0x0D) sono codificati come singolo byte,
// quindi รจ sicuro filtrarli direttamente a livello di byte.
private static byte[] sanitizeXml10(byte[] xml) {
ByteArrayOutputStream out = new ByteArrayOutputStream(xml.length);
for (byte b : xml) {
int unsigned = b & 0xFF;
if (unsigned < 0x20) {
// Mantieni solo tab (0x09), line feed (0x0A) e carriage return (0x0D)
if (unsigned == 0x09 || unsigned == 0x0A || unsigned == 0x0D) {
out.write(b);
}
// Altrimenti scarta il byte (caratteri di controllo invalidi in XML 1.0)
} else {
out.write(b);
}
}
return out.toByteArray();
}
private static InputStream sanitizeXml10(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] sanitized = sanitizeXml10(baos.toByteArray());
return new ByteArrayInputStream(sanitized);
}
private static Map<String, JAXBContext> mapJAXBContext = new ConcurrentHashMap<>();
private static synchronized void initJAXBContext(String packageName) throws JAXBException{
if(!JaxbUtils.mapJAXBContext.containsKey(packageName)){
JaxbUtils.mapJAXBContext.put(packageName, JAXBContext.newInstance(packageName) );
}
}
private static JAXBContext getJAXBContext(String packageName) throws JAXBException{
if(!JaxbUtils.mapJAXBContext.containsKey(packageName)){
JaxbUtils.initJAXBContext(packageName);
}
return JaxbUtils.mapJAXBContext.get(packageName);
}
/**
* Metodo che si occupa della generazione di un oggetto a partire da un file XML.
* Sono richiesti interattivamente i parametri che identificano il file XML.
*
* @param xmlFileName file XML
* @param classType Tipo di classe dell'oggetto da leggere
* @return L'oggetto letto dal file XML.
* @throws JAXBException
*/
public static Object xmlToObj(String xmlFileName,Class<?> classType)
throws JAXBException, IOException{
JAXBContext jc = JaxbUtils.getJAXBContext(classType.getPackage().getName());
Unmarshaller uctx = jc.createUnmarshaller();
FileInputStream fis = new FileInputStream(xmlFileName);
InputStream sanitizedIs = null;
Object objectRead = null;
try{
sanitizedIs = sanitizeXml10(fis);
objectRead = uctx.unmarshal(sanitizedIs);
}finally{
try{
fis.close();
}catch(Exception eis){
// close
}
try{
if(sanitizedIs!=null) {
sanitizedIs.close();
}
}catch(Exception eis){
// close
}
}
if(objectRead instanceof JAXBElement<?>){
return ((JAXBElement<?>)objectRead).getValue();
}
else{
return objectRead;
}
}
/**
* Metodo che si occupa della generazione di un file XML a partire da un oggetto RegistroServizi.
* Sono richiesti interattivamente i parametri che identificano il file XML da generare.
*
* @param xmlFileName Nome del file XML da utilizzare
* @param classType Tipo di classe dell'oggetto da scrivere
* @param object Oggetto da Scrivere
* @throws JAXBException
*
*/
public static void objToXml(String xmlFileName,Class<?> classType,Object object)
throws JAXBException{
JaxbUtils.objToXml(xmlFileName,classType,object,false);
}
public static void objToXml(String xmlFileName,Class<?> classType,Object object,boolean prettyDocument)
throws JAXBException{
// Se esiste gia' lo sovrascrive
File file = new File(xmlFileName);
if(file.exists()){
FileSystemUtilities.deleteFile(file);
}
JAXBContext jc = JaxbUtils.getJAXBContext(classType.getPackage().getName());
Marshaller uctx = jc.createMarshaller();
if(prettyDocument)
uctx.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
else
uctx.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
uctx.marshal(object, file);
}
/**
* Metodo che si occupa di generare un file a partire da un oggetto
*
* @param xmlFileName Nome del file XML da utilizzare
* @param object Oggetto da Scrivere
*
*/
public static void objToXml(String xmlFileName,byte[] object)
throws IOException{
// Se esiste gia' lo sovrascrive
File file = new File(xmlFileName);
if(file.exists()){
FileSystemUtilities.deleteFile(file);
}
FileOutputStream fileOut = new FileOutputStream(xmlFileName);
try{
fileOut.write(object);
fileOut.flush();
}finally{
try{
fileOut.close();
}catch(Exception eis){
// ignore
}
}
}
/**
* Metodo che si occupa della generazione di un oggetto a partire da un file XML.
* Sono richiesti interattivamente i parametri che identificano il file XML.
*
* @param classType Tipo di classe dell'oggetto da leggere
* @return L'oggetto letto dal file XML.
* @throws JAXBException
*/
public static Object xmlToObj(InputStream i,Class<?> classType)
throws JAXBException, IOException{
JAXBContext jc = JaxbUtils.getJAXBContext(classType.getPackage().getName());
Unmarshaller uctx = jc.createUnmarshaller();
InputStream sanitizedIs = null;
Object objectRead = null;
try{
sanitizedIs = sanitizeXml10(i);
objectRead = uctx.unmarshal(sanitizedIs);
}finally{
try{
i.close();
}catch(Exception eis){
// close
}
try{
if(sanitizedIs!=null) {
sanitizedIs.close();
}
}catch(Exception eis){
// close
}
}
if(objectRead instanceof JAXBElement<?>){
return ((JAXBElement<?>)objectRead).getValue();
}
else{
return objectRead;
}
}
/**
* Metodo che si occupa della generazione di un file XML a partire da un oggetto RegistroServizi.
* Sono richiesti interattivamente i parametri che identificano il file XML da generare.
*
* @param classType Tipo di classe dell'oggetto da scrivere
* @param object Oggetto da Scrivere
* @throws JAXBException
*
*/
public static void objToXml(OutputStream out,Class<?> classType,Object object)
throws JAXBException{
JaxbUtils.objToXml(out,classType,object,false);
}
public static void objToXml(OutputStream out,Class<?> classType,Object object,boolean prettyDocument)
throws JAXBException{
JaxbUtils.objToXml(out,classType,object,prettyDocument,false);
}
public static void objToXml(OutputStream out,Class<?> classType,Object object,boolean prettyDocument, boolean omitXmlDeclaration)
throws JAXBException{
JAXBContext jc = JaxbUtils.getJAXBContext(classType.getPackage().getName());
Marshaller uctx = jc.createMarshaller();
if(prettyDocument)
uctx.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
else
uctx.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
if(omitXmlDeclaration)
uctx.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
uctx.marshal(object, out);
}
}