1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.transaction;
26
27 import java.util.Properties;
28
29 import javax.naming.InitialContext;
30 import javax.naming.NamingException;
31 import javax.transaction.SystemException;
32 import javax.transaction.UserTransaction;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import org.hibernate.ConnectionReleaseMode;
38 import org.hibernate.HibernateException;
39 import org.hibernate.Transaction;
40 import org.hibernate.TransactionException;
41 import org.hibernate.jdbc.JDBCContext;
42 import org.hibernate.cfg.Environment;
43 import org.hibernate.util.NamingHelper;
44 import org.hibernate.util.JTAHelper;
45
46 /**
47 * Factory for {@link JTATransaction} instances.
48 * <p/>
49 * To be completely accurate to the JTA spec, JTA implementations should
50 * publish their contextual {@link UserTransaction} reference into JNDI.
51 * However, in practice there are quite a few <tt>stand-alone</tt>
52 * implementations intended for use outside of J2EE/JEE containers and
53 * which therefore do not publish their {@link UserTransaction} references
54 * into JNDI but which otherwise follow the aspects of the JTA specification.
55 * This {@link TransactionFactory} implementation can support both models.
56 * <p/>
57 * For complete JTA implementations (including dependence on JNDI), the
58 * {@link UserTransaction} reference is obtained by a call to
59 * {@link #resolveInitialContext}. Hibernate will then attempt to locate the
60 * {@link UserTransaction} within this resolved
61 * {@link InitialContext} based on the namespace returned by
62 * {@link #resolveUserTransactionName}.
63 * <p/>
64 * For the so-called <tt>stand-alone</tt> implementations, we do not care at
65 * all about the JNDI aspects just described. Here, the implementation would
66 * have a specific manner to obtain a reference to its contextual
67 * {@link UserTransaction}; usually this would be a static code reference, but
68 * again it varies. Anyway, for each implementation the integration would need
69 * to override the {@link #getUserTransaction} method and return the appropriate
70 * thing.
71 *
72 * @author Gavin King
73 * @author Steve Ebersole
74 * @author Les Hazlewood
75 */
76 public class JTATransactionFactory implements TransactionFactory {
77 public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
78 private static final Logger log = LoggerFactory.getLogger( JTATransactionFactory.class );
79
80 protected InitialContext initialContext;
81 protected String userTransactionName;
82
83 /**
84 * Configure this transaction factory. Specifically here we are attempting to
85 * resolve both an {@link #getInitialContext InitialContext} as well as the
86 * {@link #getUserTransactionName() JNDI namespace} for the {@link UserTransaction}.
87 *
88 * @param props The configuration properties
89 *
90 * @exception HibernateException
91 */
92 public void configure(Properties props) throws HibernateException {
93 this.initialContext = resolveInitialContext( props );
94 this.userTransactionName = resolveUserTransactionName( props );
95 log.trace( "Configured JTATransactionFactory to use [{}] for UserTransaction JDNI namespace", userTransactionName );
96 }
97
98 /**
99 * Given the lot of Hibernate configuration properties, resolve appropriate
100 * reference to JNDI {@link InitialContext}.
101 * <p/>
102 * In general, the properties in which we are interested here all begin with
103 * <tt>hibernate.jndi</tt>. Especially important depending on your
104 * environment are {@link Environment#JNDI_URL hibernate.jndi.url} and
105 * {@link Environment#JNDI_CLASS hibernate.jndi.class}
106 *
107 * @param properties The Hibernate config properties.
108 * @return The resolved InitialContext.
109 */
110 protected final InitialContext resolveInitialContext(Properties properties) {
111 try {
112 return NamingHelper.getInitialContext( properties );
113 }
114 catch ( NamingException ne ) {
115 throw new HibernateException( "Could not obtain initial context", ne );
116 }
117 }
118
119 /**
120 * Given the lot of Hibernate configuration properties, resolve appropriate
121 * JNDI namespace to use for {@link UserTransaction} resolution.
122 * <p/>
123 * We determine the namespace to use by<ol>
124 * <li>Any specified {@link Environment#USER_TRANSACTION jta.UserTransaction} config property</li>
125 * <li>If a {@link TransactionManagerLookup} was indicated, use its
126 * {@link TransactionManagerLookup#getUserTransactionName}</li>
127 * <li>finally, as a last resort, we use {@link #DEFAULT_USER_TRANSACTION_NAME}</li>
128 * </ol>
129 *
130 * @param properties The Hibernate config properties.
131 * @return The resolved {@link UserTransaction} namespace
132 */
133 protected final String resolveUserTransactionName(Properties properties) {
134 String utName = properties.getProperty( Environment.USER_TRANSACTION );
135 if ( utName == null ) {
136 TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup( properties );
137 if ( lookup != null ) {
138 utName = lookup.getUserTransactionName();
139 }
140 }
141 return utName == null ? DEFAULT_USER_TRANSACTION_NAME : utName;
142 }
143
144 /**
145 * {@inheritDoc}
146 */
147 public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
148 throws HibernateException {
149 UserTransaction ut = getUserTransaction();
150 return new JTATransaction( ut, jdbcContext, transactionContext );
151 }
152
153 /**
154 * Get the {@link UserTransaction} reference.
155 *
156 * @return The appropriate {@link UserTransaction} reference.
157 */
158 protected UserTransaction getUserTransaction() {
159 log.trace( "Attempting to locate UserTransaction via JNDI [{}]", getUserTransactionName() );
160
161 try {
162 UserTransaction ut = ( UserTransaction ) getInitialContext().lookup( getUserTransactionName() );
163 if ( ut == null ) {
164 throw new TransactionException( "Naming service lookup for UserTransaction returned null [" + getUserTransactionName() +"]" );
165 }
166
167 log.trace( "Obtained UserTransaction" );
168
169 return ut;
170 }
171 catch ( NamingException ne ) {
172 throw new TransactionException( "Could not find UserTransaction in JNDI [" + getUserTransaction() + "]", ne );
173 }
174 }
175
176 /**
177 * Getter for property 'initialContext'.
178 *
179 * @return Value for property 'initialContext'.
180 */
181 protected InitialContext getInitialContext() {
182 return initialContext;
183 }
184
185 /**
186 * Getter for property 'userTransactionName'.
187 * The algorithm here is
188 *
189 * @return Value for property 'userTransactionName'.
190 */
191 protected String getUserTransactionName() {
192 return userTransactionName;
193 }
194
195 /**
196 * {@inheritDoc}
197 */
198 public ConnectionReleaseMode getDefaultReleaseMode() {
199 return ConnectionReleaseMode.AFTER_STATEMENT;
200 }
201
202 /**
203 * {@inheritDoc}
204 */
205 public boolean isTransactionManagerRequired() {
206 return false;
207 }
208
209 /**
210 * {@inheritDoc}
211 */
212 public boolean areCallbacksLocalToHibernateTransactions() {
213 return false;
214 }
215
216 /**
217 * {@inheritDoc}
218 */
219 public boolean isTransactionInProgress(
220 JDBCContext jdbcContext,
221 Context transactionContext,
222 Transaction transaction) {
223 try {
224 // Essentially:
225 // 1) If we have a local (Hibernate) transaction in progress
226 // and it already has the UserTransaction cached, use that
227 // UserTransaction to determine the status.
228 // 2) If a transaction manager has been located, use
229 // that transaction manager to determine the status.
230 // 3) Finally, as the last resort, try to lookup the
231 // UserTransaction via JNDI and use that to determine the
232 // status.
233 if ( transaction != null ) {
234 UserTransaction ut = ( ( JTATransaction ) transaction ).getUserTransaction();
235 if ( ut != null ) {
236 return JTAHelper.isInProgress( ut.getStatus() );
237 }
238 }
239
240 if ( jdbcContext.getFactory().getTransactionManager() != null ) {
241 return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
242 }
243 else {
244 UserTransaction ut = getUserTransaction();
245 return ut != null && JTAHelper.isInProgress( ut.getStatus() );
246 }
247 }
248 catch ( SystemException se ) {
249 throw new TransactionException( "Unable to check transaction status", se );
250 }
251 }
252
253 }