001package net.gdface.thrift.exception; 002 003import java.io.PrintStream; 004import java.io.PrintWriter; 005import java.io.StringWriter; 006 007import com.facebook.swift.codec.ThriftField; 008import com.facebook.swift.codec.ThriftStruct; 009import com.google.common.base.Preconditions; 010 011/** 012 * Runtime exception wrap class<br> 013 * all {@link RuntimeException} threw from service was wrapped to the object<br> 014 * @author guyadong 015 * 016 */ 017@ThriftStruct 018public final class ServiceRuntimeException extends RuntimeException{ 019 private static final long serialVersionUID = 1L; 020 private int type = 0; 021 private String message; 022 private String causeClass; 023 /** Lazy Initialization field */ 024 private volatile String serviceStackTraceMessage; 025 private String causeFields; 026 027 public ServiceRuntimeException(String message) { 028 this(message,null); 029 } 030 public ServiceRuntimeException() { 031 this(null,null); 032 } 033 public ServiceRuntimeException(Throwable cause) { 034 this(null,cause); 035 } 036 public ServiceRuntimeException(String message, Throwable cause) { 037 super(message,stripRuntimeShell(cause)); 038 if(null != message){ 039 this.message = message; 040 }else if(null != getCause()){ 041 this.message = getCause().getMessage(); 042 if(null == this.message){ 043 this.message = getCause().toString(); 044 } 045 } 046 this.causeClass = null == getCause()? null : getCause().getClass().getName(); 047 this.causeFields = null == getCause()? null : getCause().toString(); 048 if(getCause() instanceof ServiceRuntimeException){ 049 this.causeFields = ((ServiceRuntimeException)getCause()).jsonOfDeclaredFields(); 050 } 051 } 052 /** 053 * @param type exception type 054 * @param cause 055 */ 056 public ServiceRuntimeException(int type,Throwable cause) { 057 this(cause); 058 this.type = type; 059 } 060 /** return a JSON string of declared fields,subclass override it */ 061 protected String jsonOfDeclaredFields(){ 062 return ""; 063 } 064 /** 065 * return cause wrapped by {@link RuntimeException}<br> 066 * @param e 067 * @return 068 */ 069 private static final Throwable stripRuntimeShell(Throwable e){ 070 if(null != e && null !=e.getCause() && e.getClass() == RuntimeException.class){ 071 return stripRuntimeShell(e.getCause()); 072 } 073 return e; 074 } 075 /** 076 * save error message to {@link #serviceStackTraceMessage} by calling {@link #printStackTrace(PrintWriter)} 077 * @param cause 078 * @see #printStackTrace(PrintWriter) 079 */ 080 private void fillStackTraceMessage(Throwable cause) { 081 if (null != cause) { 082 StringWriter write = new StringWriter(256); 083 PrintWriter pw = new PrintWriter(write); 084 cause.printStackTrace(pw); 085 serviceStackTraceMessage = write.toString(); 086 } 087 } 088 /** 089 * print stack trace message from service to {@link System#err} 090 * @see #printStackTrace() 091 */ 092 public void printServiceStackTrace() { 093 printServiceStackTrace(System.err); 094 } 095 096 /** 097 * @param s 098 * @see #printServiceStackTrace() 099 * @see #printStackTrace(PrintStream) 100 * @throws NullPointerException s is {@code null} 101 */ 102 public void printServiceStackTrace(PrintStream s) { 103 synchronized (Preconditions.checkNotNull(s)) { 104 s.println(serviceStackTraceMessage); 105 } 106 } 107 108 /** 109 * @param s 110 * @see #printServiceStackTrace() 111 * @see #printStackTrace(PrintWriter) 112 * @throws NullPointerException s is {@code null} 113 */ 114 public void printServiceStackTrace(PrintWriter s) { 115 synchronized (Preconditions.checkNotNull(s)) { 116 s.println(serviceStackTraceMessage); 117 } 118 } 119 /** return error message from service */ 120 @Override 121 @ThriftField(1) 122 public String getMessage() { 123 return message; 124 } 125 @ThriftField 126 public void setMessage(String message) { 127 this.message = message; 128 } 129 /** return cause exception class name */ 130 @ThriftField(2) 131 public String getCauseClass() { 132 return causeClass; 133 } 134 @ThriftField 135 public void setCauseClass(String causeClass) { 136 this.causeClass = causeClass; 137 } 138 /** return stack trace message from service */ 139 @ThriftField(3) 140 public String getServiceStackTraceMessage() { 141 // Double-checked locking 142 if(null == serviceStackTraceMessage){ 143 synchronized(this){ 144 if(null == serviceStackTraceMessage){ 145 fillStackTraceMessage(null == getCause()? this : getCause()); 146 } 147 } 148 } 149 return serviceStackTraceMessage; 150 } 151 @ThriftField 152 public void setServiceStackTraceMessage(String serviceStackTraceMessage) { 153 this.serviceStackTraceMessage = serviceStackTraceMessage; 154 } 155 /** 156 * return JSON string of declared field values if cause is subclass of this class 157 * and override {@code jsonOfDeclaredFields} method, otherwise return empty string 158 * @see {@link #jsonOfDeclaredFields()} 159 */ 160 @ThriftField(4) 161 public String getCauseFields() { 162 return causeFields; 163 } 164 @ThriftField 165 public void setCauseFields(String causeFields) { 166 this.causeFields = causeFields; 167 } 168 /** return exception type */ 169 @ThriftField(5) 170 public int getType() { 171 return type; 172 } 173 @ThriftField 174 public void setType(int type) { 175 this.type = type; 176 } 177}