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    
021    package org.granite.messaging.service;
022    
023    import java.lang.reflect.Method;
024    import java.util.ArrayList;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Map;
028    
029    import javax.servlet.ServletException;
030    
031    import org.granite.config.flex.Destination;
032    import org.granite.context.GraniteContext;
033    import org.granite.logging.Logger;
034    
035    import 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     */
049    public 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    }