From d4a7b0d540cc40bdc11f7715674f36c5165ae445 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Fri, 27 Mar 2009 14:19:00 +0000 Subject: [PATCH] Improved multi-threading testbed a bit: - Prints out a report with some statistics at the end. - Worker threads moved into their own thread group for easier profiling. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@759147 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/threading/FOPTestbed.java | 106 +++++++++++++++--- .../org/apache/fop/threading/FOProcessor.java | 6 +- .../apache/fop/threading/FOProcessorImpl.java | 45 +++++++- 3 files changed, 132 insertions(+), 25 deletions(-) diff --git a/test/java/org/apache/fop/threading/FOPTestbed.java b/test/java/org/apache/fop/threading/FOPTestbed.java index 7ced868c4..737317bec 100644 --- a/test/java/org/apache/fop/threading/FOPTestbed.java +++ b/test/java/org/apache/fop/threading/FOPTestbed.java @@ -20,9 +20,9 @@ package org.apache.fop.threading; import java.io.File; -import java.io.InputStream; import java.io.OutputStream; import java.text.DecimalFormat; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -41,6 +41,8 @@ import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.container.ContainerUtil; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.CountingOutputStream; +import org.apache.commons.io.output.NullOutputStream; /** * Testbed for multi-threading tests. The class can run a configurable set of task a number of @@ -55,13 +57,17 @@ public class FOPTestbed extends AbstractLogEnabled private File outputDir; private Configuration fopCfg; private FOProcessor foprocessor; + private boolean writeToDevNull; private int counter = 0; + private List results = Collections.synchronizedList(new java.util.LinkedList()); + /** {@inheritDoc} */ public void configure(Configuration configuration) throws ConfigurationException { this.threads = configuration.getChild("threads").getValueAsInteger(10); this.outputDir = new File(configuration.getChild("output-dir").getValue()); + this.writeToDevNull = configuration.getChild("devnull").getValueAsBoolean(false); Configuration tasks = configuration.getChild("tasks"); this.repeat = tasks.getAttributeAsInteger("repeat", 1); Configuration[] entries = tasks.getChildren("task"); @@ -85,11 +91,12 @@ public class FOPTestbed extends AbstractLogEnabled this.counter = 0; //Initialize threads + ThreadGroup workerGroup = new ThreadGroup("FOP workers"); List threadList = new java.util.LinkedList(); for (int ti = 0; ti < this.threads; ti++) { TaskRunner runner = new TaskRunner(); ContainerUtil.enableLogging(runner, getLogger()); - Thread thread = new Thread(runner); + Thread thread = new Thread(workerGroup, runner, "Worker- " + ti); threadList.add(thread); } @@ -112,7 +119,38 @@ public class FOPTestbed extends AbstractLogEnabled //ignore } } - getLogger().info("Stress test duration: " + (System.currentTimeMillis() - start) + "ms"); + long duration = System.currentTimeMillis() - start; + + report(duration); + } + + private void report(long duration) { + int count = this.results.size(); + int failures = 0; + long bytesWritten = 0; + System.out.println("Report on " + count + " tasks:"); + Iterator iter = this.results.iterator(); + while (iter.hasNext()) { + Result res = (Result)iter.next(); + if (res.failure != null) { + System.out.println("FAIL: " + (res.end - res.start) + " " + res.task); + System.out.println(" -> " + res.failure.getMessage()); + failures++; + } else { + System.out.println("good: " + (res.end - res.start) + " " + res.filesize + + " " + res.task); + bytesWritten += res.filesize; + } + } + System.out.println("Stress test duration: " + duration + "ms"); + if (failures > 0) { + System.out.println(failures + " failures of " + count + " documents!!!"); + } else { + float mb = 1024f * 1024f; + System.out.println("Bytes written: " + (bytesWritten / mb) + " MB, " + + (bytesWritten * 1000 / duration) + " bytes / sec"); + System.out.println("NO failures with " + count + " documents."); + } } private class TaskRunner extends AbstractLogEnabled implements Runnable { @@ -222,29 +260,61 @@ public class FOPTestbed extends AbstractLogEnabled public void execute() throws Exception { getLogger().info("Processing: " + def); - DecimalFormat df = new DecimalFormat("00000"); - File outfile = new File(outputDir, df.format(num) + fop.getTargetFileExtension()); - OutputStream out = new java.io.FileOutputStream(outfile); + long start = System.currentTimeMillis(); try { - InputStream in; - Templates templates; - - if (def.getFO() != null) { - in = new java.io.FileInputStream(new File(def.getFO())); - templates = null; + DecimalFormat df = new DecimalFormat("00000"); + File outfile = new File(outputDir, df.format(num) + fop.getTargetFileExtension()); + OutputStream out; + if (writeToDevNull) { + out = new NullOutputStream(); } else { - in = new java.io.FileInputStream(new File(def.getXML())); - templates = def.getTemplates(); + out = new java.io.FileOutputStream(outfile); + out = new java.io.BufferedOutputStream(out); } + CountingOutputStream cout = new CountingOutputStream(out); try { - fop.process(in, templates, out); + Source src; + Templates templates; + + if (def.getFO() != null) { + src = new StreamSource(new File(def.getFO())); + templates = null; + } else { + src = new StreamSource(new File(def.getXML())); + templates = def.getTemplates(); + } + fop.process(src, templates, cout); } finally { - IOUtils.closeQuietly(in); + IOUtils.closeQuietly(cout); } - } finally { - IOUtils.closeQuietly(out); + results.add(new Result(def, start, System.currentTimeMillis(), + cout.getByteCount())); + } catch (Exception e) { + results.add(new Result(def, start, System.currentTimeMillis(), e)); + throw e; } } } + private static class Result { + + private TaskDef task; + private long start; + private long end; + private long filesize; + private Throwable failure; + + public Result(TaskDef task, long start, long end, long filesize) { + this(task, start, end, null); + this.filesize = filesize; + } + + public Result(TaskDef task, long start, long end, Throwable failure) { + this.task = task; + this.start = start; + this.end = end; + this.failure = failure; + } + } + } \ No newline at end of file diff --git a/test/java/org/apache/fop/threading/FOProcessor.java b/test/java/org/apache/fop/threading/FOProcessor.java index 05c8f6fe1..dd663da05 100644 --- a/test/java/org/apache/fop/threading/FOProcessor.java +++ b/test/java/org/apache/fop/threading/FOProcessor.java @@ -19,9 +19,9 @@ package org.apache.fop.threading; -import java.io.InputStream; import java.io.OutputStream; +import javax.xml.transform.Source; import javax.xml.transform.Templates; /** @@ -31,12 +31,12 @@ public interface FOProcessor { /** * Process a file. - * @param in the InputStream for the FO or XML file + * @param src the Source for the FO or XML file * @param templates a JAXP Templates object for an XSLT transformation or null * @param out the OutputStream for the target file * @throws Exception if an error occurs */ - void process(InputStream in, Templates templates, OutputStream out) + void process(Source src, Templates templates, OutputStream out) throws Exception; /** diff --git a/test/java/org/apache/fop/threading/FOProcessorImpl.java b/test/java/org/apache/fop/threading/FOProcessorImpl.java index 4ba7da658..2b580bbd0 100644 --- a/test/java/org/apache/fop/threading/FOProcessorImpl.java +++ b/test/java/org/apache/fop/threading/FOProcessorImpl.java @@ -19,8 +19,9 @@ package org.apache.fop.threading; -import java.io.InputStream; import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; import javax.xml.transform.Result; import javax.xml.transform.Source; @@ -29,19 +30,23 @@ import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.stream.StreamSource; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.commons.io.FilenameUtils; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.MimeConstants; +import org.apache.fop.events.Event; +import org.apache.fop.events.EventFormatter; +import org.apache.fop.events.EventListener; +import org.apache.fop.events.model.EventSeverity; /** * Default implementation of the FOProcessor interface using FOP. @@ -71,9 +76,17 @@ public class FOProcessorImpl extends AbstractLogEnabled } /** {@inheritDoc} */ - public void process(InputStream in, Templates templates, OutputStream out) + public void process(Source src, Templates templates, OutputStream out) throws org.apache.fop.apps.FOPException, java.io.IOException { FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); + foUserAgent.setBaseURL(src.getSystemId()); + try { + URL url = new URL(src.getSystemId()); + String filename = FilenameUtils.getName(url.getPath()); + foUserAgent.getEventBroadcaster().addEventListener(new AvalonAdapter(filename)); + } catch (MalformedURLException mfue) { + throw new RuntimeException(mfue); + } Fop fop = fopFactory.newFop(this.mime, foUserAgent, out); try { @@ -83,7 +96,6 @@ public class FOProcessorImpl extends AbstractLogEnabled } else { transformer = templates.newTransformer(); } - Source src = new StreamSource(in); Result res = new SAXResult(fop.getDefaultHandler()); transformer.transform(src, res); } catch (TransformerException e) { @@ -96,4 +108,29 @@ public class FOProcessorImpl extends AbstractLogEnabled return this.fileExtension; } + private class AvalonAdapter implements EventListener { + + private String filename; + + public AvalonAdapter(String filename) { + this.filename = filename; + } + + public void processEvent(Event event) { + String msg = EventFormatter.format(event); + EventSeverity severity = event.getSeverity(); + if (severity == EventSeverity.INFO) { + //getLogger().info(filename + ": " + msg); + } else if (severity == EventSeverity.WARN) { + //getLogger().warn(filename + ": " + msg); + } else if (severity == EventSeverity.ERROR) { + getLogger().error(filename + ": " + msg); + } else if (severity == EventSeverity.FATAL) { + getLogger().fatalError(filename + ": " + msg); + } else { + assert false; + } + } + + } } \ No newline at end of file -- 2.39.5