XSDSchemaCollection.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 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.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.xml.validation.Schema;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.date.DateUtils;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.slf4j.Logger;
/**
* XSDSchemaCollection
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class XSDSchemaCollection {
private static boolean serializeXSDSchemiBuildSchemaSuccessDefault = false;
private static boolean serializeXSDSchemiBuildSchemaErrorDefault = true;
public static boolean isSerializeXSDSchemiBuildSchemaSuccessDefault() {
return serializeXSDSchemiBuildSchemaSuccessDefault;
}
public static void setSerializeXSDSchemiBuildSchemaSuccessDefault(boolean serializeXSDSchemiBuildSchemaSuccessDefault) {
XSDSchemaCollection.serializeXSDSchemiBuildSchemaSuccessDefault = serializeXSDSchemiBuildSchemaSuccessDefault;
}
public static boolean isSerializeXSDSchemiBuildSchemaErrorDefault() {
return serializeXSDSchemiBuildSchemaErrorDefault;
}
public static void setSerializeXSDSchemiBuildSchemaErrorDefault(boolean serializeXSDSchemiBuildSchemaErrorDefault) {
XSDSchemaCollection.serializeXSDSchemiBuildSchemaErrorDefault = serializeXSDSchemiBuildSchemaErrorDefault;
}
private byte[] schemaRoot;
private Map<String, byte[]> resources;
private Map<String, String> mappingNamespaceLocations;
private boolean serializeXSDSchemiBuildSchemaSuccess = false;
private boolean serializeXSDSchemiBuildSchemaError = true;
public XSDSchemaCollection() {
this.serializeXSDSchemiBuildSchemaSuccess = isSerializeXSDSchemiBuildSchemaSuccessDefault();
this.serializeXSDSchemiBuildSchemaError = isSerializeXSDSchemiBuildSchemaErrorDefault();
}
public boolean isSerializeXSDSchemiBuildSchemaSuccess() {
return this.serializeXSDSchemiBuildSchemaSuccess;
}
public void setSerializeXSDSchemiBuildSchemaSuccess(boolean serializeXSDSchemiBuildSchemaSuccess) {
this.serializeXSDSchemiBuildSchemaSuccess = serializeXSDSchemiBuildSchemaSuccess;
}
public boolean isSerializeXSDSchemiBuildSchemaError() {
return this.serializeXSDSchemiBuildSchemaError;
}
public void setSerializeXSDSchemiBuildSchemaError(boolean serializeXSDSchemiBuildSchemaErrror) {
this.serializeXSDSchemiBuildSchemaError = serializeXSDSchemiBuildSchemaErrror;
}
public byte[] getSchemaRoot() {
return this.schemaRoot;
}
public void setSchemaRoot(byte[] schemaRoot) {
this.schemaRoot = schemaRoot;
}
public Map<String, byte[]> getResources() {
return this.resources;
}
public void setResources(Map<String, byte[]> resources) {
this.resources = resources;
}
public Map<String, String> getMappingNamespaceLocations() {
return this.mappingNamespaceLocations;
}
public void setMappingNamespaceLocations(Map<String, String> mappingNamespaceLocations) {
this.mappingNamespaceLocations = mappingNamespaceLocations;
}
public void serialize(Logger log,File file) throws XMLException{
try (FileOutputStream fout = new FileOutputStream(file);){
serialize(log,fout);
fout.flush();
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
public void serialize(Logger log,String fileName) throws XMLException{
try (FileOutputStream fout = new FileOutputStream(fileName);){
serialize(log,fout);
fout.flush();
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
public byte[] serialize(Logger log) throws XMLException{
try{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
serialize(log,bout);
bout.flush();
bout.close();
return bout.toByteArray();
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
public void serialize(Logger log,OutputStream out) throws XMLException{
try (ZipOutputStream zipOut = new ZipOutputStream(out);){
this.zipSerialize(log,zipOut);
zipOut.flush();
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
public void zipSerialize(Logger log,ZipOutputStream zipOut) throws XMLException{
try{
String rootPackageDir = "";
// Il codice dopo fissa il problema di inserire una directory nel package.
// Commentare la riga sotto per ripristinare il vecchio comportamento.
rootPackageDir = "schemi"+File.separatorChar;
String nomeFile = "RootSchema.xsd";
zipOut.putNextEntry(new ZipEntry(rootPackageDir+nomeFile));
zipOut.write(this.schemaRoot);
if(this.resources!=null && this.resources.size()>0){
for (String name : this.resources.keySet()) {
nomeFile = name;
zipOut.putNextEntry(new ZipEntry(rootPackageDir+nomeFile));
zipOut.write(this.resources.get(name));
String namespaceFound = null;
String locationFound = null;
for (String namespace : this.mappingNamespaceLocations.keySet()) {
String location = this.mappingNamespaceLocations.get(namespace);
String [] split = location.split(" ");
if(split!=null){
for (int i = 0; i < split.length; i++) {
if(split[i]!=null && split[i].equals(nomeFile)){
namespaceFound = namespace;
locationFound = location;
break;
}
}
if(namespaceFound!=null){
break;}
}
}
if(namespaceFound!=null){
nomeFile = name+".namespace.txt";
zipOut.putNextEntry(new ZipEntry(rootPackageDir+nomeFile));
String valore = namespaceFound;
if(locationFound!=null){
valore = locationFound + "\n" + valore;
}
zipOut.write(valore.getBytes());
}
}
}
try{
this.buildSchemaEngine(log,false,false);
}catch(Throwable e){
log.error("Costruzione Struttura degli Schemi XSD fallita: "+e.getMessage(),e);
nomeFile = "BuildSchemaFailed.txt";
zipOut.putNextEntry(new ZipEntry(nomeFile));
String msg = e.getMessage();
if(msg==null || msg.equals("")){
if(e instanceof NullPointerException){
msg = "Internal Error (NP)";
}
else{
msg = e.toString();
if(msg==null || msg.equals("")){
msg = "Internal Error";
}
}
}
zipOut.write(msg.getBytes());
}
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
/**
* Costruisce un unico schema unendo tutti gli schemi importati
*
* @param logger logger
* @return Schema
* @throws XMLException
*/
public Schema buildSchema(Logger logger) throws XMLException {
return this.buildSchemaEngine(logger, this.serializeXSDSchemiBuildSchemaSuccess, this.serializeXSDSchemiBuildSchemaError);
}
private Schema buildSchemaEngine(Logger logger, boolean serializeXSDSchemiBuildSchemaSuccess, boolean serializeXSDSchemiBuildSchemaErrror) throws XMLException {
// Creo XSDResolver con le risorse localizzate e procedo con la validazione
XSDResourceResolver resourceResolver = new XSDResourceResolver(this.resources);
try{
// UndeclaredPrefix: Cannot resolve 'example:xxxxType' as a QName: the prefix 'example' is not declared.
// After some debugging, I've found out that this is a bug of the JAXP api's built in to the JDK.
// You can fix it by making sure that you use the Xerces version of the SchemaFactory, and not the JDK internal one.
// The algorithm for choosing a SchemaFactory is explained at http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/validation/SchemaFactory.html#newInstance(java.lang.String).
// It comes down to setting the System property "javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema" to the value "org.apache.xerces.jaxp.validation.XMLSchemaFactory".
// Note that just adding Xerces to your classpath won't fix this, for reasons explained at http://xerces.apache.org/xerces2-j/faq-general.html#faq-4
/**return new ValidatoreXSD(org.apache.xerces.jaxp.validation.XMLSchemaFactory.class.getName(),xsdResourceResolver,is);*/
ValidatoreXSD validatoreXSD = new ValidatoreXSD(logger,"org.apache.xerces.jaxp.validation.XMLSchemaFactory",resourceResolver,
new ByteArrayInputStream(this.schemaRoot));
/**ValidatoreXSD validatoreXSD = new ValidatoreXSD(this.logger,resourceResolver,new ByteArrayInputStream(schemaPerValidazione));*/
if(serializeXSDSchemiBuildSchemaSuccess){
debugPrintXSDSchemi(this.schemaRoot, resourceResolver, logger, true);
}
return validatoreXSD.getSchema();
}catch (Exception e) {
if(serializeXSDSchemiBuildSchemaErrror){
debugPrintXSDSchemi(this.schemaRoot, resourceResolver, logger, false);
}
throw new XMLException("Riscontrato errore durante l'inizializzazione dello schema: "+e.getMessage(),e);
}
}
private void debugPrintXSDSchemi(byte[]schemaPerValidazione,XSDResourceResolver resourceResolver,Logger logger, boolean success){
try{
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
File dir = Files.createTempDirectory("xsd_dir_", attr).toFile();
boolean dirCreate = dir.exists();
/**System.out.println("FILE?["+dir.getAbsolutePath()+"] ["+dirCreate+"] ["+dir.isDirectory()+"]");*/
dirCreate = dirCreate && dir.isDirectory();
/**System.out.println("DIR CREATE ["+dirCreate+"]");*/
// Provo a registrare lo schema principale
String uniqueID = XSDSchemaCollection.getIdForDebug();
File f = null;
if(dirCreate)
f = File.createTempFile("root_"+uniqueID+"_", ".xsd",dir);
else
f = FileSystemUtilities.createTempFile("root_"+uniqueID+"_", ".xsd");
FileSystemUtilities.writeFile(f, schemaPerValidazione);
// Provo a registrare gli schemi utilizzati
if(resourceResolver!=null){
XSDResourceResolver xsdResolver = resourceResolver;
for (String systemId : xsdResolver.getResources().keySet()) {
byte[] contenuto = xsdResolver.getResources().get(systemId);
File schemaTmpLog = null;
if(dirCreate)
schemaTmpLog = File.createTempFile("import_"+uniqueID+"_"+systemId+"_", ".xsd", dir);
else
schemaTmpLog = FileSystemUtilities.createTempFile("import_"+uniqueID+"_"+systemId+"_", ".xsd");
FileSystemUtilities.writeFile(schemaTmpLog, contenuto);
}
}
String motivo = null;
if(success){
motivo = "completata con successo";
}
else{
motivo = "completata con errore";
}
String msg = null;
if(dirCreate)
msg = "Inizializzazione dello schema "+motivo+", gli schemi sono stati registrati nella directory "+dir.getAbsolutePath();
else
msg = "Inizializzazione dello schema "+motivo+", gli schemi sono stati registrati nella area temporanea (root schema: "+f.getAbsolutePath()+")";
if(success){
logger.info(msg);
}
else{
logger.error(msg);
}
}catch(Exception eDebug){
logger.error("Registrazione xsd per debug non riuscita: "+eDebug.getMessage(),eDebug);
}
}
private static long counter = 0;
private static synchronized String getIdForDebug(){
SimpleDateFormat dateformat = DateUtils.getSimpleDateFormatMs();
XSDSchemaCollection.counter++;
return "ID_"+XSDSchemaCollection.counter+"_"+dateformat.format(DateManager.getDate());
}
}