001/* 002 * (C) Copyright 2014, 2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017package org.nuxeo.runtime.datasource; 018 019import java.sql.SQLException; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.common.xmap.annotation.XNode; 027import org.nuxeo.common.xmap.annotation.XNodeList; 028import org.nuxeo.common.xmap.annotation.XObject; 029import org.nuxeo.runtime.model.ContributionFragmentRegistry; 030import org.tranql.connector.ExceptionSorter; 031 032/** 033 * 034 * 035 * @since 8.3 036 */ 037public class DatasourceExceptionSorter implements ExceptionSorter { 038 039 public enum Classcode { 040 NoError("00"), 041 Warning("01"), 042 NoData("02"), 043 DynamicSQLError("07"), 044 ConnectionException("08"), 045 TriggeredActionException("09"), 046 FeatureNotSupported("0A"), 047 InvalidTransactionInitiation("0B"), 048 InvalidTargetTypeSpecification("0D"), 049 InvalidSchemaNameListSpecification("0E"), 050 LocatorException("0F"), 051 ResignalWhenHandlerNotActive("0K"), 052 InvalidGrantor("0L"), 053 InvalidSqlInvokedProcedureReference("0M"), 054 MappingError("0N"), 055 InvalidRoleSpecification("0P"), 056 InvalidTransformGroupNameSpecification("0S"), 057 TargetTableDisagreesWithCursorSpecification("0T"), 058 AttemptToAssignNonUpdatableColumn("0U"), 059 AttemptToAssignToOrderingColumn("0V"), 060 ProhibitedStatementEncouteredDuringTriggerExecution("0W"), 061 DiagnosticsException("0Z"), 062 XQuery("10"), 063 CaseNotFoundInCaseStatement("20"), 064 CardinalityViolation("21"), 065 DataException("22"), 066 IntegrityConstraintViolation("23"), 067 InvalidCursorState("24"), 068 InvalidTransactionState("25"), 069 InvalidSQLStatementName("26"), 070 TriggeredDataChangeViolation("27"), 071 InvalidAuthorizationSpeciciation("28"), 072 DependentPrivilegeDescriptorsAlreadyExsist("2B"), 073 InvalidConnectionName("2E"), 074 InvalidCharacterSetName("2C"), 075 InvalidTransactionTermination("2D"), 076 SqlRoutineException("2F"), 077 InvalidSessionCollationSpecication("2H"), 078 InvalidSqlStatementIdentifier("30"), 079 InvalidSqlDescriptorName("33"), 080 InvalidCursorName("34"), 081 InvalidConditionNumber("35"), 082 CursorSensivityException("36"), 083 SyntaxError("37"), 084 ExternalRoutineException("38"), 085 ExternalRoutineInvocationException("39"), 086 SavepointException("3B"), 087 InvalidCatalogName("3D"), 088 AmbiguousCursorName("3C"), 089 InvalidSchemaName("3F"), 090 TransactionRollback("40"), 091 SyntaxErrorOrAccessRuleViolation("42"), 092 WithCheckOptionViolation("44"), 093 JavaErrors("46"), 094 InvalidApplicationState("51"), 095 InvalidOperandOrInconsistentSpecification("53"), 096 SqlOrProductLimitExcedeed("54"), 097 ObjectNotInPrerequisiteState("55"), 098 MiscellaneoudSqlOrProductError("56"), 099 ResourceNotAvailableOrOperatorIntervention("57"), 100 SystemError("58"), 101 CommonUtilitiesAndTools("5U"), 102 RemoteDatabaseAccess("HZ"); 103 104 public String value; 105 106 Classcode(String code) { 107 value = code; 108 } 109 110 } 111 112 @XObject("sorter") 113 public static class Configuration { 114 115 @XNode("@id") 116 String id = ""; 117 118 @XNode("@override") 119 boolean override = false; 120 121 @XNode("@path") 122 String pathname; 123 124 boolean matches(String classname) { 125 return classname.startsWith(pathname); 126 } 127 128 @XNodeList(value = "code", type = String[].class, componentType = String.class) 129 public void setCodes(String... values) { 130 for (String value : values) { 131 Classcode classcode = Classcode.valueOf(value); 132 if (classcode != null) { 133 value = classcode.value; 134 } 135 int length = value.length(); 136 if (length == 2) { 137 codes.add(value); 138 } else if (length == 5) { 139 states.add(value); 140 } else { 141 LogFactory.getLog(DatasourceExceptionSorter.class).error("invalid code " + value); 142 } 143 } 144 } 145 146 final Set<String> codes = new HashSet<>(); 147 148 final Set<String> states = new HashSet<>(); 149 150 @XNodeList(value = "vendor", type = HashSet.class, componentType = Integer.class) 151 Set<Integer> vendors = new HashSet<>(); 152 153 boolean isFatal(String sqlstate, Integer vendor) { 154 String code = sqlstate.substring(0, 2); 155 return codes.contains(code) || states.contains(sqlstate) || vendors.contains(vendor); 156 } 157 } 158 159 public static class Registry extends ContributionFragmentRegistry<Configuration> { 160 161 final Map<String, Configuration> actuals = new HashMap<>(); 162 163 @Override 164 public String getContributionId(Configuration contrib) { 165 return contrib.id; 166 } 167 168 @Override 169 public void contributionUpdated(String id, Configuration contrib, Configuration newOrigContrib) { 170 actuals.put(id, contrib); 171 } 172 173 @Override 174 public void contributionRemoved(String id, Configuration origContrib) { 175 actuals.put(id, origContrib); 176 } 177 178 @Override 179 public Configuration clone(Configuration orig) { 180 Configuration cloned = new Configuration(); 181 cloned.states.addAll(orig.states); 182 cloned.codes.addAll(orig.codes); 183 return cloned; 184 } 185 186 @Override 187 public void merge(Configuration src, Configuration dst) { 188 if (src.override) { 189 dst.states.clear(); 190 dst.codes.clear(); 191 } 192 dst.states.addAll(src.states); 193 dst.codes.addAll(src.codes); 194 } 195 196 public Configuration lookup(SQLException se) { 197 for (StackTraceElement frame : se.getStackTrace()) { 198 for (Configuration config : actuals.values()) { 199 if ("".equals(config.id)) { 200 continue; 201 } 202 if (config.matches(frame.getClassName())) { 203 return config; 204 } 205 } 206 } 207 return actuals.get(""); 208 } 209 210 } 211 212 Configuration configuration; 213 214 @Override 215 public boolean isExceptionFatal(Exception e) { 216 if (!(e instanceof SQLException)) { 217 return true; 218 } 219 SQLException se = (SQLException) e; 220 String statuscode = se.getSQLState(); 221 Integer errorcode = Integer.valueOf(se.getErrorCode()); 222 if (configuration == null) { 223 configuration = DataSourceComponent.instance.sorterRegistry.lookup(se); 224 } 225 return configuration.isFatal(statuscode, errorcode); 226 } 227 228 @Override 229 public boolean rollbackOnFatalException() { 230 return true; 231 } 232 233}