You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2006/07/23 22:49:34 UTC
svn commit: r424813 - in /db/jdo/trunk/util20: project.xml
src/java/org/apache/jdo/util/web/
src/java/org/apache/jdo/util/web/JDOFilter.java
Author: mbo
Date: Sun Jul 23 13:49:34 2006
New Revision: 424813
URL: http://svn.apache.org/viewvc?rev=424813&view=rev
Log:
JDO-398: Add servlet Filter utility class managing a PersistenceManager per request
Added:
db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/
db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/JDOFilter.java
Modified:
db/jdo/trunk/util20/project.xml
Modified: db/jdo/trunk/util20/project.xml
URL: http://svn.apache.org/viewvc/db/jdo/trunk/util20/project.xml?rev=424813&r1=424812&r2=424813&view=diff
==============================================================================
--- db/jdo/trunk/util20/project.xml (original)
+++ db/jdo/trunk/util20/project.xml Sun Jul 23 13:49:34 2006
@@ -36,9 +36,19 @@
<!-- ============ -->
<dependencies>
<dependency>
+ <groupId>javax.jdo</groupId>
+ <artifactId>jdo2-api</artifactId>
+ <version>SNAPSHOT</version>
+ </dependency>
+ <dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.4_spec</artifactId>
+ <version>1.0.1</version>
</dependency>
</dependencies>
<!-- =================== -->
Added: db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/JDOFilter.java
URL: http://svn.apache.org/viewvc/db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/JDOFilter.java?rev=424813&view=auto
==============================================================================
--- db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/JDOFilter.java (added)
+++ db/jdo/trunk/util20/src/java/org/apache/jdo/util/web/JDOFilter.java Sun Jul 23 13:49:34 2006
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.jdo.util.web;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import javax.jdo.JDOHelper;
+import javax.jdo.PersistenceManagerFactory;
+import javax.jdo.PersistenceManager;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This implementation of the servlet Filter interface creates a JDO
+ * PersistenceManager, stores it as a request attribute and as a ThreadLocal.
+ * It closes the PersistenceManager after the filter chain has returned.
+ * The idea for this class is taken from the JavaOne 2003 presentation
+ * "Using Struts with Java Data Objects" by Craig Russell, Craig McClanahan
+ * and Amy Roh.
+ * <p>
+ * To setup the filter add the following to your deployment descriptor:
+ * <pre>
+ * <filter>
+ * <filter-name>JDOFilter</filter-name>
+ * <filter-class>org.apache.jdo.util.web.JDOFilter</filter-class>
+ * </filter>
+ * <filter-mapping>
+ * <filter-name>JDOFilter</filter-name>
+ * <url-pattern>/*</url-pattern>
+ * </filter-mapping>
+ * </pre>
+ * The JDOFilter supports two filter initialization paramters:
+ * <ul>
+ * <li><code>pmfPropsResource</code>: the name of the PersistenceManagerFactory
+ * properties resource. Default is <code>/WEB-INF/pmf.properties</code>.</li>
+ * <li><code>pmRequestAttrName</code>: the name of the request attribute used to
+ * store the PersistenceManager instance. Default is <code>jdoPM</code>.
+ * </ul>
+ * This is an sample filter definition using initialization parameter:
+ * <pre>
+ * <filter>
+ * <filter-name>JDOFilter</filter-name>
+ * <filter-class>org.apache.jdo.util.web.JDOFilter</filter-class>
+ * <init-param>
+ * <param-name>pmfPropsResource</param-name>
+ * <param-value>/WEB-INF/pmf.properties</param-value>
+ * </init-param>
+ * <init-param>
+ * <param-name>pmRequestAttrName</param-name>
+ * <param-value>jdoPM</param-value>
+ * </init-param>
+ * </filter>
+ * </pre>
+ * It is possible to define multiple filters in the deployment descriptor, all
+ * using the JDOFilter class. In this case it is important to specify the name
+ * of the PersistenceManager request attribute in the filter configuration by
+ * setting the pmRequestAttrName initialization paramter. Otherwise, the
+ * different filter instances would try to use the same request attribute.
+ * Please note, in case of multiple JDOFilter instances, only the first filter
+ * stores its PersistenceManager as a ThreadLocal.
+ * <p>
+ * The static method {@link #getThreadLocalPM()} allows retrieving the
+ * PersistenceManager instance bound to the current thread.
+ */
+public class JDOFilter
+ implements Filter {
+
+ /** The name of the JDOFilter initialization parameter allowing to specify
+ * the name of the pmf properties resource. */
+ public static final String PMF_PROPS_RESOURCE_PARAM = "pmfPropsResource";
+
+ /** The default PMF properties resource. */
+ public static final String PMF_PROPS_RESOURCE_DEFAULT =
+ "/WEB-INF/pmf.properties";
+
+ /** The name of the JDOFilter initialization parameter allowing to specify
+ * the name of the pm request attribute. */
+ public static final String PM_REQUEST_ATTR_NAME_PARAM = "pmRequestAttrName";
+
+ /** The name of the request attribute storing the PersistenceManager. */
+ public static final String PM_REQUEST_ATTR_NAME_DEFAULT = "jdoPM";
+
+ /** The ThreadLocal storing the PersistenceManager. */
+ private static ThreadLocal pmThreadLocal = new ThreadLocal();
+
+ /** Logging support. */
+ private static final Log logger = LogFactory.getLog(JDOFilter.class);
+
+ /** The PMF instance created by the init method. */
+ private PersistenceManagerFactory pmf;
+
+ /** The name of the request attribute holding the PersistenceManager. */
+ private String pmRequestAttrName;
+
+ // ===== javax.servlet.Filter methods =====
+
+ /** Called by the web container to indicate to a filter that it is being
+ * placed into service.
+ * <p>
+ * This implementation creates a JDO PersistenceManagerFactory instance
+ * using a properties resource specified by an initialization parameter
+ * called {@link #PMF_PROPS_RESOURCE_PARAM} or defaulted to
+ * {@link #PMF_PROPS_RESOURCE_DEFAULT}. The method checks for another
+ * initialization parameter {@link #PM_REQUEST_ATTR_NAME_PARAM} that may be
+ * used to specify the name of the request attribute holding the
+ * PersistenceManager instance. The name defaults to
+ * {@link #PM_REQUEST_ATTR_NAME_DEFAULT} if there is no such initialization
+ * parameter.
+ * @param filterConfig the filter configuration object.
+ */
+ public void init(FilterConfig filterConfig) throws ServletException {
+ String pmfPropsResource =
+ filterConfig.getInitParameter(PMF_PROPS_RESOURCE_PARAM);
+ boolean usingPMFPropsResourceDefault = false;
+ if ((pmfPropsResource == null) || pmfPropsResource.length() == 0) {
+ pmfPropsResource = PMF_PROPS_RESOURCE_DEFAULT;
+ usingPMFPropsResourceDefault = true;
+ }
+ ServletContext context = filterConfig.getServletContext();
+ InputStream stream = context.getResourceAsStream(pmfPropsResource);
+ if (stream == null) {
+ ServletException ex = new ServletException(
+ "Error during initialization of JDOFilter: " +
+ "Cannot find JDO PMF properties resource " +
+ (usingPMFPropsResourceDefault ? "defaulted to " : "") +
+ pmfPropsResource);
+ throw ex;
+ }
+ pmf = JDOHelper.getPersistenceManagerFactory(stream);
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "Created PMF " + pmf +
+ "\nPMF properties: " +
+ "\n\t driver = " + pmf.getConnectionDriverName() +
+ "\n\t URL = " + pmf.getConnectionURL() +
+ "\n\t userName = " + pmf.getConnectionUserName() +
+ "\n\t mapping = " + pmf.getMapping() +
+ "\n\t optimistic = " + pmf.getOptimistic() +
+ "\n\t retainValues = " + pmf.getRetainValues() +
+ "\n\t restoreValues = " + pmf.getRestoreValues() +
+ "\n\t nonTxRead = " + pmf.getNontransactionalRead() +
+ "\n\t nonTxWrite = " + pmf.getNontransactionalWrite() +
+ "\n\t ignoreCache = " + pmf.getIgnoreCache() +
+ "\n\t detachOnCommit = " + pmf.getDetachAllOnCommit() +
+ "\n\t properties = " + pmf.getProperties());
+ }
+
+ // get the name of the request attribute holding the PersistenceManager
+ pmRequestAttrName =
+ filterConfig.getInitParameter(PM_REQUEST_ATTR_NAME_PARAM);
+ if ((pmRequestAttrName == null) || pmRequestAttrName.length() == 0) {
+ pmRequestAttrName = PM_REQUEST_ATTR_NAME_DEFAULT;
+ }
+ }
+
+ /** The doFilter method of the Filter is called by the container each time a
+ * request/response pair is passed through the chain due to a client request
+ * for a resource at the end of the chain. This implementation creates a
+ * PersistenceManager, stores it as a request attribute and in a ThreadLocal
+ * and then calls the filter chain. It closes the PersistenceManager after
+ * the chain returns.
+ * @param request the resquest
+ * @param response the response
+ * @param chain the filter chain
+ */
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain)
+ throws IOException, ServletException {
+ PersistenceManager pm = null;
+ try {
+ pm = initializePM(request);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Created PM " + pm);
+ }
+ chain.doFilter(request, response);
+ } finally {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Close PM " + pm);
+ }
+ finalizePM(request, pm);
+ }
+ }
+
+ /** Called by the web container to indicate to a filter that it is being
+ * taken out of service. This implementation closes the
+ * PersistenceManagerFactory.
+ */
+ public void destroy() {
+ if (pmf != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Close PMF " + pmf);
+ }
+ pmf.close();
+ pmf = null;
+ }
+ }
+
+ // ===== Public methods not defined in Filter =====
+
+ /** Returns the PersistenceManager instance bound to the current thread
+ * using a ThreadLocal.
+ * @return the PersistenceManager bound to the current thread.
+ */
+ public static PersistenceManager getThreadLocalPM() {
+ return (PersistenceManager)pmThreadLocal.get();
+ }
+
+ // ===== Internal helper methods =====
+
+ /** Helper method to create and store a new PersistenceManager. The method
+ * stores the PersistenceManager as value of the request attribute denoted
+ * by {@link #pmRequestAttrName} and in a ThreadLocal. Use static method
+ * {@link #getThreadLocalPM()} to retrieve the PersistenceManager instance
+ * bound to the current thread.
+ * @param request the request to store in the PersistenceManager as
+ * attribute.
+ * @return a new PersistenceManager
+ */
+ private PersistenceManager initializePM(ServletRequest request)
+ throws ServletException {
+ PersistenceManager pm = pmf.getPersistenceManager();
+ if (request.getAttribute(pmRequestAttrName) != null) {
+ ServletException ex = new ServletException(
+ "JDOFilter: error during PM intialization, request attribute " +
+ pmRequestAttrName + " already defined as " +
+ request.getAttribute(pmRequestAttrName));
+ throw ex;
+ }
+ request.setAttribute(pmRequestAttrName, pm);
+ if (pmThreadLocal.get() == null) {
+ pmThreadLocal.set(pm);
+ }
+ return pm;
+ }
+
+ /** Helper method to finalize a PersistenceManager. The method closes the
+ * specified PersistenceManager. If there is an active transaction it is
+ * rolled back before the PersistenceManager is closed. The method also
+ * removes the request attribute holding a PersistenceManager and nullifies
+ * the ThreadLocal value, but only if it is the specified
+ * PersistenceManager.
+ * @param request the request holding the PersistenceManager as attribute.
+ * @param the PersistenceManager
+ */
+ private void finalizePM(ServletRequest request, PersistenceManager pm) {
+ if (pm == null) {
+ return;
+ }
+ if (!pm.isClosed()) {
+ if (pm.currentTransaction().isActive()) {
+ pm.currentTransaction().rollback();
+ }
+ pm.close();
+ }
+ // Remove request attribute if it is the specified pm
+ if (request.getAttribute(pmRequestAttrName) == pm) {
+ request.removeAttribute(pmRequestAttrName);
+ }
+ // Nullify the thread local if it is the specified pm
+ if (pmThreadLocal.get() == pm) {
+ pmThreadLocal.set(null);
+ }
+ }
+
+}