(no commit message)
[utils] / wicket / joe / src / main / java / org / wamblee / wicket / transactions / OpenTransactionInViewRequestCycle.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.wicket.transactions;
17
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import javax.naming.InitialContext;
22 import javax.naming.NamingException;
23 import javax.transaction.Status;
24 import javax.transaction.UserTransaction;
25
26 import org.apache.wicket.Application;
27 import org.apache.wicket.Page;
28 import org.apache.wicket.Response;
29 import org.apache.wicket.protocol.http.WebApplication;
30 import org.apache.wicket.protocol.http.WebRequest;
31 import org.apache.wicket.protocol.http.WebRequestCycle;
32
33 /**
34  * <p>
35  * This class provides an <em>Open Transaction in View</em> implementation for
36  * wicket. It provides a custom request cycle that uses the
37  * {@link UserTransaction} to make sure that all work is done within a single
38  * transaction. In Hibernate context, this is referred to as open session in
39  * view. 
40  * </p>
41  * 
42  * <p>
43  * The method used by this class is more correctly called
44  * <em>Open Transaction in View</em> and has a similar effect to open session in
45  * view. It is however more general because it supports any (JTA) transactional
46  * resource. Also it is more efficient/scalable than most open session in view
47  * implementations because a database connection is only obtained from the
48  * connection pool when it is needed.
49  * </p>
50  * 
51  * <p>
52  * To use this request cycle, add it in your wicket {@link Application} subclass
53  * by overriding
54  * {@link Application#newRequestCycle(org.apache.wicket.Request, Response)}. For
55  * example:
56  * </p>
57  * 
58  * <pre>
59  * &#064;Override
60  * public RequestCycle newRequestCycle(Request aRequest, Response aResponse) {
61  *     return new OpenTransactionInViewRequestCycle(this, (WebRequest) aRequest,
62  *         aResponse);
63  * }
64  * </pre>
65  * 
66  * @author Erik Brakkee
67  * 
68  */
69 public class OpenTransactionInViewRequestCycle extends WebRequestCycle {
70
71     private static final Logger LOG = Logger.getLogger(OpenTransactionInViewRequestCycle.class.getName());
72     
73     private static final String JAVA_COMP_USER_TRANSACTION_JNDI = "java:comp/UserTransaction";
74     private UserTransaction userTransaction;
75
76     /**
77      * Constructs the request cycle.
78      * 
79      * @param aApplication
80      *            Application to use.
81      * @param aRequest
82      *            Request
83      * @param aResponse
84      *            Response.
85      */
86     public OpenTransactionInViewRequestCycle(final WebApplication aApplication,
87         final WebRequest aRequest, final Response aResponse) {
88         super(aApplication, aRequest, aResponse);
89         userTransaction = getUserTransaction();
90     }
91
92     @Override
93     protected void onBeginRequest() {
94         try {
95             userTransaction.begin();
96         } catch (Exception e) {
97             throw new RuntimeException("Could not start transaction", e);
98         }
99         super.onBeginRequest();
100     }
101
102     @Override
103     protected void onEndRequest() {
104         try {
105             if ( LOG.isLoggable(Level.FINEST)) { 
106                 LOG.finest("Transaction status: " + userTransaction.getStatus());
107             }
108             if (userTransaction.getStatus() == Status.STATUS_ACTIVE) {
109                 userTransaction.commit();
110             } else if (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
111                 userTransaction.rollback();
112             } else { 
113                 LOG.warning("Transaction status is '" + userTransaction.getStatus() + "' not committing or rolling back");
114             }
115         } catch (Exception e) {
116             throw new RuntimeException("Could not commit transaction", e);
117         }
118         super.onEndRequest();
119     }
120
121     @Override
122     public Page onRuntimeException(Page aPage, RuntimeException aE) {
123         super.onEndRequest();
124         try {
125             userTransaction.rollback();
126         } catch (Exception e) {
127             throw new RuntimeException("Could not rollback transaction", e);
128         }
129         return super.onRuntimeException(aPage, aE);
130     }
131
132     private UserTransaction getUserTransaction() {
133         try {
134             InitialContext ctx = new InitialContext();
135             return (UserTransaction) ctx
136                 .lookup(JAVA_COMP_USER_TRANSACTION_JNDI);
137         } catch (NamingException e) {
138             throw new RuntimeException(
139                 "Could not obtain user transaction object");
140         }
141     }
142 }