001/* 002 * JKNIV, utils - Helper utilities for jdk code. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.exception; 021 022import java.lang.reflect.Constructor; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029import net.sf.jkniv.asserts.Assertable; 030import net.sf.jkniv.asserts.AssertsFactory; 031 032/** 033 * 034 * @author Alisson Gomes 035 * @since 0.6.0 036 */ 037public class HandlerException implements HandleableException 038{ 039 private final static Logger LOG = LoggerFactory.getLogger(HandlerException.class); 040 private final static Assertable notNull = AssertsFactory.getNotNull(); 041 private Map<Class<?>, MapException> exceptions; 042 private Class<? extends RuntimeException> defaultException; 043 private String defaultMessage; 044 private boolean mute; 045 private boolean enableLogInfo; 046 047 public HandlerException() 048 { 049 this(RuntimeException.class, "%s", false); 050 } 051 052 public HandlerException(boolean mute) 053 { 054 this(RuntimeException.class, "%s", mute); 055 } 056 057 /** 058 * Build a new handler exception where default Runtime exception is {@code RuntimeException}. 059 * and your default message is {@code message} 060 * @param message default message 061 */ 062 public HandlerException(String message) 063 { 064 this(RuntimeException.class, message, false); 065 } 066 067 /** 068 * Build a new handler exception where default Runtime exception is {@code defaultException}. 069 * and your default message is {@code message} 070 * @param defaultException default runtime exception when exception is catch for {@code try / catch} block. 071 * @param message default message 072 */ 073 public HandlerException(Class<? extends RuntimeException> defaultException, String message) 074 { 075 this(defaultException, message, false); 076 } 077 078 public HandlerException(Class<? extends RuntimeException> defaultException, String message, boolean mute) 079 { 080 notNull.verify(defaultException, message); 081 this.exceptions = new HashMap<Class<?>, MapException>(); 082 this.defaultException = defaultException; 083 this.defaultMessage = message; 084 this.mute = mute; 085 this.enableLogInfo = false; 086 //config(defaultException, defaultException, message); 087 } 088 089 @Override 090 public HandleableException config(Class<? extends Exception> caught, Class<? extends RuntimeException> translateTo, 091 String message) 092 { 093 if (this.exceptions.containsKey(caught)) 094 throw new UnsupportedOperationException( 095 "Already exist an exception configured to exception [" + caught.getName() + "]"); 096 097 MapException map = new MapException(caught, translateTo, message, false); 098 this.exceptions.put(caught, map); 099 return this; 100 } 101 102 @Override 103 public HandleableException config(Class<? extends Exception> caught, String message) 104 { 105 if (this.exceptions.containsKey(caught)) 106 throw new UnsupportedOperationException( 107 "Already exist an exception configured to exception [" + caught.getName() + "]"); 108 109 MapException map = new MapException(caught, this.defaultException, message, false); 110 this.exceptions.put(caught, map); 111 return this; 112 } 113 114 @Override 115 public void handle(Exception caught) 116 { 117 if (this.defaultException.isAssignableFrom(caught.getClass())) 118 throw (RuntimeException) caught; 119 120 RuntimeException theException = prepareToThrowException(null, caught); 121 if (theException != null) 122 throw theException; 123 } 124 125 @Override 126 public void handle(Exception caught, String customMessage) 127 { 128 if (this.defaultException.isAssignableFrom(caught.getClass())) 129 throw (RuntimeException) caught; 130 131 RuntimeException theException = prepareToThrowException(customMessage, caught); 132 if (theException != null) 133 throw theException; 134 } 135 136 @Override 137 public void throwMessage(String message, Object... args) 138 { 139 String msg = String.format(message, args); 140 RuntimeException re = (RuntimeException) prepareToThrowException(msg, null); 141 throw re; 142 } 143 144 private RuntimeException prepareToThrowException(String customMessage, Exception caught) 145 { 146 RuntimeException theException = null; 147 if (!isMute() && !isMute(caught)) 148 { 149 MapException theMappedException = getMappedException(customMessage, caught); 150 /* 151 * TODO test me when caught instance of the mapped exception 152 */ 153 try 154 { 155 Constructor<? extends RuntimeException> constructor = theMappedException.getTranslate() 156 .getConstructor(String.class, Throwable.class); 157 theException = constructor 158 .newInstance(buildMessage(theMappedException.getMessage(), customMessage, caught), caught); 159 if (caught != null)// TODO alternative method when caught is null 160 theException.setStackTrace(caught.getStackTrace()); 161 } 162 catch (Exception e) 163 { 164 theException = new RuntimeException(customMessage, caught); 165 if (caught != null)// TODO alternative method when caught is null 166 theException.setStackTrace(caught.getStackTrace()); 167 } 168 } 169 else if (enableLogInfo){ 170 LOG.info("Be careful the Handler exception is mute for configured exceptions [{}], message: {}", 171 caught.getClass().getName(), caught.getMessage()); 172 } 173 return theException; 174 } 175 176 private MapException getMappedException(String message, Exception rootCause) 177 { 178 Class<? extends Exception> theException = null; 179 String theMessage = null; 180 MapException theMappedException = null; 181 if (message == null) 182 theMessage = this.defaultMessage; 183 else 184 theMessage = message; 185 186 if (rootCause == null) 187 theException = this.defaultException; 188 else 189 { 190 theException = rootCause.getClass(); 191 theMappedException = this.exceptions.get(rootCause.getClass()); 192 } 193 194 if (theMappedException == null) 195 { 196 theMappedException = new MapException(theException, this.defaultException, theMessage, false); 197 } 198 return theMappedException; 199 } 200 201 @Override 202 public Class<? extends RuntimeException> getDefaultException() 203 { 204 return defaultException; 205 } 206 207 @Override 208 public HandleableException mute() 209 { 210 this.mute = true; 211 return this; 212 } 213 214 @Override 215 public HandleableException mute(Class<? extends Exception> clazz) 216 { 217 // if (this.exceptions.containsKey(clazz)) 218 // throw new UnsupportedOperationException("Already exist an exception configured to exception [" 219 // + clazz.getName() + "] cannot change mute status from exception"); 220 221 MapException map = this.exceptions.get(clazz); 222 if (map == null) 223 map = new MapException(clazz, RuntimeException.class, "", true); 224 else 225 map.mute(); 226 this.exceptions.put(clazz, map); 227 228 return this; 229 } 230 231 @Override 232 public boolean isMute() 233 { 234 return this.mute; 235 } 236 237 private boolean isMute(Exception ex) 238 { 239 boolean answer = false; 240 if (ex == null) 241 return answer; 242 243 MapException mapException = this.exceptions.get(ex.getClass()); 244 if (mapException != null) 245 answer = mapException.isMute(); 246 return answer; 247 } 248 249 @Override 250 public boolean isMute(Class<? extends Exception> clazz) 251 { 252 boolean answer = false; 253 MapException mapException = this.exceptions.get(clazz); 254 if (mapException != null) 255 answer = mapException.isMute(); 256 return answer; 257 } 258 259 @Override 260 public HandleableException logInfoOn() 261 { 262 this.enableLogInfo = true; 263 return this; 264 } 265 266 @Override 267 public HandleableException logInfoOff() 268 { 269 this.enableLogInfo = false; 270 return this; 271 } 272 273 /** 274 * 275 * @param message 276 * @param customMessage 277 * @return 278 */ 279 private String buildMessage(String message, String customMessage, Exception caught) 280 { 281 String newMessage = ""; 282 if (hasParameterAtMessage(message)) 283 newMessage = String.format(message, 284 (customMessage != null ? customMessage + " " : "") + caught.getMessage()); //newMessage = String.format(message, customMessage); 285 else 286 newMessage = message; 287 return newMessage; 288 } 289 290 /** 291 * Verify if {@code message}has inline parameter 292 * @param message the {@code message} 293 * @return return <strong>true</strong> when {@code message} has parameter 294 */ 295 private boolean hasParameterAtMessage(String message) 296 { 297 return (message.indexOf("%s") >= 0); 298 } 299}