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    }