001 /**
002 * Copyright 2010 The Kuali Foundation Licensed under the
003 * Educational Community License, Version 2.0 (the "License"); you may
004 * not use this file except in compliance with the License. You may
005 * obtain a copy of the License at
006 *
007 * http://www.osedu.org/licenses/ECL-2.0
008 *
009 * Unless required by applicable law or agreed to in writing,
010 * software distributed under the License is distributed on an "AS IS"
011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012 * or implied. See the License for the specific language governing
013 * permissions and limitations under the License.
014 */
015
016 package org.kuali.student.common.util;
017
018 import java.io.PrintWriter;
019 import java.io.StringWriter;
020 import java.lang.reflect.Constructor;
021 import java.util.Map;
022
023 import org.apache.log4j.Logger;
024 import org.springframework.aop.ThrowsAdvice;
025 import org.springframework.core.Ordered;
026
027 /**
028 * @author Daniel Epstein
029 * <p>
030 * Use this Advice to map one exception to another for use when other
031 * advice eats your runtime exceptions outside of your code. This happens in
032 * Transactions when commit is not called until outside of your DAO
033 * layer.
034 * </p>
035 *
036 * <p>
037 * Set the property "exceptionMapping" as a map that maps an exception class to
038 * your own exception class
039 * </p>
040 *
041 * <p>
042 * Remember that aspect order is important and that this bean will always be
043 * order "500"
044 * </p>
045 *
046 * Example:
047 *
048 * <pre>
049 * <tx:annotation-driven transaction-manager="JtaTxManager" order="1000"/>
050 * lt;bean id="mapExceptionAdvisor"
051 * class="org.myfoo.ExceptionMappingAdvice">
052 * <property name="exceptionMapping">
053 * <map>
054 * <entry key="javax.persistence.EntityExistsException"
055 * value="org.myfoo.exceptions.AlreadyExistsException" />
056 * </map>
057 * </property>
058 * lt;/bean>
059 * lt;aop:config>
060 * <aop:aspect id="dataAccessToBusinessException"
061 * ref="mapExceptionAdvisor">
062 * <aop:after-throwing
063 * pointcut="execution(* org.myfoo.service.*.*(..))"
064 * method="afterThrowing" throwing="ex" />
065 * </aop:aspect>
066 * lt;/aop:config>
067 * </pre>
068 */
069 public class ExceptionMappingAdvice implements ThrowsAdvice, Ordered {
070 private int order = 500;
071 private static final long serialVersionUID = 1L;
072 private Map<Class<? extends Exception>, Class<? extends Exception>> exceptionMapping;
073 private Class<? extends Exception> defaultException;
074 final Logger logger = Logger.getLogger(ExceptionMappingAdvice.class);
075
076 /**
077 * This method will use the real exception thrown and look up the exception
078 * that should be thrown
079 *
080 * @param ex
081 * @throws Exception
082 */
083 public void afterThrowing(Exception ex) throws Exception {
084 Class<? extends Exception> mappedExceptionClass = exceptionMapping
085 .get(ex.getClass());
086
087 if (mappedExceptionClass != null) {
088 logger.debug("Mapping exception "+ex.getClass()+" to "+mappedExceptionClass);
089 Constructor<? extends Exception> c = mappedExceptionClass
090 .getConstructor(String.class);
091 Exception mappedException = c.newInstance(ex.getMessage());
092 throw mappedException;
093 }
094
095 //Throw a default exception if this is a runtime exception
096 if(ex instanceof RuntimeException){
097 logger.trace("No mapping available, throwing default exception "+defaultException);
098 if (defaultException != null) {
099 //Log the error
100 StringWriter traceWriter = new StringWriter();
101 PrintWriter printWriter = new PrintWriter(traceWriter, false);
102 logger.error(printWriter, ex);
103 printWriter.close();
104 String faultMessage = traceWriter.getBuffer().toString();
105 logger.error(faultMessage);
106 //Throw the default exception
107 try{
108 Constructor<? extends Exception> c = defaultException
109 .getConstructor(String.class, Throwable.class);
110 throw c.newInstance(ex.getMessage(), ex);
111 }catch(NoSuchMethodException e){
112 Constructor<? extends Exception> c = defaultException
113 .getConstructor(String.class);
114 throw c.newInstance(ex.getMessage());
115 }
116 }
117 //Check if no default was defined
118 logger.debug("No mapping or default exception available. Exception "+ex.getClass());
119 throw new RuntimeException("Could Not Map Exception: " + ex.toString());
120 }
121 }
122
123 @Override
124 public int getOrder() {
125 return order;
126 }
127
128 /**
129 * @param order
130 * the order to set
131 */
132 public void setOrder(int order) {
133 this.order = order;
134 }
135
136 /**
137 * @return the exceptionMapping
138 */
139 public Map<Class<? extends Exception>, Class<? extends Exception>> getExceptionMapping() {
140 return exceptionMapping;
141 }
142
143 /**
144 * @param exceptionMapping
145 * the exceptionMapping to set
146 */
147 public void setExceptionMapping(
148 Map<Class<? extends Exception>, Class<? extends Exception>> exceptionMapping) {
149 this.exceptionMapping = exceptionMapping;
150 }
151
152 /**
153 * @return the defaultException
154 */
155 public Class<? extends Exception> getDefaultException() {
156 return defaultException;
157 }
158
159 /**
160 * @param defaultException
161 * the defaultException to set
162 */
163 public void setDefaultException(Class<? extends Exception> defaultException) {
164 this.defaultException = defaultException;
165 }
166
167 }