org.apache.jena.base.module.Subsystem Maven / Gradle / Ivy
Show all versions of jena-base Show documentation
/**
* 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.base.module;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.List ;
import java.util.function.Consumer ;
/** Subsystem loader "system" - simple controls for ensuring components are loaded and initialized.
* This typically uses {@link java.util.ServiceLoader} and it adds the concept of a "level"
* so that loaded plugins are executed in a controlled order.
* Levels are executed in order, from 0 upwards.
* Within a level, plugins are executed in arbitrary order.
*
* There should only be one "Subsystem" per "Class<T>".
*/
public class Subsystem {
// Also serves as the init lock.
private Class interfaceClass;
private boolean DEBUG_INIT = false ;
public Subsystem(Class cls) {
this.interfaceClass = cls;
this.initLock = cls;
}
/**
* Development support - flag to enable output during
* initialization. Output to {@code System.err}, not a logger
* to avoid the risk of recursive initialization.
*/
public void debug(boolean value) { DEBUG_INIT = value; }
// A correct way to manage without synchronized using the double checked locking pattern.
// http://en.wikipedia.org/wiki/Double-checked_locking
// http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
private volatile boolean initialized = false ;
private Object initLock;
/** Initialize
*
* This function is cheap to call when already initialized so can be called to be sure.
* A commonly used idiom in jena is a static initializer in key classes.
*
* By default, initialization happens by using {@code ServiceLoader.load} to find
* {@link SubsystemLifecycle} objects.
* See {@link #setSubsystemRegistry} to intercept that choice.
*/
public void initialize() {
// Any other thread attempting to initialize as well will
// first test the volatile outside the lock; if it's
// not INITIALIZED, the thread will attempt to grab the lock
// and hence wait, then see initialized as true.
// But we need to cope with recursive calls of JenaSystem.init() as well.
// The same thread will not stop at the lock.
// Setting initialized to true before a recursive call is possible
// handles this. The recursive call will see initialized true and
// and return on the first test.
// Net effect:
// After a top level call of init() returns, jena has
// finishes initialization.
// Recursive calls do not have this property.
if ( initialized )
return ;
synchronized(initLock) {
if ( initialized ) {
logLifecycle("Subsystem.init - return");
return ;
}
// Catches recursive calls, same thread.
initialized = true ;
logLifecycle("Subsystem.init - start");
if ( get() == null ) {
SubsystemRegistryServiceLoader x = new SubsystemRegistryServiceLoader<>(interfaceClass);
setSubsystemRegistry(x) ;
}
get().load() ;
// // Debug : what did we find?
// if ( DEBUG_INIT ) {
// logLifecycle("Found:") ;
// get().snapshot().forEach(mod->
// logLifecycle(" %-20s [%d]", mod.getClass().getSimpleName(), mod.level())) ;
// }
if ( DEBUG_INIT ) {
logLifecycle("Initialization sequence:") ;
forEach( module ->
logLifecycle(" %-20s [%d]", module.getClass().getSimpleName(), module.level()) ) ;
}
forEach( module -> {
logLifecycle("Init: %s", module.getClass().getSimpleName());
module.start() ;
}) ;
logLifecycle("Subsystem.init - finish");
}
}
/** Shutdown subsystems */
public void shutdown() {
if ( ! initialized ) {
logLifecycle("JenaSystem.shutdown - not initialized");
return ;
}
synchronized(initLock) {
if ( ! initialized ) {
logLifecycle("JenaSystem.shutdown - return");
return ;
}
logLifecycle("JenaSystem.shutdown - start");
forEachReverse(module -> {
logLifecycle("Stop: %s", module.getClass().getSimpleName());
module.stop() ;
}) ;
initialized = false ;
logLifecycle("JenaSystem.shutdown - finish");
}
}
private SubsystemRegistry singleton = null;
/**
* Set the {@link SubsystemRegistry}.
* To have any effect, this function
* must be called before any other Jena code,
* and especially before calling {@code JenaSystem.init()}.
*/
public void setSubsystemRegistry(SubsystemRegistry thing) {
singleton = thing;
}
/** The SubsystemRegistry */
protected SubsystemRegistry get() {
return singleton;
}
/**
* Call an action on each item in the registry. Calls are made sequentially
* and in increasing level order. The exact order within a level is not
* specified; it is not registration order.
*
* @param action
*/
public void forEach(Consumer action) {
forEach(action, comparator);
}
/**
* Call an action on each item in the registry but in the reverse
* enumeration order. Calls are made sequentially and in decreasing level
* order. The "reverse" is opposite order to {@link #forEach}, which may not
* be stable within a level. It is not related to registration order.
*
* @param action
*/
public void forEachReverse(Consumer action) {
forEach(action, reverseComparator);
}
// Order by level (increasing)
private static Comparator comparator = (obj1, obj2) -> Integer.compare(obj1.level(), obj2.level()) ;
// Order by level (decreasing)
private static Comparator reverseComparator = comparator.reversed();
private synchronized void forEach(Consumer action, Comparator ordering) {
synchronized(initLock) {
List x = get().snapshot() ;
Collections.sort(x, ordering);
x.forEach(action);
}
}
/** Output a debugging message if DEBUG_INIT is set */
public void logLifecycle(String fmt, Object ...args) {
if ( ! DEBUG_INIT )
return ;
System.err.printf(fmt, args) ;
System.err.println() ;
}
}