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.loadbalancer.BackendWorkerThread;
033: import org.continuent.sequoia.controller.requestmanager.TransactionMetaData;
034: import org.continuent.sequoia.controller.requests.AbstractRequest;
035: import org.continuent.sequoia.controller.requests.UnknownWriteRequest;
036:
037: /**
038: * Task to set a savepoint to a transaction.
039: *
040: * @author <a href="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
041: * </a>
042: * @version 1.0
043: */
044: public class SavepointTask extends AbstractTask {
045: /** Transaction metadata (login, transaction id, timeout) */
046: private TransactionMetaData tm;
047: /** Name of the savepoint. */
048: private String savepointName;
049: /** Savepoint that was created. */
050: private Savepoint result;
051:
052: static Trace endUserLogger = Trace
053: .getLogger("org.continuent.sequoia.enduser");
054:
055: /**
056: * Sets a savepoint given a transaction metadata and a savepoint name.
057: *
058: * @param nbToComplete number of threads that must succeed before returning
059: * @param totalNb total number of threads
060: * @param tm transaction metadata
061: * @param savepointName the savepoint to remove
062: * @throws NullPointerException if tm is null
063: */
064: public SavepointTask(int nbToComplete, int totalNb,
065: TransactionMetaData tm, String savepointName)
066: throws NullPointerException {
067: super (nbToComplete, totalNb, tm.isPersistentConnection(), tm
068: .getPersistentConnectionId());
069: if (tm == null)
070: throw new NullPointerException(
071: "Unexpected null metadata in BeginTask");
072: this .tm = tm;
073: this .savepointName = savepointName;
074: }
075:
076: /**
077: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#executeTask(org.continuent.sequoia.controller.loadbalancer.BackendWorkerThread)
078: */
079: public void executeTask(BackendWorkerThread backendThread)
080: throws SQLException {
081: DatabaseBackend backend = backendThread.getBackend();
082: Long lTid = new Long(tm.getTransactionId());
083:
084: AbstractConnectionManager cm = backend.getConnectionManager(tm
085: .getLogin());
086: if (cm == null) {
087: SQLException se = new SQLException(
088: "No Connection Manager for Virtual Login:"
089: + tm.getLogin());
090: try {
091: notifyFailure(backendThread, -1, se);
092: } catch (SQLException ignore) {
093:
094: }
095: throw se;
096: }
097:
098: Savepoint savepoint = null;
099: try {
100: AbstractRequest fakeRequest = new UnknownWriteRequest(
101: "savepoint " + savepointName, false, 0, "\n");
102: fakeRequest.setTransactionId(tm.getTransactionId());
103: // Set transaction isolation level in case the transaction was not started
104: // yet
105: fakeRequest
106: .setTransactionIsolation(org.continuent.sequoia.driver.Connection.DEFAULT_TRANSACTION_ISOLATION_LEVEL);
107: Connection c = backend
108: .getConnectionForTransactionAndLazyBeginIfNeeded(
109: fakeRequest, cm);
110:
111: // Sanity check
112: if (c == null) { // Bad connection
113: backend.stopTransaction(lTid);
114: SQLException se = new SQLException(
115: "Unable to retrieve connection for transaction "
116: + tm.getTransactionId());
117:
118: try { // All backends failed, just ignore
119: if (!notifyFailure(backendThread, tm.getTimeout(),
120: se))
121: return;
122: } catch (SQLException ignore) {
123: }
124: // Disable this backend (it is no more in sync) by killing the backend
125: // thread
126: backendThread.getLoadBalancer().disableBackend(backend,
127: true);
128: String msg = "Failed to set savepoint for transaction "
129: + tm.getTransactionId() + " on backend "
130: + backend.getName() + " but " + getSuccess()
131: + " succeeded (" + se + ")";
132: backendThread.getLogger().error(msg);
133: endUserLogger.error(Translate.get(
134: "loadbalancer.backend.disabling", backend
135: .getName()));
136: throw new SQLException(msg);
137: }
138:
139: // Execute Query
140: savepoint = c.setSavepoint(savepointName);
141: result = savepoint;
142: } catch (Exception e) {
143: try {
144: if (!notifyFailure(backendThread, tm.getTimeout(),
145: new SQLException(e.getMessage())))
146: return;
147: } catch (SQLException ignore) {
148: }
149: // Disable this backend (it is no more in sync) by killing the backend
150: // thread
151: backendThread.getLoadBalancer().disableBackend(backend,
152: true);
153: String msg = "Failed to set savepoint for transaction "
154: + tm.getTransactionId() + " on backend "
155: + backend.getName() + " but " + getSuccess()
156: + " succeeded (" + e + ")";
157: backendThread.getLogger().error(msg);
158: endUserLogger.error(Translate
159: .get("loadbalancer.backend.disabling", backend
160: .getName()));
161: throw new SQLException(msg);
162: } finally {
163: if (savepoint != null)
164: backend.addSavepoint(lTid, savepoint);
165: }
166:
167: notifySuccess(backendThread);
168: }
169:
170: /**
171: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getRequest()
172: */
173: public AbstractRequest getRequest() {
174: return null;
175: }
176:
177: /**
178: * Returns the result.
179: *
180: * @return Savepoint
181: */
182: public Savepoint getResult() {
183: return result;
184: }
185:
186: /**
187: * @return savepoint name
188: */
189: public String getSavepointName() {
190: return savepointName;
191: }
192:
193: /**
194: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getTransactionId()
195: */
196: public long getTransactionId() {
197: return tm.getTransactionId();
198: }
199:
200: /**
201: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#isAutoCommit()
202: */
203: public boolean isAutoCommit() {
204: return false;
205: }
206:
207: /**
208: * @see java.lang.Object#equals(java.lang.Object)
209: */
210: public boolean equals(Object other) {
211: if ((other == null) || !(other instanceof SavepointTask))
212: return false;
213:
214: SavepointTask savepoint = (SavepointTask) other;
215: return (this .getTransactionId() == savepoint.getTransactionId())
216: && (this .savepointName.equals(savepoint
217: .getSavepointName()));
218: }
219:
220: /**
221: * @see java.lang.Object#hashCode()
222: */
223: public int hashCode() {
224: return (int) this .getTransactionId();
225: }
226:
227: /**
228: * @see java.lang.Object#toString()
229: */
230: public String toString() {
231: return "SavepointTask for transaction " + tm.getTransactionId()
232: + " (" + savepointName + ")";
233: }
234:
235: }
|