001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Contact: sequoia@continuent.org
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: * Initial developer(s): Jean-Bernard van Zuylen.
021: * Contributor(s): ______________________.
022: */package org.continuent.sequoia.controller.loadbalancer.tasks;
023:
024: import java.sql.Connection;
025: import java.sql.SQLException;
026: import java.sql.Savepoint;
027:
028: import org.continuent.sequoia.common.i18n.Translate;
029: import org.continuent.sequoia.common.log.Trace;
030: import org.continuent.sequoia.controller.backend.DatabaseBackend;
031: import org.continuent.sequoia.controller.connection.AbstractConnectionManager;
032: import org.continuent.sequoia.controller.connection.PooledConnection;
033: import org.continuent.sequoia.controller.loadbalancer.BackendWorkerThread;
034: import org.continuent.sequoia.controller.requestmanager.TransactionMetaData;
035: import org.continuent.sequoia.controller.requests.AbstractRequest;
036: import org.continuent.sequoia.controller.requests.UnknownWriteRequest;
037:
038: /**
039: * This class defines a RollbackToSavepointTask
040: *
041: * @author <a href="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
042: * </a>
043: * @version 1.0
044: */
045: public class RollbackToSavepointTask extends AbstractTask {
046: /** Transaction metadata (login, transaction id, timeout) */
047: private TransactionMetaData tm;
048: /** Name of the savepoint. */
049: private String savepointName;
050:
051: static Trace endUserLogger = Trace
052: .getLogger("org.continuent.sequoia.enduser");
053:
054: /**
055: * Creates a new <code>RollbackToSavepointTask</code> object
056: *
057: * @param nbToComplete number of threads that must succeed before returning
058: * @param totalNb total number of threads
059: * @param tm transaction metadata
060: * @param savepointName the name of the savepoint
061: * @throws NullPointerException if tm is null
062: */
063: public RollbackToSavepointTask(int nbToComplete, int totalNb,
064: TransactionMetaData tm, String savepointName)
065: throws NullPointerException {
066: super (nbToComplete, totalNb, tm.isPersistentConnection(), tm
067: .getPersistentConnectionId());
068: if (tm == null)
069: throw new NullPointerException(
070: "Unexpected null metadata in BeginTask");
071: this .tm = tm;
072: this .savepointName = savepointName;
073: }
074:
075: /**
076: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#executeTask(org.continuent.sequoia.controller.loadbalancer.BackendWorkerThread)
077: */
078: public void executeTask(BackendWorkerThread backendThread)
079: throws SQLException {
080: DatabaseBackend backend = backendThread.getBackend();
081: Long lTid = new Long(tm.getTransactionId());
082:
083: AbstractConnectionManager cm = backend.getConnectionManager(tm
084: .getLogin());
085: if (cm == null) {
086: SQLException se = new SQLException(
087: "No Connection Manager for Virtual Login:"
088: + tm.getLogin());
089: try {
090: notifyFailure(backendThread, -1, se);
091: } catch (SQLException ignore) {
092:
093: }
094: throw se;
095: }
096:
097: PooledConnection pc = cm.retrieveConnectionForTransaction(tm
098: .getTransactionId());
099:
100: // Sanity check
101: if (pc == null) { // Bad connection
102: backend.stopTransaction(lTid);
103: SQLException se = new SQLException(
104: "Unable to retrieve connection for transaction "
105: + tm.getTransactionId());
106: try { // All backends failed, just ignore
107: if (!notifyFailure(backendThread, tm.getTimeout(), se))
108: return;
109: } catch (SQLException ignore) {
110: }
111: // Disable this backend (it is no more in sync) by killing the backend
112: // thread
113: backendThread.getLoadBalancer().disableBackend(backend,
114: true);
115: String msg = "Failed to rollback transaction "
116: + tm.getTransactionId() + " to savepoint "
117: + savepointName + " on backend "
118: + backend.getName() + " but " + getSuccess()
119: + " succeeded (" + se + ")";
120: backendThread.getLogger().error(msg);
121: endUserLogger.error(Translate
122: .get("loadbalancer.backend.disabling", backend
123: .getName()));
124: throw new SQLException(msg);
125: }
126:
127: // Execute Query
128: try {
129: Savepoint savepoint = backend.getSavepoint(lTid,
130: savepointName);
131: if (savepoint != null) {
132: Connection c = pc.getConnection();
133: c.rollback(savepoint);
134: } else {
135: String msg = "No savepoint named "
136: + savepointName
137: + " was found cannot rollback to savepoint in transaction "
138: + lTid;
139: backend.getLogger().warn(msg);
140: throw new SQLException(msg);
141: }
142:
143: if (tm.altersDatabaseSchema()) { // Flag the schema as dirty in case the transaction contained DDL
144: UnknownWriteRequest fakeRequest = new UnknownWriteRequest(
145: "rollback " + savepointName, false, 0, null);
146: fakeRequest.setLogin(tm.getLogin());
147: fakeRequest.setIsAutoCommit(false);
148: fakeRequest.setTransactionId(getTransactionId());
149: fakeRequest
150: .setPersistentConnection(isPersistentConnection());
151: fakeRequest
152: .setPersistentConnectionId(getPersistentConnectionId());
153: backendThread.getBackend().setSchemaIsDirty(true,
154: fakeRequest);
155: }
156: } catch (Exception e) {
157: try {
158: if (!notifyFailure(backendThread, tm.getTimeout(),
159: new SQLException(e.getMessage())))
160: return;
161: } catch (SQLException ignore) {
162: }
163: // Disable this backend (it is no more in sync) by killing the backend
164: // thread
165: backendThread.getLoadBalancer().disableBackend(backend,
166: true);
167: String msg = "Failed to rollback transaction "
168: + tm.getTransactionId() + " to savepoint "
169: + savepointName + " on backend "
170: + backend.getName() + " but " + getSuccess()
171: + " succeeded (" + e + ")";
172: backendThread.getLogger().error(msg);
173: endUserLogger.error(Translate
174: .get("loadbalancer.backend.disabling", backend
175: .getName()));
176: throw new SQLException(msg);
177: }
178: notifySuccess(backendThread);
179: }
180:
181: /**
182: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getRequest()
183: */
184: public AbstractRequest getRequest() {
185: return null;
186: }
187:
188: /**
189: * @return savepoint name
190: */
191: public String getSavepointName() {
192: return savepointName;
193: }
194:
195: /**
196: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getTransactionId()
197: */
198: public long getTransactionId() {
199: return tm.getTransactionId();
200: }
201:
202: /**
203: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#isAutoCommit()
204: */
205: public boolean isAutoCommit() {
206: return false;
207: }
208:
209: /**
210: * @see java.lang.Object#equals(java.lang.Object)
211: */
212: public boolean equals(Object other) {
213: if ((other == null)
214: || !(other instanceof RollbackToSavepointTask))
215: return false;
216:
217: RollbackToSavepointTask rollbackSavepoint = (RollbackToSavepointTask) other;
218: return (this .getTransactionId() == rollbackSavepoint
219: .getTransactionId())
220: && (this .savepointName.equals(rollbackSavepoint
221: .getSavepointName()));
222: }
223:
224: /**
225: * @see java.lang.Object#hashCode()
226: */
227: public int hashCode() {
228: return (int) this .getTransactionId();
229: }
230:
231: /**
232: * @see java.lang.Object#toString()
233: */
234: public String toString() {
235: return "RollbackToSavepointTask for transaction "
236: + tm.getTransactionId() + " (" + savepointName + ")";
237: }
238: }
|