Coverage Report - org.apache.myfaces.shared.context.AjaxExceptionHandlerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
AjaxExceptionHandlerImpl
0%
0/86
0%
0/58
3.833
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.shared.context;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Collections;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Queue;
 27  
 import java.util.logging.Level;
 28  
 import java.util.logging.Logger;
 29  
 
 30  
 import javax.el.ELException;
 31  
 import javax.faces.FacesException;
 32  
 import javax.faces.context.ExceptionHandler;
 33  
 import javax.faces.context.ExternalContext;
 34  
 import javax.faces.context.FacesContext;
 35  
 import javax.faces.context.PartialResponseWriter;
 36  
 import javax.faces.context.PartialViewContext;
 37  
 import javax.faces.event.AbortProcessingException;
 38  
 import javax.faces.event.ExceptionQueuedEvent;
 39  
 import javax.faces.event.ExceptionQueuedEventContext;
 40  
 import javax.faces.event.SystemEvent;
 41  
 
 42  
 /**
 43  
  * 
 44  
  * Specialized Ajax Handler, according to JSF 2.0 rev A section 13.3.7.
 45  
  */
 46  
 public class AjaxExceptionHandlerImpl extends ExceptionHandler
 47  
 {
 48  
     //The spec says this related to exception handling in ajax requests:
 49  
     //
 50  
     //"... JavaServer Faces Ajax frameworks must ensure exception information 
 51  
     // is written to the response in the format: 
 52  
     //
 53  
     //<partial-response>
 54  
     //  <error>
 55  
     //    <error-name>...</error-name>
 56  
     //    <error-message>...</error-message>
 57  
     //  </error>
 58  
     //</partial-response>
 59  
     //
 60  
     //Implementations must ensure that an ExceptionHandler suitable for writing 
 61  
     //exceptions to the partial response is installed if the current request 
 62  
     //required an Ajax response (PartialViewContext.isAjaxRequest() returns true)
 63  
     //
 64  
     //Implementations may choose to include a specialized ExceptionHandler for 
 65  
     //Ajax that extends from javax.faces.context.ExceptionHandlerWrapper, and 
 66  
     //have the javax.faces.context.ExceptionHandlerFactory implementation 
 67  
     //install it if the environment requires it. ..."
 68  
     
 69  0
     private static final Logger log = Logger.getLogger(AjaxExceptionHandlerImpl.class.getName());
 70  
     
 71  
     private Queue<ExceptionQueuedEvent> handled;
 72  
     private Queue<ExceptionQueuedEvent> unhandled;
 73  
     private ExceptionQueuedEvent handledAndThrown;
 74  
     
 75  
     public AjaxExceptionHandlerImpl()
 76  0
     {
 77  0
     }
 78  
     
 79  
     /**
 80  
      * {@inheritDoc}
 81  
      */
 82  
     @Override
 83  
     public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
 84  
     {
 85  0
         return handledAndThrown;
 86  
     }
 87  
 
 88  
     /**
 89  
      * {@inheritDoc}
 90  
      */
 91  
     @Override
 92  
     public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
 93  
     {
 94  0
         return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
 95  
     }
 96  
 
 97  
     /**
 98  
      * {@inheritDoc}
 99  
      */
 100  
     @Override
 101  
     public Throwable getRootCause(Throwable t)
 102  
     {
 103  0
         if (t == null)
 104  
         {
 105  0
             throw new NullPointerException("t");
 106  
         }
 107  
         
 108  0
         while (t != null)
 109  
         {
 110  0
             Class<?> clazz = t.getClass();
 111  0
             if (!clazz.equals(FacesException.class) && !clazz.equals(ELException.class))
 112  
             {
 113  0
                 return t;
 114  
             }
 115  
             
 116  0
             t = t.getCause();
 117  0
         }
 118  
         
 119  0
         return null;
 120  
     }
 121  
 
 122  
     /**
 123  
      * {@inheritDoc}
 124  
      */
 125  
     @Override
 126  
     public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
 127  
     {
 128  0
         return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled;
 129  
     }
 130  
 
 131  
     /**
 132  
      * {@inheritDoc}
 133  
      */
 134  
     @Override
 135  
     public void handle() throws FacesException
 136  
     {
 137  0
         if (unhandled != null && !unhandled.isEmpty())
 138  
         {
 139  0
             if (handled == null)
 140  
             {
 141  0
                 handled = new LinkedList<ExceptionQueuedEvent>();
 142  
             }
 143  
             
 144  0
             List<Throwable> throwableList = new ArrayList<Throwable>();
 145  0
             FacesContext facesContext = null;
 146  
             
 147  
             do
 148  
             {
 149  
                 // For each ExceptionEvent in the list
 150  
                 
 151  
                 // get the event to handle
 152  0
                 ExceptionQueuedEvent event = unhandled.peek();
 153  
                 try
 154  
                 {
 155  
                     // call its getContext() method
 156  0
                     ExceptionQueuedEventContext context = event.getContext();
 157  
 
 158  0
                     if (facesContext == null)
 159  
                     {
 160  0
                         facesContext = event.getContext().getContext();
 161  
                     }
 162  
                     
 163  
                     // and call getException() on the returned result
 164  0
                     Throwable exception = context.getException();
 165  
                     
 166  
                     // Upon encountering the first such Exception that is not an instance of
 167  
                     // javax.faces.event.AbortProcessingException
 168  0
                     if (!shouldSkip(exception))
 169  
                     {
 170  
                         // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
 171  0
                         handledAndThrown = event;
 172  
                         
 173  0
                         Throwable rootCause = getRootCause(exception);
 174  
                         
 175  0
                         throwableList.add(rootCause == null ? exception : rootCause);
 176  
                         
 177  
                         //break;
 178  0
                     }
 179  
                     else
 180  
                     {
 181  
                         // Testing mojarra it logs a message and the exception
 182  
                         // however, this behaviour is not mentioned in the spec
 183  0
                         log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
 184  
                                 (context.inBeforePhase() ? "beforePhase() of " : 
 185  
                                         (context.inAfterPhase() ? "afterPhase() of " : "")) + 
 186  
                                 "phase " + context.getPhaseId() + ": " +
 187  
                                 "UIComponent-ClientId=" + 
 188  
                                 (context.getComponent() != null ? 
 189  
                                         context.getComponent().getClientId(context.getContext()) : "") + ", " +
 190  
                                 "Message=" + exception.getMessage());
 191  
                         
 192  0
                         log.log(Level.SEVERE, exception.getMessage(), exception);
 193  
                     }
 194  
                 }
 195  
                 finally
 196  
                 {
 197  
                     // if we will throw the Exception or if we just logged it,
 198  
                     // we handled it in either way --> add to handled
 199  0
                     handled.add(event);
 200  0
                     unhandled.remove(event);
 201  0
                 }
 202  0
             } while (!unhandled.isEmpty());
 203  
 
 204  0
             if (!throwableList.isEmpty())
 205  
             {
 206  0
                 PartialResponseWriter partialWriter = null;
 207  0
                 boolean responseComplete = false;
 208  
 
 209  
                 //Retrieve the partial writer used to render the exception
 210  0
                 if (facesContext == null)
 211  
                 {
 212  0
                     facesContext = FacesContext.getCurrentInstance();
 213  
                 }
 214  
                 
 215  0
                 facesContext = (facesContext == null) ? FacesContext.getCurrentInstance() : facesContext;
 216  0
                 ExternalContext externalContext = facesContext.getExternalContext();
 217  
                 
 218  
                 //Clean up the response if by some reason something has been written before.
 219  0
                 if (!facesContext.getExternalContext().isResponseCommitted())
 220  
                 {
 221  0
                     facesContext.getExternalContext().responseReset();
 222  
                 }
 223  
                 
 224  0
                 PartialViewContext partialViewContext = facesContext.getPartialViewContext();
 225  0
                 partialWriter = partialViewContext.getPartialResponseWriter();
 226  
 
 227  
                 // ajax request --> xml error page 
 228  0
                 externalContext.setResponseContentType("text/xml");
 229  0
                 externalContext.setResponseCharacterEncoding("UTF-8");
 230  0
                 externalContext.addResponseHeader("Cache-control", "no-cache");
 231  
                 
 232  
                 try
 233  
                 {
 234  0
                     partialWriter.startDocument();
 235  
     
 236  0
                     for (Throwable t : throwableList)
 237  
                     {
 238  0
                         renderAjaxError(partialWriter, t);
 239  0
                     }
 240  
                     
 241  0
                     responseComplete = true;
 242  
                 }
 243  0
                 catch (IOException e)
 244  
                 {
 245  0
                     if (log.isLoggable(Level.SEVERE))
 246  
                     {
 247  0
                         log.log(Level.SEVERE, "Cannot render exception on ajax request", e);
 248  
                     }
 249  
                 }
 250  
                 finally
 251  
                 {
 252  0
                     if (responseComplete)
 253  
                     {
 254  
                         try
 255  
                         {
 256  0
                             partialWriter.endDocument();
 257  0
                             facesContext.responseComplete();
 258  
                         }
 259  0
                         catch (IOException e1)
 260  
                         {
 261  0
                             if (log.isLoggable(Level.SEVERE))
 262  
                             {
 263  0
                                 log.log(Level.SEVERE, "Cannot render exception on ajax request", e1);
 264  
                             }
 265  0
                         }
 266  
                     }
 267  
                 }
 268  
             }
 269  
         }
 270  0
     }
 271  
     
 272  
     private void renderAjaxError(PartialResponseWriter partialWriter, Throwable ex) throws IOException
 273  
     {
 274  0
         partialWriter.startError(ex.getClass().getName());
 275  0
         if (ex.getCause() != null)
 276  
         {
 277  0
             partialWriter.write(ex.getCause().toString());
 278  
         }
 279  0
         else if (ex.getMessage() != null)
 280  
         {
 281  0
             partialWriter.write(ex.getMessage());
 282  
         }
 283  0
         partialWriter.endError();
 284  0
     }
 285  
 
 286  
     /**
 287  
      * {@inheritDoc}
 288  
      */
 289  
     @Override
 290  
     public boolean isListenerForSource(Object source)
 291  
     {
 292  0
         return source instanceof ExceptionQueuedEventContext;
 293  
     }
 294  
 
 295  
     /**
 296  
      * {@inheritDoc}
 297  
      */
 298  
     @Override
 299  
     public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
 300  
     {
 301  0
         if (unhandled == null)
 302  
         {
 303  0
             unhandled = new LinkedList<ExceptionQueuedEvent>();
 304  
         }
 305  
         
 306  0
         unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
 307  0
     }
 308  
     
 309  
     protected Throwable getRethrownException(Throwable exception)
 310  
     {
 311  
         // Let toRethrow be either the result of calling getRootCause() on the Exception, 
 312  
         // or the Exception itself, whichever is non-null
 313  0
         Throwable toRethrow = getRootCause(exception);
 314  0
         if (toRethrow == null)
 315  
         {
 316  0
             toRethrow = exception;
 317  
         }
 318  
         
 319  0
         return toRethrow;
 320  
     }
 321  
     
 322  
     protected FacesException wrap(Throwable exception)
 323  
     {
 324  0
         if (exception instanceof FacesException)
 325  
         {
 326  0
             return (FacesException) exception;
 327  
         }
 328  0
         return new FacesException(exception);
 329  
     }
 330  
     
 331  
     protected boolean shouldSkip(Throwable exception)
 332  
     {
 333  0
         return exception instanceof AbortProcessingException;
 334  
     }
 335  
 
 336  
 }