Apache FOP - is there a way to embed font programmatically? - java

When creating a PDF using Apache FOP it is possible to embed a font with configuration file. The problem emerges when the app is a web application and it is necessary to embed a font that is inside WAR file (so treated as resource).
It is not acceptable to use particular container's folder structure to determine where exactly the war is located (when in configuration xml file we set tag to ./, it is set to the base folder of running container like C:\Tomcat\bin).
So the question is: Do anyone know the way to embed a font programatically?

After going through lots of FOP java code I managed to get it to work.
Descriptive version
Main idea is to force FOP to use custom PDFRendererConfigurator that will return desired font list when getCustomFontCollection() is executed.
In order to do it we need to create custom PDFDocumentHandlerMaker that will return custom PDFDocumentHandler (form method makeIFDocumentHandler()) which will in turn return our custom PDFRendererConfigurator (from getConfigurator() method) that, as above, will set out custom font list.
Then just add custom PDFDocumentHandlerMaker to RendererFactory and it will work.
FopFactory > RendererFactory > PDFDocumentHandlerMaker > PDFDocumentHandler > PDFRendererConfigurator
Full code
FopTest.java
public class FopTest {
public static void main(String[] args) throws Exception {
// the XSL FO file
StreamSource xsltFile = new StreamSource(
Thread.currentThread().getContextClassLoader().getResourceAsStream("template.xsl"));
// the XML file which provides the input
StreamSource xmlSource = new StreamSource(
Thread.currentThread().getContextClassLoader().getResourceAsStream("employees.xml"));
// create an instance of fop factory
FopFactory fopFactory = new FopFactoryBuilder(new File(".").toURI()).build();
RendererFactory rendererFactory = fopFactory.getRendererFactory();
rendererFactory.addDocumentHandlerMaker(new CustomPDFDocumentHandlerMaker());
// a user agent is needed for transformation
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// Setup output
OutputStream out;
out = new java.io.FileOutputStream("employee.pdf");
try {
// Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
// Setup XSLT
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltFile);
// Resulting SAX events (the generated FO) must be piped through to
// FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
// That's where the XML is first transformed to XSL-FO and then
// PDF is created
transformer.transform(xmlSource, res);
} finally {
out.close();
}
}
}
CustomPDFDocumentHandlerMaker.java
public class CustomPDFDocumentHandlerMaker extends PDFDocumentHandlerMaker {
#Override
public IFDocumentHandler makeIFDocumentHandler(IFContext ifContext) {
CustomPDFDocumentHandler handler = new CustomPDFDocumentHandler(ifContext);
FOUserAgent ua = ifContext.getUserAgent();
if (ua.isAccessibilityEnabled()) {
ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler());
}
return handler;
}
}
CustomPDFDocumentHandler.java
public class CustomPDFDocumentHandler extends PDFDocumentHandler {
public CustomPDFDocumentHandler(IFContext context) {
super(context);
}
#Override
public IFDocumentHandlerConfigurator getConfigurator() {
return new CustomPDFRendererConfigurator(getUserAgent(), new PDFRendererConfigParser());
}
}
CustomPDFRendererConfigurator.java
public class CustomPDFRendererConfigurator extends PDFRendererConfigurator {
public CustomPDFRendererConfigurator(FOUserAgent userAgent, RendererConfigParser rendererConfigParser) {
super(userAgent, rendererConfigParser);
}
#Override
protected FontCollection getCustomFontCollection(InternalResourceResolver resolver, String mimeType)
throws FOPException {
List<EmbedFontInfo> fontList = new ArrayList<EmbedFontInfo>();
try {
FontUris fontUris = new FontUris(Thread.currentThread().getContextClassLoader().getResource("UbuntuMono-Bold.ttf").toURI(), null);
List<FontTriplet> triplets = new ArrayList<FontTriplet>();
triplets.add(new FontTriplet("UbuntuMono", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, false, false, triplets, null, EncodingMode.AUTO, EmbeddingMode.AUTO);
fontList.add(fontInfo);
} catch (Exception e) {
e.printStackTrace();
}
return createCollectionFromFontList(resolver, fontList);
}
}

Yes you can do this. You need to set FOP's first base directory programmatically.
fopFactory = FopFactory.newInstance();
// for image base URL : images from Resource path of project
String serverPath = request.getSession().getServletContext().getRealPath("/");
fopFactory.setBaseURL(serverPath);
// for fonts base URL : .ttf from Resource path of project
fopFactory.getFontManager().setFontBaseURL(serverPath);
Then use FOB font config file.It will use above base path.
Just put your font files in web applications resource folder and refer that path in FOP's font config file.
After Comment : Reading font config programmatically (not preferred & clean way still as requested)
//This is NON tested and PSEUDO code to get understanding of logic
FontUris fontUris = new FontUris(new URI("<font.ttf relative path>"), null);
EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, "is kerning enabled boolean", "is aldvaned enabled boolean", null, "subFontName");
List<EmbedFontInfo> fontInfoList = new ArrayList<>();
fontInfoList.add(fontInfo);
//set base URL for Font Manager to use relative path of ttf file.
fopFactory.getFontManager().updateReferencedFonts(fontInfoList);
You can get more info for FOP's relative path https://xmlgraphics.apache.org/fop/2.2/configuration.html

The following approach may be useful for those who use PDFTranscoder.
Put the following xml template in the resources:
<?xml version="1.0" encoding="UTF-8"?>
<fop version="1.0">
<fonts>
<font kerning="no" embed-url="IBM_PLEX_MONO_PATH" embedding-mode="subset">
<font-triplet name="IBM Plex Mono" style="normal" weight="normal"/>
</font>
</fonts>
</fop>
Then one can load this xml and replace the line with font (IBM_PLEX_MONO_PATH) with the actual URI of the font from the resource bundle at runtime:
private val fopConfig = DefaultConfigurationBuilder()
.buildFromFile(javaClass.getResourceAsStream("/fonts/fopconf.xml")?.use {
val xml = BufferedReader(InputStreamReader(it)).use { bf ->
bf.readLines()
.joinToString("")
.replace(
"IBM_PLEX_MONO_PATH",
javaClass.getResource("/fonts/IBM_Plex_Mono/IBMPlexMono-Text.ttf")!!.toURI().toString()
)
}
val file = Files.createTempFile("fopconf", "xml")
file.writeText(xml)
file.toFile()
})
Now one can use this config with PDFTranscoder and your custom fonts will be probably rendered and embedded in PDF:
val pdfTranscoder = if (type == PDF) PDFTranscoder() else EPSTranscoder()
ContainerUtil.configure(pdfTranscoder, fopConfig)
val input = TranscoderInput(ByteArrayInputStream(svg.toByteArray()))
ByteArrayOutputStream().use { byteArrayOutputStream ->
val output = TranscoderOutput(byteArrayOutputStream)
pdfTranscoder.transcode(input, output)
byteArrayOutputStream.toByteArray()
}

Related

How to test a void method using JUnit and/or Mockito

Apologies in advance - I know this has been asked a thousand times but I've looked through so many articles/documentation and I'm just so f****** lost.
I have a class that takes in an XML file and then uses DocumentBuilder to parse it into a new file that will be used as a source for other classes to use.
I need to test my method (which is void). My project is completed but I need to test.
If anyone could be so kind to show me how this would be done, I can go ahead and follow that same logic with my other classes, as 90% of the methods in my project do not return anything.
Thanks...
public class XmlToCsv {
public static void xmlToCsv(String sourceXlsFile, String sourceCsvFile, String sourceXmlFile) throws Exception {
//define the files
File stylesheet = new File(sourceXlsFile);
File xmlSource = new File(sourceXmlFile);
//create the DocumentBuilder to parse the XML file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlSource);
//input the stylesheet to transform the XML to
StreamSource stylesource = new StreamSource(stylesheet);
Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
//write a new output file using the stylesheet format
Source source = new DOMSource(document);
Result outputTarget = new StreamResult(new File(sourceCsvFile));
transformer.transform(source, outputTarget);
}
}
What you trying to do is actually not the way to do it. You should test ONLY the XmlToCsv class and not the classes that are used by this class (DocumentBuilderFactory, DocumentBuilder, Document, StreamSource, Transformer, Source, Result).
There are now 2 ways you can go: The clean code way, or the dirty test way.
The best solution is that you have a dependency framework for the classes you use:
public class XmlToCsv {
#Inject
DocumentBuilderFactory factory;
#Inject
StreamSource stylesource;
#Inject
TransformerFactory transformerFactory;
public void xmlToCsv(String sourceXlsFile, String sourceCsvFile, String sourceXmlFile) throws Exception {
//define the files
File stylesheet = new File(sourceXlsFile);
File xmlSource = new File(sourceXmlFile);
//create the DocumentBuilder to parse the XML file
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlSource);
//input the stylesheet to transform the XML to
StreamSource stylesource = new StreamSource(stylesheet);
Transformer transformer = transformerFactory.newInstance().newTransformer(stylesource);
//write a new output file using the stylesheet format
Source source = new DOMSource(document);
Result outputTarget = new StreamResult(new File(sourceCsvFile));
transformer.transform(source, outputTarget);
}
}
Testing now can be done by injecting mocks into the injectable fields:
#RunWith(MockitoJUnitRunner.class)
public class XmlToCsvTest {
#Mock
DocumentBuilderFactory factory;
#Mock
StreamSource style source;
#Mock
TransformerFactory transformerFactory;
#InjectMocks
XmlToCsv sut; // System Under Test
#Test
public void testOk() throws Exception {
// Mocks
DocumentBuilder documentBuilder = Mockito.mock(DocumentBuilder.class);
Document document = Mockito.mock(Document.class);
// Now you control all objects created in the class and you can test if the right methods are called
// when-clauses
Mockito.when(factory.newDocumentBuilder).thenReturn(documentBuilder);
Mockito.when(documentBuilder.parse(any(File.class)).thenReturn(document);
// Add all when's here
// now call the class
sut.xmlToCsv("", "", "");
// now verify all calls
verify(factory, times(1)).newDocumentBuilder();
verify(documentBuilder, times(1)).parse(any(File.class));
// etc.
}
}
The dirty way is using PowerMockito. With PowerMockito you can override the new methods of existing classes. It is really a last resort and I wouldn't recommend it, but you can use it when you can't change the source code. It will look something like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({XmlToCsv.class, DocumentBuilderFactory.class})
public class XmlToCsvTest {
XmlToCsv sut;
#Test
public void testXmlToCsv() throws Exception {
DocumentBuilder documentBuilder = Mockito.mock(DocumentBuilder.class);
Document document = Mockito.mock(Document.class);
//when phase
PowerMockito.mockStatic(DocumentBuilderFactory.newInstance).thenReturn(documentBuilder);
Mockito.when(factory.newDocumentBuilder).thenReturn(documentBuilder);
Mockito.when(documentBuilder.parse(any(File.class)).thenReturn(document);
// now call the class
sut.xmlToCsv("", "", "");
//now verify
verify(documentBuilder, times(1)).parse(any(File.class));
}
}
As you see the examples aren't complete, but you get the difference.
It looks like the way you'd want to test this method is to validate the expected contents of the file written to the sourceCsvFile argument, which you could do by reading in the contents after your method is called. I don't think you need to do anything with Mockito - all of your arguments are String objects, and so there's no need to create any mocks.
To test a code generator, this is the best approach I found:
Prepare a set of test cases with the same XSL: For each one, an XML input file and an expected CSV output file. Put the input files into a directory, and the expected ones into another one, but set the same names for each pair of files (case1.xml and case1.csv).
Code a JUnit class with a private method which should do the test and the comparison, and then add one #Test method for each case you want to test:
import java.io.File;
import org.apache.commons.io.FileUtils;
public class XmlToCsvTest
{
private final File inputDir=new File("my_input_xml_files");
private final File expectedDir=new File("my_expected_csv_files");
private final File generatedDir=new File("my_generated_files"); // This is just a working dir
private void xmlToCsv(String xslFile, String inputFileName)
{
try
{
File inputXmlFile=new File(this.inputDir, inputFileName + ".xml");
File outputCsvFile=new File(this.generatedDir, inputFileName + ".csv");
File expectedCsvFile=new File(this.expectedDir, inputFileName + ".csv");
xmlToCsv(xslFile, outputCsvFile.getAbsolutePath(), inputXmlFile.getAbsolutePath());
FileUtils.contentEquals(expectedCsvFile, outputCsvFile);
}
catch (Exception e)
{
fail(e.toString());
}
}
#Test
public void xmlToCsvWithCase1()
{
xmlToCsv("myFirst.xsl", "case1");
}
#Test
public void xmlToCsvWithEmptyFile()
{
xmlToCsv("myFirst.xsl", "empty");
}
#Test
public void xmlToCsvWithOneRow()
{
xmlToCsv("myFirst.xsl", "one-row");
}
...
}
Once you have mastered this technique, you can add more complexity to your tests, by adding other XSLs, with its own testing cases.
Don't forget to add the set of files to your project as resources, to become part of the source control system.
Note: This approach assumes that each output file depends only on the contents of the input file. If the generator adds some independent content (as the current date, current user, or so) a previous pre-processing must be done.

Add CSS to JasperReports' HTML Export

I've created a report with JasperReports 6.4.3 which is normally exported to PDF. Now, I'm trying to export this report to HTML as well.
I don't want to create a HTML file via JasperExportManager, so I'm using JasperReport's HtmlExporter to wirte the report directly into an outputstream.
Her is my code:
public void exportToHtml(OutputStream outputStream, JRDataSource jrDataSource, Map<String, Object> parameter, File reportFile)
throws IOException {
try {
JasperPrint jasperprint = JasperFillManager.fillReport(reportFile.getAbsolutePath(), parameter, jrDataSource);
HtmlExporter exporter = new HtmlExporter();
SimpleHtmlExporterOutput exporterOutput = new SimpleHtmlExporterOutput(outputStream);
Map<String, String> images = Maps.newHashMap();
exporterOutput.setImageHandler(new HtmlResourceHandler() {
#Override
public void handleResource(String id, byte[] data) {
System.err.println("id" + id);
images.put(id, "data:image/jpg;base64," + Base64.encodeBytes(data));
}
#Override
public String getResourcePath(String id) {
return images.get(id);
}
});
exporter.setExporterOutput(exporterOutput);
exporter.setExporterInput(new SimpleExporterInput(jasperprint));
SimpleHtmlExporterConfiguration exporterConfiguration = new SimpleHtmlExporterConfiguration();
exporterConfiguration.setBetweenPagesHtml("<div style='page-break-after:always'></div>");
exporter.setConfiguration(exporterConfiguration);
exporter.exportReport();
} catch (JRException jrException) {
throw new IOException(jrException);
}
}
The output looks good, but I need to add some styles to the HTML output. So I wonder if it is possible to add a reference to a css file to the exported html report.
Similarly to how you are setting the between pages HTML, you could do the same for the header/footer with:
exporterConfiguration.setHtmlHeader("...");
exporterConfiguration.setHtmlFooter("...");
The default HTML code for header is set here and the one for footer is set here.
You need to match the opening/closing tags when modifying any of them.

Jxls: Transformer is null

I am using Jxls to write data into an excel file.
For that I have a xls template R.raw.object_collection_xmlbuilder_template_products_list which is in */res>raw folder and xml builder file R.xml.excel_template_products_list is in */res>xml folder .
try (InputStream is = mContext.getResources().openRawResource(R.raw.object_collection_xmlbuilder_template_products_list)) {
try (OutputStream os = new FileOutputStream(excel_file)) {
Transformer transformer = TransformerFactory.createTransformer(is, os);
try (InputStream configInputStream = mContext.getResources().openRawResource(+R.xml.excel_template_products_list)) {
AreaBuilder areaBuilder = new XmlAreaBuilder(configInputStream, transformer);
List<Area> xlsAreaList = areaBuilder.build();
Area xlsArea = xlsAreaList.get(0);
org.jxls.common.Context context = new org.jxls.common.Context();
context.putVar("products", hashmap_list);
xlsArea.applyAt(new CellRef("Result!A1"), context);
transformer.write();
}
}
} catch (Exception ex) {
aLog.error(ex);
}
But Transformer Object is returning as null.
And for line List<Area> xlsAreaList = areaBuilder.build();, it throws :
Method threw 'java.lang.NoClassDefFoundError'
exception.org.jxls.builder.xml.AreaAction
These are the jar files I had added:
'libs/jxls-2.4.0.jar'
'libs/jxls-poi-1.0.12.jar'
'libs/slf4j.api-1.6.1.jar'
One solution suggested that I add the maven dependency, like so:
maven {
url "https://repo1.maven.org/maven2/"
}
But I still get the exception.
So, any idea why could the transformer be null and how can I fix the exception ? Thank you.
Edit:
This is xml content:
<?xml version="1.0" encoding="utf-8"?>
<xls>
<area ref="Template!A1:T31">
<each items="products" var="products" ref="Template!A4:T31">
<area ref="Template!A4:T31"/>
</each>
</area>
</xls>
This is the xls template:
Check the jxls-demo project repo to see how the Jxls dependencies are configured for Maven project.

How can I convert a document to landscape mode using a Java library?

I am writing a program in Java (I am using Ubuntu). I am using Jodconverter to convert the document to PDF. I have to convert the document to landscape mode but I have read that Jodconverter doesn't support orientation changes. I also tried with OpenOffice API but I am facing the same issue.
Is there any Java library that does conversion to landscape?
From a similar question regarding using Jodconverter with an Open Office document:
http://groups.google.com/group/jodconverter/browse_thread/thread/dc96df64c7d60ada/c1692fee92513b7a
Short answer: you can't. The page orientation is a property of the
document (menu Format > Page in Calc), not a PDF export option. So it
should be set already in the XLS document.
Export to PDF and then use a PDF library like PDFbox to rotate the pages by 90 degrees.
Try PDPage.setRotation(int) on all pages (PDDocument.getDocumentCatalog().getAllPages()).
I have found the solution. I have converted document to landscape pdf using open office API for java. Here is the code for the same.
System.out.println("starting...");
String oooExeFolder = "/usr/lib/openoffice/program";
XComponentContext xContext = BootstrapSocketConnector.bootstrap(oooExeFolder);
XMultiComponentFactory xMCF = xContext.getServiceManager();
Object oDesktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
XComponentLoader xCLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, oDesktop);
System.out.println("loading ");
PropertyValue[] printerDesc = new PropertyValue[1];
printerDesc[0] = new PropertyValue();
printerDesc[0].Name = "PaperOrientation";
printerDesc[0].Value = PaperOrientation.LANDSCAPE;
// Create a document
XComponent document = xCLoader.loadComponentFromURL(loadUrl, "_blank", 0, printerDesc);
// Following property will convert doc into requested orientation.
XPrintable xPrintable = (XPrintable) UnoRuntime.queryInterface(XPrintable.class, document);
xPrintable.setPrinter(printerDesc);
PropertyValue[] conversionProperties = new PropertyValue[3];
conversionProperties[1] = new PropertyValue();
conversionProperties[1].Name = "FilterName";
conversionProperties[1].Value = "writer_pdf_Export";//
conversionProperties[0] = new PropertyValue();
conversionProperties[0].Name = "Overwrite ";
conversionProperties[0].Value = new Boolean(true);
System.out.println("closing");
XStorable xstorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, document);
xstorable.storeToURL(storeUrl, conversionProperties);
System.out.println("closing");
XCloseable xcloseable = (XCloseable) UnoRuntime.queryInterface(XCloseable.class, document);
xcloseable.close(false);
Try overriding OfficeDocumentConverter
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager) {
private Map<String, Object> createDefaultLoadProperties() {
Map<String, Object> loadProperties = new HashMap<String, Object>();
loadProperties.put("Hidden", true);
loadProperties.put("ReadOnly", true);
loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE);
return loadProperties;
}
#Override
public void convert(File inputFile, File outputFile, DocumentFormat outputFormat) throws OfficeException {
String inputExtension = FilenameUtils.getExtension(inputFile.getName());
DocumentFormat inputFormat = getFormatRegistry().getFormatByExtension(inputExtension);
inputFormat.setLoadProperties(Collections.singletonMap("PaperOrientation", PaperOrientation.LANDSCAPE));
StandardConversionTask conversionTask = new StandardConversionTask(inputFile, outputFile, outputFormat) {
#Override
protected void modifyDocument(XComponent document) throws OfficeException {
PropertyValue[] printerDesc = OfficeUtils.toUnoProperties(Collections.singletonMap("PaperOrientation", PaperOrientation.LANDSCAPE));
XPrintable xPrintable = cast(XPrintable.class, document);
try {
xPrintable.setPrinter(printerDesc);
} catch (com.sun.star.lang.IllegalArgumentException e) {
logger.error(e.getMessage());
}
super.modifyDocument(document);
}
};
conversionTask.setDefaultLoadProperties(createDefaultLoadProperties());
conversionTask.setInputFormat(inputFormat);
officeManager.execute(conversionTask);
}
};

how can i unmarshall in jaxb and enjoy the schema validation without using an explicit schema file

I am using jaxb for my application configurations
I feel like I am doing something really crooked and I am looking for a way to not need an actual file or this transaction.
As you can see in code I:
1.create a schema into a file from my JaxbContext (from my class annotation actually)
2.set this schema file in order to allow true validation when I unmarshal
JAXBContext context = JAXBContext.newInstance(clazz);
Schema mySchema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaFile);
jaxbContext.generateSchema(new MySchemaOutputResolver()); // ultimately creates schemaFile
Unmarshaller u = m_context.createUnmarshaller();
u.setSchema(mySchema);
u.unmarshal(...);
do any of you know how I can validate jaxb without needing to create a schema file that sits in my computer?
Do I need to create a schema for validation, it looks redundant when I get it by JaxbContect.generateSchema ?
How do you do this?
Regarding ekeren's solution above, it's not a good idea to use PipedOutputStream/PipedInputStream in a single thread, lest you overflow the buffer and cause a deadlock. ByteArrayOutputStream/ByteArrayInputStream works, but if your JAXB classes generate multiple schemas (in different namespaces) you need multiple StreamSources.
I ended up with this:
JAXBContext jc = JAXBContext.newInstance(Something.class);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i=0; i<outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
// to examine schema: System.out.append(new String(out.toByteArray()));
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()),"");
}
SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
m.setSchema(sf.newSchema(sources));
m.marshal(docs, new DefaultHandler()); // performs the schema validation
I had the exact issue and found a solution in the Apache Axis 2 source code:
protected List<DOMResult> generateJaxbSchemas(JAXBContext context) throws IOException {
final List<DOMResult> results = new ArrayList<DOMResult>();
context.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String ns, String file) throws IOException {
DOMResult result = new DOMResult();
result.setSystemId(file);
results.add(result);
return result;
}
});
return results;
}
and after you've acquired your list of DOMResults that represent the schemas, you will need to transform them into DOMSource objects before you can feed them into a schema generator. This second step might look something like this:
Unmarshaller u = myJAXBContext.createUnmarshaller();
List<DOMSource> dsList = new ArrayList<DOMSource>();
for(DOMResult domresult : myDomList){
dsList.add(new DOMSource(domresult.getNode()));
}
String schemaLang = "http://www.w3.org/2001/XMLSchema";
SchemaFactory sFactory = SchemaFactory.newInstance(schemaLang);
Schema schema = sFactory.newSchema((DOMSource[]) dsList.toArray(new DOMSource[0]));
u.setSchema(schema);
I believe you just need to set a ValidationEventHandler on your unmarshaller. Something like this:
public class JAXBValidator extends ValidationEventCollector {
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
// change RuntimeException to something more appropriate
throw new RuntimeException("XML Validation Exception: " +
event.getMessage() + " at row: " + locator.getLineNumber() +
" column: " + locator.getColumnNumber());
}
return true;
}
}
And in your code:
Unmarshaller u = m_context.createUnmarshaller();
u.setEventHandler(new JAXBValidator());
u.unmarshal(...);
If you use maven using jaxb2-maven-plugin can help you. It generates schemas in generate-resources phase.

Categories