View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.transaction;
18  
19  import java.util.concurrent.TimeUnit;
20  
21  import org.apache.commons.transaction.locking.LockException;
22  import org.apache.commons.transaction.locking.LockManager;
23  
24  /**
25   * Abstract base class for transactional resource managers.
26   * <p>
27   * This implementation takes care of most administrative tasks a transactional
28   * resource manager has to perform. Sublcass
29   * {@link AbstractTransactionalResourceManager.AbstractTxContext} to hold all
30   * information necessary for each transaction. Additionally, you have to
31   * implement {@link #createContext()} to create an object of that type, and
32   * {@link #commitCanFail()}.
33   * 
34   * <p>
35   * This implementation is <em>thread-safe</em>.
36   */
37  public abstract class AbstractTransactionalResourceManager<T extends AbstractTransactionalResourceManager.AbstractTxContext>
38          implements ManageableResourceManager {
39      protected ThreadLocal<T> activeTx = new ThreadLocal<T>();
40  
41      private LockManager<Object, Object> lm;
42  
43      private String name;
44  
45      private boolean partofComplexTransaction = false;
46  
47      protected abstract T createContext();
48  
49      public AbstractTransactionalResourceManager(String name) {
50          this.name = name;
51      }
52  
53      // can be used to share a lock manager with other transactional resource
54      // managers
55      public AbstractTransactionalResourceManager(String name, LockManager<Object, Object> lm) {
56          this.name = name;
57          this.lm = lm;
58      }
59  
60      @Override
61      public boolean isRollbackOnly() {
62          T txContext = getCheckedActiveTx();
63  
64          return (txContext.isMarkedForRollback());
65      }
66  
67      @Override
68      public void startTransaction(long timeout, TimeUnit unit) {
69          if (getActiveTx() != null) {
70              throw new IllegalStateException("Active thread " + Thread.currentThread()
71                      + " already associated with a transaction!");
72          }
73          T txContext = createContext();
74          txContext.start(timeout, unit);
75          setActiveTx(txContext);
76  
77      }
78  
79      @Override
80      public void rollbackTransaction() {
81          T txContext = getCheckedActiveTx();
82  
83          txContext.rollback();
84          forgetTransaction();
85      }
86  
87      @Override
88      public void forgetTransaction() {
89          T txContext = getCheckedActiveTx();
90  
91          txContext.dispose();
92          setActiveTx(null);
93          partofComplexTransaction = false;
94      }
95  
96      @Override
97      public boolean commitTransaction() {
98          T txContext = getCheckedActiveTx();
99  
100         if (txContext.isMarkedForRollback()) {
101             throw new IllegalStateException("Active thread " + Thread.currentThread()
102                     + " is marked for rollback!");
103         }
104 
105         txContext.commit();
106         forgetTransaction();
107         return true;
108     }
109 
110     protected T getActiveTx() {
111         return activeTx.get();
112     }
113 
114     protected T getCheckedActiveTx() {
115         T txContext = getActiveTx();
116 
117         if (txContext == null) {
118             throw new IllegalStateException("Active thread " + Thread.currentThread()
119                     + " not associated with a transaction!");
120         }
121         return txContext;
122     }
123 
124     protected void setActiveTx(T txContext) {
125         activeTx.set(txContext);
126     }
127 
128     public boolean isReadOnly() {
129         T txContext = getCheckedActiveTx();
130         return (txContext.isReadOnly());
131     }
132 
133     public abstract class AbstractTxContext {
134         private boolean readOnly = true;
135 
136         private boolean markedForRollback = false;
137 
138         public void join() {
139         }
140 
141         public void start(long timeout, TimeUnit unit) {
142             getLm().startWork(timeout, unit);
143         }
144 
145         public boolean isReadOnly() {
146             return readOnly;
147         }
148 
149         public void setReadOnly(boolean readOnly) {
150             this.readOnly = readOnly;
151         }
152 
153         public void readLock(Object id) throws LockException {
154             getLm().lock(getName(), id, false);
155         }
156 
157         public void writeLock(Object id) throws LockException {
158             getLm().lock(getName(), id, true);
159         }
160 
161         public boolean isMarkedForRollback() {
162             return markedForRollback;
163         }
164 
165         public void markForRollback() {
166             markedForRollback = true;
167         }
168 
169         public void dispose() {
170             if (!isPartofComplexTransaction())
171                 getLm().endWork();
172         }
173 
174         public void commit() {
175 
176         }
177 
178         public void rollback() {
179 
180         }
181 
182         public boolean prepare() {
183             return !isMarkedForRollback();
184         }
185 
186     }
187 
188     protected LockManager<Object, Object> getLm() {
189         return lm;
190     }
191 
192     public void setLm(LockManager<Object, Object> lm) {
193         this.lm = lm;
194     }
195 
196     public String getName() {
197         return name;
198     }
199 
200     public void setName(String name) {
201         this.name = name;
202     }
203 
204     public abstract boolean commitCanFail();
205 
206     @Override
207     public void joinTransaction(LockManager<Object, Object> lm) {
208         if (getActiveTx() != null) {
209             throw new IllegalStateException("Active thread " + Thread.currentThread()
210                     + " already associated with a transaction!");
211         }
212         setLm(lm);
213         T txContext = createContext();
214         txContext.join();
215         setActiveTx(txContext);
216         partofComplexTransaction = true;
217     }
218 
219     @Override
220     public boolean prepareTransaction() {
221         T txContext = getCheckedActiveTx();
222         return txContext.prepare();
223 
224     }
225 
226     public boolean isPartofComplexTransaction() {
227         return partofComplexTransaction;
228     }
229 
230 }