blob: 7e5ecbf61a6935c50582eee7f31eecf5b3a3144f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flex.messaging.io;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import flex.messaging.MessageException;
/**
* Serializes a Throwable as a Status Info object. The object is sent
* back to the client as an ActionScript object with the following
* keys:
* <ul>
* <li>code: In 1.0, we always report this as &quot;Server.Processing&quot;. To be expanded in a future version.</li>
* <li>description: The error message.</li>
* <li>details: The error stack trace.</li>
* <li>type: The error class name.</li>
* <li>rootcause: If the exception was a ServletException, the rootcause will be included as a nested Status Info object.</li>
* </ul>
*/
public class StatusInfoProxy extends AbstractProxy {
static final long serialVersionUID = 8860353096401173320L;
/**
* Brief description of the error. This is intended to describe the
* problem in a short manner that may be displayed to a human during
* run-time of the client's application.
*/
public static final String DESCRIPTION = "description";
/**
* Long description of the error. This may contain a verbose description
* that may be provided to a more intelligent human for diagnosis.
*/
public static final String DETAILS = "details";
/**
* The class name of the Throwable instance, if allowed to be
* revealed to the client...
*/
public static final String CLASS = "type";
/**
* Hierarchical dot "." string description of the problem.
* <p>
* The top error string shall be on of the following....
* </p>
* Client - Indicates the error was caused by something the client
* did. Possibly the message was not well formed and/or did not
* contain the proper information.
* <p>
* Server - Indicates the error was caused by something not directly
* related to the contents of the message. Possibly the server could
* not gain a needed resource or communicate with the desire WebService.
* </p>
* <p>VersionMismatch - The received AMF packet was of a version that
* the Server does not recognize.
* </p>
* <p>The server can generate any number of sub errors to be attached to one
* of the 3 above roots. The idea is that these strings provide an
* intuitive description of where the problem happened and what it was.
* Ultimately, finding and fixing the errors fast is the goal.
* </p>
*/
public static final String CODE = "code";
public static final String ROOTCAUSE = "rootcause";
public static final List propertyNameCache = new ArrayList();
static {
propertyNameCache.add(CODE);
propertyNameCache.add(CLASS);
propertyNameCache.add(DESCRIPTION);
propertyNameCache.add(DETAILS);
propertyNameCache.add(ROOTCAUSE);
}
protected boolean showStacktraces;
public StatusInfoProxy() {
super(null);
}
public StatusInfoProxy(Throwable defaultInstance) {
super(defaultInstance);
}
public void setShowStacktraces(boolean value) {
showStacktraces = value;
}
public String getAlias(Object instance) {
// Status Info objects are anonymous
return null;
}
public List getPropertyNames(Object instance) {
return propertyNameCache;
}
public Class getType(Object instance, String propertyName) {
Class type = null;
if (CODE.equals(propertyName)) {
type = String.class;
} else if (CLASS.equals(propertyName)) {
type = String.class;
} else if (DESCRIPTION.equals(propertyName)) {
type = String.class;
} else if (DETAILS.equals(propertyName)) {
type = String.class;
} else if (ROOTCAUSE.equals(propertyName)) {
type = Map.class;
}
return type;
}
public Object getValue(Object instance, String propertyName) {
Object value = null;
if (CODE.equals(propertyName)) {
value = getCode(instance);
} else if (CLASS.equals(propertyName)) {
value = getType(instance);
} else if (DESCRIPTION.equals(propertyName)) {
value = getDescription(instance);
} else if (DETAILS.equals(propertyName)) {
value = getDetails(instance);
} else if (ROOTCAUSE.equals(propertyName)) {
value = getRootCause(instance);
}
return value;
}
public void setValue(Object instance, String propertyName, Object value) {
return; // Throwable is essentially read-only
}
private String getCode(Object ex) {
String code = null;
if (ex instanceof MessageException) {
code = ((MessageException) ex).getCode();
}
if (code == null) {
code = "Server.Processing";
}
return code;
}
private String getType(Object ex) {
String type = "";
if (ex != null && showStacktraces) {
type = ex.getClass().getName();
}
return type;
}
private String getDescription(Object ex) {
String desc = null;
if (ex instanceof Throwable) {
desc = ((Throwable) ex).getMessage();
}
return desc;
}
private String getDetails(Object ex) {
StringBuffer details = new StringBuffer();
if (ex instanceof MessageException) {
MessageException e = (MessageException) ex;
if (e.getDetails() != null)
details.append(e.getDetails());
}
if (showStacktraces && ex instanceof Throwable)
details.append(getTraceback((Throwable) ex));
return details.toString();
}
private Throwable getRootCauseException(Object ex) {
if (ex == null)
return null;
if (ex instanceof ServletException) {
ex = ((ServletException) ex).getRootCause();
}
if (ex instanceof Throwable)
return ((Throwable) ex).getCause();
else
return null;
}
private Map getRootCause(Object ex) {
Throwable t = getRootCauseException(ex);
if (t != null)
return getExceptionInfo(t);
else
return null;
}
private Map getExceptionInfo(Throwable t) {
Map info = new HashMap();
info.put(CODE, getCode(t));
info.put(CLASS, getType(t));
info.put(DESCRIPTION, getDescription(t));
info.put(DETAILS, getDetails(t));
info.put(ROOTCAUSE, t);
return info;
}
private static String getTraceback(Throwable e) {
String trace = "";
if (e != null) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintWriter pr = new PrintWriter(outputStream);
pr.println();
e.printStackTrace(pr);
pr.flush();
trace = outputStream.toString();
}
return trace;
}
}