jena-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a...@apache.org
Subject [1/6] jena git commit: ServerCtl ported from Fuseki2.
Date Wed, 19 Oct 2016 13:10:05 GMT
Repository: jena
Updated Branches:
  refs/heads/master e5d9db53e -> 60302eeee


ServerCtl ported from Fuseki2.

Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/015b871e
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/015b871e
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/015b871e

Branch: refs/heads/master
Commit: 015b871e80afb0348414d4bea9e29e396b31d05f
Parents: fd30b2f
Author: Andy Seaborne <andy@apache.org>
Authored: Tue Oct 18 22:02:08 2016 +0100
Committer: Andy Seaborne <andy@apache.org>
Committed: Tue Oct 18 22:09:52 2016 +0100

----------------------------------------------------------------------
 .../jena/fuseki/embedded/FusekiTestServer.java  | 241 +++++++++++++++++++
 1 file changed, 241 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/015b871e/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/FusekiTestServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/FusekiTestServer.java
b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/FusekiTestServer.java
new file mode 100644
index 0000000..351010e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/FusekiTestServer.java
@@ -0,0 +1,241 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.embedded;
+
+import static org.apache.jena.fuseki.embedded.FusekiTestServer.ServerScope.CLASS ;
+import static org.apache.jena.fuseki.embedded.FusekiTestServer.ServerScope.SUITE ;
+import static org.apache.jena.fuseki.embedded.FusekiTestServer.ServerScope.TEST ;
+
+import java.io.IOException ;
+import java.net.ServerSocket ;
+import java.util.concurrent.atomic.AtomicInteger ;
+
+import org.apache.http.client.HttpClient ;
+import org.apache.http.impl.client.CloseableHttpClient ;
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.riot.web.HttpOp ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.DatasetGraphFactory ;
+import org.apache.jena.sparql.modify.request.Target ;
+import org.apache.jena.sparql.modify.request.UpdateDrop ;
+import org.apache.jena.system.Txn ;
+import org.apache.jena.update.Update ;
+import org.apache.jena.update.UpdateExecutionFactory ;
+import org.apache.jena.update.UpdateProcessor ;
+
+// NOT FINISHED
+
+/**
+ * Manage a single server for use with tests. It supports three modes:
+ * <ul>
+ * <li>{@code ServerScope.SUITE} : One server for a whole test suite
+ * <li>{@code ServerScope.CLASS} : One server per test class
+ * <li>{@code ServerScope.TEST} :One server per individual test
+ * </ul>
+ * One server per individual test can be troublesome due to connections not closing down
+ * fast enough and can also be slow.
+ * <p> One server per test class is a good compromise. 
+ * <p> The data in the server is always reset between tests in all modes.
+ * <p>
+ * Using a connection pooling HttpClient (see {@link HttpOp#createPoolingHttpClient()}) is
important,
+ * both for test performance and for reducing the TCP connection load on the operating system.
 
+ * <p>
+ * Usage:
+ * </p>
+ * <p>
+ * In the test suite, put:
+ * 
+ * <pre>
+ *  {@literal @BeforeClass} static public void beforeSuiteClass() { ServerCtl.ctlBeforeTestSuite();
} 
+ *  {@literal @AfterClass}  static public void afterSuiteClass()  { ServerCtl.ctlAfterTestSuite();
}
+ * </pre>
+ * <p>
+ * In the test class, put:
+ * <pre>
+ * {@literal @BeforeClass} public static void ctlBeforeClass() { ServerCtl.ctlBeforeClass();
}
+ * {@literal @AfterClass}  public static void ctlAfterClass()  { ServerCtl.ctlAfterClass();
}
+ * {@literal @Before}      public void ctlBeforeTest()         { ServerCtl.ctlBeforeTest();
}
+ * {@literal @After}       public void ctlAfterTest()          { ServerCtl.ctlAfterTest();
}
+ * </pre>
+ */
+public class FusekiTestServer {
+    /* Cut&Paste versions:
+
+    Test suite (TS_*)
+    @BeforeClass static public void beforeSuiteClass() { ServerCtl.ctlBeforeTestSuite();
} 
+    @AfterClass  static public void afterSuiteClass()  { ServerCtl.ctlAfterTestSuite(); }
+
+    Test class (Test*)
+    @BeforeClass public static void ctlBeforeClass() { ServerCtl.ctlBeforeClass(); }
+    @AfterClass  public static void ctlAfterClass()  { ServerCtl.ctlAfterClass(); }
+    @Before      public void ctlBeforeTest()         { ServerCtl.ctlBeforeTest(); }
+    @After       public void ctlAfterTest()          { ServerCtl.ctlAfterTest(); }
+    */
+    
+    static HttpClient defaultHttpClient = HttpOp.getDefaultHttpClient();
+
+    // Note: it is important to cleanly close a PoolingHttpClient across server restarts
+    // otherwise the pooled connections remain for the old server. 
+    
+    /*package : for import static */ enum ServerScope { SUITE, CLASS, TEST }
+    private static ServerScope serverScope = ServerScope.CLASS ;
+    private static int currentPort = choosePort() ;
+    
+    public static int port() {
+        return currentPort ;
+    }
+
+    // Whether to use a transaction on the dataset or to use SPARQL Update. 
+    static boolean CLEAR_DSG_DIRECTLY = true ;
+    static private DatasetGraph dsgTesting ;
+    
+    // reference count of start/stop server
+    private static AtomicInteger countServer    = new AtomicInteger() ; 
+    private static FusekiEmbeddedServer server  = null ;
+    
+    public static final String urlRoot()        { return "http://localhost:"+port()+"/" ;
}
+    public static final String datasetPath()    { return "/ds_test" ; }
+    public static final String urlDataset()     { return "http://localhost:"+port()+datasetPath()
; }
+    
+    public static final String serviceUpdate()  { return "http://localhost:"+port()+datasetPath()+"/update"
; } 
+    public static final String serviceQuery()   { return "http://localhost:"+port()+datasetPath()+"/query"
; }
+    public static final String serviceGSP()     { return "http://localhost:"+port()+datasetPath()+"/data"
; }
+    
+    public static void ctlBeforeTestSuite() {
+        if ( serverScope == SUITE  ) {
+            setPoolingHttpClient() ;
+            allocServer();
+        }
+    }
+    
+    public static void ctlAfterTestSuite()  {
+        if ( serverScope == SUITE  ) {
+            freeServer();
+            resetDefaultHttpClient() ;
+        }
+    }
+    
+    /**
+     * Setup for the tests by allocating a Fuseki instance to work with
+     */
+    public static void ctlBeforeClass() {
+        if ( serverScope == CLASS  ) {
+            setPoolingHttpClient() ;
+            allocServer();
+        }
+    }
+    
+    /**
+     * Clean up after tests by de-allocating the Fuseki instance
+     */
+    public static void ctlAfterClass() {
+        if ( serverScope == CLASS  ) {
+            freeServer();
+            resetDefaultHttpClient() ;
+        }
+    }
+
+    /**
+     * Placeholder.
+     */
+    public static void ctlBeforeTest() {
+        if ( serverScope == TEST  ) {
+            setPoolingHttpClient() ;
+            allocServer();
+        }
+    }
+
+    /**
+     * Clean up after each test by resetting the Fuseki dataset
+     */
+    public static void ctlAfterTest() {
+        if ( serverScope == TEST  ) {
+            freeServer();
+            resetDefaultHttpClient() ;
+        } else
+            resetServer();
+    }
+
+    /** Set a PoolingHttpClient */
+    private static void setPoolingHttpClient() {
+        setHttpClient(HttpOp.createPoolingHttpClient()) ;
+    }
+
+    /** Restore the original setup */
+    private static void resetDefaultHttpClient() {
+        setHttpClient(defaultHttpClient);
+    }
+    
+    /** Set the HttpClient - close the old one if appropriate */
+    /*package*/ static void setHttpClient(HttpClient newHttpClient) {
+        HttpClient hc = HttpOp.getDefaultHttpClient() ;
+        if ( hc instanceof CloseableHttpClient )
+            IO.close((CloseableHttpClient)hc) ;
+        HttpOp.setDefaultHttpClient(newHttpClient) ;
+    }
+    
+    /*package*/ static void allocServer() {
+        if ( countServer.getAndIncrement() == 0 )
+            setupServer(true) ;
+    }
+    
+    /*package*/ static void freeServer() {
+        if ( countServer.decrementAndGet() == 0 )
+            teardownServer() ;
+    }
+    
+    /*package*/ static void setupServer(boolean updateable) {
+        dsgTesting = DatasetGraphFactory.createTxnMem() ;
+        server =
+            FusekiEmbeddedServer.create()
+            .setPort(port())
+            .setLoopback(true)
+            .add(datasetPath(), dsgTesting)
+            .build();
+    }
+    
+    /*package*/ static void teardownServer() {
+        if ( server != null )
+            server.stop() ;
+        server = null ;
+    }
+
+    /*package*/ static void resetServer() {
+        if (countServer.get() == 0)  
+            throw new RuntimeException("No server started!");
+        if ( CLEAR_DSG_DIRECTLY ) {
+            Txn.executeWrite(dsgTesting, ()->dsgTesting.clear()) ;   
+        } else {
+            Update clearRequest = new UpdateDrop(Target.ALL) ;
+            UpdateProcessor proc = UpdateExecutionFactory.createRemote(clearRequest, serviceUpdate())
;
+            try {proc.execute() ; }
+            catch (Throwable e) {e.printStackTrace(); throw e;}
+        }
+    }
+    
+    /** Choose an unused port for a server to listen on */
+    private static int choosePort() {
+        try (ServerSocket s = new ServerSocket(0)) {
+            return s.getLocalPort();
+        } catch (IOException ex) {
+            throw new FusekiException("Failed to find a port for tests!");
+        }
+    }
+}


Mime
View raw message