Commit c8eea908 authored by R.W.Majeed's avatar R.W.Majeed

pass-through for ExportException and IOException during export() call

parent 8a82b7b7
package de.sekmi.histream.export;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.function.Consumer;
import de.sekmi.histream.ObservationException;
import de.sekmi.histream.export.config.ExportException;
/**
* Export error handler which unwraps {@link ExportException}s and {@link IOException}s
* and rethrows them in unchecked exceptions {@link UncheckedExportException} and
* {@link UncheckedIOException}.
* <p>
* All other ObservationExceptions are forwarded to the parent error handler.
* </p>
* @author R.W.Majeed
*
*/
class ExportErrorHandler implements Consumer<ObservationException>{
private Consumer<ObservationException> parentHandler;
public void setErrorHandler(Consumer<ObservationException> parentHandler){
this.parentHandler = parentHandler;
}
@Override
public void accept(ObservationException t) {
Throwable cause = t.getCause();
if( cause != null ){
// unwrap export exception
if( cause.getClass() == ExportException.class ){
throw new UncheckedExportException((ExportException)cause);
}else if( cause.getClass() == IOException.class ){
throw new UncheckedIOException((IOException)cause);
}
}
// pass to parent error handler
if( parentHandler != null ){
parentHandler.accept(t);
}else{
// or throw runtime exception if not specified
throw new RuntimeException(t);
}
}
}
package de.sekmi.histream.export;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.function.Consumer;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import de.sekmi.histream.ObservationException;
import de.sekmi.histream.ObservationSupplier;
import de.sekmi.histream.export.config.ExportDescriptor;
import de.sekmi.histream.export.config.ExportException;
import de.sekmi.histream.io.Streams;
import de.sekmi.histream.xml.NamespaceResolver;
public class TableExportFactory {
/**
* Primary class for exporting encounter/visit based
* tables. The data to export is configured via
* the {@link ExportDescriptor}.
*
* @author R.W.Majeed
*
*/
public class TableExport {
private ExportDescriptor desc;
private XPathFactory factory;
private NamespaceContext ns;
private ExportErrorHandler errorHandler;
public TableExportFactory(ExportDescriptor desc){
public TableExport(ExportDescriptor desc){
this.desc = desc;
factory = XPathFactory.newInstance();
ns = new NamespaceResolver();
errorHandler = new ExportErrorHandler();
}
/**
* Specify an error handler to handle ObservationException during
* the {@link #export(ObservationSupplier, ExportWriter)} operation.
* <p>
* If the ObservationException is caused by an ExportException or
* an IOException, the cause is unwrapped and rethrown during
* {@link #export(ObservationSupplier, ExportWriter)}.
* </p>
* @param handler error handler
*/
public void setErrorHandler(Consumer<ObservationException> handler){
errorHandler.setErrorHandler(handler);
}
private XPath createXPath(){
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(ns);
return xpath;
}
public void export(ObservationSupplier supplier, ExportWriter writer) throws ExportException{
/**
* Export all observations by the given supplier to the specified {@link ExportWriter}.
* <p>
* Errors which are not {@link ExportException} and {@link IOException} (these two
* are usually unrecoverable) that occur during observation processing can be
* handled via {@link #setErrorHandler(Consumer)}.
* </p>
* @param supplier observation supplier
* @param writer export writer
* @throws ExportException export exception
* @throws IOException IO exception
*/
public void export(ObservationSupplier supplier, ExportWriter writer) throws ExportException, IOException{
try( FragmentExporter e = new FragmentExporter(createXPath(), desc, writer) ){
e.setErrorHandler(new ExportErrorHandler());
Streams.transfer(supplier, e);
} catch (XMLStreamException | ParserConfigurationException e) {
throw new ExportException("Unable to create exporter", e);
} catch (UncheckedExportException e ){
// unwrap and rethrow
throw e.getCause();
} catch (UncheckedIOException e ){
// unwrap and rethrow
throw e.getCause();
}
}
}
package de.sekmi.histream.export;
import de.sekmi.histream.export.config.ExportException;
/**
* Unchecked export exception which is used locally (in this package)
* to pass export exception outside of stream operations.
*
* @author R.W.Majeed
*
*/
class UncheckedExportException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public UncheckedExportException(ExportException cause){
super(cause);
}
public ExportException getCause(){
return (ExportException)super.getCause();
}
}
package de.sekmi.histream.export;
import java.io.IOException;
import java.util.function.Supplier;
/**
* Export writer, which will cause IOExceptions at
* the specified table and operation.
*
* @author R.W.Majeed
*
*/
public class ExceptionCausingWriter implements ExportWriter{
private Supplier<IOException> exceptions;
private WhenToThrow when;
private WhereToThrow where;
protected enum WhenToThrow{
Open, WriteHeader, WriteRow, CloseTable
}
protected enum WhereToThrow{
PatientTable, VisitTable, EAVTable
}
public ExceptionCausingWriter(WhereToThrow where, WhenToThrow when) {
this.exceptions = () -> new IOException("Expected IO exception");
this.where = where;
this.when = when;
}
private void throwIf(WhereToThrow whereToThrow, WhenToThrow whenToThrow)throws IOException{
if( where == whereToThrow && when == whenToThrow ){
throw exceptions.get();
}
}
private class ExceptionThrowingTable implements TableWriter{
private WhereToThrow table;
public ExceptionThrowingTable(WhereToThrow table) {
this.table = table;
}
@Override
public void header(String[] headers) throws IOException {
throwIf(table, WhenToThrow.WriteHeader);
}
@Override
public void row(String[] columns) throws IOException {
throwIf(table, WhenToThrow.WriteRow);
}
@Override
public void close() throws IOException {
throwIf(table, WhenToThrow.CloseTable);
}
}
@Override
public TableWriter openPatientTable() throws IOException {
throwIf(WhereToThrow.PatientTable,WhenToThrow.Open);
return new ExceptionThrowingTable(WhereToThrow.PatientTable);
}
@Override
public TableWriter openVisitTable() throws IOException {
throwIf(WhereToThrow.VisitTable,WhenToThrow.Open);
return new ExceptionThrowingTable(WhereToThrow.VisitTable);
}
@Override
public TableWriter openEAVTable(String id) throws IOException {
throwIf(WhereToThrow.EAVTable,WhenToThrow.Open);
return new ExceptionThrowingTable(WhereToThrow.EAVTable);
}
}
package de.sekmi.histream.export;
import javax.xml.bind.JAXB;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.sekmi.histream.ObservationSupplier;
import de.sekmi.histream.export.ExceptionCausingWriter.WhenToThrow;
import de.sekmi.histream.export.ExceptionCausingWriter.WhereToThrow;
import de.sekmi.histream.export.config.ExportDescriptor;
import de.sekmi.histream.io.FileObservationProviderTest;
public class TestExport {
private ExportDescriptor descriptor;
private TableExport export;
@Before
public void initialize() throws Exception{
try( InputStream in = getClass().getResourceAsStream("/export1.xml") ){
descriptor = ExportDescriptor.parse(in);
}
export = new TableExport(descriptor);
}
@Test
public void verifyExport() throws Exception{
ExportDescriptor d = JAXB.unmarshal(getClass().getResourceAsStream("/export1.xml"), ExportDescriptor.class);
MemoryExportWriter m = new MemoryExportWriter();
TableExportFactory e = new TableExportFactory(d);
FileObservationProviderTest t = new FileObservationProviderTest();
t.initializeObservationFactory();
try( ObservationSupplier s = t.getExampleSupplier() ){
e.export(s, m);
export.export(s, m);
}
m.dump();
// TODO something wrong with namespaces in xpath/dom
}
/**
* IOExceptions occurring during stream operations should
* be unwrapped and directly passed through to the export
* call.
*
* @throws Exception unexpected failure
*/
@Test
public void expectIOExceptionPassThrough() throws Exception{
ExceptionCausingWriter w = new ExceptionCausingWriter(WhereToThrow.VisitTable, WhenToThrow.CloseTable);
FileObservationProviderTest t = new FileObservationProviderTest();
t.initializeObservationFactory();
try( ObservationSupplier s = t.getExampleSupplier() ){
export.export(s, w);
Assert.fail("IOException should have been thrown");
}catch( IOException e ){
// success
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment