Browse files

rfe10512: Java JNDI connection pool

This is a new, optional feature, has no impact for
existing users. Tested for functionality and performance
with an uncommitted change to Events.java.

tests-added:   none
tests-run:     prepush, manual test with tomcat, glassfish
performance:   no impact

<release-notes>
rfe10512: Java JNDI connection pool

Implements Pool for AllegroGraph using Apache-Commons-Pool.

Implements JNDI ObjectFactory to provide a
connection pool to AllegroGraph server.

See javadocs for package: com.franz.agraph.pool.
</release-notes>

Change-Id: I2afbc0bce37db9afb5b3c11004a73012b81188e6
Reviewed-on: https://gerrit.franz.com:9080/1588
Reviewed-by: Kevin Layer <layer@franz.com>
Tested-by: Kevin Layer <layer@franz.com>
  • Loading branch information...
1 parent 21db3ea commit 50ae9c2d55207b5157fb6dd010ede3e2d18fbdcb Mike Hinchey committed with dklayer Sep 29, 2011
View
2 .classpath
@@ -20,5 +20,7 @@
<classpathentry kind="lib" path="lib/logging/commons-logging-1.1.1.jar" sourcepath="lib/logging/commons-logging-1.1.1-sources.jar"/>
<classpathentry kind="lib" path="lib/logging/slf4j-api-1.6.1.jar" sourcepath="lib/logging/slf4j-api-1.6.1-sources.jar"/>
<classpathentry kind="lib" path="lib/logging/slf4j-jcl-1.6.1.jar" sourcepath="lib/logging/slf4j-jcl-1.6.1-sources.jar"/>
+ <classpathentry kind="lib" path="lib/servlet-api-2.5.jar"/>
+ <classpathentry kind="lib" path="lib/commons-pool-1.5.6.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
View
16 build.xml
@@ -52,6 +52,8 @@
<path location="${lib}/jena-2.6.2/wstx-asl-3.2.9.jar"/>
<path location="${lib}/jena-2.6.2/xercesImpl-2.7.1.jar"/>
<path location="${lib}/commons-cli-1.2.jar"/>
+ <path location="${lib}/commons-pool-1.5.6.jar"/>
+ <path location="${lib}/servlet-api-2.5.jar"/>
</path>
<target name="init">
@@ -172,7 +174,7 @@
noindex="false"
nonavbar="false"
notree="false"
- packagenames="com.franz.agraph.jena,com.franz.agraph.repository,com.franz.openrdf.rio.nquads,com.franz.util"
+ packagenames="com.franz.agraph.jena,com.franz.agraph.repository,com.franz.openrdf.rio.nquads,com.franz.util,com.franz.agraph.pool"
source="1.6"
sourcepath="src"
splitindex="true"
@@ -434,5 +436,17 @@
<pom refid="sesame"/>
</artifact:install>
</target>
+
+ <target name="test-war"
+ depends="compile"
+ description="builds agraph-test.war">
+ <war destfile="target/agraph-test.war"
+ webxml="src/test/web/web.xml">
+ <classes dir="classes" includes="**/*"/>
+ <lib dir="lib" includes="*.jar" excludes="servlet-api-2.5.jar"/>
+ <lib dir="lib/sesame-2.3.2" includes="*.jar"/>
+ <lib dir="lib/logging" includes="*.jar"/>
+ </war>
+ </target>
</project>
View
5 events.sh
@@ -2,7 +2,10 @@
java -cp classes:agraph.jar\
:lib/commons-cli-1.2.jar\
-:lib/sesame-2.3.2/*\
+:lib/commons-pool-1.5.6.jar\
+:lib/sesame-2.3.2/commons-codec-1.3.jar\
+:lib/sesame-2.3.2/commons-httpclient-3.1.jar\
+:lib/sesame-2.3.2/openrdf-sesame-2.3.2-onejar.jar\
:lib/logging/*\
:lib/json.jar\
test.stress.Events $*
View
BIN lib/commons-pool-1.5.6.jar
Binary file not shown.
View
BIN lib/servlet-api-2.5.jar
Binary file not shown.
View
1 makefile
@@ -73,6 +73,7 @@ endif
cp agraph.jar $(DIST)/lib/agraph-$(VERSION).jar
cp agraph-src.jar $(DIST)/lib/agraph-$(VERSION)-src.jar
cp lib/json.jar $(DIST)/lib/json.jar
+ cp lib/commons-pool-1.5.6.jar $(DIST)/lib/commons-pool-1.5.6.jar
mkdir -p $(DIST)/lib/logging
cp lib/logging/*.jar $(DIST)/lib/logging
mkdir -p $(DIST)/lib/sesame-2.3.2
View
25 src/com/franz/agraph/http/AGHTTPClient.java
@@ -6,9 +6,6 @@
** http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
-/**
- *
- */
package com.franz.agraph.http;
import static com.franz.agraph.http.AGProtocol.AMOUNT_PARAM_NAME;
@@ -64,7 +61,7 @@
private final HttpClient httpClient;
private AuthScope authScope;
- final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private static final Logger logger = LoggerFactory.getLogger(AGHTTPClient.class);
private MultiThreadedHttpConnectionManager mManager = null;
@@ -87,6 +84,15 @@ public AGHTTPClient(String serverURL, HttpConnectionManager manager) {
manager.setParams(params);
}
httpClient = new HttpClient(manager);
+ logger.debug("connect: " + serverURL + " " + httpClient + " " + manager);
+ }
+
+ @Override
+ public String toString() {
+ return "{" + super.toString()
+ + " " + serverURL
+ + " " + httpClient
+ + "}";
}
public String getServerURL() {
@@ -356,9 +362,9 @@ public String getString(String url) throws AGHttpException {
try {
get(url, headers, data, handler);
} catch (RepositoryException e) {
- throw new AGHttpException(e.getMessage());
+ throw new AGHttpException(e);
} catch (IOException e) {
- throw new AGHttpException(e.getMessage());
+ throw new AGHttpException(e);
}
return handler.getString();
}
@@ -384,7 +390,12 @@ public String openSession(String spec, boolean autocommit) throws RepositoryExce
@Override
public void close() {
- Util.close(this.mManager);
+ logger.debug("close: " + serverURL + " " + mManager);
+ mManager = Util.close(mManager);
+ }
+
+ boolean isClosed() {
+ return mManager == null;
}
public String[] generateURIs(String repositoryURL, String namespace,
View
12 src/com/franz/agraph/http/AGHttpException.java
@@ -10,9 +10,6 @@
public class AGHttpException extends Exception {
- /**
- *
- */
private static final long serialVersionUID = -2608901334300829491L;
private final AGErrorInfo errorInfo;
@@ -27,6 +24,15 @@ public AGHttpException(String message) {
errorInfo = new AGErrorInfo(message);
}
+ public AGHttpException(String message, Throwable cause) {
+ super(message, cause);
+ errorInfo = new AGErrorInfo(message);
+ }
+
+ public AGHttpException(Throwable cause) {
+ this(cause.getMessage(), cause);
+ }
+
public AGErrorInfo getErrorInfo() {
return errorInfo;
}
View
13 src/com/franz/agraph/http/AGHttpRepoClient.java
@@ -123,6 +123,15 @@ public AGHttpRepoClient(Repository repo, AGHTTPClient client, String repoRoot, S
this.client = client;
savedQueryDeleteQueue = new ConcurrentLinkedQueue<String>();
}
+
+ @Override
+ public String toString() {
+ return "{" + super.toString()
+ + " " + client
+ + " rr=" + repoRoot
+ + " sr=" + sessionRoot
+ + "}";
+ }
public String getRoot() throws RepositoryException {
if (sessionRoot != null) return sessionRoot;
@@ -276,7 +285,7 @@ private void useDedicatedSession(boolean autoCommit)
private void closeSession(String sessionRoot) throws IOException,
RepositoryException, UnauthorizedException {
- if (sessionRoot != null) {
+ if (sessionRoot != null && !getHTTPClient().isClosed()) {
String url = AGProtocol.getSessionCloseLocation(sessionRoot);
Header[] headers = new Header[0];
NameValuePair[] params = new NameValuePair[0];
@@ -932,7 +941,7 @@ public void deleteSavedQuery(String queryName) throws RepositoryException {
}
}
- public void close() throws RepositoryException {
+ public synchronized void close() throws RepositoryException {
if (sessionRoot != null) {
try {
closeSession(sessionRoot);
View
58 src/com/franz/agraph/pool/AGConnConfig.java
@@ -0,0 +1,58 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import java.util.Map;
+
+import com.franz.agraph.pool.AGConnProp.Session;
+
+/**
+ * @since v4.3.3
+ */
+public class AGConnConfig {
+
+ public final String serverUrl;
+ public final String username;
+ public final String password;
+ public final String catalog;
+ public final String repository;
+ public final Session session;
+ public final Integer sessionLifetime;
+
+ public AGConnConfig(Map<AGConnProp, String> props) {
+ if (props.containsKey(AGConnProp.serverUrl)) {
+ serverUrl = props.get(AGConnProp.serverUrl);
+ } else {
+ throw new IllegalArgumentException("Property required for AGConn: " + AGConnProp.serverUrl);
+ }
+ if (props.containsKey(AGConnProp.username)) {
+ username = props.get(AGConnProp.username);
+ } else {
+ throw new IllegalArgumentException("Property required for AGConn: " + AGConnProp.username);
+ }
+ if (props.containsKey(AGConnProp.password)) {
+ password = props.get(AGConnProp.password);
+ } else {
+ throw new IllegalArgumentException("Property required for AGConn: " + AGConnProp.password);
+ }
+ catalog = props.get(AGConnProp.catalog);
+ if (props.containsKey(AGConnProp.repository)) {
+ repository = props.get(AGConnProp.repository);
+ } else {
+ throw new IllegalArgumentException("Property required for AGConn: " + AGConnProp.repository);
+ }
+ session = Session.valueOfCaseInsensitive(props.get(AGConnProp.session), Session.SHARED);
+ if (props.containsKey(AGConnProp.sessionLifetime)) {
+ sessionLifetime = Integer.parseInt( props.get(AGConnProp.sessionLifetime));
+ } else {
+ sessionLifetime = null;
+ }
+ }
+
+}
View
158 src/com/franz/agraph/pool/AGConnFactory.java
@@ -0,0 +1,158 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.openrdf.repository.RepositoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.franz.agraph.repository.AGCatalog;
+import com.franz.agraph.repository.AGRepository;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.agraph.repository.AGServer;
+import com.franz.util.Closer;
+
+/**
+ * Adapts the {@link AGRepositoryConnection} API
+ * to the commons-pool factory interface,
+ * leaving creation of the connection (and configuration)
+ * to a subclass, defined by users of this library.
+ *
+ * @since v4.3.3
+ */
+public class AGConnFactory
+extends Closer
+implements PoolableObjectFactory {
+
+ private final static Logger log = LoggerFactory.getLogger(AGConnFactory.class);
+
+ private final AGConnConfig props;
+
+ public AGConnFactory(AGConnConfig props) throws RepositoryException {
+ this.props = props;
+ }
+
+ @Override
+ public Object makeObject() throws Exception {
+ return makeConnection();
+ }
+
+ protected AGRepositoryConnection makeConnection() throws RepositoryException {
+ AGServer server = closeLater( new AGServer(props.serverUrl, props.username, props.password) );
+ AGCatalog cat;
+ if (props.catalog != null) {
+ cat = server.getCatalog(props.catalog);
+ } else {
+ cat = server.getRootCatalog();
+ }
+
+ AGRepository repo = closeLater( cat.createRepository(props.repository) );
+ repo.initialize();
+
+ AGRepositoryConnection conn = closeLater( new AGRepositoryConnectionCloseup(this, closeLater( repo.getConnection())));
+ if (props.sessionLifetime != null) {
+ conn.setSessionLifetime(props.sessionLifetime);
+ }
+ activateConnection(conn);
+ return conn;
+ }
+
+ protected void activateConnection(AGRepositoryConnection conn) throws RepositoryException {
+ switch (props.session) {
+ case SHARED:
+ if (!conn.isAutoCommit()) {
+ // it must have been set by the user, but restore it anyway
+ // it is no longer actually SHARED but DEDICATED
+ conn.setAutoCommit(true);
+ if (log.isDebugEnabled())
+ log.debug("Dedicated (not shared) backend: " + conn.getHttpRepoClient().getRoot());
+ }
+ break;
+ case DEDICATED:
+ if (!conn.isAutoCommit()) {
+ conn.setAutoCommit(true);
+ if (log.isDebugEnabled())
+ log.debug("Dedicated backend: " + conn.getHttpRepoClient().getRoot());
+ }
+ break;
+ case TX:
+ if (conn.isAutoCommit()) {
+ conn.setAutoCommit(false);
+ if (log.isDebugEnabled())
+ log.debug("TX dedicated backend: " + conn.getHttpRepoClient().getRoot());
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void activateObject(Object obj) throws Exception {
+ activateConnection( (AGRepositoryConnection) obj);
+ }
+
+ @Override
+ public void destroyObject(Object obj) throws Exception {
+ close(obj);
+ }
+
+ @Override
+ public void passivateObject(Object obj) throws Exception {
+ AGRepositoryConnection conn = (AGRepositoryConnection) obj;
+ if (!conn.isAutoCommit() && conn.getRepository().isWritable()) {
+ conn.rollback();
+ // TODO MH: any reason to do this?
+ //conn.setAutoCommit(true);
+ }
+ }
+
+ @Override
+ public boolean validateObject(Object obj) {
+ AGRepositoryConnection conn = (AGRepositoryConnection) obj;
+ try {
+ conn.ping();
+ return true;
+ } catch (RepositoryException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Delegates all methods to the wrapped conn except for close.
+ */
+ class AGRepositoryConnectionCloseup extends AGRepositoryConnection {
+
+ final AGRepositoryConnection conn;
+ private final Closer closer;
+
+ public AGRepositoryConnectionCloseup(Closer closer, AGRepositoryConnection conn) {
+ super((AGRepository) conn.getRepository(), conn.getHttpRepoClient());
+ this.closer = closer;
+ this.conn = conn;
+ }
+
+ /**
+ * Closes the {@link AGRepositoryConnection}, {@link AGRepository}, and {@link AGServer}.
+ */
+ @Override
+ public void close() throws RepositoryException {
+ try {
+ super.close();
+ } catch (Exception e) {
+ // it is likely the session has timed out, so only log to debug
+ logger.debug("ignoring error with close", e);
+ }
+ closer.close(conn);
+ closer.close(conn.getRepository());
+ closer.close(conn.getRepository().getCatalog().getServer());
+ }
+
+ }
+
+}
View
312 src/com/franz/agraph/pool/AGConnPool.java
@@ -0,0 +1,312 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.servlet.ServletContextListener;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.openrdf.repository.RepositoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.franz.agraph.repository.AGRepository;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.util.Closeable;
+import com.franz.util.Closer;
+import com.franz.util.Util;
+
+/**
+ * Pooling for {@link AGRepositoryConnection}s.
+ * The recommended way to create a pool is by {@link #create(Object...)}
+ * or by configuring a {@link AGConnPoolJndiFactory} with your appserver.
+ *
+ * <p><code><pre>
+ * AGConnPool pool = AGConnPool.create(
+ * AGConnProp.serverUrl, "http://localhost:10035/",
+ * AGConnProp.username, "test",
+ * AGConnProp.password, "xyzzy",
+ * AGConnProp.catalog, "/",
+ * AGConnProp.repository, "my_repo",
+ * AGConnProp.session, AGConnProp.Session.DEDICATED,
+ * AGPoolProp.shutdownHook, true,
+ * AGPoolProp.initialSize, 2);
+ * AGRepositoryConnection conn = pool.borrowConnection();
+ * try {
+ * ...
+ * conn.commit();
+ * } finally {
+ * conn.close();
+ * // or equivalently
+ * pool.returnObject(conn);
+ * }
+ * </pre></code></p>
+ *
+ * <p>This pool delegates the pooling implementation to another
+ * pool (usually {@link GenericObjectPool}),
+ * adding {@link #borrowConnection()}, which also wraps
+ * the {@link AGRepositoryConnection} so that {@link Closeable#close()}
+ * will call {@link #returnObject(Object)} instead of actually closing.</p>
+ *
+ * <p>Warning: Since the objects {@link #borrowConnection() borrowed}
+ * from this class are wrapped, you can not use them directly with
+ * the delegate pool.
+ * The delegate pool should only be used for its pooling
+ * implementation and configuration.
+ * </p>
+ *
+ * <p>Closing the connection pool is important because server sessions will
+ * stay active until {@link AGConnProp#sessionLifetime}.
+ * The option to use a Runtime shutdownHook is built-in with {@link AGPoolProp#shutdownHook}.
+ * Another option is to use {@link ServletContextListener} - this is appropriate if the
+ * agraph jar is deployed within your webapp and not with the webserver.
+ * With tomcat, a <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html#Lifecycle_Listeners"
+ * >Lifecycle Listener</a> can be configured, but the implementation to do this
+ * is not included in this library.
+ * </p>
+ *
+ * <p>Note, when {@link AGConnPool#close()} is called
+ * on a {@link AGConnPool},
+ * connections will be closed whether they are
+ * idle (have been returned) or not.
+ * This is different from {@link GenericObjectPool#close()}.
+ * </p>
+ *
+ * @param <ObjectPoolType>
+ *
+ * @since v4.3.3
+ */
+public class AGConnPool <ObjectPoolType extends ObjectPool>
+extends Closer
+implements ObjectPool, Closeable {
+
+ private static final Logger log = LoggerFactory.getLogger(AGConnPool.class);
+
+ private final ObjectPoolType delegate;
+
+ private final AGConnFactory factory;
+
+ /**
+ * Delegates all methods to the wrapped conn except for close.
+ *
+ * <p>Note, these objects are never closed for real, but they don't hold any resources.</p>
+ */
+ class AGRepositoryConnectionPooled extends AGRepositoryConnection {
+
+ final AGRepositoryConnection conn;
+
+ public AGRepositoryConnectionPooled(AGRepositoryConnection conn) {
+ super((AGRepository) conn.getRepository(), conn.getHttpRepoClient());
+ this.conn = conn;
+ }
+
+ @Override
+ public void close() throws RepositoryException {
+ try {
+ returnObject(this);
+ } catch (Exception e) {
+ throw new RepositoryException(e);
+ }
+ }
+ }
+
+ private static class ShutdownHookCloser extends Thread implements Closeable {
+
+ private static final Logger log = LoggerFactory.getLogger(ShutdownHookCloser.class);
+
+ private final Closer closer;
+
+ public ShutdownHookCloser(Closer closer) {
+ this.closer = closer;
+ }
+
+ @Override
+ public void run() {
+ log.info("closing " + closer);
+ // remove this before closing, because removeShutdownHook will throw if inside of run
+ closer.remove(this);
+ Util.close(closer);
+ log.debug("closed " + closer);
+ }
+
+ /**
+ * Removing is useful for pools that get closed manually
+ * so the Runtime doesn't hold on to this and everything.
+ */
+ @Override
+ public void close() {
+ Runtime.getRuntime().removeShutdownHook(this);
+ }
+ }
+
+ /**
+ * @see #create(Object...)
+ */
+ public AGConnPool(ObjectPoolType delegate, AGConnFactory factory, AGPoolConfig config) {
+ this.delegate = delegate;
+ closeLater(delegate);
+ this.factory = factory;
+
+ if (config.initialSize > 0) {
+ List<AGRepositoryConnection> conns = new ArrayList<AGRepositoryConnection>(config.initialSize);
+ try {
+ for (int i = 0; i < config.initialSize; i++) {
+ conns.add(borrowConnection());
+ }
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ // return them to the pool
+ closeAll(conns);
+ }
+ if (config.shutdownHook) {
+ Runtime.getRuntime().addShutdownHook( closeLater( new ShutdownHookCloser(this)));
+ }
+ }
+
+ /**
+ * A {@link GenericObjectPool} is used.
+ */
+ public static AGConnPool create(AGConnFactory fact, AGPoolConfig poolConfig) {
+ return new AGConnPool<GenericObjectPool>(new GenericObjectPool(fact, poolConfig), fact, poolConfig);
+ }
+
+ /**
+ * Create a pool from configuration properties.
+ * A {@link GenericObjectPool} is used.
+ * @param connProps keys are {@link AGConnProp}
+ * @param poolProps keys are {@link AGPoolProp}
+ */
+ public static AGConnPool create(Map<AGConnProp, String> connProps, Map<AGPoolProp, String> poolProps) throws RepositoryException {
+ AGConnFactory fact = new AGConnFactory(new AGConnConfig(connProps));
+ final AGConnPool pool = AGConnPool.create(fact, new AGPoolConfig(poolProps));
+ pool.closeLater(fact);
+ return pool;
+ }
+
+ /**
+ * Create a pool from configuration properties.
+ * A {@link GenericObjectPool} is used.
+ * @param keyValuePairs alternating key/value pairs where keys are {@link AGConnProp} and {@link AGPoolProp}
+ */
+ public static AGConnPool create(Object...keyValuePairs) throws RepositoryException {
+ Map<AGConnProp, String> connProps = (Map<AGConnProp, String>) toMap(keyValuePairs, EnumSet.allOf(AGConnProp.class));
+ Map<AGPoolProp, String> poolProps = (Map<AGPoolProp, String>) toMap(keyValuePairs, EnumSet.allOf(AGPoolProp.class));
+ return create(connProps, poolProps);
+ }
+
+ protected static Map<? extends Enum, String> toMap(Object[] keyValuePairs, EnumSet<? extends Enum> enumSet) {
+ Map<Enum, String> map = new HashMap<Enum, String>();
+ for (int i = 0; i < keyValuePairs.length; i=i+2) {
+ Enum key = (Enum) keyValuePairs[i];
+ if (enumSet.contains(key)) {
+ Object val = keyValuePairs[i+1];
+ map.put(key, val==null ? null : val.toString());
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
+ delegate.addObject();
+ }
+
+ @Override
+ public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
+ return delegate.borrowObject();
+ }
+
+ public AGRepositoryConnection borrowConnection() throws RepositoryException {
+ try {
+ return new AGRepositoryConnectionPooled((AGRepositoryConnection) delegate.borrowObject());
+ } catch (Exception e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ @Override
+ public void clear() throws Exception, UnsupportedOperationException {
+ delegate.clear();
+ }
+
+ @Override
+ public int getNumActive() throws UnsupportedOperationException {
+ return delegate.getNumActive();
+ }
+
+ @Override
+ public int getNumIdle() throws UnsupportedOperationException {
+ return delegate.getNumIdle();
+ }
+
+ @Override
+ public void invalidateObject(Object obj) throws Exception {
+ delegate.invalidateObject(unwrap(obj));
+ }
+
+ @Override
+ public void returnObject(Object obj) throws Exception {
+ delegate.returnObject(unwrap(obj));
+ }
+
+ public Object unwrap(Object obj) {
+ if (obj instanceof AGConnPool.AGRepositoryConnectionPooled) {
+ return ((AGConnPool.AGRepositoryConnectionPooled)obj).conn;
+ } else {
+ return obj;
+ }
+ }
+
+ @Override
+ @Deprecated
+ public void setFactory(PoolableObjectFactory obj) throws IllegalStateException, UnsupportedOperationException {
+ delegate.setFactory(obj);
+ }
+
+ public void ensureIdle(int n) throws Exception {
+ if (delegate instanceof GenericObjectPool) {
+ GenericObjectPool gop = (GenericObjectPool) delegate;
+ gop.setMinIdle(n);
+ if (gop.getMaxIdle() < n) {
+ gop.setMaxIdle(n);
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ addObject();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ if (getNumActive() > 0) {
+ close();
+ log.warn("Finalizing with open connections, please close the pool properly. " + this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{AGConnPool"
+ + " active=" + getNumActive()
+ + " idle=" + getNumIdle()
+ + " delegate=" + delegate
+ + " factory=" + factory
+ + " this=" + super.toString()
+ + "}";
+ }
+
+}
View
163 src/com/franz/agraph/pool/AGConnPoolJndiFactory.java
@@ -0,0 +1,163 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+import javax.servlet.ServletContextListener;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+import com.franz.agraph.repository.AGRepository;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.agraph.repository.AGServer;
+import com.franz.util.Closeable;
+
+/**
+ * JNDI factory for {@link AGConnPool}.
+ *
+ * <p>The Commons-Pool library is required to use this package:
+ * <a href="http://commons.apache.org/pool/">Apache Commons Pool, commons-pool-1.5.6.jar</a>.
+ * Note, this jar along with the agraph-java-client jar and all of its dependencies
+ * must be in the webserver's library for it to be able to load.
+ * </p>
+ *
+ * <p>The properties supported for the connections are specified
+ * by {@link AGConnProp}.
+ * </p>
+ *
+ * <p>The properties supported for the pooling are specified
+ * by {@link AGPoolProp}.
+ * </p>
+ *
+ * <p>Note, when {@link Closeable#close()} is called
+ * on an {@link AGConnPool},
+ * connections will be closed whether they are
+ * idle (have been returned) or not.
+ * This is different from {@link GenericObjectPool#close()}.
+ * Also note, when a {@link AGRepositoryConnection} from the pool is closed,
+ * the {@link AGRepository} and {@link AGServer} will also be closed
+ * since these are not shared with other {@link AGRepositoryConnection}s.
+ * </p>
+ *
+ * <p>
+ * Example Tomcat JNDI configuration, based on
+ * <a href="http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html#Adding_Custom_Resource_Factories"
+ * >Tomcat HOW-TO create custom resource factories</a>:
+ * In /WEB-INF/web.xml:
+ * <code><pre>
+ * &lt;resource-env-ref&gt;
+ * &lt;description&gt;AllegroGraph connection pool&lt;/description&gt;
+ * &lt;resource-env-ref-name&gt;connection-pool/agraph&lt;/resource-env-ref-name&gt;
+ * &lt;resource-env-ref-type&gt;com.franz.agraph.pool.AGConnPool&lt;/resource-env-ref-type&gt;
+ * &lt;/resource-env-ref&gt;
+ * </pre></code>
+ * Your code:
+ * <code><pre>
+ * Context initCtx = new InitialContext();
+ * Context envCtx = (Context) initCtx.lookup("java:comp/env");
+ * AGConnPool pool = (AGConnPool) envCtx.lookup("connection-pool/agraph");
+ * AGRepositoryConnection conn = pool.borrowConnection();
+ * try {
+ * ...
+ * conn.commit();
+ * } finally {
+ * conn.close();
+ * // or equivalently
+ * pool.returnObject(conn);
+ * }
+ * </pre></code>
+ * Tomcat's resource factory:
+ * <code><pre>
+ * &lt;Context ...&gt;
+ * ...
+ * &lt;Resource name="connection-pool/agraph"
+ * auth="Container"
+ * type="com.franz.agraph.pool.AGConnPool"
+ * factory="com.franz.agraph.pool.AGConnPoolJndiFactory"
+ * username="test"
+ * password="xyzzy"
+ * serverUrl="http://localhost:10035"
+ * catalog="/"
+ * repository="my_repo"
+ * session="TX"
+ * testOnBorrow="true"
+ * initialSize="5"
+ * maxIdle="10"
+ * maxActive="40"
+ * maxWait="60000"/&gt;
+ * ...
+ * &lt;/Context&gt;
+ * </pre></code>
+ * </p>
+ *
+ * <p>Closing the connection pool is important because server sessions will
+ * stay active until {@link AGConnProp#sessionLifetime}.
+ * The option to use a Runtime shutdownHook is built-in with {@link AGPoolProp#shutdownHook}.
+ * Another option is to use {@link ServletContextListener} - this is appropriate if the
+ * agraph jar is deployed within your webapp and not with the webserver.
+ * With tomcat, a <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html#Lifecycle_Listeners"
+ * >Lifecycle Listener</a> can be configured, but the implementation to do this
+ * is not included in this library.
+ * </p>
+ *
+ * @since v4.3.3
+ */
+public class AGConnPoolJndiFactory implements ObjectFactory {
+
+ /**
+ * From the obj {@link Reference}, gets the {@link RefAddr}
+ * names and values, converts to Maps and
+ * returns {@link AGConnPool#create(Object...)}.
+ */
+ @Override
+ public Object getObjectInstance(Object obj,
+ Name name,
+ Context nameCtx,
+ Hashtable<?,?> environment)
+ throws Exception {
+ if (!(obj instanceof Reference)) {
+ return null;
+ }
+ Reference ref = (Reference) obj;
+ if (! AGConnPool.class.getName().equals(ref.getClassName())) {
+ return null;
+ }
+ Map<AGConnProp, String> connProps = (Map<AGConnProp, String>) refToMap(ref, AGConnProp.values());
+ Map<AGPoolProp, String> poolProps = (Map<AGPoolProp, String>) refToMap(ref, AGPoolProp.values());
+ return AGConnPool.create(connProps, poolProps);
+ }
+
+ /**
+ * @param values enum values
+ * @return map suitable for {@link #createPool(Map, Map)}
+ */
+ private static Map<? extends Enum, String> refToMap(Reference ref, Enum[] values) {
+ Map<Enum, String> props = new HashMap<Enum, String>();
+ for (Enum prop : values) {
+ RefAddr ra = ref.get(prop.name());
+ if (ra == null) {
+ ra = ref.get(prop.name().toLowerCase());
+ }
+ if (ra != null) {
+ String propertyValue = ra.getContent().toString();
+ props.put(prop, propertyValue);
+ }
+ }
+ return props;
+ }
+
+}
View
93 src/com/franz/agraph/pool/AGConnProp.java
@@ -0,0 +1,93 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import com.franz.agraph.repository.AGCatalog;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.agraph.repository.AGServer;
+
+/**
+ * Property names to open a {@link AGRepositoryConnection}.
+ *
+ * <p>TODO: {@link AGRepositoryConnection#setSessionLoadInitFile(boolean)}</p>
+ * <p>TODO: {@link AGRepositoryConnection#addSessionLoadScript(String)}</p>
+ */
+public enum AGConnProp {
+
+ /**
+ * @see AGServer#AGServer(String, String, String)
+ */
+ serverUrl,
+
+ /**
+ * @see AGServer#AGServer(String, String, String)
+ */
+ username,
+
+ /**
+ * @see AGServer#AGServer(String, String, String)
+ */
+ password,
+
+ /**
+ * Catalog name or no value for {@link AGServer#getRootCatalog()}
+ * @see AGServer#getCatalog(String)
+ */
+ catalog,
+
+ /**
+ * @see AGCatalog#openRepository(String)
+ */
+ repository,
+
+ /**
+ * Value must be one of {@link Session}.
+ */
+ session,
+
+ /**
+ * @see AGRepositoryConnection#setSessionLifetime(int)
+ */
+ sessionLifetime;
+
+ /**
+ * Property values for {@link AGConnProp#session}.
+ */
+ public static enum Session {
+
+ /**
+ * No dedicated session, and autoCommit is true
+ * (that is, {@link AGRepositoryConnection#setAutoCommit(boolean)} is not called).
+ */
+ SHARED,
+
+ /**
+ * Calls {@link AGRepositoryConnection#setAutoCommit(boolean)} with true.
+ */
+ DEDICATED,
+
+ /**
+ * Calls {@link AGRepositoryConnection#setAutoCommit(boolean)} with false.
+ */
+ TX;
+
+ static Session valueOfCaseInsensitive(String name, Session defaultVal) {
+ Session s = Session.valueOf(name);
+ if (s == null) {
+ s = Session.valueOf(name.toUpperCase());
+ }
+ if (s == null) {
+ return defaultVal;
+ } else {
+ return s;
+ }
+ }
+ }
+
+}
View
85 src/com/franz/agraph/pool/AGPoolConfig.java
@@ -0,0 +1,85 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import java.util.Map;
+
+import org.apache.commons.pool.impl.GenericObjectPool.Config;
+
+/**
+ * Extension to {@link Config} to add more properties.
+ *
+ * @see AGPoolProp
+ * @since v4.3.3
+ */
+public class AGPoolConfig extends Config {
+
+ public static final int DEFAULT_INITIAL_SIZE = 0;
+
+ /**
+ * @see AGPoolProp#initialSize
+ * @see #DEFAULT_INITIAL_SIZE
+ */
+ public final int initialSize;
+
+ public static final boolean DEFAULT_SHUTDOWN_HOOK = false;
+
+ /**
+ * @see AGPoolProp#shutdownHook
+ * @see #DEFAULT_SHUTDOWN_HOOK
+ */
+ public final boolean shutdownHook;
+
+ public AGPoolConfig(Map<AGPoolProp, String> props) {
+ if (props.containsKey(AGPoolProp.initialSize)) {
+ initialSize = Integer.parseInt(props.get(AGPoolProp.initialSize));
+ } else {
+ initialSize = DEFAULT_INITIAL_SIZE;
+ }
+ if (props.containsKey(AGPoolProp.shutdownHook)) {
+ shutdownHook = Boolean.valueOf(props.get(AGPoolProp.shutdownHook));
+ } else {
+ shutdownHook = DEFAULT_SHUTDOWN_HOOK;
+ }
+ if (props.containsKey(AGPoolProp.maxIdle)) {
+ maxIdle = Integer.parseInt(props.get(AGPoolProp.maxIdle));
+ }
+ if (props.containsKey(AGPoolProp.minIdle)) {
+ minIdle = Integer.parseInt(props.get(AGPoolProp.minIdle));
+ }
+ if (props.containsKey(AGPoolProp.maxActive)) {
+ maxActive = Integer.parseInt(props.get(AGPoolProp.maxActive));
+ }
+ if (props.containsKey(AGPoolProp.maxWait)) {
+ maxWait = Long.parseLong(props.get(AGPoolProp.maxWait));
+ }
+ if (props.containsKey(AGPoolProp.testOnBorrow)) {
+ testOnBorrow = Boolean.valueOf(props.get(AGPoolProp.testOnBorrow));
+ }
+ if (props.containsKey(AGPoolProp.testOnReturn)) {
+ testOnReturn = Boolean.valueOf(props.get(AGPoolProp.testOnReturn));
+ }
+ if (props.containsKey(AGPoolProp.timeBetweenEvictionRunsMillis)) {
+ timeBetweenEvictionRunsMillis = Long.parseLong(props.get(AGPoolProp.timeBetweenEvictionRunsMillis));
+ }
+ if (props.containsKey(AGPoolProp.minEvictableIdleTimeMillis)) {
+ minEvictableIdleTimeMillis = Long.parseLong(props.get(AGPoolProp.minEvictableIdleTimeMillis));
+ }
+ if (props.containsKey(AGPoolProp.testWhileIdle)) {
+ testWhileIdle = Boolean.valueOf(props.get(AGPoolProp.testWhileIdle));
+ }
+ if (props.containsKey(AGPoolProp.softMinEvictableIdleTimeMillis)) {
+ softMinEvictableIdleTimeMillis = Long.parseLong(props.get(AGPoolProp.softMinEvictableIdleTimeMillis));
+ }
+ if (props.containsKey(AGPoolProp.numTestsPerEvictionRun)) {
+ numTestsPerEvictionRun = Integer.parseInt(props.get(AGPoolProp.numTestsPerEvictionRun));
+ }
+ }
+
+}
View
113 src/com/franz/agraph/pool/AGPoolProp.java
@@ -0,0 +1,113 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.agraph.pool;
+
+import org.apache.commons.pool.impl.GenericKeyedObjectPool.Config;
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+import com.franz.agraph.repository.AGRepositoryConnection;
+
+/**
+ * Property names for {@link AGPoolConfig}.
+ *
+ * Property names to open a {@link AGRepositoryConnection}.
+ *
+ * <p>TODO: {@link AGRepositoryConnection#setSessionLoadInitFile(boolean)}</p>
+ * <p>TODO: {@link AGRepositoryConnection#addSessionLoadScript(String)}</p>
+ *
+ * Many of these properties are specified and used by {@link GenericObjectPool}.
+ *
+ * @see GenericObjectPool
+ * @see Config
+ */
+public enum AGPoolProp {
+
+ /**
+ * When the pool is created, this many connections will be
+ * initialized, then returned to the pool.
+ * @see AGPoolConfig#initialSize
+ */
+ initialSize,
+
+ /**
+ * When the pool is created, if this is true (default is false),
+ * a hook will be registered to close the pool.
+ * Connections will be closed whether idle or not.
+ *
+ * <p>When the pool is closed, from outside of the hook, the
+ * hook will be {@link Runtime#removeShutdownHook(Thread) removed}
+ * so it is not leaked in the list of hooks.</p>
+ *
+ * @see AGPoolConfig#shutdownHook
+ * @see Runtime#addShutdownHook(Thread)
+ */
+ shutdownHook,
+
+ /**
+ * @see GenericObjectPool#setMinIdle(int)
+ */
+
+ minIdle,
+ /**
+ * @see GenericObjectPool#setMaxIdle(int)
+ */
+
+ maxIdle,
+ /**
+ * @see GenericObjectPool#setMaxActive(int)
+ */
+ maxActive,
+
+ /**
+ * @see GenericObjectPool#setMaxWait(long)
+ */
+ maxWait,
+
+ /**
+ * Ping - Important, because sessions may timeout while idle in the pool.
+ * @see AGRepositoryConnection#ping()
+ * @see GenericObjectPool#setTestOnBorrow(boolean)
+ */
+ testOnBorrow,
+
+ /**
+ * Ping.
+ * @see AGRepositoryConnection#ping()
+ * @see GenericObjectPool#setTestOnReturn(boolean)
+ */
+ testOnReturn,
+
+ /**
+ * @see GenericObjectPool#setTimeBetweenEvictionRunsMillis(long)
+ */
+ timeBetweenEvictionRunsMillis,
+
+ /**
+ * @see GenericObjectPool#setMinEvictableIdleTimeMillis(long)
+ */
+ minEvictableIdleTimeMillis,
+
+ /**
+ * @see GenericObjectPool#setTestWhileIdle(boolean)
+ */
+ testWhileIdle,
+
+ /**
+ * @see GenericObjectPool#setSoftMinEvictableIdleTimeMillis(long)
+ */
+ softMinEvictableIdleTimeMillis,
+
+ /**
+ * @see GenericObjectPool#setNumTestsPerEvictionRun(int)
+ */
+ numTestsPerEvictionRun,
+
+ // TODO whenExhaustedAction
+
+}
View
28 src/com/franz/agraph/pool/package-info.java
@@ -0,0 +1,28 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+/**
+ * <p>Connection pool implementation for AllegroGraph.
+ * </p>
+ *
+ * <p>The recommended way to use this package is with
+ * {@link com.franz.agraph.pool.AGConnPool}
+ * or with the {@link com.franz.agraph.pool.AGConnPoolJndiFactory}
+ * using JNDI configuration of your webserver.
+ * </p>
+ *
+ * <p>The Pool library is required to use this package:
+ * <a href="http://commons.apache.org/pool/">Apache Commons Pool, commons-pool-1.5.6.jar</a>
+ * </p>
+ *
+ * @see com.franz.agraph.pool.AGConnPool
+ * @see com.franz.agraph.pool.AGConnPoolJndiFactory
+ *
+ * @since v4.3.3
+ */
+package com.franz.agraph.pool;
View
7 src/com/franz/agraph/repository/AGRepositoryConnection.java
@@ -169,6 +169,13 @@ public AGRepositoryConnection(AGVirtualRepository repository, AGHttpRepoClient c
setStreamResults("true".equals(System.getProperty("com.franz.agraph.repository.AGRepositoryConnection.streamResults")));
vf = new AGValueFactory(repository.wrapped, this);
}
+
+ @Override
+ public String toString() {
+ return "{" + super.toString()
+ + " " + repoclient
+ + "}";
+ }
/*
* @Override protected void finalize() throws Throwable { try { if
View
6 src/com/franz/util/Closeable.java
@@ -16,11 +16,11 @@
* checked exception.
*/
public interface Closeable {
-
+
/**
* Releases system resources associated with the object.
* @see java.io.Closeable#close()
*/
- void close() throws Exception;
-
+ void close() throws Exception;
+
}
View
197 src/com/franz/util/Closer.java
@@ -0,0 +1,197 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package com.franz.util;
+
+import info.aduna.iteration.CloseableIteration;
+
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.hp.hpl.jena.rdf.model.Model;
+
+/**
+ * Extend this class to add easy ability to safely close various resources.
+ *
+ * <p>TODO: track lastUsed and add method to removeAbandoned (beyond a timeout)</p>
+ *
+ * <p>Also, static Close functions for various object types.
+ * These close functions are null safe and will catch Exception
+ * and call log.warn instead of throwing.</p>
+ *
+ * @since v4.3.3
+ */
+public class Closer implements Closeable {
+
+ private final static Logger log = LoggerFactory.getLogger(Closer.class);
+
+ private final Deque toClose = new LinkedList();
+
+ /**
+ * Add a resource to be closed with {@link #close()}.
+ */
+ public <Obj extends Object>
+ Obj closeLater(Obj o) {
+ toClose.push(o);
+ return o;
+ }
+
+ /**
+ * Remove from {@link #closeLater(Object)}.
+ */
+ public boolean remove(Object o) {
+ return toClose.remove(o);
+ }
+
+ /**
+ * Must be called in a finally block, to close all resources
+ * added with closeLater().
+ */
+ @Override
+ public void close() {
+ while (toClose.isEmpty() == false) {
+ close( toClose.pop() );
+ }
+ }
+
+ /**
+ * Close all objects immediately, will not be closed "later".
+ */
+ public Collection closeAll(Collection objects) {
+ for (Object object : objects) {
+ close(object);
+ }
+ return null;
+ }
+
+ /**
+ * Close an object immediately, will not be closed "later".
+ */
+ public <Obj extends Object>
+ Obj close(Obj o) {
+ o = Close(o);
+ while (toClose.remove(o)) {
+ }
+ return o;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + super.toString()
+ + " openObjects=" + toClose.size()
+ + "}";
+ }
+
+ public static <Obj extends Object>
+ Obj Close(Obj o) {
+ if (o instanceof Closeable) {
+ return (Obj) Close((Closeable)o);
+ } else if (o instanceof java.io.Closeable) {
+ return (Obj) Close((java.io.Closeable)o);
+ } else if (o instanceof CloseableIteration) {
+ return (Obj) Close((CloseableIteration)o);
+ } else if (o instanceof XMLStreamReader) {
+ return (Obj) Close((XMLStreamReader)o);
+ } else if (o instanceof MultiThreadedHttpConnectionManager) {
+ return (Obj) Close((MultiThreadedHttpConnectionManager)o);
+ } else if (o instanceof Model) {
+ return (Obj) Close((Model)o);
+ } else if (o != null) {
+ try {
+ o.getClass().getMethod("close").invoke(o);
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static <CloseableType extends Closeable>
+ CloseableType Close(CloseableType o) {
+ if (o != null) {
+ try {
+ o.close();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static <CloseableType extends java.io.Closeable>
+ CloseableType Close(CloseableType o) {
+ if (o != null) {
+ try {
+ o.close();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static MultiThreadedHttpConnectionManager Close(MultiThreadedHttpConnectionManager o) {
+ if (o != null) {
+ try {
+ o.shutdown();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static <Elem extends Object, Exc extends Exception>
+ CloseableIteration<Elem, Exc> Close(CloseableIteration<Elem, Exc> o) {
+ if (o != null) {
+ try {
+ o.close();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static XMLStreamReader Close(XMLStreamReader o) {
+ if (o != null) {
+ try {
+ o.close();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+ public static Model Close(Model o) {
+ if (o != null) {
+ try {
+ o.close();
+ } catch (Exception e) {
+ log.warn("ignoring error with close", e);
+ return o;
+ }
+ }
+ return null;
+ }
+
+}
View
94 src/com/franz/util/Util.java
@@ -13,78 +13,30 @@
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-/**
- * Close functions for various object types.
- * These close functions are null safe and will catch Exception
- * and call log.warn instead of throwing.
- */
public class Util {
-
- final static Logger logger = LoggerFactory.getLogger(Util.class);
-
+
public static <CloseableType extends Closeable>
- CloseableType close(CloseableType o) {
- if (o != null) {
- try {
- o.close();
- } catch (Exception e) {
- if (logger.isWarnEnabled())
- logger.warn("ignoring error with close:" + e);
- }
- }
- return null;
- }
-
- public static <CloseableType extends java.io.Closeable>
- CloseableType close(CloseableType o) {
- if (o != null) {
- try {
- o.close();
- } catch (Exception e) {
- if (logger.isWarnEnabled())
- logger.warn("ignoring error with close:" + e);
- }
- }
- return null;
- }
-
- public static MultiThreadedHttpConnectionManager close(MultiThreadedHttpConnectionManager o) {
- if (o != null) {
- try {
- o.shutdown();
- } catch (Exception e) {
- if (logger.isWarnEnabled())
- logger.warn("ignoring error with close:" + e);
- }
- }
- return null;
- }
-
- public static CloseableIteration close(CloseableIteration o) {
- if (o != null) {
- try {
- o.close();
- } catch (Exception e) {
- if (logger.isWarnEnabled())
- logger.warn("ignoring error with close:" + e);
- }
- }
- return null;
- }
-
- public static XMLStreamReader close(XMLStreamReader o) {
- if (o != null) {
- try {
- o.close();
- } catch (Exception e) {
- if (logger.isWarnEnabled())
- logger.warn("ignoring error with close:" + e);
- }
- }
- return null;
- }
-
+ CloseableType close(CloseableType o) {
+ return Closer.Close(o);
+ }
+
+ public static <CloseableType extends java.io.Closeable>
+ CloseableType close(CloseableType o) {
+ return Closer.Close(o);
+ }
+
+ public static MultiThreadedHttpConnectionManager close(MultiThreadedHttpConnectionManager o) {
+ return Closer.Close(o);
+ }
+
+ public static <Elem extends Object, Exc extends Exception>
+ CloseableIteration<Elem, Exc> close(CloseableIteration<Elem, Exc> o) {
+ return Closer.Close(o);
+ }
+
+ public static XMLStreamReader close(XMLStreamReader o) {
+ return Closer.Close(o);
+ }
+
}
View
1 src/test/AGAbstractTest.java
@@ -41,6 +41,7 @@
import com.franz.agraph.repository.AGRepositoryConnection;
import com.franz.agraph.repository.AGServer;
import com.franz.agraph.repository.AGValueFactory;
+import com.franz.util.Closer;
public class AGAbstractTest extends Closer {
View
67 src/test/Closer.java
@@ -1,67 +0,0 @@
-/******************************************************************************
-** Copyright (c) 2008-2011 Franz Inc.
-** All rights reserved. This program and the accompanying materials
-** are made available under the terms of the Eclipse Public License v1.0
-** which accompanies this distribution, and is available at
-** http://www.eclipse.org/legal/epl-v10.html
-******************************************************************************/
-
-package test;
-
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import com.franz.util.Closeable;
-
-/**
- * Extend this class to add easy ability to safely close various resources.
- *
- * TODO: move class to com.franz.util.
- */
-public abstract class Closer implements Closeable {
-
- private final List toClose = new LinkedList();
-
- /**
- * Add a resource to be closed with {@link #close()}.
- */
- public <Obj extends Object>
- Obj closeLater(Obj o) {
- toClose.add(o);
- return o;
- }
-
- /**
- * Must be called in a finally block, to close all resources
- * added with closeLater().
- */
- @Override
- public void close() {
- while (toClose.isEmpty() == false) {
- close( toClose.get(0) );
- }
- }
-
- /**
- * Close all objects immediately, will not be closed "later".
- */
- public Collection closeAll(Collection objects) {
- for (Object object : objects) {
- close(object);
- }
- return null;
- }
-
- /**
- * Close an object immediately, will not be closed "later".
- */
- public <Obj extends Object>
- Obj close(Obj o) {
- test.Util.close(o);
- while (toClose.remove(o)) {
- }
- return null;
- }
-
-}
View
45 src/test/Util.java
@@ -8,8 +8,6 @@
package test;
-import info.aduna.iteration.CloseableIteration;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@@ -24,56 +22,15 @@
import java.util.Random;
import java.util.zip.GZIPOutputStream;
-import com.franz.util.Closeable;
-
public class Util extends com.franz.util.Util {
-
-
+
public static String get(String[] arr, int i, String defaultVal) {
if (arr != null && arr.length > i) {
return arr[i];
}
return defaultVal;
}
- /**
- * TODO: move to com.franz.util.Util
- */
- public static <Elem extends Object, Exc extends Exception>
- CloseableIteration<Elem, Exc> close(CloseableIteration<Elem, Exc> o) {
- if (o != null) {
- try {
- o.close();
- } catch (Exception e) {
- System.err.println("ignoring error with close:" + e);
- e.printStackTrace();
- }
- }
- return null;
- }
-
- /**
- * TODO: move to com.franz.util.Util
- */
- public static <Obj extends Object>
- Obj close(Obj o) {
- if (o instanceof Closeable) {
- com.franz.util.Util.close((Closeable)o);
- } else if (o instanceof java.io.Closeable) {
- com.franz.util.Util.close((java.io.Closeable)o);
- } else if (o instanceof CloseableIteration) {
- close((CloseableIteration)o);
- } else if (o != null) {
- try {
- o.getClass().getMethod("close").invoke(o);
- } catch (Exception e) {
- System.err.println("ignoring error with close:" + e);
- e.printStackTrace();
- }
- }
- return null;
- }
-
public static List<String> readLines(File file) {
List list = new ArrayList<String>();
FileReader f = null;
View
92 src/test/stress/AGConnPoolTest.java
@@ -0,0 +1,92 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package test.stress;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.repository.RepositoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import test.AGAbstractTest;
+
+import com.franz.agraph.pool.AGConnPool;
+import com.franz.agraph.pool.AGConnProp;
+import com.franz.agraph.pool.AGPoolProp;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.util.Util;
+
+public class AGConnPoolTest {
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private static final int NUM = 10;
+
+ private AGConnPool pool;
+
+ @Before
+ public void connect() throws RepositoryException {
+ Assert.assertNull(pool);
+ pool = AGConnPool.create(
+ AGConnProp.serverUrl, AGAbstractTest.findServerUrl(),
+ AGConnProp.username, AGAbstractTest.username(),
+ AGConnProp.password, AGAbstractTest.password(),
+ AGConnProp.catalog, AGAbstractTest.CATALOG_ID,
+ AGConnProp.repository, "javatest",
+ AGConnProp.session, AGConnProp.Session.DEDICATED,
+ AGPoolProp.shutdownHook, true,
+ AGPoolProp.initialSize, 2
+ // AGPoolProp.maxActive, 5,
+ // AGPoolProp.maxWait, TimeUnit.MINUTES.toMillis(5),
+ // AGPoolProp.maxIdle, 2,
+ // AGPoolProp.minIdle, 2,
+ // AGPoolProp.timeBetweenEvictionRunsMillis, TimeUnit.MINUTES.toMillis(5),
+ // AGPoolProp.testWhileIdle, true
+ );
+ }
+
+ @After
+ public void closePool() {
+ log.info("closing " + pool);
+ AGConnPool closed = Util.close(pool);
+ log.info("closed " + pool);
+ pool = closed;
+ }
+
+ @Test
+ public void openAG() throws Exception {
+ int activeConnections = pool.getNumActive();
+ ExecutorService exec = Executors.newFixedThreadPool(NUM);
+ for (int i = 0; i < NUM; i++) {
+ exec.execute(new Runnable() {
+ public void run() {
+ try {
+ AGRepositoryConnection conn = pool.borrowConnection();
+ try {
+ conn.ping();
+ } finally {
+ conn.close();
+ }
+ } catch (Throwable e) {
+ log.error(this.toString(), e);
+ }
+ }
+ });
+ }
+ exec.awaitTermination(30, TimeUnit.SECONDS);
+ Assert.assertEquals(pool.toString(), activeConnections, pool.getNumActive());
+ }
+
+}
View
5 src/test/stress/Events.java
@@ -30,7 +30,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
@@ -50,15 +49,12 @@
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryLanguage;
-import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.query.TupleQueryResultHandler;
import org.openrdf.query.TupleQueryResultHandlerException;
import org.openrdf.repository.RepositoryException;
import org.openrdf.rio.ntriples.NTriplesUtil;
-import test.Closer;
-
import com.franz.agraph.repository.AGCatalog;
import com.franz.agraph.repository.AGQueryLanguage;
import com.franz.agraph.repository.AGRepository;
@@ -67,6 +63,7 @@
import com.franz.agraph.repository.AGStreamTupleQuery;
import com.franz.agraph.repository.AGTupleQuery;
import com.franz.util.Closeable;
+import com.franz.util.Closer;
import com.franz.util.Util;
public class Events extends Closer {
View
32 src/test/web/AGExampleServletContextListener.java
@@ -0,0 +1,32 @@
+package test.web;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import com.franz.agraph.pool.AGConnPool;
+import com.franz.util.Closer;
+
+public class AGExampleServletContextListener implements ServletContextListener {
+
+ @Override
+ public void contextDestroyed(ServletContextEvent event) {
+ Closer c = new Closer();
+ try {
+ Context initCtx = c.closeLater(new InitialContext());
+ Context envCtx = (Context) c.closeLater(initCtx.lookup("java:comp/env"));
+ AGConnPool pool = (AGConnPool) envCtx.lookup("connection-pool/agraph");
+ pool.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally{
+ c.close();
+ }
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent event) {
+ }
+
+}
View
69 src/test/web/AGTestServlet.java
@@ -0,0 +1,69 @@
+/******************************************************************************
+** Copyright (c) 2008-2011 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package test.web;
+
+import java.io.IOException;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.franz.agraph.pool.AGConnPool;
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.util.Closer;
+
+public class AGTestServlet extends HttpServlet {
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private static final long serialVersionUID = 770497520167657818L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ Closer c = new Closer();
+ try {
+ Context initCtx = c.closeLater(new InitialContext());
+ Context envCtx = (Context) c.closeLater(initCtx.lookup("java:comp/env"));
+ AGConnPool pool = (AGConnPool) envCtx.lookup("connection-pool/agraph");
+ AGRepositoryConnection conn = c.closeLater(pool.borrowConnection());
+
+ resp.getWriter().println("size=" + conn.size());
+ resp.getWriter().flush();
+ } catch (Exception e) {
+ throw new ServletException(e);
+ } finally{
+ c.close();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ Closer c = new Closer();
+ AGConnPool pool = null;
+ try {
+ Context initCtx = c.closeLater(new InitialContext());
+ Context envCtx = (Context) c.closeLater(initCtx.lookup("java:comp/env"));
+ pool = (AGConnPool) envCtx.lookup("connection-pool/agraph");
+ pool.close();
+ } catch (Exception e) {
+ RuntimeException re = new RuntimeException("Error closing the AGConnPool: " + pool, e);
+ log.error(re.getMessage(), re);
+ throw re;
+ } finally{
+ c.close();
+ }
+ }
+
+}
View
32 src/test/web/web.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <description>
+ Example use of AllegroGraph in a web context.
+ </description>
+ <display-name>agraph-java-client web example</display-name>
+
+ <servlet>
+ <servlet-name>agraph-test</servlet-name>
+ <servlet-class>test.web.AGTestServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>agraph-test</servlet-name>
+ <url-pattern>/agraph-test</url-pattern>
+ </servlet-mapping>
+
+ <resource-env-ref>
+ <description>AllegroGraph connection pool</description>
+ <resource-env-ref-name>connection-pool/agraph</resource-env-ref-name>
+ <resource-env-ref-type>com.franz.agraph.pool.AGConnPool</resource-env-ref-type>
+ </resource-env-ref>
+
+ <listener>
+ <listener-class></listener-class>
+ </listener>
+
+</web-app>
View
1 templates/.classpath
@@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry exported="true" kind="lib" path="lib/agraph-VERSION.jar" sourcepath="lib/agraph-VERSION-src.jar"/>
<classpathentry kind="lib" path="lib/json.jar"/>
+ <classpathentry kind="lib" path="lib/commons-pool-1.5.6.jar"/>
<classpathentry kind="lib" path="lib/logging/commons-logging-1.1.1.jar" sourcepath="lib/logging/commons-logging-1.1.1-sources.jar"/>
<classpathentry kind="lib" path="lib/logging/slf4j-api-1.6.1.jar" sourcepath="lib/logging/slf4j-api-1.6.1-sources.jar"/>
<classpathentry kind="lib" path="lib/logging/slf4j-jcl-1.6.1.jar" sourcepath="lib/logging/slf4j-jcl-1.6.1-sources.jar"/>

0 comments on commit 50ae9c2

Please sign in to comment.