001    package org.apache.ojb.otm.core;
002    
003    /* Copyright 2003-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.ArrayList;
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import org.apache.ojb.otm.OTMKit;
023    import org.apache.ojb.otm.OTMConnection;
024    
025    /**
026     *  Transaction delivers the core function of OTMKit - to manage objects within the
027     *  context of a transaction.
028     *
029     *  @author <a href="mailto:rraghuram@hotmail.com">Raghu Rajah</a>
030     */
031    public class Transaction
032    {
033        private boolean _isInProgress;
034        private List _listeners;
035        private List _connections;
036        private OTMKit _kit;
037    
038        public Transaction()
039        {
040            _listeners = new ArrayList();
041            _connections = new ArrayList();
042        }
043    
044        public OTMKit getKit()
045        {
046            return _kit;
047        }
048    
049        public void setKit(OTMKit kit)
050        {
051            _kit = kit;
052        }
053    
054        public void begin()
055                throws TransactionException
056        {
057            if (_isInProgress)
058            {
059                throw new TransactionInProgressException(
060                    "Transaction already in progress, cannot restart");
061            }
062    
063            _isInProgress = true;
064    
065            for (Iterator iterator = _connections.iterator(); iterator.hasNext();)
066            {
067                BaseConnection connection = (BaseConnection) iterator.next();
068                connection.transactionBegin();
069            }
070    
071            for (Iterator iterator = _listeners.iterator(); iterator.hasNext();)
072            {
073                TransactionListener listener = (TransactionListener) iterator.next();
074                listener.transactionBegan(this);
075            }
076        }
077    
078        /**
079         *
080         *  Commit this transaction. A commit notifies all listeners of this transaction. It then
081         *  initiates a two phase commit on connections. Since, connections cannot be associated to
082         *  more than one transaction at any given point in time, there is no neccessity to identify
083         *  transaction.
084         *
085         */
086        public void commit()
087                throws TransactionException
088        {
089            if (!_isInProgress)
090            {
091                throw new TransactionNotInProgressException(
092                    "Transaction not in progress, nothing to commit");
093            }
094    
095            for (Iterator iterator = _listeners.iterator(); iterator.hasNext();)
096            {
097                TransactionListener listener = (TransactionListener) iterator.next();
098    
099                listener.transactionCommitting(this);
100            }
101    
102            for (Iterator iterator = _connections.iterator(); iterator.hasNext();)
103            {
104                BaseConnection connection = (BaseConnection) iterator.next();
105                ConcreteEditingContext context =
106                        (ConcreteEditingContext) connection.getEditingContext();
107    
108                context.commit();
109                connection.transactionPrepare();
110            }
111    
112            for (Iterator iterator = _connections.iterator(); iterator.hasNext();)
113            {
114                BaseConnection connection = (BaseConnection) iterator.next();
115    
116                connection.transactionCommit();
117                connection.setTransaction(null);
118            }
119    
120            _connections.clear();
121            _isInProgress = false;
122        }
123    
124        /**
125         *
126         *  Checkpoint this transaction.
127         *
128         */
129        public void checkpoint()
130                throws TransactionException
131        {
132            if (!_isInProgress)
133            {
134                throw new TransactionNotInProgressException(
135                    "Transaction not in progress, cannot checkpoint");
136            }
137    
138            for (Iterator iterator = _connections.iterator(); iterator.hasNext();)
139            {
140                BaseConnection connection = (BaseConnection) iterator.next();
141                ConcreteEditingContext context =
142                        (ConcreteEditingContext) connection.getEditingContext();
143    
144                context.checkpoint();
145            }
146        }
147    
148        /**
149         *
150         * Rollback this transaction. A rollback on the transaction, notifies all its listeners. It,
151         * then initiates a rollback on all associated connections.
152         *
153         */
154        public void rollback()
155                throws TransactionException
156        {
157            if (!_isInProgress)
158            {
159                throw new TransactionNotInProgressException(
160                    "Transaction not in progress, nothing to commit");
161            }
162    
163            for (Iterator iterator = _listeners.iterator(); iterator.hasNext();)
164            {
165                TransactionListener listener = (TransactionListener) iterator.next();
166                listener.transactionRollingBack(this);
167            }
168    
169            for (Iterator iterator = _connections.iterator(); iterator.hasNext();)
170            {
171                BaseConnection connection = (BaseConnection) iterator.next();
172                ConcreteEditingContext context =
173                        (ConcreteEditingContext) connection.getEditingContext();
174    
175                context.rollback();
176                connection.transactionRollback();
177                connection.setTransaction(null);
178            }
179    
180            _connections.clear();
181            _isInProgress = false;
182        }
183    
184        public boolean isInProgress()
185        {
186            return _isInProgress;
187        }
188        
189        /**
190         *
191         *  Associate a connection to this transaction. A OTMConnection can be registered to atmost one
192         *  transaction, while a transaction can manage multiple connections.
193         *
194         *  @param connection       the connection to register
195         *
196         */
197        public void registerConnection(OTMConnection connection)
198        {
199            Transaction connectionTx = connection.getTransaction();
200            if ((connectionTx != null) && (connectionTx != this))
201            {
202                throw new TransactionException(
203                    "Attempt to re-assign a different transaction to a open connection");
204            }
205    
206            if (!_connections.contains(connection))
207            {
208                _connections.add(connection);
209    
210                connection.setTransaction(this);
211    
212            }
213        }
214    
215        /**
216         *
217         *  Adds a listener to this transaction. Listeners get boundary notifications of this
218         *  transaction.
219         *
220         *  @param listener         the listener of this transaction
221         *
222         */
223        public void registerListener(TransactionListener listener)
224        {
225            if (_listeners.indexOf(listener) < 0)
226            {
227                _listeners.add(listener);
228            }
229        }
230    }