View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.util;
17  
18  import java.io.PrintWriter;
19  import java.io.StringWriter;
20  import java.lang.reflect.Constructor;
21  import java.util.Map;
22  
23  import org.apache.log4j.Logger;
24  import org.springframework.aop.ThrowsAdvice;
25  import org.springframework.core.Ordered;
26  
27  /**
28   * @author Daniel Epstein
29   *         <p>
30   *         Use this Advice to map one exception to another for use when other
31   *         advice eats your runtime exceptions outside of your code. This happens in
32   *         Transactions when commit is not called until outside of your DAO
33   *         layer.
34   *         </p>
35   * 
36   * <p>
37   * Set the property "exceptionMapping" as a map that maps an exception class to
38   * your own exception class
39   * </p>
40   * 
41   * <p>
42   * Remember that aspect order is important and that this bean will always be
43   * order "500"
44   * </p>
45   * 
46   * Example:
47   * 
48   * <pre>
49   * &lt;tx:annotation-driven transaction-manager=&quot;JtaTxManager&quot; order=&quot;1000&quot;/&gt;
50   * lt;bean id=&quot;mapExceptionAdvisor&quot;
51   * class=&quot;org.myfoo.ExceptionMappingAdvice&quot;&gt;
52   * &lt;property name=&quot;exceptionMapping&quot;&gt;
53   * 	&lt;map&gt;
54   * 		&lt;entry key=&quot;javax.persistence.EntityExistsException&quot;
55   * 			value=&quot;org.myfoo.exceptions.AlreadyExistsException&quot; /&gt;
56   * 	&lt;/map&gt;
57   * &lt;/property&gt;
58   * lt;/bean&gt;
59   * lt;aop:config&gt;
60   * &lt;aop:aspect id=&quot;dataAccessToBusinessException&quot;
61   * 	ref=&quot;mapExceptionAdvisor&quot;&gt;
62   * 	&lt;aop:after-throwing
63   * 		pointcut=&quot;execution(* org.myfoo.service.*.*(..))&quot;
64   * 		method=&quot;afterThrowing&quot; throwing=&quot;ex&quot; /&gt;
65   * &lt;/aop:aspect&gt;
66   * lt;/aop:config&gt;
67   * </pre>
68   */
69  public class ExceptionMappingAdvice implements ThrowsAdvice, Ordered {
70  	private int order = 500;
71  	private static final long serialVersionUID = 1L;
72  	private Map<Class<? extends Exception>, Class<? extends Exception>> exceptionMapping;
73  	private Class<? extends Exception> defaultException;
74  	final Logger logger = Logger.getLogger(ExceptionMappingAdvice.class);
75  
76  	/**
77  	 * This method will use the real exception thrown and look up the exception
78  	 * that should be thrown
79  	 * 
80  	 * @param ex
81  	 * @throws Exception
82  	 */
83  	public void afterThrowing(Exception ex) throws Exception {
84  		Class<? extends Exception> mappedExceptionClass = exceptionMapping
85  				.get(ex.getClass());
86  
87  		if (mappedExceptionClass != null) {
88  	        logger.debug("Mapping exception "+ex.getClass()+" to "+mappedExceptionClass);
89  	        Constructor<? extends Exception> c = mappedExceptionClass
90  					.getConstructor(String.class);
91  			Exception mappedException = c.newInstance(ex.getMessage());
92  			throw mappedException;
93  		}
94  		
95  		//Throw a default exception if this is a runtime exception
96  		if(ex instanceof RuntimeException){
97  			logger.trace("No mapping available, throwing default exception "+defaultException);
98  			if (defaultException != null) {
99  				//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 }