1 /*
2 * Copyright (c) 2002-2014, Mairie de Paris
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice
10 * and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice
13 * and the following disclaimer in the documentation and/or other materials
14 * provided with the distribution.
15 *
16 * 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * License 1.0
33 */
34 package fr.paris.lutece.util.sql;
35
36 import fr.paris.lutece.portal.service.plugin.Plugin;
37 import fr.paris.lutece.portal.service.util.AppLogService;
38
39 import java.sql.Connection;
40 import java.sql.SQLException;
41
42 import java.util.HashMap;
43 import java.util.Map;
44
45
46 /**
47 * Class to manage transactions
48 */
49 public final class TransactionManager
50 {
51 private static final String DEFAULT_POOL_NAME = "portal";
52 private static ThreadLocal<Map<String, MultiPluginTransaction>> _tlTransactions = new ThreadLocal<Map<String, MultiPluginTransaction>>( );
53
54 /**
55 * Default constructor
56 */
57 private TransactionManager( )
58 {
59 // Do nothing
60 }
61
62 /**
63 * Begin a transaction for the current thread using the pool of a specific
64 * plugin with the default transaction isolation. The default transaction
65 * isolation is {@link Connection#TRANSACTION_READ_COMMITTED }.<br />
66 * Note that only one transaction may be active at a time by pool for each
67 * thread.
68 * @param plugin The plugin to use the pool of, or null to use the default
69 * pool.
70 */
71 public static void beginTransaction( Plugin plugin )
72 {
73 beginTransaction( plugin, Connection.TRANSACTION_READ_COMMITTED );
74 }
75
76 /**
77 * Begin a transaction for the current thread using the pool of a specific
78 * plugin
79 * @param plugin The plugin to use the pool of, or null to use the default
80 * pool.
81 * @param nTransactionIsolation The transaction isolation. View
82 * {@link Connection#setTransactionIsolation(int) } to view the
83 * different available transaction isolations.
84 */
85 public static void beginTransaction( Plugin plugin, int nTransactionIsolation )
86 {
87 Map<String, MultiPluginTransaction> mapTransactions = _tlTransactions.get( );
88 MultiPluginTransaction transaction = null;
89
90 if ( mapTransactions == null )
91 {
92 mapTransactions = new HashMap<String, MultiPluginTransaction>( );
93 _tlTransactions.set( mapTransactions );
94 }
95 else
96 {
97 transaction = mapTransactions.get( getPluginPool( plugin ) );
98 }
99
100 if ( ( transaction == null ) || ( transaction.getNbTransactionsOpened( ) <= 0 ) )
101 {
102 transaction = new MultiPluginTransaction( plugin );
103
104 try
105 {
106 transaction.getConnection( ).setTransactionIsolation( nTransactionIsolation );
107 }
108 catch ( SQLException e )
109 {
110 AppLogService.error( e.getMessage( ), e );
111 }
112
113 mapTransactions.put( getPluginPool( plugin ), transaction );
114 }
115 else
116 {
117 // A transaction has already been opened for this pool,
118 // so we save that information to prevent the next call to the commit method to close the transaction.
119 transaction.incrementNbTransactionsOpened( );
120 }
121 }
122
123 /**
124 * Get the current transaction for the pool of a given plugin.
125 * @param plugin The plugin to use the pool of, or null to use the default
126 * pool.
127 * @return The transaction, or null if no transaction is currently running.
128 */
129 public static MultiPluginTransaction getCurrentTransaction( Plugin plugin )
130 {
131 Map<String, MultiPluginTransaction> mapTransactions = _tlTransactions.get( );
132
133 if ( mapTransactions != null )
134 {
135 return mapTransactions.get( getPluginPool( plugin ) );
136 }
137
138 return null;
139 }
140
141 /**
142 * Commit the transaction associated to the pool of a given plugin.
143 * @param plugin The plugin associated to the pool to commit the transaction
144 * of, or null to use the default pool.
145 */
146 public static void commitTransaction( Plugin plugin )
147 {
148 Map<String, MultiPluginTransaction> mapTransactions = _tlTransactions.get( );
149
150 if ( mapTransactions != null )
151 {
152 String strPoolName = getPluginPool( plugin );
153 MultiPluginTransaction transaction = mapTransactions.get( strPoolName );
154
155 if ( transaction != null )
156 {
157 // If the number of transactions opened is 1 or less, then we commit the transaction
158 if ( transaction.getNbTransactionsOpened( ) <= 1 )
159 {
160 transaction.commit( );
161 mapTransactions.remove( strPoolName );
162 }
163 else
164 {
165 // Otherwise, we decrement the number
166 transaction.decrementNbTransactionsOpened( );
167 }
168 }
169 }
170 }
171
172 /**
173 * Roll back a transaction associated to the pool of a given plugin. Note
174 * that any plugin can roll a transaction back.
175 * @param plugin The plugin associated to the pool to roll back the
176 * transaction of, or null to use the default pool.
177 */
178 public static void rollBack( Plugin plugin )
179 {
180 rollBack( plugin, null );
181 }
182
183 /**
184 * Roll back a transaction associated to the pool of a given plugin with an
185 * exception.
186 * @param plugin The plugin associated to the pool to roll back the
187 * transaction of, or null to use the default pool.
188 * @param e The exception to associates with the roll back.
189 */
190 public static void rollBack( Plugin plugin, Exception e )
191 {
192 Map<String, MultiPluginTransaction> mapTransactions = _tlTransactions.get( );
193
194 if ( mapTransactions != null )
195 {
196 String strPoolName = getPluginPool( plugin );
197 MultiPluginTransaction transaction = mapTransactions.get( strPoolName );
198
199 // We roll back the transaction, no matter how much transactions has been opened.
200 if ( transaction != null )
201 {
202 transaction.rollback( e );
203 mapTransactions.remove( strPoolName );
204 }
205 }
206 }
207
208 /**
209 * Roll back every transactions opened by the current thread. This method
210 * attempt to prevent connection leak.
211 */
212 public static void rollBackEveryTransaction( )
213 {
214 rollBackEveryTransaction( null );
215 }
216
217 /**
218 * Roll back every transactions opened by the current thread. This method
219 * attempt to prevent connection leak.
220 * @param e The exception that occurs and that may have prevent transaction
221 * from being properly closed (committed or roll backed)
222 */
223 public static void rollBackEveryTransaction( Throwable e )
224 {
225 Map<String, MultiPluginTransaction> mapTransactions = _tlTransactions.get( );
226
227 if ( ( mapTransactions != null ) && ( mapTransactions.size( ) > 0 ) )
228 {
229 for ( MultiPluginTransaction transaction : mapTransactions.values( ) )
230 {
231 transaction.rollback( null );
232 }
233
234 mapTransactions.clear( );
235 }
236 }
237
238 /**
239 * Get the name of the pool of a given plugin
240 * @param plugin The plugin to get the name of the pool, or null to get the
241 * name of the default pool.
242 * @return The name of the pool
243 */
244 private static String getPluginPool( Plugin plugin )
245 {
246 return ( plugin != null ) ? plugin.getDbPoolName( ) : DEFAULT_POOL_NAME;
247 }
248 }