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 javax.faces.event;
20
21 import javax.el.ELContext;
22 import javax.el.ELException;
23 import javax.el.ExpressionFactory;
24 import javax.el.MethodExpression;
25 import javax.el.MethodNotFoundException;
26 import javax.faces.component.StateHolder;
27 import javax.faces.context.FacesContext;
28
29 /**
30 * See Javadoc of <a href="https://javaserverfaces.dev.java.net/nonav/docs/2.0/javadocs/javax/faces/event/MethodExpressionValueChangeListener.html">JSF Specification</a>
31 *
32 * @author Stan Silvert
33 * @author Jakob Korherr
34 */
35 public class MethodExpressionValueChangeListener implements ValueChangeListener, StateHolder
36 {
37
38 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
39 private static final Object[] EMPTY_PARAMS = new Object[0];
40
41 private MethodExpression methodExpressionOneArg;
42 private MethodExpression methodExpressionZeroArg;
43 private boolean isTransient = false;
44
45 /** Creates a new instance of MethodExpressionValueChangeListener */
46 public MethodExpressionValueChangeListener()
47 {
48 // constructor for state-saving
49 }
50
51 public MethodExpressionValueChangeListener(MethodExpression methodExpressionOneArg)
52 {
53 this.methodExpressionOneArg = methodExpressionOneArg;
54
55 _createZeroArgsMethodExpression(methodExpressionOneArg);
56 }
57
58 public MethodExpressionValueChangeListener(MethodExpression methodExpressionOneArg, MethodExpression methodExpressionZeroArg)
59 {
60 this.methodExpressionOneArg = methodExpressionOneArg;
61 if (methodExpressionZeroArg != null)
62 {
63 this.methodExpressionZeroArg = methodExpressionZeroArg;
64 }
65 else
66 {
67 _createZeroArgsMethodExpression(methodExpressionOneArg);
68 }
69 }
70
71 public void processValueChange(ValueChangeEvent event) throws AbortProcessingException
72 {
73 try
74 {
75 try
76 {
77 // call to the one argument MethodExpression
78 Object[] params = new Object[] { event };
79 methodExpressionOneArg.invoke(getElContext(), params);
80 }
81 catch (MethodNotFoundException mnfe)
82 {
83 // call to the zero argument MethodExpression
84 methodExpressionZeroArg.invoke(getElContext(), EMPTY_PARAMS);
85 }
86 }
87 catch (ELException e)
88 {
89 // "... If that fails for any reason, throw an AbortProcessingException, including the cause of the failure ..."
90 // -= Leonardo Uribe =- after discussing this topic on MYFACES-3199, the conclusion is the part is an advice
91 // for the developer implementing a listener in a method expressions that could be wrapped by this class.
92 // The spec wording is poor but, to keep this coherently with ExceptionHandler API, the spec and code on UIViewRoot we need:
93 // 2a) "exception is instance of APE or any of the causes of the exception are an APE,
94 // DON'T publish ExceptionQueuedEvent and terminate processing for current event".
95 // 2b) for any other exception publish ExceptionQueuedEvent and continue broadcast processing.
96 Throwable cause = e.getCause();
97 AbortProcessingException ape = null;
98 if (cause != null)
99 {
100 do
101 {
102 if (cause != null && cause instanceof AbortProcessingException)
103 {
104 ape = (AbortProcessingException) cause;
105 break;
106 }
107 cause = cause.getCause();
108 }
109 while (cause != null);
110 }
111
112 if (ape != null)
113 {
114 // 2a) "exception is instance of APE or any of the causes of the exception are an APE,
115 // DON'T publish ExceptionQueuedEvent and terminate processing for current event".
116 // To do this throw an AbortProcessingException here, later on UIViewRoot.broadcastAll,
117 // this exception will be received and stored to handle later.
118 throw ape;
119 }
120 //for any other exception publish ExceptionQueuedEvent and continue broadcast processing.
121 throw e;
122 //Throwable cause = e.getCause();
123 //if (cause == null)
124 //{
125 // cause = e;
126 //}
127 //if (cause instanceof AbortProcessingException)
128 //{
129 // throw (AbortProcessingException) cause;
130 //}
131 //else
132 //{
133 // throw new AbortProcessingException(cause);
134 //}
135 }
136 }
137
138 public void restoreState(FacesContext context, Object state)
139 {
140 methodExpressionOneArg = (MethodExpression) ((Object[]) state)[0];
141 methodExpressionZeroArg = (MethodExpression) ((Object[]) state)[1];
142 }
143
144 public Object saveState(FacesContext context)
145 {
146 return new Object[] {methodExpressionOneArg, methodExpressionZeroArg};
147 }
148
149 public void setTransient(boolean newTransientValue)
150 {
151 isTransient = newTransientValue;
152 }
153
154 public boolean isTransient()
155 {
156 return isTransient;
157 }
158
159 private ELContext getElContext()
160 {
161 return getFacesContext().getELContext();
162 }
163
164 private FacesContext getFacesContext()
165 {
166 return FacesContext.getCurrentInstance();
167 }
168
169 /**
170 * Creates a {@link MethodExpression} with no params and with the same Expression as
171 * param <code>methodExpression</code>
172 * <b>WARNING!</b> This method creates new {@link MethodExpression} with expressionFactory.createMethodExpression.
173 * That means is not decorating MethodExpression passed as parameter - support for EL VariableMapper will not be available!
174 * This is a problem when using facelets and <ui:decorate/> with EL params (see MYFACES-2541 for details).
175 */
176 private void _createZeroArgsMethodExpression(MethodExpression methodExpression)
177 {
178 ExpressionFactory expressionFactory = getFacesContext().getApplication().getExpressionFactory();
179
180 this.methodExpressionZeroArg = expressionFactory.createMethodExpression(getElContext(),
181 methodExpression.getExpressionString(), Void.class, EMPTY_CLASS_ARRAY);
182 }
183
184 }