1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jdo.util.web;
18
19 import java.io.InputStream;
20 import java.io.IOException;
21
22 import javax.jdo.JDOHelper;
23 import javax.jdo.PersistenceManagerFactory;
24 import javax.jdo.PersistenceManager;
25
26 import javax.servlet.Filter;
27 import javax.servlet.FilterChain;
28 import javax.servlet.FilterConfig;
29 import javax.servlet.ServletContext;
30 import javax.servlet.ServletException;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.ServletResponse;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 /***
38 * This implementation of the servlet Filter interface creates a JDO
39 * PersistenceManager, stores it as a request attribute and as a ThreadLocal.
40 * It closes the PersistenceManager after the filter chain has returned.
41 * The idea for this class is taken from the JavaOne 2003 presentation
42 * "Using Struts with Java Data Objects" by Craig Russell, Craig McClanahan
43 * and Amy Roh.
44 * <p>
45 * To setup the filter add the following to your deployment descriptor:
46 * <pre>
47 * <filter>
48 * <filter-name>JDOFilter</filter-name>
49 * <filter-class>org.apache.jdo.util.web.JDOFilter</filter-class>
50 * </filter>
51 * <filter-mapping>
52 * <filter-name>JDOFilter</filter-name>
53 * <url-pattern>/*</url-pattern>
54 * </filter-mapping>
55 * </pre>
56 * The JDOFilter supports two filter initialization paramters:
57 * <ul>
58 * <li><code>pmfPropsResource</code>: the name of the PersistenceManagerFactory
59 * properties resource. Default is <code>/WEB-INF/pmf.properties</code>.</li>
60 * <li><code>pmRequestAttrName</code>: the name of the request attribute used to
61 * store the PersistenceManager instance. Default is <code>jdoPM</code>.
62 * </ul>
63 * This is an sample filter definition using initialization parameter:
64 * <pre>
65 * <filter>
66 * <filter-name>JDOFilter</filter-name>
67 * <filter-class>org.apache.jdo.util.web.JDOFilter</filter-class>
68 * <init-param>
69 * <param-name>pmfPropsResource</param-name>
70 * <param-value>/WEB-INF/pmf.properties</param-value>
71 * </init-param>
72 * <init-param>
73 * <param-name>pmRequestAttrName</param-name>
74 * <param-value>jdoPM</param-value>
75 * </init-param>
76 * </filter>
77 * </pre>
78 * It is possible to define multiple filters in the deployment descriptor, all
79 * using the JDOFilter class. In this case it is important to specify the name
80 * of the PersistenceManager request attribute in the filter configuration by
81 * setting the pmRequestAttrName initialization paramter. Otherwise, the
82 * different filter instances would try to use the same request attribute.
83 * Please note, in case of multiple JDOFilter instances, only the first filter
84 * stores its PersistenceManager as a ThreadLocal.
85 * <p>
86 * The static method {@link #getThreadLocalPM()} allows retrieving the
87 * PersistenceManager instance bound to the current thread.
88 */
89 public class JDOFilter
90 implements Filter {
91
92 /*** The name of the JDOFilter initialization parameter allowing to specify
93 * the name of the pmf properties resource. */
94 public static final String PMF_PROPS_RESOURCE_PARAM = "pmfPropsResource";
95
96 /*** The default PMF properties resource. */
97 public static final String PMF_PROPS_RESOURCE_DEFAULT =
98 "/WEB-INF/pmf.properties";
99
100 /*** The name of the JDOFilter initialization parameter allowing to specify
101 * the name of the pm request attribute. */
102 public static final String PM_REQUEST_ATTR_NAME_PARAM = "pmRequestAttrName";
103
104 /*** The name of the request attribute storing the PersistenceManager. */
105 public static final String PM_REQUEST_ATTR_NAME_DEFAULT = "jdoPM";
106
107 /*** The ThreadLocal storing the PersistenceManager. */
108 private static ThreadLocal pmThreadLocal = new ThreadLocal();
109
110 /*** Logging support. */
111 private static final Log logger = LogFactory.getLog(JDOFilter.class);
112
113 /*** The PMF instance created by the init method. */
114 private PersistenceManagerFactory pmf;
115
116 /*** The name of the request attribute holding the PersistenceManager. */
117 private String pmRequestAttrName;
118
119
120
121 /*** Called by the web container to indicate to a filter that it is being
122 * placed into service.
123 * <p>
124 * This implementation creates a JDO PersistenceManagerFactory instance
125 * using a properties resource specified by an initialization parameter
126 * called {@link #PMF_PROPS_RESOURCE_PARAM} or defaulted to
127 * {@link #PMF_PROPS_RESOURCE_DEFAULT}. The method checks for another
128 * initialization parameter {@link #PM_REQUEST_ATTR_NAME_PARAM} that may be
129 * used to specify the name of the request attribute holding the
130 * PersistenceManager instance. The name defaults to
131 * {@link #PM_REQUEST_ATTR_NAME_DEFAULT} if there is no such initialization
132 * parameter.
133 * @param filterConfig the filter configuration object.
134 */
135 public void init(FilterConfig filterConfig) throws ServletException {
136 String pmfPropsResource =
137 filterConfig.getInitParameter(PMF_PROPS_RESOURCE_PARAM);
138 boolean usingPMFPropsResourceDefault = false;
139 if ((pmfPropsResource == null) || pmfPropsResource.length() == 0) {
140 pmfPropsResource = PMF_PROPS_RESOURCE_DEFAULT;
141 usingPMFPropsResourceDefault = true;
142 }
143 ServletContext context = filterConfig.getServletContext();
144 InputStream stream = context.getResourceAsStream(pmfPropsResource);
145 if (stream == null) {
146 ServletException ex = new ServletException(
147 "Error during initialization of JDOFilter: " +
148 "Cannot find JDO PMF properties resource " +
149 (usingPMFPropsResourceDefault ? "defaulted to " : "") +
150 pmfPropsResource);
151 throw ex;
152 }
153 pmf = JDOHelper.getPersistenceManagerFactory(stream);
154 if (logger.isDebugEnabled()) {
155 logger.debug(
156 "Created PMF " + pmf +
157 "\nPMF properties: " +
158 "\n\t driver = " + pmf.getConnectionDriverName() +
159 "\n\t URL = " + pmf.getConnectionURL() +
160 "\n\t userName = " + pmf.getConnectionUserName() +
161 "\n\t mapping = " + pmf.getMapping() +
162 "\n\t optimistic = " + pmf.getOptimistic() +
163 "\n\t retainValues = " + pmf.getRetainValues() +
164 "\n\t restoreValues = " + pmf.getRestoreValues() +
165 "\n\t nonTxRead = " + pmf.getNontransactionalRead() +
166 "\n\t nonTxWrite = " + pmf.getNontransactionalWrite() +
167 "\n\t ignoreCache = " + pmf.getIgnoreCache() +
168 "\n\t detachOnCommit = " + pmf.getDetachAllOnCommit() +
169 "\n\t properties = " + pmf.getProperties());
170 }
171
172
173 pmRequestAttrName =
174 filterConfig.getInitParameter(PM_REQUEST_ATTR_NAME_PARAM);
175 if ((pmRequestAttrName == null) || pmRequestAttrName.length() == 0) {
176 pmRequestAttrName = PM_REQUEST_ATTR_NAME_DEFAULT;
177 }
178 }
179
180 /*** The doFilter method of the Filter is called by the container each time a
181 * request/response pair is passed through the chain due to a client request
182 * for a resource at the end of the chain. This implementation creates a
183 * PersistenceManager, stores it as a request attribute and in a ThreadLocal
184 * and then calls the filter chain. It closes the PersistenceManager after
185 * the chain returns.
186 * @param request the resquest
187 * @param response the response
188 * @param chain the filter chain
189 */
190 public void doFilter(ServletRequest request, ServletResponse response,
191 FilterChain chain)
192 throws IOException, ServletException {
193 PersistenceManager pm = null;
194 try {
195 pm = initializePM(request);
196 if (logger.isDebugEnabled()) {
197 logger.debug("Created PM " + pm);
198 }
199 chain.doFilter(request, response);
200 } finally {
201 if (logger.isDebugEnabled()) {
202 logger.debug("Close PM " + pm);
203 }
204 finalizePM(request, pm);
205 }
206 }
207
208 /*** Called by the web container to indicate to a filter that it is being
209 * taken out of service. This implementation closes the
210 * PersistenceManagerFactory.
211 */
212 public void destroy() {
213 if (pmf != null) {
214 if (logger.isDebugEnabled()) {
215 logger.debug("Close PMF " + pmf);
216 }
217 pmf.close();
218 pmf = null;
219 }
220 }
221
222
223
224 /*** Returns the PersistenceManager instance bound to the current thread
225 * using a ThreadLocal.
226 * @return the PersistenceManager bound to the current thread.
227 */
228 public static PersistenceManager getThreadLocalPM() {
229 return (PersistenceManager)pmThreadLocal.get();
230 }
231
232
233
234 /*** Helper method to create and store a new PersistenceManager. The method
235 * stores the PersistenceManager as value of the request attribute denoted
236 * by {@link #pmRequestAttrName} and in a ThreadLocal. Use static method
237 * {@link #getThreadLocalPM()} to retrieve the PersistenceManager instance
238 * bound to the current thread.
239 * @param request the request to store in the PersistenceManager as
240 * attribute.
241 * @return a new PersistenceManager
242 */
243 private PersistenceManager initializePM(ServletRequest request)
244 throws ServletException {
245 PersistenceManager pm = pmf.getPersistenceManager();
246 if (request.getAttribute(pmRequestAttrName) != null) {
247 ServletException ex = new ServletException(
248 "JDOFilter: error during PM intialization, request attribute " +
249 pmRequestAttrName + " already defined as " +
250 request.getAttribute(pmRequestAttrName));
251 throw ex;
252 }
253 request.setAttribute(pmRequestAttrName, pm);
254 if (pmThreadLocal.get() == null) {
255 pmThreadLocal.set(pm);
256 }
257 return pm;
258 }
259
260 /*** Helper method to finalize a PersistenceManager. The method closes the
261 * specified PersistenceManager. If there is an active transaction it is
262 * rolled back before the PersistenceManager is closed. The method also
263 * removes the request attribute holding a PersistenceManager and nullifies
264 * the ThreadLocal value, but only if it is the specified
265 * PersistenceManager.
266 * @param request the request holding the PersistenceManager as attribute.
267 * @param the PersistenceManager
268 */
269 private void finalizePM(ServletRequest request, PersistenceManager pm) {
270 if (pm == null) {
271 return;
272 }
273 if (!pm.isClosed()) {
274 if (pm.currentTransaction().isActive()) {
275 pm.currentTransaction().rollback();
276 }
277 pm.close();
278 }
279
280 if (request.getAttribute(pmRequestAttrName) == pm) {
281 request.removeAttribute(pmRequestAttrName);
282 }
283
284 if (pmThreadLocal.get() == pm) {
285 pmThreadLocal.set(null);
286 }
287 }
288
289 }