blob: 3d460c6947412cc7ca71b4bf12c476126e98e0e2 [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;
}
}