001 package org.apache.ojb.broker.util;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.sql.SQLException;
019 import java.sql.BatchUpdateException;
020 import java.sql.SQLWarning;
021
022 import org.apache.commons.lang.SystemUtils;
023 import org.apache.commons.lang.exception.ExceptionUtils;
024 import org.apache.ojb.broker.KeyConstraintViolatedException;
025 import org.apache.ojb.broker.PersistenceBrokerSQLException;
026 import org.apache.ojb.broker.core.ValueContainer;
027 import org.apache.ojb.broker.metadata.ClassDescriptor;
028 import org.apache.ojb.broker.metadata.FieldDescriptor;
029 import org.apache.ojb.broker.metadata.JdbcTypesHelper;
030 import org.apache.ojb.broker.util.logging.Logger;
031
032 /**
033 * A helper class which endorse dealing with exceptions.
034 *
035 * @version $Id: ExceptionHelper.java,v 1.1 2007-08-24 22:17:36 ewestfal Exp $
036 */
037 abstract public class ExceptionHelper
038 {
039 /**
040 * Method which support the conversion of {@link java.sql.SQLException} to
041 * OJB's runtime exception (with additional message details).
042 *
043 * @param message The error message to use, if <em>null</em> a standard message is used.
044 * @param ex The exception to convert (mandatory).
045 * @param sql The used sql-statement or <em>null</em>.
046 * @param logger The {@link org.apache.ojb.broker.util.logging.Logger} to log an detailed message
047 * to the specified {@link org.apache.ojb.broker.util.logging.Logger} or <em>null</em> to skip logging message.
048 * @return A new created {@link org.apache.ojb.broker.PersistenceBrokerSQLException} based on the specified
049 * arguments.
050 */
051 public static PersistenceBrokerSQLException generateException(String message, SQLException ex, String sql, Logger logger)
052 {
053 return generateException(message, ex, sql, null, null, logger, null);
054 }
055
056 /**
057 * Method which support the conversion of {@link java.sql.SQLException} to
058 * OJB's runtime exception (with additional message details).
059 *
060 * @param ex The exception to convert (mandatory).
061 * @param sql The used sql-statement or <em>null</em>.
062 * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the target object or <em>null</em>.
063 * @param logger The {@link org.apache.ojb.broker.util.logging.Logger} to log an detailed message
064 * to the specified {@link org.apache.ojb.broker.util.logging.Logger} or <em>null</em> to skip logging message.
065 * @param obj The target object or <em>null</em>.
066 * @return A new created {@link org.apache.ojb.broker.PersistenceBrokerSQLException} based on the specified
067 * arguments.
068 */
069 public static PersistenceBrokerSQLException generateException(SQLException ex, String sql, ClassDescriptor cld, Logger logger, Object obj)
070 {
071 return generateException(ex, sql, cld, null, logger, obj);
072 }
073
074 /**
075 * Method which support the conversion of {@link java.sql.SQLException} to
076 * OJB's runtime exception (with additional message details).
077 *
078 * @param ex The exception to convert (mandatory).
079 * @param sql The used sql-statement or <em>null</em>.
080 * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the target object or <em>null</em>.
081 * @param values The values set in prepared statement or <em>null</em>.
082 * @param logger The {@link org.apache.ojb.broker.util.logging.Logger} to log an detailed message
083 * to the specified {@link org.apache.ojb.broker.util.logging.Logger} or <em>null</em> to skip logging message.
084 * @param obj The target object or <em>null</em>.
085 * @return A new created {@link org.apache.ojb.broker.PersistenceBrokerSQLException} based on the specified
086 * arguments.
087 */
088 public static PersistenceBrokerSQLException generateException(SQLException ex, String sql, ClassDescriptor cld, ValueContainer[] values, Logger logger, Object obj)
089 {
090 return generateException(null, ex, sql, cld, values, logger, obj);
091 }
092
093 /**
094 * Method which support the conversion of {@link java.sql.SQLException} to
095 * OJB's runtime exception (with additional message details).
096 *
097 * @param message The error message to use, if <em>null</em> a standard message is used.
098 * @param ex The exception to convert (mandatory).
099 * @param sql The used sql-statement or <em>null</em>.
100 * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the target object or <em>null</em>.
101 * @param values The values set in prepared statement or <em>null</em>.
102 * @param logger The {@link org.apache.ojb.broker.util.logging.Logger} to log an detailed message
103 * to the specified {@link org.apache.ojb.broker.util.logging.Logger} or <em>null</em> to skip logging message.
104 * @param obj The target object or <em>null</em>.
105 * @return A new created {@link org.apache.ojb.broker.PersistenceBrokerSQLException} based on the specified
106 * arguments.
107 */
108 public static PersistenceBrokerSQLException generateException(String message, SQLException ex, String sql, ClassDescriptor cld, ValueContainer[] values, Logger logger, Object obj)
109 {
110 /*
111 X/OPEN codes within class 23:
112 23000 INTEGRITY CONSTRAINT VIOLATION
113 23001 RESTRICT VIOLATION
114 23502 NOT NULL VIOLATION
115 23503 FOREIGN KEY VIOLATION
116 23505 UNIQUE VIOLATION
117 23514 CHECK VIOLATION
118 */
119 String eol = SystemUtils.LINE_SEPARATOR;
120 StringBuffer msg = new StringBuffer(eol);
121 eol += "* ";
122
123 if(ex instanceof BatchUpdateException)
124 {
125 BatchUpdateException tmp = (BatchUpdateException) ex;
126 if(message != null)
127 {
128 msg.append("* ").append(message);
129 }
130 else
131 {
132 msg.append("* BatchUpdateException during execution of sql-statement:");
133 }
134 msg.append(eol).append("Batch update count is '").append(tmp.getUpdateCounts()).append("'");
135 }
136 else if(ex instanceof SQLWarning)
137 {
138 if(message != null)
139 {
140 msg.append("* ").append(message);
141 }
142 else
143 {
144 msg.append("* SQLWarning during execution of sql-statement:");
145 }
146 }
147 else
148 {
149 if(message != null)
150 {
151 msg.append("* ").append(message);
152 }
153 else
154 {
155 msg.append("* SQLException during execution of sql-statement:");
156 }
157 }
158
159 if(sql != null)
160 {
161 msg.append(eol).append("sql statement was '").append(sql).append("'");
162 }
163 String stateCode = null;
164 if(ex != null)
165 {
166 msg.append(eol).append("Exception message is [").append(ex.getMessage()).append("]");
167 msg.append(eol).append("Vendor error code [").append(ex.getErrorCode()).append("]");
168 msg.append(eol).append("SQL state code [");
169
170 stateCode = ex.getSQLState();
171 if("23000".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=INTEGRITY CONSTRAINT VIOLATION");
172 else if("23001".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=RESTRICT VIOLATION");
173 else if("23502".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=NOT NULL VIOLATION");
174 else if("23503".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=FOREIGN KEY VIOLATION");
175 else if("23505".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=UNIQUE VIOLATION");
176 else if("23514".equalsIgnoreCase(stateCode)) msg.append(stateCode).append("=CHECK VIOLATION");
177 else msg.append(stateCode);
178 msg.append("]");
179 }
180
181 if(cld != null)
182 {
183 msg.append(eol).append("Target class is '")
184 .append(cld.getClassNameOfObject())
185 .append("'");
186 FieldDescriptor[] fields = cld.getPkFields();
187 msg.append(eol).append("PK of the target object is [");
188 for(int i = 0; i < fields.length; i++)
189 {
190 try
191 {
192 if(i > 0) msg.append(", ");
193 msg.append(fields[i].getPersistentField().getName());
194 if(obj != null)
195 {
196 msg.append("=");
197 msg.append(fields[i].getPersistentField().get(obj));
198 }
199 }
200 catch(Exception ignore)
201 {
202 msg.append(" PK field build FAILED! ");
203 }
204 }
205 msg.append("]");
206 }
207 if(values != null)
208 {
209 msg.append(eol).append(values.length).append(" values performed in statement: ").append(eol);
210 for(int i = 0; i < values.length; i++)
211 {
212 ValueContainer value = values[i];
213 msg.append("[");
214 msg.append("jdbcType=").append(JdbcTypesHelper.getSqlTypeAsString(value.getJdbcType().getType()));
215 msg.append(", value=").append(value.getValue());
216 msg.append("]");
217 }
218 }
219 if(obj != null)
220 {
221 msg.append(eol).append("Source object: ");
222 try
223 {
224 msg.append(obj.toString());
225 }
226 catch(Exception e)
227 {
228 msg.append(obj.getClass());
229 }
230 }
231
232 // message string for PB exception
233 String shortMsg = msg.toString();
234
235 if(ex != null)
236 {
237 // add causing stack trace
238 Throwable rootCause = ExceptionUtils.getRootCause(ex);
239 if(rootCause == null) rootCause = ex;
240 msg.append(eol).append("The root stack trace is --> ");
241 String rootStack = ExceptionUtils.getStackTrace(rootCause);
242 msg.append(eol).append(rootStack);
243 }
244 msg.append(SystemUtils.LINE_SEPARATOR).append("**");
245
246 // log error message
247 if(logger != null) logger.error(msg.toString());
248
249 // throw a specific type of runtime exception for a key constraint.
250 if("23000".equals(stateCode) || "23505".equals(stateCode))
251 {
252 throw new KeyConstraintViolatedException(shortMsg, ex);
253 }
254 else
255 {
256 throw new PersistenceBrokerSQLException(shortMsg, ex);
257 }
258 }
259 }