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