| /* |
| * 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 "Server.Processing". 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; |
| } |
| } |