1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.jca.ra;
9 
10 import java.io.PrintWriter;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.Map;
15 
16 import javax.resource.ResourceException;
17 import javax.resource.spi.ConnectionEvent;
18 import javax.resource.spi.ConnectionEventListener;
19 import javax.resource.spi.ConnectionRequestInfo;
20 import javax.resource.spi.LocalTransaction;
21 import javax.resource.spi.ManagedConnection;
22 import javax.resource.spi.ManagedConnectionMetaData;
23 import javax.security.auth.Subject;
24 import javax.transaction.xa.XAResource;
25 
26 import com.sleepycat.je.Database;
27 import com.sleepycat.je.DatabaseConfig;
28 import com.sleepycat.je.DatabaseException;
29 import com.sleepycat.je.DbInternal;
30 import com.sleepycat.je.SecondaryConfig;
31 import com.sleepycat.je.SecondaryDatabase;
32 import com.sleepycat.je.TransactionConfig;
33 import com.sleepycat.je.XAEnvironment;
34 
35 public class JEManagedConnection implements ManagedConnection {
36     private final ArrayList<ConnectionEventListener> listeners;
37     private JEConnection conn;
38     private XAEnvironment env;
39     private JELocalTransaction savedLT;
40     private TransactionConfig savedTransConfig;
41     private final Map<String,Database> rwDatabaseHandleCache;
42     private final Map<String,Database> roDatabaseHandleCache;
43     private final Map<String,Database> rwSecondaryDatabaseHandleCache;
44     private final Map<String,Database> roSecondaryDatabaseHandleCache;
45 
JEManagedConnection(Subject subject, JERequestInfo jeInfo)46     JEManagedConnection(Subject subject, JERequestInfo jeInfo)
47         throws ResourceException {
48 
49         try {
50             savedTransConfig = jeInfo.getTransactionConfig();
51             this.env = new XAEnvironment(jeInfo.getJERootDir(),
52                                          jeInfo.getEnvConfig());
53         } catch (DatabaseException DE) {
54             throw new ResourceException(DE.toString());
55         }
56           listeners = new ArrayList<ConnectionEventListener>();
57         savedLT = null;
58         rwDatabaseHandleCache = new HashMap<String,Database>();
59         roDatabaseHandleCache = new HashMap<String,Database>();
60         rwSecondaryDatabaseHandleCache = new HashMap<String,Database>();
61         roSecondaryDatabaseHandleCache = new HashMap<String,Database>();
62     }
63 
getConnection(Subject subject, ConnectionRequestInfo connectionRequestInfo)64     public Object getConnection(Subject subject,
65                                 ConnectionRequestInfo connectionRequestInfo) {
66         if (conn == null) {
67             conn = new JEConnection(this);
68         }
69         return conn;
70     }
71 
getEnvironment()72     protected XAEnvironment getEnvironment() {
73         return env;
74     }
75 
getLocalTransaction()76     public LocalTransaction getLocalTransaction() {
77 
78         /*
79          * If there is no JEConnection associated with this ManagedConnection
80          * yet, then the ManagedConnection holds on to the JELocalTransaction.
81          * Once a JEConnection is associated (it may not ever happen), we hand
82          * off the JELocalTransaction to the JEConnection and forget about it
83          * in the ManagedConnection.
84          */
85         if (conn == null) {
86             savedLT = new JELocalTransaction(env, savedTransConfig, this);
87             return savedLT;
88         }
89 
90         JELocalTransaction lt = conn.getLocalTransaction();
91         if (lt == null) {
92             if (savedLT == null) {
93                 lt = new JELocalTransaction(env, savedTransConfig, this);
94             } else {
95                 lt = savedLT;
96             }
97             conn.setLocalTransaction(lt);
98             savedLT = null;
99         }
100         return lt;
101     }
102 
getXAResource()103     public XAResource getXAResource() {
104         return env;
105     }
106 
associateConnection(Object connection)107     public void associateConnection(Object connection) {
108         conn = (JEConnection) connection;
109         conn.setManagedConnection(this, savedLT);
110         savedLT = null;
111     }
112 
addConnectionEventListener(ConnectionEventListener listener)113     public void addConnectionEventListener(ConnectionEventListener listener) {
114         listeners.add(listener);
115     }
116 
117     public void
removeConnectionEventListener(ConnectionEventListener listener)118         removeConnectionEventListener(ConnectionEventListener listener) {
119 
120         listeners.remove(listener);
121     }
122 
getMetaData()123     public ManagedConnectionMetaData getMetaData() {
124         return new JEConnectionMetaData();
125     }
126 
setLogWriter(PrintWriter out)127     public void setLogWriter(PrintWriter out) {
128     }
129 
getLogWriter()130     public PrintWriter getLogWriter() {
131         return null;
132     }
133 
close()134     protected void close() {
135         ConnectionEvent connEvent =
136             new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
137         connEvent.setConnectionHandle(conn);
138         sendConnectionEvent(connEvent);
139     }
140 
sendConnectionEvent(ConnectionEvent connEvent)141     protected void sendConnectionEvent(ConnectionEvent connEvent) {
142         for (int i = listeners.size() - 1; i >= 0; i--) {
143             ConnectionEventListener listener =
144                 listeners.get(i);
145             if (connEvent.getId() == ConnectionEvent.CONNECTION_CLOSED) {
146                 listener.connectionClosed(connEvent);
147             } else if (connEvent.getId() ==
148                        ConnectionEvent.CONNECTION_ERROR_OCCURRED) {
149                 listener.connectionErrorOccurred(connEvent);
150             } else if (connEvent.getId() ==
151                        ConnectionEvent.LOCAL_TRANSACTION_STARTED) {
152                 listener.localTransactionStarted(connEvent);
153             } else if (connEvent.getId() ==
154                        ConnectionEvent.LOCAL_TRANSACTION_COMMITTED) {
155                 listener.localTransactionCommitted(connEvent);
156             } else if (connEvent.getId() ==
157                        ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK) {
158                 listener.localTransactionRolledback(connEvent);
159             }
160         }
161     }
162 
destroy()163     public void destroy()
164         throws ResourceException {
165 
166         try {
167             cleanupDatabaseHandleCache(roDatabaseHandleCache);
168             cleanupDatabaseHandleCache(rwDatabaseHandleCache);
169             cleanupDatabaseHandleCache(roSecondaryDatabaseHandleCache);
170             cleanupDatabaseHandleCache(rwSecondaryDatabaseHandleCache);
171             env.close();
172         } catch (DatabaseException DE) {
173             throw new ResourceException(DE.toString());
174         }
175     }
176 
cleanup()177     public void cleanup() {
178     }
179 
removeDatabase(String dbName)180     void removeDatabase(String dbName)
181         throws DatabaseException {
182 
183         removeDatabaseFromCache(roDatabaseHandleCache, dbName);
184         removeDatabaseFromCache(rwDatabaseHandleCache, dbName);
185         removeDatabaseFromCache(roSecondaryDatabaseHandleCache, dbName);
186         removeDatabaseFromCache(rwSecondaryDatabaseHandleCache, dbName);
187         env.removeDatabase(null, dbName);
188     }
189 
truncateDatabase(String dbName, boolean returnCount)190     long truncateDatabase(String dbName, boolean returnCount)
191         throws DatabaseException {
192 
193         removeDatabaseFromCache(roDatabaseHandleCache, dbName);
194         removeDatabaseFromCache(rwDatabaseHandleCache, dbName);
195         removeDatabaseFromCache(roSecondaryDatabaseHandleCache, dbName);
196         removeDatabaseFromCache(rwSecondaryDatabaseHandleCache, dbName);
197         return env.truncateDatabase(null, dbName, returnCount);
198     }
199 
openDatabase(String dbName, DatabaseConfig config)200     Database openDatabase(String dbName, DatabaseConfig config)
201         throws DatabaseException {
202 
203         if (config.getReadOnly()) {
204             synchronized (roDatabaseHandleCache) {
205                 return openDatabaseInternal
206                     (roDatabaseHandleCache, dbName, config);
207             }
208         } else {
209             synchronized (rwDatabaseHandleCache) {
210                 return openDatabaseInternal
211                     (rwDatabaseHandleCache, dbName, config);
212             }
213         }
214     }
215 
openSecondaryDatabase(String dbName, Database primaryDatabase, SecondaryConfig config)216     SecondaryDatabase openSecondaryDatabase(String dbName,
217                                             Database primaryDatabase,
218                                             SecondaryConfig config)
219         throws DatabaseException {
220 
221         if (config.getReadOnly()) {
222             synchronized (roSecondaryDatabaseHandleCache) {
223                 return openSecondaryDatabaseInternal
224                     (roSecondaryDatabaseHandleCache, dbName,
225                      primaryDatabase, config);
226             }
227         } else {
228             synchronized (rwSecondaryDatabaseHandleCache) {
229                 return openSecondaryDatabaseInternal
230                     (rwSecondaryDatabaseHandleCache, dbName,
231                      primaryDatabase, config);
232             }
233         }
234     }
235 
236     private Database
openDatabaseInternal(Map<String,Database> databaseHandleCache, String dbName, DatabaseConfig config)237         openDatabaseInternal(Map<String,Database> databaseHandleCache,
238                              String dbName,
239                              DatabaseConfig config)
240         throws DatabaseException {
241 
242         Database db;
243         if (config.getExclusiveCreate()) {
244             db = env.openDatabase(null, dbName, config);
245             databaseHandleCache.put(dbName, db);
246         } else {
247             db = databaseHandleCache.get(dbName);
248             if (db == null) {
249                 db = env.openDatabase(null, dbName, config);
250                 databaseHandleCache.put(dbName, db);
251             } else {
252                 DbInternal.validate(config, db.getConfig());
253             }
254         }
255         return db;
256     }
257 
258     private SecondaryDatabase
openSecondaryDatabaseInternal(Map<String,Database> databaseHandleCache, String dbName, Database primaryDatabase, SecondaryConfig config)259         openSecondaryDatabaseInternal(Map<String,Database> databaseHandleCache,
260                                       String dbName,
261                                       Database primaryDatabase,
262                                       SecondaryConfig config)
263         throws DatabaseException {
264 
265         SecondaryDatabase db;
266         if (config.getExclusiveCreate()) {
267             db = env.openSecondaryDatabase(null, dbName,
268                                            primaryDatabase, config);
269             databaseHandleCache.put(dbName, db);
270         } else {
271             db = (SecondaryDatabase) databaseHandleCache.get(dbName);
272             if (db == null) {
273                 db = env.openSecondaryDatabase(null, dbName,
274                                                primaryDatabase, config);
275                 databaseHandleCache.put(dbName, db);
276             } else {
277                 DbInternal.validate(config, db.getConfig());
278             }
279         }
280         return db;
281     }
282 
removeDatabaseFromCache(Map<String,Database> cache, String dbName)283     private void removeDatabaseFromCache(Map<String,Database> cache,
284                                          String dbName)
285         throws DatabaseException {
286 
287         synchronized (cache) {
288             Database db = cache.get(dbName);
289             if (db == null) {
290                 return;
291             }
292             db.close();
293             cache.remove(dbName);
294         }
295     }
296 
cleanupDatabaseHandleCache(Map<String,Database> cache)297     private void cleanupDatabaseHandleCache(Map<String,Database> cache)
298         throws DatabaseException {
299 
300         synchronized (cache) {
301             Iterator<Database> iter = cache.values().iterator();
302 
303             while (iter.hasNext()) {
304                 Database db = iter.next();
305                 db.close();
306             }
307         }
308     }
309 }
310