package com.tesora.dve.mysqlapi.repl;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.sql.SQLException;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
public class MyReplErrorAdjudicator {
Logger logger = Logger.getLogger(MyReplErrorAdjudicator.class);
enum ReplSkipErrorOption {
UNKNOWN("UNKNOWN"),
ALL("ALL"),
OFF("OFF")
;
private final String option;
private ReplSkipErrorOption(String option) {
this.option = option;
}
public String asString() {
return option;
}
static public ReplSkipErrorOption fromString(String option) {
for(ReplSkipErrorOption rseo : values()) {
if (StringUtils.equalsIgnoreCase(rseo.asString(), option)) {
return rseo;
}
}
return UNKNOWN;
}
}
boolean skipAll = false;
boolean off = true;
TreeSet<Integer> skipErrorList = new TreeSet<Integer>();
public MyReplErrorAdjudicator() {
skipAll = false;
off = true;
skipErrorList.clear();
}
public MyReplErrorAdjudicator(Set<String> skipErrors) {
parseOptions(skipErrors);
if (!isOff()) {
if (skipAll()) {
logger.warn("Replication Slave has been configured with option '" + ReplSkipErrorOption.ALL + "'. Any errors encountered during processing will be ignored and replication will continue.");
} else {
logger.warn("Replication Slave has been configured to ignore the following SQL error code(s): " + buildErrorCodeString());
}
}
}
public boolean skipAll() {
return skipAll;
}
public boolean isOff() {
return off;
}
public boolean validateErrorAndStop(int masterErrorCode) {
return validateErrorAndStop(masterErrorCode, 0);
}
public boolean validateErrorAndStop(int masterErrorCode, Throwable slaveException) {
if (skipAll()) {
return false;
}
Throwable t = getRootException(slaveException);
if (t instanceof SQLException) {
return validateErrorAndStop(masterErrorCode, ((SQLException)t).getErrorCode());
} else {
return true;
}
}
public boolean validateErrorAndStop(int masterErrorCode, int slaveErrorCode) {
if (skipAll() || (masterErrorCode == slaveErrorCode))
return false;
if (isOff() && (masterErrorCode != slaveErrorCode))
return true;
return !skipErrorList.contains(masterErrorCode) || !skipErrorList.contains(slaveErrorCode);
}
// for testing
TreeSet<Integer> getSkipErrorList() {
return skipErrorList;
}
private void parseOptions(Set<String> skipErrors) {
if (skipErrors == null) {
return;
}
skipErrorList.clear();
skipErrorList.add(0);
for(String errorCode : skipErrors) {
try {
off = false;
skipAll = false;
skipErrorList.add(Integer.parseInt(errorCode));
} catch (NumberFormatException e) {
// could be all or off
switch(ReplSkipErrorOption.fromString(errorCode)) {
case ALL:
off = false;
skipAll = true;
skipErrorList.clear();
return;
case OFF:
default: // reset to off with invalid option
off = true;
skipAll = false;
skipErrorList.clear();
return;
}
}
}
}
private String buildErrorCodeString() {
StringBuilder ret = new StringBuilder();
boolean first = true;
for(Integer code : skipErrorList) {
if (!first) {
ret.append(", ");
}
ret.append(code);
first = false;
}
return ret.toString();
}
private Throwable getRootException(Throwable exception) {
Throwable root, lastEx = exception;
for (root = exception; (root = root.getCause()) != null;) {
lastEx = root;
}
return lastEx;
}
}