001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.helpers;
007:
008: import java.io.File;
009: import java.util.HashSet;
010: import java.util.IdentityHashMap;
011: import java.util.Iterator;
012: import java.util.Map;
013: import java.util.Set;
014:
015: import org.slf4j.Logger;
016: import org.slf4j.LoggerFactory;
017:
018: import org.openrdf.sail.Sail;
019: import org.openrdf.sail.SailChangedEvent;
020: import org.openrdf.sail.SailChangedListener;
021: import org.openrdf.sail.SailConnection;
022: import org.openrdf.sail.SailException;
023:
024: /**
025: * SailBase is an abstract Sail implementation that takes care of common sail
026: * tasks, including proper closing of active connections and a grace period for
027: * active connections during shutdown of the store.
028: *
029: * @author Herko ter Horst
030: * @author jeen
031: * @author Arjohn Kampman
032: */
033: public abstract class SailBase implements Sail {
034:
035: /*-----------*
036: * Constants *
037: *-----------*/
038:
039: protected final Logger logger = LoggerFactory.getLogger(this
040: .getClass());
041:
042: /**
043: * Default connection timeout on shutdown: 20,000 milliseconds.
044: */
045: protected final static long DEFAULT_CONNECTION_TIMEOUT = 20000L;
046:
047: // Note: the following variable and method are package protected so that they
048: // can be removed when open connections no longer block other connections and
049: // they can be closed silently (just like in JDBC).
050: static final String DEBUG_PROP = "org.openrdf.repository.debug";
051:
052: static boolean debugEnabled() {
053: String value = System.getProperty(DEBUG_PROP);
054: return value != null && !value.equals("false");
055: }
056:
057: /*-----------*
058: * Variables *
059: *-----------*/
060:
061: /**
062: * Directory to store information related to this sail in.
063: */
064: private File dataDir;
065:
066: /**
067: * Flag indicating whether the Sail is shutting down.
068: */
069: private boolean shutDownInProgress = false;
070:
071: /**
072: * Connection timeout on shutdown (in ms). Defaults to
073: * {@link #DEFAULT_CONNECTION_TIMEOUT}.
074: */
075: protected long connectionTimeOut = DEFAULT_CONNECTION_TIMEOUT;
076:
077: /**
078: * Map used to track active connections and where these were acquired. The
079: * Throwable value may be null in case debugging was disable at the time the
080: * connection was acquired.
081: */
082: private Map<SailConnection, Throwable> activeConnections = new IdentityHashMap<SailConnection, Throwable>();
083:
084: /**
085: * Objects that should be notified of changes to the data in this Sail.
086: */
087: private Set<SailChangedListener> sailChangedListeners = new HashSet<SailChangedListener>(
088: 0);
089:
090: /*---------*
091: * Methods *
092: *---------*/
093:
094: public void setDataDir(File dataDir) {
095: this .dataDir = dataDir;
096: }
097:
098: public File getDataDir() {
099: return dataDir;
100: }
101:
102: public SailConnection getConnection() throws SailException {
103: if (shutDownInProgress) {
104: throw new IllegalStateException("shut down in progress");
105: }
106:
107: SailConnection connection = getConnectionInternal();
108:
109: Throwable stackTrace = debugEnabled() ? new Throwable() : null;
110: activeConnections.put(connection, stackTrace);
111:
112: return connection;
113: }
114:
115: /**
116: * returns a store-specific SailConnection object.
117: *
118: * @return a SailConnection
119: * @throws SailException
120: */
121: protected abstract SailConnection getConnectionInternal()
122: throws SailException;
123:
124: public void shutDown() throws SailException {
125: // indicate no more new connections should be given out.
126: shutDownInProgress = true;
127:
128: synchronized (activeConnections) {
129: // check if any active connections exist, if so, wait for a grace
130: // period for them to finish.
131: if (!activeConnections.isEmpty()) {
132: logger
133: .info("Waiting for active connections to close before shutting down...");
134: try {
135: activeConnections.wait(DEFAULT_CONNECTION_TIMEOUT);
136: } catch (InterruptedException e) {
137: // ignore and continue
138: }
139: }
140:
141: // Forcefully close any connections that are still open
142: Iterator<Map.Entry<SailConnection, Throwable>> iter = activeConnections
143: .entrySet().iterator();
144: while (iter.hasNext()) {
145: Map.Entry<SailConnection, Throwable> entry = iter
146: .next();
147: SailConnection con = entry.getKey();
148: Throwable stackTrace = entry.getValue();
149:
150: iter.remove();
151:
152: if (stackTrace == null) {
153: logger
154: .warn(
155: "Closing active connection due to shut down; consider setting the {} system property",
156: DEBUG_PROP);
157: } else {
158: logger
159: .warn(
160: "Closing active connection due to shut down, connection was acquired in",
161: stackTrace);
162: }
163:
164: try {
165: con.close();
166: } catch (SailException e) {
167: logger.error("Failed to close connection", e);
168: }
169: }
170: }
171:
172: try {
173: shutDownInternal();
174: } finally {
175: shutDownInProgress = false;
176: }
177: }
178:
179: /**
180: * Do store-specific operations to ensure proper shutdown of the store.
181: *
182: * @throws SailException
183: */
184: protected abstract void shutDownInternal() throws SailException;
185:
186: /**
187: * Signals to the store that the supplied connection has been closed.
188: *
189: * @param connection
190: */
191: protected void connectionClosed(SailConnection connection) {
192: synchronized (activeConnections) {
193: if (activeConnections.containsKey(connection)) {
194: activeConnections.remove(connection);
195:
196: if (activeConnections.isEmpty()) {
197: // only notify waiting threads if all active connections have
198: // been closed.
199: activeConnections.notifyAll();
200: }
201: } else {
202: logger
203: .warn("tried to remove unknown connection object from store.");
204: }
205: }
206: }
207:
208: public void addSailChangedListener(SailChangedListener listener) {
209: synchronized (sailChangedListeners) {
210: sailChangedListeners.add(listener);
211: }
212: }
213:
214: public void removeSailChangedListener(SailChangedListener listener) {
215: synchronized (sailChangedListeners) {
216: sailChangedListeners.remove(listener);
217: }
218: }
219:
220: /**
221: * Notifies all registered SailChangedListener's of changes to the contents
222: * of this Sail.
223: */
224: public void notifySailChanged(SailChangedEvent event) {
225: synchronized (sailChangedListeners) {
226: for (SailChangedListener l : sailChangedListeners) {
227: l.sailChanged(event);
228: }
229: }
230: }
231: }
|