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.application;
20
21 import javax.el.ELException;
22 import javax.el.MethodExpression;
23 import javax.faces.FacesException;
24 import javax.faces.application.Application;
25 import javax.faces.application.NavigationHandler;
26 import javax.faces.component.ActionSource;
27 import javax.faces.component.ActionSource2;
28 import javax.faces.component.UIComponent;
29 import javax.faces.context.FacesContext;
30 import javax.faces.el.EvaluationException;
31 import javax.faces.el.MethodBinding;
32 import javax.faces.event.AbortProcessingException;
33 import javax.faces.event.ActionEvent;
34 import javax.faces.event.ActionListener;
35
36
37 /**
38 * @author Manfred Geiler (latest modification by $Author: bommel $)
39 * @author Anton Koinov
40 * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
41 */
42 public class ActionListenerImpl implements ActionListener
43 {
44 public void processAction(ActionEvent actionEvent) throws AbortProcessingException
45 {
46 FacesContext facesContext = FacesContext.getCurrentInstance();
47 Application application = facesContext.getApplication();
48 UIComponent component = actionEvent.getComponent();
49
50 MethodExpression methodExpression = null;
51 MethodBinding methodBinding = null;
52
53 String fromAction = null;
54 String outcome = null;
55
56 if (component instanceof ActionSource2)
57 {
58 // Must be an instance of ActionSource2, so don't look on action if the actionExpression is set
59 methodExpression = ((ActionSource2) component).getActionExpression();
60 }
61 if (methodExpression == null && component instanceof ActionSource)
62 {
63 // Backwards compatibility for pre-1.2.
64 methodBinding = ((ActionSource) component).getAction();
65 }
66
67 if (methodExpression != null)
68 {
69 fromAction = methodExpression.getExpressionString();
70 try
71 {
72 Object objOutcome = methodExpression.invoke(facesContext.getELContext(), null);
73 if (objOutcome != null)
74 {
75 outcome = objOutcome.toString();
76 }
77 }
78 catch (ELException e)
79 {
80 // "... If that fails for any reason, throw an AbortProcessingException, including the cause of the failure ..."
81 // -= Leonardo Uribe =- after discussing this topic on MYFACES-3199, the conclusion is the part is an advice
82 // for the developer implementing a listener in a method expressions that could be wrapped by this class.
83 // The spec wording is poor but, to keep this coherently with ExceptionHandler API, the spec and code on UIViewRoot we need:
84 // 2a) "exception is instance of APE or any of the causes of the exception are an APE,
85 // DON'T publish ExceptionQueuedEvent and terminate processing for current event".
86 // 2b) for any other exception publish ExceptionQueuedEvent and continue broadcast processing.
87 Throwable cause = e.getCause();
88 AbortProcessingException ape = null;
89 if (cause != null)
90 {
91 do
92 {
93 if (cause != null && cause instanceof AbortProcessingException)
94 {
95 ape = (AbortProcessingException) cause;
96 break;
97 }
98 cause = cause.getCause();
99 }
100 while (cause != null);
101 }
102
103 if (ape != null)
104 {
105 // 2a) "exception is instance of APE or any of the causes of the exception are an APE,
106 // DON'T publish ExceptionQueuedEvent and terminate processing for current event".
107 // To do this throw an AbortProcessingException here, later on UIViewRoot.broadcastAll,
108 // this exception will be received and stored to handle later.
109 throw ape;
110 }
111
112 // Since this ActionListener is the one who handles navigation, if we have another different exception
113 // here we can't queue it on ExceptionHandler stack, instead throw it as a FacesException.
114 throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e);
115 }
116 catch (RuntimeException e)
117 {
118 throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e);
119 }
120 }
121
122 else if (methodBinding != null) {
123 fromAction = methodBinding.getExpressionString();
124 try
125 {
126 Object objOutcome = methodBinding.invoke(facesContext, null);
127
128 if (objOutcome != null)
129 {
130 outcome = objOutcome.toString();
131 }
132 }
133 catch (EvaluationException e)
134 {
135 Throwable cause = e.getCause();
136 if (cause != null && cause instanceof AbortProcessingException)
137 {
138 throw (AbortProcessingException)cause;
139 }
140
141 throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e);
142
143 }
144 catch (RuntimeException e)
145 {
146 throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e);
147 }
148 }
149
150 NavigationHandler navigationHandler = application.getNavigationHandler();
151 navigationHandler.handleNavigation(facesContext, fromAction, outcome);
152 //Render Response if needed
153 facesContext.renderResponse();
154
155 }
156 }