001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.messaging.service;
022
023import java.lang.reflect.Method;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import javax.servlet.ServletException;
030
031import org.granite.config.flex.Destination;
032import org.granite.context.GraniteContext;
033import org.granite.logging.Logger;
034
035import flex.messaging.messages.Message;
036
037
038/**
039 * @author Venkat DANDA
040 * @author Cameron INGRAM
041 *
042 * Update services-config.xml to use the seam service exception handler
043 * <factory id="tideSeamFactory" class="org.granite.tide.seam.SeamServiceFactory" >
044 *      <properties>
045 *              <service-exception-handler>org.granite.tide.seam.SeamServiceExceptionHandler</service-exception-handler>
046 *      </properties>
047 * </factory>
048 */
049public class ExtendedServiceExceptionHandler extends DefaultServiceExceptionHandler {
050
051    private static final long serialVersionUID = -1L;
052    private static final Logger log = Logger.getLogger(ExtendedServiceExceptionHandler.class);
053
054    public static final Class<?> JAVAX_EJB_EXCEPTION;
055    static {
056        Class<?> exception = null;
057        try {
058            exception = Thread.currentThread().getContextClassLoader().loadClass("javax.ejb.EJBException");
059        }
060        catch (Exception e) {
061        }
062        JAVAX_EJB_EXCEPTION = exception;
063    }
064
065    public ExtendedServiceExceptionHandler() {
066        this(true);
067    }
068
069    public ExtendedServiceExceptionHandler(boolean logException) {
070        super(logException);
071    }
072
073    @Override
074    protected ServiceException getServiceException(Message request, Destination destination, String method, Throwable t) {
075        if (t == null)
076            throw new NullPointerException("Parameter t cannot be null");
077
078        Map<String, Object> extendedData = new HashMap<String, Object>();
079
080        if (t instanceof ServiceException) {
081            ((ServiceException)t).getExtendedData().putAll(extendedData);
082            return (ServiceException)t;
083        }
084
085        List<Throwable> causes = new ArrayList<Throwable>();
086        for (Throwable cause = t; cause != null; cause = getCause(cause))
087            causes.add(cause);
088
089        String detail = "\n" +
090            "- destination: " + (destination != null ? destination.getId() : "") + "\n" +
091            "- method: " + method + "\n" +
092            "- exception: " + t.toString() + "\n";
093
094        for (int i = causes.size()-1; i >= 0; i--) {
095            Throwable cause = causes.get(i);
096                for (ExceptionConverter ec : GraniteContext.getCurrentInstance().getGraniteConfig().getExceptionConverters()) {
097                if (ec.accepts(cause, t))
098                    return ec.convert(cause, detail, extendedData);
099            }
100        }
101        
102        if (getLogException())
103                log.error(t, "Could not process remoting message: %s", request);
104
105        // Default exception handler
106        ServiceException se = new ServiceException(t.getClass().getSimpleName() + ".Call.Failed", t.getMessage(), detail, t);
107        se.getExtendedData().putAll(extendedData);
108        return se;
109    }
110    
111    
112    public static Throwable getCause(Throwable t) {
113        Throwable cause = null;
114        try {
115            if (JAVAX_EJB_EXCEPTION != null && JAVAX_EJB_EXCEPTION.isInstance(t)) {
116                Method m = JAVAX_EJB_EXCEPTION.getMethod("getCausedByException");
117                cause = (Throwable)m.invoke(t);
118            }
119            else if (t instanceof ServletException)
120                cause = ((ServletException)t).getRootCause();
121            else
122                cause = t.getCause();
123        }
124        catch (Exception x) {
125            return null;
126        }
127        return cause == t ? null : (Throwable)cause;
128    }
129}