(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
72         .getLogger(OpenTransactionInViewRequestCycle.class.getName());
73
74     private static final String JAVA_COMP_USER_TRANSACTION_JNDI = "java:comp/UserTransaction";
75     private UserTransaction userTransaction;
76
77     /**
78      * Constructs the request cycle.
79      * 
80      * @param aApplication
81      *            Application to use.
82      * @param aRequest
83      *            Request
84      * @param aResponse
85      *            Response.
86      */
87     public OpenTransactionInViewRequestCycle(final WebApplication aApplication,
88         final WebRequest aRequest, final Response aResponse) {
89         super(aApplication, aRequest, aResponse);
90         userTransaction = getUserTransaction();
91     }
92
93     @Override
94     protected void onBeginRequest() {
95         try {
96             userTransaction.begin();
97         } catch (Exception e) {
98             throw new RuntimeException("Could not start transaction", e);
99         }
100         super.onBeginRequest();
101     }
102
103     @Override
104     protected void onEndRequest() {
105         try {
106             if (LOG.isLoggable(Level.FINEST)) {
107                 LOG
108                     .finest("Transaction status: " +
109                         userTransaction.getStatus());
110             }
111             if (userTransaction.getStatus() == Status.STATUS_ACTIVE) {
112                 userTransaction.commit();
113             } else if (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
114                 userTransaction.rollback();
115             } else {
116                 LOG.warning("Transaction status is '" +
117                     userTransaction.getStatus() +
118                     "' not committing or rolling back");
119             }
120         } catch (Exception e) {
121             throw new RuntimeException("Could not commit transaction", e);
122         }
123         super.onEndRequest();
124     }
125
126     @Override
127     public Page onRuntimeException(Page aPage, RuntimeException aE) {
128         super.onEndRequest();
129         try {
130             userTransaction.rollback();
131         } catch (Exception e) {
132             throw new RuntimeException("Could not rollback transaction", e);
133         }
134         return super.onRuntimeException(aPage, aE);
135     }
136
137     private UserTransaction getUserTransaction() {
138         try {
139             InitialContext ctx = new InitialContext();
140             return (UserTransaction) ctx
141                 .lookup(JAVA_COMP_USER_TRANSACTION_JNDI);
142         } catch (NamingException e) {
143             throw new RuntimeException(
144                 "Could not obtain user transaction object");
145         }
146     }
147 }