/* * (C) Copyright 2014, 2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.nuxeo.runtime.datasource; import java.sql.SQLException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.xmap.annotation.XNode; import org.nuxeo.common.xmap.annotation.XNodeList; import org.nuxeo.common.xmap.annotation.XObject; import org.nuxeo.runtime.model.ContributionFragmentRegistry; import org.tranql.connector.ExceptionSorter; /** * * * @since 8.3 */ public class DatasourceExceptionSorter implements ExceptionSorter { public enum Classcode { NoError("00"), Warning("01"), NoData("02"), DynamicSQLError("07"), ConnectionException("08"), TriggeredActionException("09"), FeatureNotSupported("0A"), InvalidTransactionInitiation("0B"), InvalidTargetTypeSpecification("0D"), InvalidSchemaNameListSpecification("0E"), LocatorException("0F"), ResignalWhenHandlerNotActive("0K"), InvalidGrantor("0L"), InvalidSqlInvokedProcedureReference("0M"), MappingError("0N"), InvalidRoleSpecification("0P"), InvalidTransformGroupNameSpecification("0S"), TargetTableDisagreesWithCursorSpecification("0T"), AttemptToAssignNonUpdatableColumn("0U"), AttemptToAssignToOrderingColumn("0V"), ProhibitedStatementEncouteredDuringTriggerExecution("0W"), DiagnosticsException("0Z"), XQuery("10"), CaseNotFoundInCaseStatement("20"), CardinalityViolation("21"), DataException("22"), IntegrityConstraintViolation("23"), InvalidCursorState("24"), InvalidTransactionState("25"), InvalidSQLStatementName("26"), TriggeredDataChangeViolation("27"), InvalidAuthorizationSpeciciation("28"), DependentPrivilegeDescriptorsAlreadyExsist("2B"), InvalidConnectionName("2E"), InvalidCharacterSetName("2C"), InvalidTransactionTermination("2D"), SqlRoutineException("2F"), InvalidSessionCollationSpecication("2H"), InvalidSqlStatementIdentifier("30"), InvalidSqlDescriptorName("33"), InvalidCursorName("34"), InvalidConditionNumber("35"), CursorSensivityException("36"), SyntaxError("37"), ExternalRoutineException("38"), ExternalRoutineInvocationException("39"), SavepointException("3B"), InvalidCatalogName("3D"), AmbiguousCursorName("3C"), InvalidSchemaName("3F"), TransactionRollback("40"), SyntaxErrorOrAccessRuleViolation("42"), WithCheckOptionViolation("44"), JavaErrors("46"), InvalidApplicationState("51"), InvalidOperandOrInconsistentSpecification("53"), SqlOrProductLimitExcedeed("54"), ObjectNotInPrerequisiteState("55"), MiscellaneoudSqlOrProductError("56"), ResourceNotAvailableOrOperatorIntervention("57"), SystemError("58"), CommonUtilitiesAndTools("5U"), RemoteDatabaseAccess("HZ"); public String value; Classcode(String code) { value = code; } } @XObject("sorter") public static class Configuration { @XNode("@id") String id = ""; @XNode("@override") boolean override = false; @XNode("@path") String pathname; boolean matches(String classname) { return classname.startsWith(pathname); } @XNodeList(value = "code", type = String[].class, componentType = String.class) public void setCodes(String... values) { for (String value : values) { Classcode classcode = Classcode.valueOf(value); if (classcode != null) { value = classcode.value; } int length = value.length(); if (length == 2) { codes.add(value); } else if (length == 5) { states.add(value); } else { LogFactory.getLog(DatasourceExceptionSorter.class).error("invalid code " + value); } } } final Set<String> codes = new HashSet<>(); final Set<String> states = new HashSet<>(); @XNodeList(value = "vendor", type = HashSet.class, componentType = Integer.class) Set<Integer> vendors = new HashSet<>(); boolean isFatal(String sqlstate, Integer vendor) { String code = sqlstate.substring(0, 2); return codes.contains(code) || states.contains(sqlstate) || vendors.contains(vendor); } } public static class Registry extends ContributionFragmentRegistry<Configuration> { final Map<String, Configuration> actuals = new HashMap<>(); @Override public String getContributionId(Configuration contrib) { return contrib.id; } @Override public void contributionUpdated(String id, Configuration contrib, Configuration newOrigContrib) { actuals.put(id, contrib); } @Override public void contributionRemoved(String id, Configuration origContrib) { actuals.put(id, origContrib); } @Override public Configuration clone(Configuration orig) { Configuration cloned = new Configuration(); cloned.states.addAll(orig.states); cloned.codes.addAll(orig.codes); return cloned; } @Override public void merge(Configuration src, Configuration dst) { if (src.override) { dst.states.clear(); dst.codes.clear(); } dst.states.addAll(src.states); dst.codes.addAll(src.codes); } public Configuration lookup(SQLException se) { for (StackTraceElement frame : se.getStackTrace()) { for (Configuration config : actuals.values()) { if ("".equals(config.id)) { continue; } if (config.matches(frame.getClassName())) { return config; } } } return actuals.get(""); } } Configuration configuration; @Override public boolean isExceptionFatal(Exception e) { if (!(e instanceof SQLException)) { return true; } SQLException se = (SQLException) e; String statuscode = se.getSQLState(); Integer errorcode = Integer.valueOf(se.getErrorCode()); if (configuration == null) { configuration = DataSourceComponent.instance.sorterRegistry.lookup(se); } return configuration.isFatal(statuscode, errorcode); } @Override public boolean rollbackOnFatalException() { return true; } }