I convert an XML file to PDF, through XSL-LO.
This process involves a Transformer based on a XSLT file.
This is how I get the Transformer:
private void prepareTransformer(final TransformerFactory tFactory)
throws TransformerConfigurationException {
xmlTransformer = tFactory.newTransformer(
new StreamSource(getClass().getResourceAsStream(xsltPath)));
}
My problem is that if the XSLT is not well formed, then the above mentioned code would logs an exception but not throws it. This is the output in the console:
System-ID unbekannt; Zeilennummer328; Spaltennummer69; xsl:when ist an dieser Position in der Formatvorlage nicht zulässig!
System-ID unbekannt; Zeilennummer63; Spaltennummer54; org.xml.sax.SAXException: javax.xml.transform.TransformerException: ElemTemplateElement-Fehler: testcaseDetails
javax.xml.transform.TransformerException: ElemTemplateElement-Fehler: testcaseDetails
(Position des Fehlers unbekannt)org.apache.fop.fo.ValidationException: Dem Element "fo:simple-page-master" fehlt ein verlangtes Property "master-name"! (Keine Kontextinformationen verfügbar)
As no exception is thrown by the librairy, I can not so easily handle this situation.
Expected behaviour: #newTransformer(Source source) would throw an exception. Is there a way to achieve that?
EDIT: here the full class:
#Log
public class Report {
private final String xsltPath;
private FopFactory fopFactory;
private Transformer xmlTransformer;
private Report(final String xsltPath) {
this.xsltPath = xsltPath;
try {
prepareTransformer(createFobTransformer());
} catch (final SAXException | IOException | ConfigurationException |
TransformerConfigurationException ex) {
log.log(Level.SEVERE, null, ex);
throw new PdfReportException(ex);
}
}
public static Report getInstance() {
return new Report("/report/report-regd.xslt");
}
private void prepareTransformer(final TransformerFactory tFactory)
throws TransformerConfigurationException {
xmlTransformer = tFactory.newTransformer(
new StreamSource(getClass().getResourceAsStream(xsltPath)));
}
private TransformerFactory createFobTransformer()
throws SAXException, IOException, ConfigurationException {
final InputStream inFop = getClass().getResourceAsStream("/report/fop.xconf");
final var fopBuilder = new FopFactoryBuilder(new File(".").toURI(),
new ClasspathResolverURIAdapter());
final var cfgBuilder = new DefaultConfigurationBuilder();
final Configuration cfg = cfgBuilder.build(inFop);
fopBuilder.setConfiguration(cfg);
fopFactory = fopBuilder.build();
final TransformerFactory tFactory = TransformerFactory.newInstance();
tFactory.setURIResolver(new ClasspathURIResolver());
return tFactory;
}
public byte[] generatePDFReport(final String xml) {
final var out = new ByteArrayOutputStream();
final var agent = fopFactory.newFOUserAgent();
try {
final var fop = agent.newFop(MimeConstants.MIME_PDF, out);
final var result = new SAXResult(fop.getDefaultHandler());
final var src = new StreamSource(new StringReader(xml));
xmlTransformer.transform(src, result);
} catch (final FOPException | TransformerException e) {
throw new PdfReportException(e);
}
return out.toByteArray();
}
public static class PdfReportException extends RuntimeException {
private static final long serialVersionUID = 6009368304491510184L;
public PdfReportException(final Throwable cause) {
super(cause);
}
}
}
No exception is catched in Report constructor:
Related
I am attempting to write out a very large XML object, using the code below. I am processing 200K-350K objects/nodes, and the output-to-file is unbearably slow.
Any suggestions on how to improve the performance of the output implementation? I understand that the IndentingXMLStreamWriter may be one of the culprits, but I really need the output to be human readable (even if it is likely not going to be read due to size).
driver implementation...
public class SomeClient {
public static void main(String args[]) {
TransactionXmlWriter txw = new TransactionXmlWriter();
TransactionType tranType = getNextTransaction();
try {
txw.openXmlOutput("someFileName.xml");
while(tranType != null) {
txw.processObject(tranType);
tranType = getNextTransaction();
}
txw.closeXmlOutput();
} catch(JAXBException e) {
} catch(FileNotFoundException e) {
} catch(XMLStreamExceptoin e) {
}
}
}
implementation class...
public class TransactionXmlWriter {
private final QName root = new QName("ipTransactions");
private Marshaller marshaller = null;
private FileOutputStream fileOutputStream = null;
private XMLOutputFactory xmlOutputFactory = null;
private XMLStreamWriter xmlStreamWriter = null;
// constructor
public TransactionXmlWriter() throws JAXBException{
JAXBContext jaxbContext = JAXBContext.newInstance(TransactionType.class);
xmlOutputFactory = XMLOutputFactory.newFactory();
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
}
// write out "body" of XML
public void processObject(TransactionType transaction) {
JAXBElement<TransactionType> transactionJaxB = null;
try {
transactionJaxB = new JAXBElement<>(root, TransactionType.class, transaction);
marshaller.marshal(transactionJaxB, xmlStreamWriter);
} catch(JAXBException e) {
// TO DO : some kind of error handling
System.out.println(e.getMessage());
System.out.println(e.getStackTrace());
}
}
// open file to write XML into
public void openXmlOutput(String fileName) throws FileNotFoundException,
XMLStreamException {
fileOutputStream = new FileOutputStream(fileName);
xmlStreamWriter = new IndentingXMLStreamWriter(xmlOutputFactory.createXMLStreamWriter(fileOutputStream));
writeXmlHeader();
}
// write XML footer and close the stream/file
public void closeXmlOutput() throws XMLStreamException {
writeXmlFooter();
xmlStreamWriter.close();
}
private void writeXmlHeader() throws XMLStreamException {
xmlStreamWriter.writeStartDocument("UTF-8", "1.0");
xmlStreamWriter.writeStartElement("ipTransactions");
}
private void writeXmlFooter() throws XMLStreamException {
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeEndDocument();
}
}
I want to convert XMLStreamReader object to StreamSource object so that I can apply XSD validation. Can any one help me to convert this.
public XMLStreamReader getStreamReader(InputStream inputStream) {
try {
XMLInputFactory xif = XMLInputFactory.newInstance();
return new XmlStreamReaderDelegate(xif.createXMLStreamReader(inputStream, "UTF-8"));
} catch (XMLStreamException e) {
//logger.info(XmlValidatorSettings.class,e.getMessage(),e.getMessage());
throw new UnmarshallingException(e);
}
}
I need to call this method to validate the XML against XSD.
public List<SAXParseException> validateSrcXmlAgainstSchema(Source xmlFile, String schemaFilePath) throws SAXException, IOException {
List<SAXParseException> exceptions = new LinkedList<SAXParseException>();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File(schemaFilePath));
// logger.debug("Schema Initialised for Schema located in : " + schemaFilePath);
XmlValidationExceptionHandler validationErrorHandler = new XmlValidationExceptionHandler();
Validator validator = schema.newValidator();
validator.setErrorHandler(validationErrorHandler);
// logger.debug("START - validate xml against schema ");
validator.validate(xmlFile);
// logger.debug("END - validate xml against schema ");
exceptions = validationErrorHandler.getExceptionList();
return exceptions;
}
XmlStreamReaderDelegate class used to avoid XML case-sensitive issue.
public class XmlStreamReaderDelegate extends StreamReaderDelegate {
public XmlStreamReaderDelegate(XMLStreamReader xsr) {
super(xsr);
}
#Override
public String getAttributeLocalName(int index) {
return super.getAttributeLocalName(index).toLowerCase().intern();
}
#Override
public String getLocalName() {
return super.getLocalName().toLowerCase().intern();
}
}
I want to ignore external entities and external stylesheets (eg. <?xml-stylesheet type="text/xsl" href="......."?>).
I know I have to set XMLReader property to ignore external entities but I don't know how to ignore stylesheets...
import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.XMLReader;
//...
final XMLReader parser = new SAXParser();
// Ignore entities
parser.setProperty("http://xml.org/sax/features/external-general-entities", false);
// IS CORRECT???
parser.setProperty("http://xml.org/sax/features/external-general-entities", false);
There are more properties to set to avoid external entities and stylesheet?
How Can I understand if there are external entities o stylesheets?
Working for me:
public class SaxParser extends DefaultHandler
implements ContentHandler, DTDHandler, EntityResolver{
public transient static final String STYLE_SHEET_TAG = "xml-stylesheet";
public transient static final String EXTERNAL_ENTITY = "ExternalEntity";
public static void main(String[] args) {
new SaxParser().execute();
}
public void execute() {
String pathFileXml = "test/XML.xml";
final XMLReader parser = new SAXParser();
parser.setContentHandler(this);
parser.setDTDHandler(this);
parser.setEntityResolver(this);
try {
parser.parse(pathFileXml);
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
if (SaxParser.STYLE_SHEET_TAG.equals(e.getMessage())
|| SaxParser.EXTERNAL_ENTITY.equals(e.getMessage())) {
System.out.println("CATCH ERRORE");
}
e.printStackTrace();
}
System.out.println("OK");
}
#Override
public void processingInstruction(String target, String data)
throws SAXException {
System.out.println("Processing Instruction");
System.out.println("PI=> target: " + target + ", data: " + data);
if (STYLE_SHEET_TAG.equalsIgnoreCase(target.trim())) {
throw new SAXException(STYLE_SHEET_TAG);
}
return;
}
#Override
public InputSource resolveEntity(String publicId, String systemId)
throws IOException, SAXException {
System.out.println("publicId: " + publicId + ", systemId: " + systemId);
throw new SAXException(SaxParser.EXTERNAL_ENTITY);
}
}
The external stylesheet declaration is a standard processing instruction.
You can ignore processing instructions by not implementing the handler method:
void processingInstruction(java.lang.String target, java.lang.String data) {}
in your SAX handler.
I have a Java class which implements an interface, this class have a constructor that takes a String value and all the methods is that class are rely on that value in order to get work, so, what can i do if i want to deal with the interface directly and access the methods from it and as you know the interfaces can't have constructors so i can't assign that string value from it.
The Class:
public class XmlSource implements XmlInterface{
XmlConf xconf = new XmlConf();
URLProcess urlp = new URLProcess();
private URL url;
private String surl;
public XmlSource(String surl) throws MalformedURLException {
this.surl = surl;
result = urlp.validate(surl);
if(result == true){
configure();
}
}
public boolean configure() throws MalformedURLException {
url = new URL(surl);
xconf.setUrl(url);
xconf.setParameters(urlp.parameters);
xconf.setUrlPath(urlp.path);
xconf.setHostName(urlp.hostName);
return result;
}
public Document load() throws IOException, ParserConfigurationException,
SAXException {
// encoding the URL
InputStream is = url.openStream();
// loading the XML
domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
builder = domFactory.newDocumentBuilder();
doc = builder.parse(is);
return doc;
}
}
The Interface:
public interface XmlInterface {
public boolean configure() throws Exception;
public Document load() throws Exception;
}
You can assign a XmlSource object to XmlInterface type reference variable and then use that reference variable to call methods.
XmlInterface obj = new XmlSource(surl);
try
{
boolean configure = obj.configure();
Document document = obj.load();
}
catch(Exception e){
// perform exception handling
}
I'm trying to cut a xml into parts and then apply it some transformations. Currently I have this code:
public class XMLStax_xslt {
static boolean allowStream = false;
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("SourceExternalFile.xml");
XMLInputFactory xmlif = null;
xmlif = XMLInputFactory.newInstance();
Source xslt = new StreamSource(new File("myTransformFile.xslt"));
StreamFilter filter = new StreamFilter() {
#Override
public boolean accept(XMLStreamReader reader) {
int eventType = reader.getEventType();
if ( eventType == XMLEvent.START_ELEMENT )
{
String currentTag = reader.getLocalName();
if (currentTag.equals("wantedTag"))
{
allowStream = true;
}
}
if ( eventType == XMLEvent.END_ELEMENT )
{
String currentTag = reader.getLocalName();
if (currentTag.equals("wantedTag"))
{
allowStream = false;
}
}
return allowStream;
}
};
XMLStreamReader xmlR = xmlif.createFilteredReader(xmlif.createXMLStreamReader(fis),filter);
while (xmlR.hasNext())
{
TransformerFactory transformerXSLT = TransformerFactory.newInstance();
Transformer currentXslt = transformerXSLT.newTransformer(xslt);
currentXslt.transform(new StAXSource(xmlR), new StreamResult("targetFile.xml"));
}
fis.close();
}
}
Which works when the line return allowStream; is changed to return true;. So, what I need is send only the part I need to the transformation because sending the whole XML is not an option.
How can I achieve that?
Thanks.
The trouble was that I was passing the string to the transformer, instead of the whole node. Changing XMLStreamReader by XMLEventReader does the trick.
Here's the change:
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("SourceExternalFile.xml");
XMLInputFactory xmlif = null;
xmlif = XMLInputFactory.newInstance();
Source xslt = new StreamSource(new File("myTransformFile.xslt"));
XMLEventReader xmlR = xmlif.createXMLEventReader(xmlif.createXMLStreamReader(fis));
TransformerFactory transformerXSLT = TransformerFactory.newInstance();
Transformer currentXslt = transformerXSLT.newTransformer(xslt);
while (xmlR.hasNext())
{
XMLEvent xmlEvent = xmlR.nextEvent();
if ( xmlEvent.equals("wantedTag") )
{
currentXslt.transform(new StAXSource(xmlR), new StreamResult("targetFile.xml"));
}
}
xmlR.close();
fis.close();
}