001package net.gdface.thrift.exception; 002 003import java.io.PrintWriter; 004import java.io.StringWriter; 005 006import com.facebook.swift.codec.ThriftField; 007import com.facebook.swift.codec.ThriftStruct; 008 009/** 010 * service exception abstract class<br> 011 * @author guyadong 012 * 013 */ 014@ThriftStruct 015public abstract class BaseServiceException extends Exception{ 016 private static final long serialVersionUID = 1L; 017 private String message; 018 private String causeClass; 019 /** Lazy Initialization field */ 020 private volatile String serviceStackTraceMessage; 021 private String causeFields; 022 023 public BaseServiceException() { 024 this(null,null); 025 } 026 public BaseServiceException(String message) { 027 this(message,null); 028 } 029 public BaseServiceException(Throwable cause) { 030 this(null,cause); 031 } 032 public BaseServiceException(String message, Throwable cause) { 033 super(message,stripRuntimeShell(cause)); 034 if(null != message){ 035 this.message = message; 036 }else if(null != getCause()){ 037 this.message = getCause().getMessage(); 038 if(null == this.message){ 039 this.message = getCause().toString(); 040 } 041 } 042 this.causeClass = null == getCause()? null : getCause().getClass().getName(); 043 this.causeFields = null == getCause()? null : getCause().toString(); 044 if(getCause() instanceof BaseServiceException){ 045 this.causeFields = ((BaseServiceException)getCause()).jsonOfDeclaredFields(); 046 } 047 } 048 /** return a JSON string of declared fields,subclass override it */ 049 protected String jsonOfDeclaredFields(){ 050 return ""; 051 } 052 /** 053 * return cause wrapped by {@link RuntimeException}<br> 054 * @param e 055 * @return 056 */ 057 private static final Throwable stripRuntimeShell(Throwable e){ 058 if(null != e && null !=e.getCause() && e.getClass() == RuntimeException.class){ 059 return stripRuntimeShell(e.getCause()); 060 } 061 return e; 062 } 063 /** 064 * save error message to {@link #serviceStackTraceMessage} by calling {@link #printStackTrace(PrintWriter)} 065 * @param cause 066 * @see #printStackTrace(PrintWriter) 067 */ 068 private void fillStackTraceMessage(Throwable cause) { 069 if (null != cause) { 070 StringWriter write = new StringWriter(256); 071 PrintWriter pw = new PrintWriter(write); 072 cause.printStackTrace(pw); 073 serviceStackTraceMessage = write.toString(); 074 } 075 } 076 077 /** return error message from service */ 078 @Override 079 @ThriftField(1) 080 public String getMessage() { 081 return message; 082 } 083 @ThriftField 084 public void setMessage(String message) { 085 this.message = message; 086 } 087 /** return cause exception class name */ 088 @ThriftField(2) 089 public String getCauseClass() { 090 return causeClass; 091 } 092 @ThriftField 093 public void setCauseClass(String causeClass) { 094 this.causeClass = causeClass; 095 } 096 /** return stack trace message from service */ 097 @ThriftField(3) 098 public String getServiceStackTraceMessage() { 099 // Double-checked locking 100 if(null == serviceStackTraceMessage){ 101 synchronized(this){ 102 if(null == serviceStackTraceMessage){ 103 fillStackTraceMessage(null == getCause()? this : getCause()); 104 } 105 } 106 } 107 return serviceStackTraceMessage; 108 } 109 @ThriftField 110 public void setServiceStackTraceMessage(String serviceStackTraceMessage) { 111 this.serviceStackTraceMessage = serviceStackTraceMessage; 112 } 113 /** 114 * return JSON string of declared field values if cause is subclass of this class 115 * and override {@code jsonOfDeclaredFields} method, otherwise return empty string 116 * @see {@link #jsonOfDeclaredFields()} 117 */ 118 @ThriftField(4) 119 public String getCauseFields() { 120 return causeFields; 121 } 122 @ThriftField 123 public void setCauseFields(String causeFields) { 124 this.causeFields = causeFields; 125 } 126}