/** * 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.system; import java.util.Collections ; import java.util.Comparator ; import java.util.List ; import java.util.function.Consumer ; import org.slf4j.Logger ; import org.slf4j.LoggerFactory ; /** Jena "system" - simple controls for ensuring components are loaded and initialized. * <p> * All initialization should be concurrent and thread-safe. In particular, * some subsystems need initialization in some sort of order (e.g. ARQ before TDB). * <p> * This is achieved by "levels": levels less than 100 are considered "jena system levels" * and are reserved. * <ul> * <li>0 - reserved * <li>10 - jena-core * <li>20 - RIOT * <li>30 - ARQ * <li>40 - TDB * <li>9999 - other * </ul> * See also the <a href="http://jena.apache.org/documentation/notes/system-initialization.html">notes on Jena initialization</a>. */ public class JenaSystem { /** Development support - flag to enable output during * initialization. Output to {@code System.err}, not a logger * to avoid the risk of recursive initialization. */ public static boolean DEBUG_INIT = false ; // A correct way to manage without synchonized 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 static volatile boolean initialized = false ; private static Object initLock = new Object() ; /** Initialize Jena. * <p> * 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. * <p> * By default, initialization happens by using {@code ServiceLoader.load} to find * {@link JenaSubsystemLifecycle} objects. * See {@link #setSubsystemRegistry} to intercept that choice. */ public static void init() { // 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. // Set initialized to true before a recursive call is possible // handles this. The recursive call will see initialized true and // and returnn on the first test. // Net effect: // After a top level call of JenaSystem.init() returns, tjena has // finishes initialization. // Recursive calls do not have this property. if ( initialized ) return ; synchronized(initLock) { if ( initialized ) { logLifecycle("JenaSystem.init - return"); return ; } // Catches recursive calls, same thread. initialized = true ; logLifecycle("JenaSystem.init - start"); if ( get() == null ) setSubsystemRegistry(new JenaSubsystemRegistryBasic()) ; get().load() ; // Debug : what did we find? if ( JenaSystem.DEBUG_INIT ) { logLifecycle("Found:") ; get().snapshot().forEach(mod-> logLifecycle(" %-20s [%d]", mod.getClass().getSimpleName(), mod.level())) ; } get().add(new JenaInitLevel0()) ; if ( JenaSystem.DEBUG_INIT ) { logLifecycle("Initialization sequence:") ; JenaSystem.forEach( module -> logLifecycle(" %-20s [%d]", module.getClass().getSimpleName(), module.level()) ) ; } JenaSystem.forEach( module -> { logLifecycle("Init: %s", module.getClass().getSimpleName()); module.start() ; }) ; logLifecycle("JenaSystem.init - finish"); } } /** Shutdown subsystems */ public static void shutdown() { if ( ! initialized ) { logLifecycle("JenaSystem.shutdown - not initialized"); return ; } synchronized(initLock) { if ( ! initialized ) { logLifecycle("JenaSystem.shutdown - return"); return ; } logLifecycle("JenaSystem.shutdown - start"); JenaSystem.forEachReverse(module -> { logLifecycle("Stop: %s", module.getClass().getSimpleName()); module.stop() ; }) ; initialized = false ; logLifecycle("JenaSystem.shutdown - finish"); } } private static JenaSubsystemRegistry singleton = null; /** * Set the {@link JenaSubsystemRegistry}. * To have any effect, this function * must be called before any other Jena code, * and especially before calling {@code JenaSystem.init()}. */ public static void setSubsystemRegistry(JenaSubsystemRegistry thing) { singleton = thing; } /** The current JenaSubsystemRegistry */ public static JenaSubsystemRegistry 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 static void forEach(Consumer<JenaSubsystemLifecycle> 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 static void forEachReverse(Consumer<JenaSubsystemLifecycle> action) { forEach(action, reverseComparator); } // Order by level (increasing) private static Comparator<JenaSubsystemLifecycle> comparator = (obj1, obj2) -> Integer.compare(obj1.level(), obj2.level()) ; // Order by level (decreasing) private static Comparator<JenaSubsystemLifecycle> reverseComparator = (obj1, obj2) -> -1 * comparator.compare(obj1, obj2) ; private synchronized static void forEach(Consumer<JenaSubsystemLifecycle> action, Comparator<JenaSubsystemLifecycle> ordering) { List<JenaSubsystemLifecycle> x = get().snapshot() ; Collections.sort(x, ordering); x.forEach(action); } /** Output a debugging message if DEBUG_INIT is set */ public static void logLifecycle(String fmt, Object ...args) { if ( ! DEBUG_INIT ) return ; System.err.printf(fmt, args) ; System.err.println() ; } /** The level 0 subsystem - inserted without using the Registry load function. * There should be only one such level 0 handler. */ private static class JenaInitLevel0 implements JenaSubsystemLifecycle { private static Logger log = LoggerFactory.getLogger("Jena") ; @Override public void start() { log.debug("Jena initialization"); } @Override public void stop() { log.debug("Jena shutdown"); } @Override public int level() { return 0; } } }