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.txn;
9 
10 import com.sleepycat.je.Database;
11 import com.sleepycat.je.DbInternal;
12 import com.sleepycat.je.Environment;
13 import com.sleepycat.je.Transaction;
14 import com.sleepycat.je.TransactionConfig;
15 import com.sleepycat.je.dbi.DatabaseImpl;
16 import com.sleepycat.je.dbi.EnvironmentImpl;
17 import com.sleepycat.je.log.ReplicationContext;
18 
19 /**
20  * Factory of static methods for creating Locker objects.
21  */
22 public class LockerFactory {
23 
24     /**
25      * Get a locker for a write operation, checking whether the db and
26      * environment is transactional or not. Must return a non null locker.
27      */
getWritableLocker(final Environment env, final Transaction userTxn, final boolean isInternalDb, final boolean dbIsTransactional, final boolean autoTxnIsReplicated)28     public static Locker getWritableLocker(final Environment env,
29                                            final Transaction userTxn,
30                                            final boolean isInternalDb,
31                                            final boolean dbIsTransactional,
32                                            final boolean autoTxnIsReplicated) {
33 
34         return getWritableLocker(
35             env, userTxn, isInternalDb, dbIsTransactional,
36             autoTxnIsReplicated, null /*autoCommitConfig*/);
37     }
38 
39     /**
40      * Get a locker for a write operation.
41      *
42      * @param autoTxnIsReplicated is true if this transaction is
43      * executed on a rep group master, and needs to be broadcast.
44      * Currently, all application-created transactions are of the type
45      * com.sleepycat.je.txn.Txn, and are replicated if the parent
46      * environment is replicated. Auto Txns are trickier because they may
47      * be created for a local write operation, such as log cleaning.
48      */
getWritableLocker( final Environment env, final Transaction userTxn, final boolean isInternalDb, final boolean dbIsTransactional, final boolean autoTxnIsReplicated, TransactionConfig autoCommitConfig)49     public static Locker getWritableLocker(
50         final Environment env,
51         final Transaction userTxn,
52         final boolean isInternalDb,
53         final boolean dbIsTransactional,
54         final boolean autoTxnIsReplicated,
55         TransactionConfig autoCommitConfig) {
56 
57         final EnvironmentImpl envImpl = DbInternal.getEnvironmentImpl(env);
58         final boolean envIsTransactional = envImpl.isTransactional();
59 
60         if (userTxn == null) {
61             final Transaction xaLocker = env.getThreadTransaction();
62             if (xaLocker != null) {
63                 return DbInternal.getLocker(xaLocker);
64             }
65         }
66 
67         if (dbIsTransactional && userTxn == null) {
68 
69             if (autoCommitConfig == null) {
70                 autoCommitConfig = DbInternal.getDefaultTxnConfig(env);
71             }
72 
73             return Txn.createAutoTxn(
74                 envImpl, autoCommitConfig,
75                 (autoTxnIsReplicated ?
76                  ReplicationContext.MASTER :
77                  ReplicationContext.NO_REPLICATE));
78 
79         }
80 
81         if (userTxn == null) {
82             /* Non-transactional user operations use ThreadLocker. */
83             return
84                 ThreadLocker.createThreadLocker(envImpl, autoTxnIsReplicated);
85         }
86 
87         /*
88          * The user provided a transaction, so the environment and the
89          * database had better be opened transactionally.
90          */
91         if (!isInternalDb && !envIsTransactional) {
92             throw new IllegalArgumentException(
93                 "A Transaction cannot be used because the"+
94                 " environment was opened non-transactionally");
95         }
96         if (!dbIsTransactional) {
97             throw new IllegalArgumentException(
98                 "A Transaction cannot be used because the" +
99                 " database was opened non-transactionally");
100         }
101 
102         /*
103          * Use the locker for the given transaction.  For read-committed,
104          * wrap the given transactional locker in a special locker for that
105          * isolation level.
106          */
107         final Locker locker = DbInternal.getLocker(userTxn);
108         if (locker.isReadCommittedIsolation()) {
109             return ReadCommittedLocker.createReadCommittedLocker(
110                 envImpl, locker);
111         }
112 
113         return locker;
114     }
115 
116     /**
117      * Get a locker for a read or cursor operation.
118      */
getReadableLocker( final Database dbHandle, final Transaction userTxn, final boolean readCommittedIsolation)119     public static Locker getReadableLocker(
120         final Database dbHandle,
121         final Transaction userTxn,
122         final boolean readCommittedIsolation) {
123 
124         return getReadableLocker(
125             dbHandle,
126             (userTxn != null) ? DbInternal.getLocker(userTxn) : null,
127             readCommittedIsolation);
128     }
129 
130     /**
131      * Get a locker for this database handle for a read or cursor operation.
132      */
getReadableLocker( final Database dbHandle, Locker locker, boolean readCommittedIsolation)133     public static Locker getReadableLocker(
134         final Database dbHandle,
135         Locker locker,
136         boolean readCommittedIsolation) {
137 
138         final DatabaseImpl dbImpl = DbInternal.getDatabaseImpl(dbHandle);
139 
140         if (!dbImpl.isTransactional() &&
141             locker != null &&
142             locker.isTransactional()) {
143             throw new IllegalArgumentException(
144                 "A Transaction cannot be used because the" +
145                 " database was opened non-transactionally");
146         }
147 
148         /* Don't reuse a non-transactional locker. */
149         if (locker != null && !locker.isTransactional()) {
150             locker = null;
151         }
152 
153         /*
154          * Request read-committed if that isolation level is configured for the
155          * locker being reused, or if true is passed for the parameter (this is
156          * the case when read-committed is configured for the cursor).
157          */
158         if (locker != null && locker.isReadCommittedIsolation()) {
159             readCommittedIsolation = true;
160         }
161 
162         final boolean autoTxnIsReplicated =
163             dbImpl.isReplicated() &&
164             dbImpl.getEnv().isReplicated();
165 
166         return getReadableLocker(
167             dbHandle.getEnvironment(), locker, autoTxnIsReplicated,
168             readCommittedIsolation);
169     }
170 
171     /**
172      * Get a locker for a read or cursor operation.
173      */
getReadableLocker( final Environment env, final Locker locker, final boolean autoTxnIsReplicated, final boolean readCommittedIsolation)174     private static Locker getReadableLocker(
175         final Environment env,
176         final Locker locker,
177         final boolean autoTxnIsReplicated,
178         final boolean readCommittedIsolation) {
179 
180         final EnvironmentImpl envImpl = DbInternal.getEnvironmentImpl(env);
181 
182         if (locker == null) {
183             final Transaction xaTxn = env.getThreadTransaction();
184             if (xaTxn != null) {
185                 return DbInternal.getLocker(xaTxn);
186             }
187             /* Non-transactional user operations use ThreadLocker. */
188             return
189                 ThreadLocker.createThreadLocker(envImpl, autoTxnIsReplicated);
190         }
191 
192         /*
193          * Use the given locker.  For read-committed, wrap the given
194          * transactional locker in a special locker for that isolation level.
195          */
196         if (readCommittedIsolation) {
197             return ReadCommittedLocker.createReadCommittedLocker(
198                 envImpl, locker);
199         }
200 
201         return locker;
202     }
203 
204     /**
205      * Get a non-transactional locker for internal database operations.  Always
206      * non replicated.
207      *
208      * This method is not called for user txns and should not throw a Java
209      * runtime exception (IllegalArgument, etc).
210      */
getInternalReadOperationLocker( final EnvironmentImpl envImpl)211     public static Locker getInternalReadOperationLocker(
212         final EnvironmentImpl envImpl) {
213 
214         return BasicLocker.createBasicLocker(envImpl);
215     }
216 }
217