Index: project.xml
===================================================================
--- project.xml (Revision 422888)
+++ project.xml (Arbeitskopie)
@@ -36,10 +36,20 @@
+ * <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> + *+ * The JDOFilter supports two filter initialization paramters: + *
pmfPropsResource: the name of the PersistenceManagerFactory
+ * properties resource. Default is /WEB-INF/pmf.properties.pmRequestAttrName: the name of the request attribute used to
+ * store the PersistenceManager instance. Default is jdoPM.
+ * + * <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> + *+ * 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. + *
+ * 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. + *
+ * 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( + "Cannot find JDO PersistenceManagerFactory properties resource " + + (usingPMFPropsResourceDefault ? "defaulted to " : "") + + pmfPropsResource); + logger.error("Error during initialization of JDOFilter", ex); + 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( + "Request attribute " + pmRequestAttrName + + " already defined as " + + request.getAttribute(pmRequestAttrName)); + logger.error( + "JDOFilter: error during PersistenceManager intialization", ex); + 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); + } + } + +}