You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2021/10/11 14:05:07 UTC
[jena] branch main updated: JENA-2182: Fuseki Modules
This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/main by this push:
new 3e49b31 JENA-2182: Fuseki Modules
new bd1289b Merge pull request #1084 from afs/fuseki-modules
3e49b31 is described below
commit 3e49b3126f3fa5a0f7d9e6c4a2d5cc8a4ee6c9f8
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Thu Oct 7 13:07:06 2021 +0100
JENA-2182: Fuseki Modules
---
.../module/SubsystemRegistryServiceLoader.java | 1 -
.../main/java/org/apache/jena/sys/JenaSystem.java | 5 +-
.../org/apache/jena/fuseki/main/FusekiServer.java | 23 ++++--
.../apache/jena/fuseki/main/sys/FusekiModule.java | 75 +++++++++++++++++++
.../apache/jena/fuseki/main/sys/FusekiModules.java | 68 ++++++++++++++++++
.../apache/jena/fuseki/main/sys/FusekiMonitor.java | 58 +++++++++++++++
.../apache/jena/fuseki/main/sys/InitFuseki.java | 61 ++++++++++++++++
.../org.apache.jena.sys.JenaSubsystemLifecycle | 1 +
.../org/apache/jena/fuseki/main/ModuleForTest.java | 65 +++++++++++++++++
.../org/apache/jena/fuseki/main/TS_FusekiMain.java | 4 +-
.../apache/jena/fuseki/main/TestFusekiModules.java | 84 ++++++++++++++++++++++
.../org.apache.jena.fuseki.main.sys.FusekiModule | 1 +
12 files changed, 436 insertions(+), 10 deletions(-)
diff --git a/jena-base/src/main/java/org/apache/jena/base/module/SubsystemRegistryServiceLoader.java b/jena-base/src/main/java/org/apache/jena/base/module/SubsystemRegistryServiceLoader.java
index f1a0ea4..764a015 100644
--- a/jena-base/src/main/java/org/apache/jena/base/module/SubsystemRegistryServiceLoader.java
+++ b/jena-base/src/main/java/org/apache/jena/base/module/SubsystemRegistryServiceLoader.java
@@ -45,7 +45,6 @@ public class SubsystemRegistryServiceLoader<T extends SubsystemLifecycle> implem
ServiceLoader<T> sl =
// Use this->classloader form : better for OSGi
ServiceLoader.load(moduleClass, this.getClass().getClassLoader());
- // Must also be T
sl.forEach(this::add);
}
}
diff --git a/jena-core/src/main/java/org/apache/jena/sys/JenaSystem.java b/jena-core/src/main/java/org/apache/jena/sys/JenaSystem.java
index 86a754a..4b5cf0e 100644
--- a/jena-core/src/main/java/org/apache/jena/sys/JenaSystem.java
+++ b/jena-core/src/main/java/org/apache/jena/sys/JenaSystem.java
@@ -35,8 +35,9 @@ import org.apache.jena.base.module.SubsystemRegistryServiceLoader;
* <li>20 - RIOT
* <li>30 - ARQ
* <li>40 - TDB
- * <li>50-99 - Other Jena system modules.
- * <li>100-9998 - Application
+ * <li>50-100 - Other Jena system modules.
+ * <li>101 - Fuseki
+ * <li>102-9998 - Application
* <li>9999 - other
* </ul>
* See also the <a href="http://jena.apache.org/documentation/notes/system-initialization.html">notes on Jena initialization</a>.
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
index ba202cf..43222fb 100644
--- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
@@ -50,6 +50,7 @@ import org.apache.jena.fuseki.ctl.*;
import org.apache.jena.fuseki.jetty.FusekiErrorHandler;
import org.apache.jena.fuseki.jetty.JettyLib;
import org.apache.jena.fuseki.main.cmds.FusekiMain;
+import org.apache.jena.fuseki.main.sys.FusekiMonitor;
import org.apache.jena.fuseki.metrics.MetricsProviderRegistry;
import org.apache.jena.fuseki.server.*;
import org.apache.jena.fuseki.servlets.*;
@@ -273,7 +274,10 @@ public class FusekiServer {
* To synchronise with the server stopping, call {@link #join}.
*/
public FusekiServer start() {
- try { server.start(); }
+ try {
+ FusekiMonitor.serverStarting(this);
+ server.start();
+ }
catch (IOException ex) {
if ( ex.getCause() instanceof java.security.UnrecoverableKeyException )
// Unbundle for clearer message.
@@ -333,8 +337,10 @@ public class FusekiServer {
/** Stop the server. */
public void stop() {
Fuseki.serverLog.info("Stop Fuseki");
- try { server.stop(); }
- catch (Exception e) { throw new FusekiException(e); }
+ try {
+ server.stop();
+ FusekiMonitor.serverStopped(this);
+ } catch (Exception e) { throw new FusekiException(e); }
}
/** Wait for the server to exit. This call is blocking. */
@@ -372,7 +378,6 @@ public class FusekiServer {
// DataServices provided from the caller. These are immutable.
private Registry<String, DataService> providedDataServices = new Registry<>();
-
private final OperationRegistry operationRegistry;
// Default values.
private int serverHttpPort = PortUnset;
@@ -389,7 +394,7 @@ public class FusekiServer {
private boolean withTasks = false;
private String jettyServerConfig = null;
-
+ private Model configModel = null;
private Map<String, String> corsInitParams = null;
// Server wide authorization policy.
@@ -662,6 +667,7 @@ public class FusekiServer {
List<DataAccessPoint> x = FusekiConfig.processServerConfiguration(model, Fuseki.getContext());
// Can further modify the services in the configuration file.
x.forEach(dap->addDataAccessPoint(dap));
+ configModel = model;
return this;
}
@@ -1045,6 +1051,8 @@ public class FusekiServer {
DataAccessPointRegistry dapRegistry = buildStart();
+ FusekiMonitor.configuration(this, dapRegistry, configModel);
+
buildSecurity(dapRegistry);
try {
validate();
@@ -1070,7 +1078,10 @@ public class FusekiServer {
}
if ( networkLoopback )
applyLocalhost(server);
- return new FusekiServer(httpPort, httpsPort, server, staticContentDir, handler.getServletContext());
+
+ FusekiServer fusekiServer = new FusekiServer(httpPort, httpsPort, server, staticContentDir, handler.getServletContext());
+ FusekiMonitor.server(fusekiServer);
+ return fusekiServer;
} finally {
buildFinish();
}
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModule.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModule.java
new file mode 100644
index 0000000..842dc2a
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModule.java
@@ -0,0 +1,75 @@
+/*
+ * 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.main.sys;
+
+import java.util.UUID;
+
+import org.apache.jena.base.module.SubsystemLifecycle;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.rdf.model.Model;
+
+/**
+ * Module interface for Fuseki.
+ * <p>
+ * A module is additional code, usually in a separate jar, but can also be part of the application code.
+ */
+public interface FusekiModule extends SubsystemLifecycle {
+ /**
+ * Unique (within this server) name to identify this module.
+ * The default is to generate an UUID.
+ */
+ public default String name() { return UUID.randomUUID().toString(); }
+
+ /** Module loaded */
+ @Override
+ public default void start() {}
+
+ // ---- Build cycle
+
+ /**
+ * Call at the start of "build" step.
+ * The builder has been set according to the configuration.
+ * The "configModel" parameter is set if a configuration file was used else it is null.
+ */
+ public default void configuration(FusekiServer.Builder builder, DataAccessPointRegistry dapRegistry, Model configModel) {}
+
+ /** Built, not started, about to be returned to the builder caller. */
+ public default void server(FusekiServer server) { }
+
+ /** Server starting - called just before server.start happens. */
+ public default void serverStarting(FusekiServer server) { }
+
+ /** Server stopping.
+ * Do not rely on this to clear up external resources.
+ * Usually there is no stop phase and the JVM just exits or is killed externally.
+ *
+ */
+ public default void serverStopped(FusekiServer server) { }
+
+ /** Module unloaded */
+ @Override
+ public default void stop() {}
+
+ // Maybe later.
+// // ---- Execution.
+//
+// public default void action(HttpAction action) { }
+// public default void action(Endpoint endpoint) { }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
new file mode 100644
index 0000000..8b3a8e4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
@@ -0,0 +1,68 @@
+/*
+ * 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.main.sys;
+
+import java.util.function.BiConsumer;
+
+import org.apache.jena.atlas.lib.Registry;
+import org.apache.jena.base.module.Subsystem;
+
+/** Registry of modules */
+public class FusekiModules {
+
+ private static Registry<String, FusekiModule> registry = null;
+
+ private static Subsystem<FusekiModule> subsystem = null;
+
+ public static void load() {
+ if ( registry == null )
+ reload();
+ }
+
+ public static void reload() {
+ registry = new Registry<>();
+ subsystem = new Subsystem<FusekiModule>(FusekiModule.class);
+ subsystem.initialize();
+ subsystem.forEach(FusekiModules::add);
+ }
+
+ /** Add a code module */
+ public static void add(FusekiModule module) {
+ registry.put(module.name(), module);
+ }
+
+ /** Remove a code module */
+ public static void remove(FusekiModule module) {
+ registry.remove(module.name());
+ module.stop();
+ }
+
+ /** Test whether a code module is registered. */
+ public static boolean contains(FusekiModule module) {
+ return registry.isRegistered(module.name());
+ }
+
+ /*package*/ static void forEachModule(BiConsumer<String, FusekiModule> action) {
+ if ( registry == null )
+ load();
+ if ( registry == null || registry.isEmpty() )
+ return ;
+ registry.forEach(action);
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiMonitor.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiMonitor.java
new file mode 100644
index 0000000..62b7366
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiMonitor.java
@@ -0,0 +1,58 @@
+/*
+ * 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.main.sys;
+
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.rdf.model.Model;
+
+public class FusekiMonitor {
+
+ /**
+ * Call at the start of "build" step.
+ * The builder has been set according to the configuration.
+ * The "configModel" parameter is set if a configuration file was used else it is null.
+ */
+ public static void configuration(FusekiServer.Builder builder, DataAccessPointRegistry dapRegistry, Model configModel) {
+ FusekiModules.forEachModule((n,m) -> m.configuration(builder, dapRegistry, configModel));
+ }
+
+ /**
+ * The outcome of the "build" step.
+ */
+ public static void server(FusekiServer server) {
+ FusekiModules.forEachModule((n,m) -> m.server(server));
+ }
+
+ /**
+ * Called just before {@code server.start()} called.
+ */
+ public static void serverStarting(FusekiServer server) {
+ FusekiModules.forEachModule((n,m) -> m.serverStarting(server));
+ }
+
+ /**
+ * Called just after {@code server.stop()} has happened.
+ * Often, this is not called - the server, or application containing the server,
+ * simply exits the JVM or is killed externally.
+ */
+ public static void serverStopped(FusekiServer server) {
+ FusekiModules.forEachModule((n,m) -> m.serverStopped(server));
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
new file mode 100644
index 0000000..b8204ab
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
@@ -0,0 +1,61 @@
+/*
+ * 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.main.sys;
+
+import org.apache.jena.sys.JenaSubsystemLifecycle;
+import org.apache.jena.sys.JenaSystem;
+
+/**
+ * Jena initialization.
+ * Level 101 - called during Jena system initialization
+ * and after Jena itself has initialized.
+ */
+public class InitFuseki implements JenaSubsystemLifecycle {
+
+ private static volatile boolean initialized = false;
+
+ @Override
+ public void start() { init(); }
+
+ @Override
+ public void stop() {}
+
+ @Override
+ public int level() { return 101; }
+
+ /**
+ * Load modules, call "start()".
+ * Each {@link FusekiModule} will be called during the server build process.
+ */
+ public static void init() {
+ if ( initialized ) {
+ return;
+ }
+ synchronized (InitFuseki.class) {
+ if ( initialized ) {
+ JenaSystem.logLifecycle("Fuseki.init - skip");
+ return;
+ }
+ initialized = true;
+ JenaSystem.logLifecycle("Fuseki.init - start");
+ FusekiModules.load();
+ JenaSystem.logLifecycle("Fuseki.init - finish");
+ }
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle b/jena-fuseki2/jena-fuseki-main/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle
new file mode 100644
index 0000000..892450f
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle
@@ -0,0 +1 @@
+org.apache.jena.fuseki.main.sys.InitFuseki
diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/ModuleForTest.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/ModuleForTest.java
new file mode 100644
index 0000000..47d5ec9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/ModuleForTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.main;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.jena.fuseki.main.sys.FusekiModule;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.rdf.model.Model;
+
+public class ModuleForTest implements FusekiModule {
+
+ public static ModuleForTest module = null;
+
+ public AtomicInteger countStart = new AtomicInteger(0);
+ public AtomicInteger countConfiguration = new AtomicInteger(0);
+ public AtomicInteger countServer = new AtomicInteger(0);
+ public AtomicInteger countServerStarting = new AtomicInteger(0);
+
+ public ModuleForTest() {
+ module = this;
+ }
+
+ public void clearLifecycle() {
+ countConfiguration.set(0);
+ countServer.set(0);
+ countServerStarting.set(0);
+ }
+
+ @Override
+ public void start() {
+ countStart.getAndIncrement();
+ }
+
+ @Override
+ public void configuration(FusekiServer.Builder builder, DataAccessPointRegistry dapRegistry, Model configModel) {
+ countConfiguration.getAndIncrement();
+ }
+
+ // Built, not started, about to be returned to the builder caller
+ @Override public void server(FusekiServer server) {
+ countServer.getAndIncrement();
+ }
+
+ // Server starting
+ @Override public void serverStarting(FusekiServer server) {
+ countServerStarting.getAndIncrement();
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
index f4b8090..28efc20 100644
--- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
+++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
@@ -24,9 +24,11 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
- //17
TestPlainServer.class
+
+ // This tests modules and modifies the system state.
+ , TestFusekiModules.class
, TestEmbeddedFuseki.class
, TestMultipleEmbedded.class
, TestFusekiCustomOperation.class
diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiModules.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiModules.java
new file mode 100644
index 0000000..9938f61
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiModules.java
@@ -0,0 +1,84 @@
+/*
+ * 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.main;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.fuseki.main.sys.FusekiModules;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestFusekiModules {
+
+ // Module loading.
+ // file :: src/test/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiModule
+ // Module: ModuleForTest
+
+ @BeforeClass public static void beforeClass() {
+ FusekiModules.load();
+ }
+
+ @Before public void beforeTest() {
+ ModuleForTest.module.clearLifecycle();
+ }
+
+ @AfterClass public static void afterTest() {
+ FusekiModules.remove(ModuleForTest.module);
+ }
+
+ @Test public void modules_0() {
+ ModuleForTest module = findModule();
+ module.clearLifecycle();
+ assertEquals(1, module.countStart.get());
+ assertEquals(0, module.countConfiguration.get());
+ }
+
+ @Test public void lifecycle_1() {
+ ModuleForTest module = findModule();
+
+ FusekiServer.Builder builder = FusekiServer.create().port(0);
+
+ assertEquals(1, module.countStart.get());
+ assertEquals(0, module.countConfiguration.get());
+ assertEquals(0, module.countServer.get());
+ assertEquals(0, module.countServerStarting.get());
+
+ FusekiServer server = builder.build();
+
+ assertEquals(1, module.countStart.get());
+ assertEquals(1, module.countConfiguration.get());
+ assertEquals(1, module.countServer.get());
+ assertEquals(0, module.countServerStarting.get());
+
+ server.start();
+
+ assertEquals(1, module.countStart.get());
+ assertEquals(1, module.countConfiguration.get());
+ assertEquals(1, module.countServer.get());
+ assertEquals(1, module.countServerStarting.get());
+
+ server.stop();
+ }
+
+ private ModuleForTest findModule() {
+ return ModuleForTest.module;
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-main/src/test/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiModule b/jena-fuseki2/jena-fuseki-main/src/test/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiModule
new file mode 100644
index 0000000..d40dfe8
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/test/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiModule
@@ -0,0 +1 @@
+org.apache.jena.fuseki.main.ModuleForTest