1+ /*
2+ * Copyright 2000-2025 Vaadin Ltd.
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+ * use this file except in compliance with the License. You may obtain a copy of
6+ * the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+ * License for the specific language governing permissions and limitations under
14+ * the License.
15+ */
16+ package com .vaadin .flow .server .communication .rpc ;
17+
18+ import java .util .Arrays ;
19+ import java .util .List ;
20+
21+ import tools .jackson .databind .JsonNode ;
22+ import tools .jackson .databind .node .ArrayNode ;
23+ import tools .jackson .databind .node .ObjectNode ;
24+ import org .junit .After ;
25+ import org .junit .Assert ;
26+ import org .junit .Before ;
27+ import org .junit .Test ;
28+
29+ import com .vaadin .flow .component .ClientCallable ;
30+ import com .vaadin .flow .component .Component ;
31+ import com .vaadin .flow .component .EventData ;
32+ import com .vaadin .flow .component .Tag ;
33+ import com .vaadin .flow .component .UI ;
34+ import com .vaadin .flow .internal .JacksonUtils ;
35+ import com .vaadin .flow .server .MockServletServiceSessionSetup ;
36+ import com .vaadin .flow .server .VaadinService ;
37+ import com .vaadin .flow .server .VaadinSession ;
38+ import com .vaadin .flow .shared .JsonConstants ;
39+ import com .vaadin .tests .util .MockDeploymentConfiguration ;
40+ import com .vaadin .tests .util .MockUI ;
41+
42+ /**
43+ * Tests for @ClientCallable method support with bean and collection parameters
44+ * and return values.
45+ */
46+ public class ClientCallableBeanSupportTest {
47+
48+ private MockServletServiceSessionSetup mocks ;
49+ private UI ui ;
50+ private PublishedServerEventHandlerRpcHandler handler ;
51+
52+ // Test bean classes
53+ public static class SimpleBean {
54+ public String name ;
55+ public int value ;
56+ public boolean active ;
57+
58+ public SimpleBean () {
59+ }
60+
61+ public SimpleBean (String name , int value , boolean active ) {
62+ this .name = name ;
63+ this .value = value ;
64+ this .active = active ;
65+ }
66+ }
67+
68+ public static class NestedBean {
69+ public String title ;
70+ public SimpleBean simple ;
71+
72+ public NestedBean () {
73+ }
74+
75+ public NestedBean (String title , SimpleBean simple ) {
76+ this .title = title ;
77+ this .simple = simple ;
78+ }
79+ }
80+
81+ // Test component with @ClientCallable methods
82+ @ Tag (Tag .DIV )
83+ public static class ComponentWithClientCallableMethods extends Component {
84+ private SimpleBean receivedBean ;
85+ private List <SimpleBean > receivedList ;
86+ private NestedBean receivedNestedBean ;
87+ private List <Integer > receivedIntegerList ;
88+
89+ @ ClientCallable
90+ public void handleSimpleBean (@ EventData ("bean" ) SimpleBean bean ) {
91+ this .receivedBean = bean ;
92+ }
93+
94+ @ ClientCallable
95+ public void handleBeanList (@ EventData ("list" ) List <SimpleBean > list ) {
96+ this .receivedList = list ;
97+ }
98+
99+ @ ClientCallable
100+ public void handleNestedBean (@ EventData ("nested" ) NestedBean nested ) {
101+ this .receivedNestedBean = nested ;
102+ }
103+
104+ @ ClientCallable
105+ public void handleIntegerList (
106+ @ EventData ("integers" ) List <Integer > integers ) {
107+ this .receivedIntegerList = integers ;
108+ }
109+
110+ @ ClientCallable
111+ public SimpleBean returnSimpleBean () {
112+ return new SimpleBean ("returned" , 42 , true );
113+ }
114+
115+ @ ClientCallable
116+ public List <SimpleBean > returnBeanList () {
117+ return Arrays .asList (new SimpleBean ("first" , 1 , true ),
118+ new SimpleBean ("second" , 2 , false ));
119+ }
120+
121+ @ ClientCallable
122+ public NestedBean returnNestedBean () {
123+ return new NestedBean ("outer" , new SimpleBean ("inner" , 100 , false ));
124+ }
125+
126+ @ ClientCallable
127+ public List <Integer > returnIntegerList () {
128+ return Arrays .asList (10 , 20 , 30 );
129+ }
130+
131+ // Getters for test verification
132+ public SimpleBean getReceivedBean () {
133+ return receivedBean ;
134+ }
135+
136+ public List <SimpleBean > getReceivedList () {
137+ return receivedList ;
138+ }
139+
140+ public NestedBean getReceivedNestedBean () {
141+ return receivedNestedBean ;
142+ }
143+
144+ public List <Integer > getReceivedIntegerList () {
145+ return receivedIntegerList ;
146+ }
147+ }
148+
149+ @ Before
150+ public void setUp () throws Exception {
151+ mocks = new MockServletServiceSessionSetup ();
152+ VaadinService service = mocks .getService ();
153+ service .init ();
154+
155+ ui = new MockUI ();
156+ VaadinSession .setCurrent (mocks .getSession ());
157+ UI .setCurrent (ui );
158+
159+ handler = new PublishedServerEventHandlerRpcHandler ();
160+ }
161+
162+ @ After
163+ public void tearDown () {
164+ mocks .cleanup ();
165+ }
166+
167+ @ Test
168+ public void testSimpleBeanParameter () throws Exception {
169+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
170+ ui .add (component );
171+
172+ // Create JSON for SimpleBean parameter
173+ ObjectNode beanJson = JacksonUtils .createObjectNode ();
174+ beanJson .put ("name" , "TestBean" );
175+ beanJson .put ("value" , 123 );
176+ beanJson .put ("active" , true );
177+
178+ // Create parameters array
179+ ArrayNode params = JacksonUtils .createArrayNode ();
180+ params .add (beanJson );
181+
182+ // Invoke the method directly
183+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
184+ component .getClass (), "handleSimpleBean" , params , -1 );
185+
186+ // Verify the bean was properly deserialized
187+ SimpleBean received = component .getReceivedBean ();
188+ Assert .assertNotNull ("Bean should be received" , received );
189+ Assert .assertEquals ("TestBean" , received .name );
190+ Assert .assertEquals (123 , received .value );
191+ Assert .assertTrue (received .active );
192+ }
193+
194+ @ Test
195+ public void testBeanListParameter () throws Exception {
196+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
197+ ui .add (component );
198+
199+ // Create JSON array with bean objects
200+ ArrayNode beanArray = JacksonUtils .createArrayNode ();
201+
202+ ObjectNode bean1 = JacksonUtils .createObjectNode ();
203+ bean1 .put ("name" , "First" );
204+ bean1 .put ("value" , 1 );
205+ bean1 .put ("active" , true );
206+ beanArray .add (bean1 );
207+
208+ ObjectNode bean2 = JacksonUtils .createObjectNode ();
209+ bean2 .put ("name" , "Second" );
210+ bean2 .put ("value" , 2 );
211+ bean2 .put ("active" , false );
212+ beanArray .add (bean2 );
213+
214+ // Create parameters array
215+ ArrayNode params = JacksonUtils .createArrayNode ();
216+ params .add (beanArray );
217+
218+ // Invoke the method directly
219+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
220+ component .getClass (), "handleBeanList" , params , -1 );
221+
222+ // Verify the list was properly deserialized
223+ List <SimpleBean > received = component .getReceivedList ();
224+ Assert .assertNotNull ("List should be received" , received );
225+ Assert .assertEquals ("Should have 2 beans" , 2 , received .size ());
226+
227+ Assert .assertEquals ("First" , received .get (0 ).name );
228+ Assert .assertEquals (1 , received .get (0 ).value );
229+ Assert .assertTrue (received .get (0 ).active );
230+
231+ Assert .assertEquals ("Second" , received .get (1 ).name );
232+ Assert .assertEquals (2 , received .get (1 ).value );
233+ Assert .assertFalse (received .get (1 ).active );
234+ }
235+
236+ @ Test
237+ public void testNestedBeanParameter () throws Exception {
238+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
239+ ui .add (component );
240+
241+ // Create JSON for nested bean
242+ ObjectNode innerBean = JacksonUtils .createObjectNode ();
243+ innerBean .put ("name" , "InnerBean" );
244+ innerBean .put ("value" , 456 );
245+ innerBean .put ("active" , false );
246+
247+ ObjectNode nestedBean = JacksonUtils .createObjectNode ();
248+ nestedBean .put ("title" , "OuterBean" );
249+ nestedBean .set ("simple" , innerBean );
250+
251+ // Create parameters array
252+ ArrayNode params = JacksonUtils .createArrayNode ();
253+ params .add (nestedBean );
254+
255+ // Invoke the method directly
256+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
257+ component .getClass (), "handleNestedBean" , params , -1 );
258+
259+ // Verify the nested bean was properly deserialized
260+ NestedBean received = component .getReceivedNestedBean ();
261+ Assert .assertNotNull ("Nested bean should be received" , received );
262+ Assert .assertEquals ("OuterBean" , received .title );
263+ Assert .assertNotNull ("Inner bean should be present" , received .simple );
264+ Assert .assertEquals ("InnerBean" , received .simple .name );
265+ Assert .assertEquals (456 , received .simple .value );
266+ Assert .assertFalse (received .simple .active );
267+ }
268+
269+ @ Test
270+ public void testIntegerListParameter () throws Exception {
271+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
272+ ui .add (component );
273+
274+ // Create JSON array with integers
275+ ArrayNode intArray = JacksonUtils .createArrayNode ();
276+ intArray .add (10 );
277+ intArray .add (20 );
278+ intArray .add (30 );
279+
280+ // Create parameters array
281+ ArrayNode params = JacksonUtils .createArrayNode ();
282+ params .add (intArray );
283+
284+ // Invoke the method directly
285+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
286+ component .getClass (), "handleIntegerList" , params , -1 );
287+
288+ // Verify the integer list was properly deserialized
289+ List <Integer > received = component .getReceivedIntegerList ();
290+ Assert .assertNotNull ("Integer list should be received" , received );
291+ Assert .assertEquals ("Should have 3 integers" , 3 , received .size ());
292+ Assert .assertEquals (Integer .valueOf (10 ), received .get (0 ));
293+ Assert .assertEquals (Integer .valueOf (20 ), received .get (1 ));
294+ Assert .assertEquals (Integer .valueOf (30 ), received .get (2 ));
295+ }
296+ }
0 commit comments