Author: rwesten
Date: Tue Jul  3 08:46:18 2012
New Revision: 1356618

URL: http://svn.apache.org/viewvc?rev=1356618&view=rev
Log:
implementation of STANBOL-670

Modified:
    incubator/stanbol/trunk/integration-tests/pom.xml
    
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/EnhancerTestBase.java
    
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/MultiThreadedTest.java

Modified: incubator/stanbol/trunk/integration-tests/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/integration-tests/pom.xml?rev=1356618&r1=1356617&r2=1356618&view=diff
==============================================================================
--- incubator/stanbol/trunk/integration-tests/pom.xml (original)
+++ incubator/stanbol/trunk/integration-tests/pom.xml Tue Jul  3 08:46:18 2012
@@ -149,6 +149,11 @@
       <artifactId>org.apache.stanbol.entityhub.test</artifactId>
       <version>0.10.1-incubating-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.commons.indexedgraph</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
     <!-- Used to access constants such as Ontology URIs -->
     <dependency>
       <groupId>org.apache.stanbol</groupId>

Modified: 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/EnhancerTestBase.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/EnhancerTestBase.java?rev=1356618&r1=1356617&r2=1356618&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/EnhancerTestBase.java
 (original)
+++ 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/EnhancerTestBase.java
 Tue Jul  3 08:46:18 2012
@@ -41,7 +41,7 @@ public class EnhancerTestBase extends St
     boolean enginesReady;
     boolean timedOut;
 
-    protected final String endpoint;
+    protected String endpoint;
     /*  List of expected engines could be made configurable via system
      *  properties, but we don't expect it to change often. 
      */

Modified: 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/MultiThreadedTest.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/MultiThreadedTest.java?rev=1356618&r1=1356617&r2=1356618&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/MultiThreadedTest.java
 (original)
+++ 
incubator/stanbol/trunk/integration-tests/src/test/java/org/apache/stanbol/enhancer/it/MultiThreadedTest.java
 Tue Jul  3 08:46:18 2012
@@ -27,11 +27,17 @@ import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -40,8 +46,10 @@ import java.util.zip.GZIPInputStream;
 import javax.ws.rs.core.MediaType;
 
 import org.apache.clerezza.rdf.core.Literal;
+import org.apache.clerezza.rdf.core.NonLiteral;
 import org.apache.clerezza.rdf.core.Resource;
 import org.apache.clerezza.rdf.core.Triple;
+import org.apache.clerezza.rdf.core.TripleCollection;
 import org.apache.clerezza.rdf.core.UriRef;
 import org.apache.clerezza.rdf.core.impl.SimpleMGraph;
 import org.apache.clerezza.rdf.core.serializedform.Parser;
@@ -53,14 +61,21 @@ import org.apache.commons.compress.archi
 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
 import 
org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
 import org.apache.http.client.params.ClientPNames;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.CoreConnectionPNames;
 import org.apache.http.params.CoreProtocolPNames;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
 import org.apache.stanbol.commons.testing.http.Request;
 import org.apache.stanbol.commons.testing.http.RequestExecutor;
+import org.apache.stanbol.enhancer.servicesapi.helper.execution.Execution;
+import 
org.apache.stanbol.enhancer.servicesapi.helper.execution.ExecutionMetadata;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.apache.stanbol.entityhub.servicesapi.defaults.NamespaceEnum;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -71,17 +86,51 @@ import org.slf4j.LoggerFactory;
 /** Test that the default chain is called by requesting the "/enhancer" 
endpoint. */
 public class MultiThreadedTest extends EnhancerTestBase {
     
-    
+    /**
+     * The name of the Enhancement Chain this test runs against. If not defined
+     * the default chain is used.
+     */
+    public static final String PROPERTY_CHAIN = 
"stanbol.it.multithreadtest.chain";
+    /**
+     * The reference to the test data. Can be a File, a Resource available via 
the
+     * Classpath or an URL. This also supports compressed files. In case of ZIP
+     * only the first entry is processed.
+     */
     public static final String PROPERTY_TEST_DATA = 
"stanbol.it.multithreadtest.data";
+    /**
+     * Can be used to explicitly parse the Media-Type of the test data. If not 
set
+     * the Media-Type is parsed based on the file extension.
+     */
     public static final String PROPERTY_TEST_DATA_TYPE = 
"stanbol.it.multithreadtest.media-type";
+    /**
+     * The RDF property used to filter triples their values are used as texts 
for
+     * Enhancer requests. Only used of test data are provided as RDF<p>
+     * Note:<ul>
+     * <li> Only triples where their Object are Literals are used
+     * <li> the default property is "http://dbpedia.org/ontology/abstract";
+     * <li> if set to "*" than all triples with literal values are used.
+     * </ul>
+     */
     public static final String PROPERTY_TEST_DATA_PROPERTY = 
"stanbol.it.multithreadtest.data-property";
+    /**
+     * The maximum number of concurrent requests
+     */
     public static final String PROPERTY_THREADS = 
"stanbol.it.multithreadtest.threads";
+    /**
+     * The maximum number of requests. Can be used to limit the number of 
requests if
+     * the provided data do contain more samples.
+     */
     public static final String PROPERTY_REQUESTS = 
"stanbol.it.multithreadtest.requests";
+    /**
+     * The RDF serialisation used as Accept header for Stanbol Enhancer 
requests
+     */
+    public static final String PROPERTY_RDF_FORMAT = 
"stanbol.it.multithreadtest.rdf-format";
     
     private static final Logger log = 
LoggerFactory.getLogger(MultiThreadedTest.class);
     
     public final static int DEFAULT_NUM_THREADS = 5;
     public final static int DEFAULT_NUM_REQUESTS = 500;
+    public final static String DEFAULT_RDF_FORMAT = SupportedFormat.RDF_JSON;
     public final static String DEFAULT_TEST_DATA = 
"10k_long_abstracts_en.nt.bz2";
     public final static String DEFAULT_TEST_DATA_PROPERTY = 
"http://dbpedia.org/ontology/abstract";;
     
@@ -95,13 +144,21 @@ public class MultiThreadedTest extends E
     private PoolingClientConnectionManager connectionManager;
     
     public MultiThreadedTest(){
-        super(null,new String[]{});
+        this(System.getProperty(PROPERTY_CHAIN),new String[]{});
     }
-    protected MultiThreadedTest(String endpoint){
-        super(endpoint);
+    protected MultiThreadedTest(String chain){
+        this(chain,new String[]{});
     }
-    protected MultiThreadedTest(String endpoint,String...assertEngines){
-        super(endpoint,assertEngines);
+    protected MultiThreadedTest(String chain,String...assertEngines){
+        super(null,assertEngines);
+        if(chain != null && !chain.isEmpty()){
+            log.info("Testing with Enhancement Chain '{}'",chain);
+            this.endpoint = endpoint+"/chain/"+chain;
+        } else { //else no chain configured ... use default
+            log.info("Testing default Enhancement Chain");
+        }
+        //add the parameter for the execution metadata
+        this.endpoint = this.endpoint+"?executionmetadata=true";
     }
 
     @BeforeClass
@@ -144,27 +201,33 @@ public class MultiThreadedTest extends E
         }
         log.info("Start Multi Thread testing of max. {} requests using {} 
threads",
             maxRequests,numThreads);
-        ExcutionTracker tracker = new 
ExcutionTracker(Executors.newFixedThreadPool(numThreads));
+        ExcutionTracker tracker = new ExcutionTracker(
+            Executors.newFixedThreadPool(numThreads),
+            Math.max(100, numThreads*5));
+        String rdfFormat = 
System.getProperty(PROPERTY_RDF_FORMAT,DEFAULT_RDF_FORMAT);
         int testNum;
         for(testNum = 0;testDataIterator.hasNext() && testNum < maxRequests; 
testNum++){
             String test = testDataIterator.next();
             Request request = builder.buildPostRequest(getEndpoint())
-                    .withHeader("Accept","text/rdf+nt")
+                    .withHeader("Accept",rdfFormat)
                     .withContent(test);
             tracker.register(request);
             if(testNum%100 == 0){
                 log.info("  ... sent {} Requests ({} finished, {} pending, {} 
failed",
                     new Object[]{testNum,tracker.getNumCompleted(),
-                                 
tracker.getPending().size(),tracker.getFailed().size()});
+                                 
tracker.getNumPending(),tracker.getFailed().size()});
             }
         }
         log.info("> All {} requests sent!",testNum);
         log.info("  ... wait for all requests to complete");
-        while(!tracker.getPending().isEmpty()){
+        while(tracker.getNumPending() > 0){
             tracker.wait(3);
             log.info("  ... {} finished, {} pending, {} failed",
-                new 
Object[]{tracker.getNumCompleted(),tracker.getPending().size(),tracker.getFailed().size()});
+                new 
Object[]{tracker.getNumCompleted(),tracker.getNumPending(),tracker.getFailed().size()});
         }
+        log.info("Multi Thread testing of {} requests (failed: {}) using {} 
threads completed",
+            new 
Object[]{tracker.getNumCompleted(),tracker.getFailed().size(),numThreads});
+        tracker.printStatistics();
         Assert.assertTrue(tracker.getFailed()+"/"+numThreads+" failed", 
tracker.getFailed().isEmpty());
     }
     
@@ -175,9 +238,11 @@ public class MultiThreadedTest extends E
     
     private static void initTestData() throws IOException {
         String testData = System.getProperty(PROPERTY_TEST_DATA, 
DEFAULT_TEST_DATA);
+        log.info("Read Testdata from '{}'",testData);
         File testFile = new File(testData);
         InputStream is = null;
         if(testFile.isFile()){
+            log.info(" ... init from File");
             is = new FileInputStream(testFile);
         } 
         if(is == null) {
@@ -189,12 +254,16 @@ public class MultiThreadedTest extends E
         if(is == null){
             try {
               is = new URL(testData).openStream();
+              log.info(" ... init from URL");
             }catch (MalformedURLException e) {
                 //not a URL
             }
+        } else {
+            log.info(" ... init via Classpath");
         }
         Assert.assertNotNull("Unable to load the parsed TestData '"
-            +testData+"'!", is != null);
+            +testData+"'!", is);
+        log.info("  - InputStream: {}", is == null ? null: 
is.getClass().getSimpleName());
         
         String name = FilenameUtils.getName(testData);
         if ("gz".equalsIgnoreCase(FilenameUtils.getExtension(name))) {
@@ -211,8 +280,9 @@ public class MultiThreadedTest extends E
             log.info("For ZIP archives only the 1st Entry will be processed!");
             name = FilenameUtils.getName(entry.getName());
             log.info("  - processed Entry: {}",entry.getName());
-        } // else uncompressed data ...
-        
+        } else { // else uncompressed data ...
+            log.info("  - uncompressed source: {}",name);
+        }
         String mediaTypeString = System.getProperty(PROPERTY_TEST_DATA_TYPE);
         MediaType mediaType;
         if(mediaTypeString != null){
@@ -245,14 +315,25 @@ public class MultiThreadedTest extends E
             + name+"'. Please use the property '"+PROPERTY_TEST_DATA_TYPE
             + "' to manually parse the MediaType!", mediaType);
         
+        log.info("  - Media-Type: {}", mediaType);
         //now init the iterator for the test data
         testDataIterator = mediaType.isCompatible(MediaType.TEXT_PLAIN_TYPE) ?
             createTextDataIterator(is, mediaType) :
-            createRdfDataIterator(is,mediaType);
+            createRdfDataIterator(is, mediaType);
     }
     /**
-     * @param is
-     * @param mediaType
+     * Iterator implementation that parses an RDF graph from the parsed
+     * {@link InputStream}. The RDF data are loaded in-memory. Because of this
+     * only test data that fit in-memory can be used. <p>
+     * Literal values (objects) of the {@link #PROPERTY_TEST_DATA_PROPERTY} are
+     * used as data. If this property is not present {@link 
#DEFAULT_TEST_DATA_PROPERTY}
+     * is used. If {@link #PROPERTY_TEST_DATA_PROPERTY} is set to '*' than all
+     * Triples with Literal values are used.<p>
+     * This supports all RDF-formats supported by the {@link 
JenaParserProvider} and
+     * {@link RdfJsonParsingProvider}. The charset is expected to be UTF-8.
+     * @param is the input stream providing the RDF test data.
+     * @param mediaType the Media-Type of the stream. MUST BE supported by
+     * the Apache Clerezza RDF parsers.
      */
     private static Iterator<String> createRdfDataIterator(InputStream is, 
MediaType mediaType) {
         final SimpleMGraph graph = new SimpleMGraph();
@@ -276,8 +357,11 @@ public class MultiThreadedTest extends E
                     propertyString.trim();
                     if("*".equals(propertyString)){
                         property = null; //wildcard
+                        log.info("Iterate over values of all Triples");
                     } else {
+                        propertyString = 
NamespaceEnum.getFullName(propertyString);
                         property = new UriRef(propertyString);
+                        log.info("Iterate over values of property {}", 
propertyString);
                     }
                     it = graph.filter(null, property, null);
                 }
@@ -321,8 +405,14 @@ public class MultiThreadedTest extends E
         };
     }
     /**
-     * @param is
-     * @param mediaType
+     * Iterator reading Content elements from the input stream. Two (ore more)
+     * empty lines are used to separate multiple content items.<p>
+     * NOTE: This iterator does not keep the whole text in-memory. Therefore
+     * it can be possible used to process test data that would not fit
+     * in-memory.
+     * @param is The input stream to read the data from
+     * @param mediaType the Media-Type - only used to parse the charset from. 
If
+     * no charset is specified UTF-8 is uses as default.
      */
     private static Iterator<String> createTextDataIterator(InputStream is, 
MediaType mediaType) {
         String charsetString = mediaType.getParameters().get("charset");
@@ -337,12 +427,14 @@ public class MultiThreadedTest extends E
                 int emtptyLines = 0;
                 try {
                     while((line = reader.readLine()) != null && emtptyLines < 
2){
-                        data.append(line).append('\n');
                         if(line.isEmpty()){
-                            emtptyLines++;
+                            if(data.length() != 0){
+                                emtptyLines++;
+                            } //do not count empty lines at the beginning!
                         } else {
                             emtptyLines = 0;
                         }
+                        data.append(line).append('\n');
                     }
                 } catch (IOException e) {
                     log.warn("IOException while reading from Stream",e);
@@ -370,7 +462,6 @@ public class MultiThreadedTest extends E
                     next = null;
                     return elem;
                 }
-                
             }
 
             @Override
@@ -390,38 +481,78 @@ public class MultiThreadedTest extends E
 
     private class ExcutionTracker {
         
+        
+        
+        private int maxRegistered;
+
+        
         private int completed = 0;
-        private final Set<Request> registered = 
Collections.synchronizedSet(new HashSet<Request>());
-        private final List<Request> failed = Collections.synchronizedList(new 
ArrayList<Request>());
+        private final Set<Request> registered = new HashSet<Request>();
+        private final List<HttpResponse> failed = 
Collections.synchronizedList(new ArrayList<HttpResponse>());
+        
+        private ExecutionStatistics statistics = new ExecutionStatistics();
+        
         private ExecutorService executorService;
         
-        public ExcutionTracker(ExecutorService executorService) {
+        protected ExcutionTracker(ExecutorService executorService){
+            this(executorService,100);
+        }
+        public ExcutionTracker(ExecutorService executorService,int 
maxRegistered) {
             this.executorService = executorService;
+            this.maxRegistered = maxRegistered <= 0 ? Integer.MAX_VALUE : 
maxRegistered;
         }
         
         public void register(Request request){
-            registered.add(request);
-            executorService.execute(new AsyncExecuter(request, this));
+            synchronized (registered) {
+                while(registered.size() >= maxRegistered){
+                    try {
+                        registered.wait();
+                    } catch (InterruptedException e) {
+                        //interrupped
+                    }
+                }
+                registered.add(request);
+                executorService.execute(new AsyncExecuter(request, this));
+            }
         }
 
-        public void succeed(Request request) {
-            if(registered.remove(request)){
-                completed++;
+        void succeed(Request request, UriRef contentItemUri, TripleCollection 
results,Long rtt) {
+            ExecutionMetadata em = ExecutionMetadata.parseFrom(results, 
(UriRef)contentItemUri);
+            if(em != null){
+                synchronized (statistics) {
+                    statistics.addResult(em,rtt);
+                }
+            } //no executionData available ... unable to collect statistics
+            synchronized (registered) {
+                if(registered.remove(request)){
+                    completed++;
+                    registered.notifyAll();
+                }
             }
         }
 
-        public void failed(Request request) {
-            if(registered.remove(request)){
-                completed++;
+        void failed(Request request, RequestExecutor executor) {
+            synchronized (registered) {
+                failed.add(executor.getResponse());
+                if(registered.remove(request)){
+                    completed++;
+                    registered.notifyAll();
+                }
             }
-            failed.add(request);
         }
         
-        public Set<Request> getPending(){
-            return registered;
+        public int getNumPending(){
+            synchronized (registered) {
+                return registered.size();
+            }
         }
-        
-        public List<Request> getFailed(){
+        /**
+         * Live list of the failed requests. Non basic access MUST BE
+         * syncronized on the list while the requests are still pending as 
newly
+         * failed requests will modify this list
+         * @return
+         */
+        public List<HttpResponse> getFailed(){
             return failed;
         }
         public int getNumCompleted(){
@@ -433,7 +564,42 @@ public class MultiThreadedTest extends E
             } catch (InterruptedException e) {
             }
         }
-        
+        public void printStatistics(){
+            log.info("Statistics:");
+            synchronized (statistics) {
+                log.info("Chain:");
+                log.info("  Round Trip Time (Server + Transfer + Client):");
+                if(statistics.getNumRtt() < 1){
+                    log.info("    - not available");
+                } else {
+                    log.info("     max: {}ms | min: {}ms | avr: {}ms over {} 
requests",
+                        new Object[]{statistics.getMaxRtt(),
+                                     statistics.getMinRtt(),
+                                     statistics.getAverageRtt(),
+                                     statistics.getNumRtt()});
+                }
+                log.info("  processing time (server side)");
+                if(statistics.getNumSamples() < 1){
+                    log.info("    - not available. Make shure the used "
+                        + "EnhancementJobManager supports ExecutionMetadata!");
+                } else {
+                    log.info("     max: {}ms | min: {}ms | avr: {}ms over {} 
requests",
+                        new Object[]{statistics.getMaxDuration(),
+                                     statistics.getMinDuration(),
+                                     statistics.getAverageDuration(),
+                                     statistics.getNumSamples()});
+                    log.info("Enhancement Engines");
+                    for(String name :statistics.getEngineNames()){
+                        log.info("  {}: max: {}ms | min: {}ms | avr: {}ms over 
{} requests",
+                            new Object[]{name,
+                                         statistics.getMaxDuration(name),
+                                         statistics.getMinDuration(name),
+                                         statistics.getAverage(name),
+                                         statistics.getNumSamples(name)});
+                    }
+                }
+            }
+        }
     }
     private class AsyncExecuter implements Runnable{
 
@@ -445,16 +611,145 @@ public class MultiThreadedTest extends E
         }
         @Override
         public void run() {
+            RequestExecutor executor = new RequestExecutor(pooledHttpClient);
+            long start = System.currentTimeMillis();
+            Long rtt;
             try {
-                RequestExecutor executor = new 
RequestExecutor(pooledHttpClient);
                 executor.execute(request).assertStatus(200);
-                tracker.succeed(request);
+                rtt = System.currentTimeMillis()-start;
             } catch (Throwable e) {
                 log.warn("Error while sending Request ",e);
-                tracker.failed(request);
+                tracker.failed(request,executor);
+                rtt = null;
             }
+            IndexedMGraph graph = new IndexedMGraph();
+            try {
+                rdfParser.parse(graph,executor.getStream(), 
executor.getContentType().getMimeType());
+            }catch (Exception e) {
+                Assert.fail("Unable to parse RDF data from Response with 
Content-Type "
+                    + executor.getContentType().getMimeType()+" ( 
"+e.getClass().getSimpleName()
+                    + ": "+e.getMessage()+")");
+            }
+//            log.info("Content:\n{}",executor.getContent());
+//            
+//            log.info("Triples");
+//            for(Triple t : graph){
+//                log.info(t.toString());
+//            }
+            Iterator<Triple> ciIt = graph.filter(null, 
Properties.ENHANCER_EXTRACTED_FROM, null);
+            Assert.assertTrue("Enhancement Results do not caontain a single 
Enhancement",ciIt.hasNext());
+            Resource contentItemUri = ciIt.next().getObject();
+            Assert.assertTrue("ContentItem URI is not an UriRef but an 
instance of "
+                    + contentItemUri.getClass().getSimpleName(), 
contentItemUri instanceof UriRef);
+            tracker.succeed(request,(UriRef)contentItemUri,graph,rtt);
         }
-        
     }
 
+    private class ExecutionStatistics {
+        private int numSamples;
+        private long maxDuration = -1;
+        private long minDuration = Long.MAX_VALUE;
+        private long sumDuration = 0;
+        private int numRtt;
+        private long maxRtt = -1;
+        private long minRtt = Long.MAX_VALUE;
+        private long sumRtt = 0;
+        
+        private Map<String, long[]> engineStats = new TreeMap<String,long[]>();
+        
+        void addResult(ExecutionMetadata em,Long roundTripTime){
+            Long durationNumber = em.getChainExecution().getDuration();
+            long duration;
+            if(durationNumber != null){
+                duration = durationNumber.longValue();
+                if(duration > maxDuration){
+                    maxDuration = duration;
+                }
+                if(duration < minDuration){
+                    minDuration = duration;
+                }
+                sumDuration = sumDuration+duration;
+                numSamples++;
+            }
+            if(roundTripTime != null){
+                long rtt = roundTripTime;
+                if(rtt > maxRtt){
+                    maxRtt = rtt;
+                }
+                if(rtt < minRtt){
+                    minRtt = rtt;
+                }
+                sumRtt = sumRtt+rtt;
+                numRtt++;
+            }
+            for(Entry<String,Execution> ex : 
em.getEngineExecutions().entrySet()){
+                long[] stats = engineStats.get(ex.getKey());
+                if(stats == null){
+                    stats = new long[]{-1L,Long.MAX_VALUE,0L,0L};
+                    engineStats.put(ex.getKey(), stats);
+                }
+                durationNumber = ex.getValue().getDuration();
+                if(durationNumber != null){
+                    duration = durationNumber.longValue();
+                    if(duration > stats[0]){ //max duration
+                        stats[0] = duration;
+                    }
+                    if(duration < stats[1]){ //min duration
+                        stats[1] = duration;
+                    }
+                    stats[2] = stats[2]+duration; //sum duration
+                    stats[3]++; //num Samples
+                }
+            }
+        }
+        
+        
+        public Set<String> getEngineNames(){
+            return engineStats.keySet();
+        }
+        public Long getMaxDuration(){
+            return maxDuration < 0 ? null : maxDuration;
+        }
+        public Long getMinDuration(){
+            return minDuration == Long.MAX_VALUE ? null : minDuration;
+        }
+        public Long getAverageDuration(){
+            return sumDuration <= 0 && numSamples <= 0 ? null : 
Math.round((double)sumDuration/(double)numSamples);
+        }
+        public int getNumSamples(){
+            return numSamples;
+        }
+        public Long getMaxRtt(){
+            return maxRtt < 0 ? null : maxRtt;
+        }
+        public Long getMinRtt(){
+            return minRtt == Long.MAX_VALUE ? null : minRtt;
+        }
+        public Long getAverageRtt(){
+            return sumRtt <= 0 && numRtt <= 0 ? null : 
Math.round((double)sumRtt/(double)numRtt);
+        }
+        public int getNumRtt(){
+            return numRtt;
+        }
+        public Long getMaxDuration(String engine){
+            long[] stats = engineStats.get(engine);
+            return stats == null ? null : stats[0];
+        }
+        public Long getMinDuration(String engine){
+            long[] stats = engineStats.get(engine);
+            return stats == null ? null : stats[1];
+        }
+        public Long getAverage(String engine){
+            long[] stats = engineStats.get(engine);
+            return stats == null && stats[2] <= 0 && stats[3] <= 0 ? 
+                    null : Math.round((double)stats[2]/(double)stats[3]);
+        }
+        public int getNumSamples(String engine){
+            long[] stats = engineStats.get(engine);
+            return stats == null ? null : (int)stats[3];
+        }
+        
+    }
+    
+    
 }


Reply via email to