All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.rdf4j.sail.helpers.AbstractSail Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
/*******************************************************************************
 * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *******************************************************************************/
package org.eclipse.rdf4j.sail.helpers;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.eclipse.rdf4j.IsolationLevel;
import org.eclipse.rdf4j.IsolationLevels;
import org.eclipse.rdf4j.sail.Sail;
import org.eclipse.rdf4j.sail.SailConnection;
import org.eclipse.rdf4j.sail.SailException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An abstract Sail implementation that takes care of common sail tasks, including proper closing of active
 * connections and a grace period for active connections during shutdown of the store.
 * 
 * @author Herko ter Horst
 * @author jeen
 * @author Arjohn Kampman
 */
public abstract class AbstractSail implements Sail {

	/*-----------*
	 * Constants *
	 *-----------*/

	/**
	 * Default connection timeout on shutdown: 20,000 milliseconds.
	 */
	protected final static long DEFAULT_CONNECTION_TIMEOUT = 20000L;

	/**
	 * default transaction isolation level, set to {@link IsolationLevels#READ_COMMITTED }.
	 */
	private IsolationLevel defaultIsolationLevel = IsolationLevels.READ_COMMITTED;

	/**
	 * list of supported isolation levels. By default set to include {@link IsolationLevels#READ_UNCOMMITTED}
	 * and {@link IsolationLevels#SERIALIZABLE}. Specific store implementations are expected to alter this
	 * list according to their specific capabilities.
	 */
	private List supportedIsolationLevels = new ArrayList();

	/**
	 * default value for the Iteration item sync threshold
	 */
	protected static final long DEFAULT_ITERATION_SYNC_THRESHOLD = 0L;

	// Note: the following variable and method are package protected so that they
	// can be removed when open connections no longer block other connections and
	// they can be closed silently (just like in JDBC).
	static final String DEBUG_PROP = "org.eclipse.rdf4j.repository.debug";

	protected static boolean debugEnabled() {
		try {
			String value = System.getProperty(DEBUG_PROP);
			return value != null && !value.equals("false");
		}
		catch (SecurityException e) {
			// Thrown when not allowed to read system properties, for example when
			// running in applets
			return false;
		}
	}

	/*-----------*
	 * Variables *
	 *-----------*/

	protected final Logger logger = LoggerFactory.getLogger(this.getClass());

	/**
	 * Directory to store information related to this sail in (if any).
	 */
	private volatile File dataDir;

	/**
	 * Flag indicating whether the Sail has been initialized. Sails are initialized from {@link #initialize()
	 * initialization} until {@link #shutDown() shutdown}.
	 */
	private volatile boolean initialized = false;

	/**
	 * Lock used to synchronize the initialization state of a sail.
	 * 
    *
  • write lock: initialize(), shutDown() *
  • read lock: getConnection() *
*/ protected final ReentrantReadWriteLock initializationLock = new ReentrantReadWriteLock(); /** * Connection timeout on shutdown (in ms). Defaults to {@link #DEFAULT_CONNECTION_TIMEOUT}. */ protected volatile long connectionTimeOut = DEFAULT_CONNECTION_TIMEOUT; private long iterationCacheSyncThreshold = DEFAULT_ITERATION_SYNC_THRESHOLD; /** * Map used to track active connections and where these were acquired. The Throwable value may be null in * case debugging was disable at the time the connection was acquired. */ private final Map activeConnections = new IdentityHashMap(); /* * constructors */ public AbstractSail() { super(); this.addSupportedIsolationLevel(IsolationLevels.READ_UNCOMMITTED); this.addSupportedIsolationLevel(IsolationLevels.SERIALIZABLE); } /*---------* * Methods * *---------*/ /** * Set connection timeout on shutdown (in ms). * * @param connectionTimeOut timeout (in ms) */ public void setConnectionTimeOut(long connectionTimeOut) { this.connectionTimeOut = connectionTimeOut; } @Override public void setDataDir(File dataDir) { if (isInitialized()) { throw new IllegalStateException("sail has already been initialized"); } this.dataDir = dataDir; } @Override public File getDataDir() { return dataDir; } @Override public String toString() { if (dataDir == null) { return super.toString(); } else { return dataDir.toString(); } } /** * Checks whether the Sail has been initialized. Sails are initialized from {@link #initialize() * initialization} until {@link #shutDown() shutdown}. * * @return true if the Sail has been initialized, false otherwise. */ protected boolean isInitialized() { return initialized; } @Override public void initialize() throws SailException { initializationLock.writeLock().lock(); try { logger.trace("is initialized: {}", isInitialized()); if (isInitialized()) { throw new IllegalStateException( "Sail has already been intialized. Ensure this Sail is being used via a Repository."); } initializeInternal(); initialized = true; } finally { initializationLock.writeLock().unlock(); } } /** * Do store-specific operations to initialize the store. The default implementation of this method does * nothing. */ protected void initializeInternal() throws SailException { } @Override public void shutDown() throws SailException { initializationLock.writeLock().lock(); try { if (!isInitialized()) { return; } Map activeConnectionsCopy; synchronized (activeConnections) { // Check if any active connections exist. If so, wait for a grace // period for them to finish. if (!activeConnections.isEmpty()) { logger.debug("Waiting for active connections to close before shutting down..."); try { activeConnections.wait(connectionTimeOut); } catch (InterruptedException e) { // ignore and continue } } // Copy the current contents of the map so that we don't have to // synchronize on activeConnections. This prevents a potential // deadlock with concurrent calls to connectionClosed() activeConnectionsCopy = new IdentityHashMap(activeConnections); } // Forcefully close any connections that are still open for (Map.Entry entry : activeConnectionsCopy.entrySet()) { SailConnection con = entry.getKey(); Throwable stackTrace = entry.getValue(); if (stackTrace == null) { logger.warn( "Closing active connection due to shut down; consider setting the {} system property", DEBUG_PROP); } else { logger.warn("Closing active connection due to shut down, connection was acquired in", stackTrace); } try { con.close(); } catch (SailException e) { logger.error("Failed to close connection", e); } } // All connections should be closed now synchronized (activeConnections) { activeConnections.clear(); } shutDownInternal(); } finally { initialized = false; initializationLock.writeLock().unlock(); } } /** * Do store-specific operations to ensure proper shutdown of the store. */ protected abstract void shutDownInternal() throws SailException; @Override public SailConnection getConnection() throws SailException { initializationLock.readLock().lock(); try { if (!isInitialized()) { throw new IllegalStateException("Sail is not initialized or has been shut down"); } SailConnection connection = getConnectionInternal(); Throwable stackTrace = debugEnabled() ? new Throwable() : null; synchronized (activeConnections) { activeConnections.put(connection, stackTrace); } return connection; } finally { initializationLock.readLock().unlock(); } } /** * Returns a store-specific SailConnection object. * * @return A connection to the store. */ protected abstract SailConnection getConnectionInternal() throws SailException; /** * Signals to the store that the supplied connection has been closed; called by * {@link AbstractSailConnection#close()}. * * @param connection * The connection that has been closed. */ protected void connectionClosed(SailConnection connection) { synchronized (activeConnections) { if (activeConnections.containsKey(connection)) { activeConnections.remove(connection); if (activeConnections.isEmpty()) { // only notify waiting threads if all active connections have // been closed. activeConnections.notifyAll(); } } else { logger.warn("tried to remove unknown connection object from store."); } } } /** * Appends the provided {@link IsolationLevels} to the SAIL's list of supported isolation levels. * * @param level * a supported IsolationLevel. */ protected void addSupportedIsolationLevel(IsolationLevels level) { this.supportedIsolationLevels.add(level); } /** * Removes all occurrences of the provided {@link IsolationLevels} in the list of supported Isolation * levels. * * @param level * the isolation level to remove. */ protected void removeSupportedIsolationLevel(IsolationLevel level) { while (this.supportedIsolationLevels.remove(level)) { } } /** * Sets the list of supported {@link IsolationLevels}s for this SAIL. The list is expected to be ordered * in increasing complexity. * * @param supportedIsolationLevels * a list of supported isolation levels. */ protected void setSupportedIsolationLevels(List supportedIsolationLevels) { this.supportedIsolationLevels = supportedIsolationLevels; } /** * Sets the list of supported {@link IsolationLevels}s for this SAIL. The list is expected to be ordered * in increasing complexity. * * @param supportedIsolationLevels * a list of supported isolation levels. */ protected void setSupportedIsolationLevels(IsolationLevel... supportedIsolationLevels) { this.supportedIsolationLevels = Arrays.asList(supportedIsolationLevels); } @Override public List getSupportedIsolationLevels() { return Collections.unmodifiableList(supportedIsolationLevels); } @Override public IsolationLevel getDefaultIsolationLevel() { return defaultIsolationLevel; } /** * Sets the default {@link IsolationLevel} on which transactions in this Sail operate. * * @param defaultIsolationLevel * The defaultIsolationLevel to set. */ public void setDefaultIsolationLevel(IsolationLevel defaultIsolationLevel) { if (defaultIsolationLevel == null) { throw new IllegalArgumentException("default isolation level may not be null"); } this.defaultIsolationLevel = defaultIsolationLevel; } /** * Retrieves the currently configured threshold for syncing query evaluation iteration caches to disk. * * @return Returns the iterationCacheSyncThreshold. */ public long getIterationCacheSyncThreshold() { return iterationCacheSyncThreshold; } /** * Set the threshold for syncing query evaluation iteration caches to disk. * * @param iterationCacheSyncThreshold * The iterationCacheSyncThreshold to set. */ public void setIterationCacheSyncThreshold(long iterationCacheSyncThreshold) { this.iterationCacheSyncThreshold = iterationCacheSyncThreshold; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy