package fm.liu.timo.server.session; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap.KeySetView; import fm.liu.timo.config.ErrorCode; import fm.liu.timo.mysql.packet.ErrorPacket; import fm.liu.timo.mysql.packet.OkPacket; import fm.liu.timo.net.connection.BackendConnection; import fm.liu.timo.server.ServerConnection; import fm.liu.timo.server.session.handler.CommitHandler; import fm.liu.timo.server.session.handler.ResultHandler; import fm.liu.timo.server.session.handler.RollbackHandler; import fm.liu.timo.server.session.handler.RollbackToSavepointHandler; import fm.liu.timo.server.session.handler.SavepointHandler; /** * @author liuhuanting */ public class TransactionSession extends AbstractSession { protected final String savepoint; protected final String rollbackToSavepoint; public TransactionSession(ServerConnection front) { super(front); String tag = "savepoint" + this.hashCode(); this.savepoint = "savepoint " + tag; this.rollbackToSavepoint = "rollback to savepoint " + tag; variables.setAutocommit(false); } public String getSavepoint() { return savepoint; } @Override public void release(BackendConnection con) {} @Override public void clear() { front.reset(); ArrayList<BackendConnection> rollbacks = new ArrayList<>(); KeySetView<Integer, BackendConnection> keys = connections.keySet(); for (Integer id : keys) { BackendConnection con = connections.remove(id); if (con.isClosed()) { continue; } if (con.isRunning()) { con.setHandler(null); con.close("cleared"); } else { rollbacks.add(con); } } ResultHandler handler = new RollbackHandler(); rollbacks.forEach(con -> con.query("rollback", handler)); } @Override public void commit() { if (getConnections().isEmpty()) { super.commit(); return; } Collection<BackendConnection> cons = availableConnections(); if (cons.size() == getConnections().size()) { ResultHandler handler = new CommitHandler(this, cons); cons.forEach(con -> con.query("commit", handler)); } else { onError(); } } @Override public void rollback(boolean response) { clear(); if (response) { super.rollback(response); } } public void savepoint(OkPacket ok) { Collection<BackendConnection> cons = availableConnections(); if (cons.size() == getConnections().size()) { ResultHandler handler = new SavepointHandler(this, cons.size(), ok); cons.forEach(con -> con.query(savepoint, handler)); } else { onError(); } } public void rollbackToSavepoint(ErrorPacket err) { Collection<BackendConnection> cons = availableConnections(); if (cons.size() == getConnections().size()) { ResultHandler handler = new RollbackToSavepointHandler(this, cons.size(), err); cons.forEach(con -> con.query(rollbackToSavepoint, handler)); } else { onError(); } } protected void onError() { front.reset(); this.clear(); front.writeErrMessage(ErrorCode.ER_YES, "some connection already closed, transaction have been rollbacked automatically"); } }