1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
4 * as indicated by the @author tags. See the copyright.txt file in the
5 * distribution for a full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.web.tomcat.service.jca;
23
24 import java.io.IOException;
25 import java.util.HashSet;
26 import java.util.Set;
27
28 import javax.management.MBeanServer;
29 import javax.management.ObjectName;
30 import javax.resource.ResourceException;
31 import javax.servlet.ServletException;
32 import javax.transaction.Status;
33 import javax.transaction.SystemException;
34 import javax.transaction.Transaction;
35 import javax.transaction.TransactionManager;
36
37 import org.apache.catalina.Lifecycle;
38 import org.apache.catalina.LifecycleException;
39 import org.apache.catalina.LifecycleListener;
40 import org.apache.catalina.Wrapper;
41 import org.apache.catalina.connector.Request;
42 import org.apache.catalina.connector.Response;
43 import org.apache.catalina.valves.ValveBase;
44 import org.apache.catalina.util.LifecycleSupport;
45 import org.jboss.logging.Logger;
46 import org.jboss.mx.util.MBeanServerLocator;
47 import org.jboss.resource.connectionmanager.CachedConnectionManager;
48
49 /**
50 * This valve checks for unclosed connections on a servlet request
51 *
52 * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
53 * @version $Revision: 57206 $
54 */
55 public class CachedConnectionValve extends ValveBase implements Lifecycle
56 {
57 /**
58 * The log
59 */
60 private static final Logger log = Logger.getLogger(CachedConnectionValve.class);
61
62 /**
63 * The info string for this Valve
64 */
65 private static final String info = "CachedConnectionValve/1.0";
66
67 /**
68 * Valve-lifecycle helper object
69 */
70 protected LifecycleSupport support = new LifecycleSupport(this);
71
72 /**
73 * The object name of the cached connection manager
74 */
75 protected String ccmName;
76
77 /**
78 * The cached connection manager
79 */
80 protected CachedConnectionManager ccm;
81
82 /**
83 * The object name of the transaction manager service
84 */
85 protected String tmName;
86
87 /**
88 * The transaction manager
89 */
90 protected TransactionManager tm;
91
92 /**
93 * The unshareable resources
94 */
95 protected Set unsharableResources = new HashSet();
96
97 /**
98 * Create a new valve
99 *
100 * @param ccm the cached connection manager for the valve
101 */
102 public CachedConnectionValve()
103 {
104 super();
105 }
106
107 /**
108 * Get information about this Valve.
109 */
110 public String getInfo()
111 {
112 return info;
113 }
114
115 /**
116 * Get the cached connection manager object name
117 */
118 public String getCachedConnectionManagerObjectName()
119 {
120 return ccmName;
121 }
122
123 /**
124 * Set the cached connection manager object name
125 */
126 public void setCachedConnectionManagerObjectName(String ccmName)
127 {
128 this.ccmName = ccmName;
129 }
130
131 /**
132 * Get the transaction manager object name
133 */
134 public String getTransactionManagerObjectName()
135 {
136 return tmName;
137 }
138
139 /**
140 * Set the transaction manager object name
141 */
142 public void setTransactionManagerObjectName(String tmName)
143 {
144 this.tmName = tmName;
145 }
146
147 public void invoke(Request request, Response response) throws IOException, ServletException
148 {
149 if(ccm == null)
150 throw new IllegalStateException("Uncomment the dependency on CachedConnectionManager"
151 + " in META-INF/jboss-service.xml of jbossweb-tomcatxxx.sar");
152 try
153 {
154 ccm.pushMetaAwareObject(this, unsharableResources);
155 try
156 {
157 getNext().invoke(request, response);
158 }
159 finally
160 {
161 try
162 {
163 ccm.popMetaAwareObject(unsharableResources);
164 }
165 finally
166 {
167 checkTransactionComplete(request);
168 }
169 }
170 }
171 catch (ResourceException e)
172 {
173 throw new ServletException("Error invoking cached connection manager", e);
174 }
175 }
176
177 // Lifecycle-interface
178 public void addLifecycleListener(LifecycleListener listener)
179 {
180 support.addLifecycleListener(listener);
181 }
182
183 public void removeLifecycleListener(LifecycleListener listener)
184 {
185 support.removeLifecycleListener(listener);
186 }
187
188 public LifecycleListener[] findLifecycleListeners()
189 {
190 return support.findLifecycleListeners();
191 }
192
193 public void start() throws LifecycleException
194 {
195 try
196 {
197 MBeanServer server = MBeanServerLocator.locateJBoss();
198 ccm = (CachedConnectionManager) server.getAttribute(new ObjectName(ccmName), "Instance");
199 tm = (TransactionManager) server.getAttribute(new ObjectName(tmName), "TransactionManager");
200 }
201 catch (Exception e)
202 {
203 throw new LifecycleException(e);
204 }
205
206 // TODO unshareable resources
207 support.fireLifecycleEvent(START_EVENT, this);
208 }
209
210 public void stop() throws LifecycleException
211 {
212 support.fireLifecycleEvent(STOP_EVENT, this);
213 unsharableResources.clear();
214 }
215
216 protected void checkTransactionComplete(Request request)
217 {
218 int status = Status.STATUS_NO_TRANSACTION;
219
220 try
221 {
222 status = tm.getStatus();
223 }
224 catch (SystemException ex)
225 {
226 log.error("Failed to get status", ex);
227 }
228
229 try
230 {
231 switch (status)
232 {
233 case Status.STATUS_ACTIVE:
234 case Status.STATUS_COMMITTING:
235 case Status.STATUS_MARKED_ROLLBACK:
236 case Status.STATUS_PREPARING:
237 case Status.STATUS_ROLLING_BACK:
238 try
239 {
240 tm.rollback();
241 }
242 catch (Exception ex)
243 {
244 log.error("Failed to rollback", ex);
245 }
246 // fall through...
247 case Status.STATUS_PREPARED:
248 String servletName = "<Unknown>";
249 try
250 {
251 Wrapper servlet = request.getWrapper();
252 if (servlet != null)
253 {
254 servletName = servlet.getName();
255 if (servlet.getJspFile() != null)
256 servletName = servlet.getJspFile();
257 }
258 }
259 catch (Throwable ignored)
260 {
261 }
262
263 String msg = "Application error: " + servletName + " did not complete its transaction";
264 log.error(msg);
265 }
266 }
267 finally
268 {
269 try
270 {
271 Transaction tx = tm.suspend();
272 if (tx != null)
273 {
274 String servletName = "<Unknown>";
275 try
276 {
277 Wrapper servlet = request.getWrapper();
278 if (servlet != null)
279 {
280 servletName = servlet.getName();
281 if (servlet.getJspFile() != null)
282 servletName = servlet.getJspFile();
283 }
284 }
285 catch (Throwable ignored)
286 {
287 }
288 String msg = "Application error: " + servletName +
289 " did not complete its transaction suspended tx=" + tx ;
290 log.error(msg);
291 }
292 }
293 catch (SystemException ex)
294 {
295 log.error("Failed to suspend transaction", ex);
296 }
297 }
298 }
299 }