View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.rest.client;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.io.*;
23  import java.util.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.collections.*;
27  import org.apache.juneau.json.*;
28  import org.apache.juneau.parser.*;
29  import org.apache.juneau.rest.annotation.*;
30  import org.apache.juneau.rest.httppart.*;
31  import org.apache.juneau.rest.mock.*;
32  import org.apache.juneau.rest.servlet.*;
33  import org.apache.juneau.swap.*;
34  import org.junit.jupiter.api.*;
35  
36  class RestClient_Config_BeanContext_Test extends TestBase {
37  
38  	@Rest
39  	public static class A extends BasicRestObject {
40  		@RestPost
41  		public Reader echoBody(org.apache.juneau.rest.RestRequest req) throws IOException {
42  			return req.getContent().getReader();
43  		}
44  		@RestGet
45  		public String[] checkHeader(org.apache.juneau.rest.RestRequest req) {
46  			return req.getHeaders().getAll(req.getHeaderParam("Check").orElse(null)).stream().map(RequestHeader::getValue).toArray(String[]::new);
47  		}
48  		@RestGet
49  		public Reader checkQuery(org.apache.juneau.rest.RestRequest req) {
50  			return reader(req.getQueryParams().asQueryString());
51  		}
52  		@RestPost
53  		public Reader checkFormData(org.apache.juneau.rest.RestRequest req) {
54  			return reader(req.getFormParams().asQueryString());
55  		}
56  	}
57  
58  	protected static class A1 {
59  		public int f = 1;
60  		@Override
61  		public String toString() {
62  			return "O1";
63  		}
64  	}
65  
66  	@Test void a01_beanClassVisibility() throws Exception {
67  		var x1 = client().build();
68  		var x2 = client(A.class).beanClassVisibility(Visibility.PROTECTED).build();
69  		x1.post("/echoBody",new A1()).run().assertContent("'O1'");
70  		x2.post("/echoBody",new A1()).run().assertContent("{f:1}");
71  		x1.get("/checkQuery").queryData("foo",new A1()).run().assertContent("foo=O1");
72  		x2.get("/checkQuery").queryData("foo",new A1()).run().assertContent("foo=f%3D1").assertContent().asString().asUrlDecode().is("foo=f=1");
73  		x1.formPost("/checkFormData").formData("foo",new A1()).run().assertContent("foo=O1");
74  		x2.formPost("/checkFormData").formData("foo",new A1()).run().assertContent("foo=f%3D1").assertContent().asString().asUrlDecode().is("foo=f=1");
75  		x1.get("/checkHeader").header("foo",new A1()).header("Check","foo").run().assertContent("['O1']");
76  		x2.get("/checkHeader").header("foo",new A1()).header("Check","foo").run().assertContent("['f=1']");
77  	}
78  
79  	public static class A2a {
80  		private int f;
81  		protected A2a(int f) {
82  			this.f = f;
83  		}
84  		public int toInt() {
85  			return f;
86  		}
87  	}
88  
89  	@Rest
90  	public static class A2b extends BasicRestObject {
91  		@RestPost
92  		public Reader test(org.apache.juneau.rest.RestRequest req,org.apache.juneau.rest.RestResponse res) throws IOException {
93  			res.setHeader("X",req.getHeaderParam("X").orElse(null));
94  			return req.getContent().getReader();
95  		}
96  	}
97  
98  	@Test void a02_beanConstructorVisibility() throws Exception {
99  		var x = client(A2b.class)
100 			.beanConstructorVisibility(Visibility.PROTECTED)
101 			.build()
102 			.post("/test",new A2a(1))
103 			.header("X",new A2a(1))
104 			.run()
105 			.cacheContent()
106 			.assertContent("1")
107 			.assertHeader("X").is("1");
108 		assertEquals(1,x.getContent().as(A2a.class).f);
109 		assertEquals(1,x.getHeader("X").as(A2a.class).get().f);
110 	}
111 
112 	public static class A3 {
113 		public int f1;
114 		protected int f2;
115 		static A3 get() {
116 			var x = new A3();
117 			x.f1 = 1;
118 			x.f2 = 2;
119 			return x;
120 		}
121 		@Override
122 		public String toString() {
123 			return f1 + "/" + f2;
124 		}
125 	}
126 
127 	@Test void a03_beanFieldVisibility() throws Exception {
128 		var x = client(A2b.class)
129 			.beanFieldVisibility(Visibility.PROTECTED)
130 			.build()
131 			.post("/test",A3.get())
132 			.header("X",A3.get())
133 			.run()
134 			.cacheContent()
135 			.assertContent("{f1:1,f2:2}")
136 			.assertHeader("X").is("f1=1,f2=2");
137 		assertEquals(2,x.getContent().as(A3.class).f2);
138 		assertEquals(2,x.getHeader("X").as(A3.class).get().f2);
139 	}
140 
141 	public interface A4a {
142 		int getF3();
143 		void setF3(int f3);
144 	}
145 
146 	public static class A4b implements A4a {
147 		public int f1, f2;
148 
149 		private int f3;
150 		@Override public int getF3() { return f3; }
151 		@Override public void setF3(int v) { f3 = v; }
152 
153 		static A4b get() {
154 			var x = new A4b();
155 			x.f1 = 1;
156 			x.f2 = 2;
157 			x.f3 = 3;
158 			return x;
159 		}
160 		@Override
161 		public String toString() {
162 			return f1 + "/" + f2;
163 		}
164 	}
165 
166 	@Test void a04_beanFilters() throws Exception {
167 		var x = client(A2b.class)
168 			.beanProperties(A4b.class,"f1")
169 			.build()
170 			.post("/test",A4b.get())
171 			.header("X",A4b.get())
172 			.run()
173 			.cacheContent()
174 			.assertContent()
175 			.is("{f1:1}")
176 			.assertHeader("X").is("f1=1");
177 		assertEquals(0,x.getContent().as(A4b.class).f2);
178 		assertEquals(0,x.getHeader("X").as(A4b.class).get().f2);
179 
180 		x = client(A2b.class)
181 			.beanProperties(A4b.class,"f1")
182 			.build()
183 			.post("/test",A4b.get())
184 			.header("X",A4b.get())
185 			.run()
186 			.cacheContent()
187 			.assertContent("{f1:1}")
188 			.assertHeader("X").is("f1=1");
189 		assertEquals(0,x.getContent().as(A4b.class).f2);
190 		assertEquals(0,x.getHeader("X").as(A4b.class).get().f2);
191 
192 		x = client(A2b.class)
193 			.beanProperties(A4b.class,"f1")
194 			.build()
195 			.post("/test",A4b.get())
196 			.header("X",A4b.get())
197 			.run()
198 			.cacheContent()
199 			.assertContent("{f1:1}")
200 			.assertHeader("X").is("f1=1");
201 		assertEquals(0,x.getContent().as(A4b.class).f2);
202 		assertEquals(0,x.getHeader("X").as(A4b.class).get().f2);
203 
204 		x = client(A2b.class)
205 			.beanProperties(A4b.class,"f1")
206 			.build()
207 			.post("/test",A4b.get())
208 			.header("X",A4b.get())
209 			.run()
210 			.cacheContent()
211 			.assertContent("{f1:1}")
212 			.assertHeader("X").is("f1=1");
213 		assertEquals(0,x.getContent().as(A4b.class).f2);
214 		assertEquals(0,x.getHeader("X").as(A4b.class).get().f2);
215 
216 		x = client(A2b.class)
217 			.interfaces(A4a.class)
218 			.build()
219 			.post("/test",A4b.get())
220 			.header("X",A4b.get())
221 			.run()
222 			.cacheContent()
223 			.assertContent("{f3:3}")
224 			.assertHeader("X").is("f3=3");
225 		assertEquals(3,x.getContent().as(A4b.class).f3);
226 		assertEquals(3,x.getHeader("X").as(A4b.class).get().f3);
227 	}
228 
229 	public static class A5  {
230 		private int f1;
231 		public int getF1() { return f1; }
232 		public void setF1(int v) { f1 = v; }
233 
234 		private int f2;
235 		protected int getF2() { return f2; }
236 		protected void setF2(int v) { f2 = v; }
237 
238 		static A5 get() {
239 			var x = new A5();
240 			x.f1 = 1;
241 			x.f2 = 2;
242 			return x;
243 		}
244 
245 		@Override
246 		public String toString() {
247 			return f1 + "/" + f2;
248 		}
249 	}
250 
251 	@Test void a05_beanMethodVisibility() throws Exception {
252 		var x = client(A2b.class)
253 			.beanMethodVisibility(Visibility.PROTECTED)
254 			.build()
255 			.post("/test",A5.get())
256 			.header("X",A5.get())
257 			.run()
258 			.cacheContent()
259 			.assertContent("{f1:1,f2:2}")
260 			.assertHeader("X").is("f1=1,f2=2");
261 		assertEquals(2,x.getContent().as(A5.class).f2);
262 		assertEquals(2,x.getHeader("X").as(A5.class).get().f2);
263 	}
264 
265 	public static class A6 {}
266 
267 	@Test void a06_disableBeansRequireSomeProperties() throws Exception {
268 		client().disableBeansRequireSomeProperties().build().post("/echoBody",new A6()).run().assertContent("{}");
269 	}
270 
271 	public static class A7  {
272 		public String f1;
273 		public A7(String i) {
274 			f1 = i;
275 		}
276 		@Override
277 		public String toString() {
278 			return f1;
279 		}
280 	}
281 
282 	@Test void a07_beansRequireDefaultConstructor() throws Exception {
283 		client(A2b.class)
284 			.build()
285 			.post("/test",new A7("1"))
286 			.header("X",new A7("1"))
287 			.run()
288 			.assertContent("{f1:'1'}")
289 			.assertHeader("X").is("f1=1");
290 		client(A2b.class)
291 			.beansRequireDefaultConstructor()
292 			.build()
293 			.post("/test",new A7("1"))
294 			.header("X",new A7("1"))
295 			.run()
296 			.assertContent("'1'")
297 			.assertHeader("X").is("1");
298 	}
299 
300 	@Test void a08_beansRequireSerializable() throws Exception {
301 		client(A2b.class)
302 			.build()
303 			.post("/test",new A7("1"))
304 			.header("X",new A7("1"))
305 			.run()
306 			.assertContent("{f1:'1'}")
307 			.assertHeader("X").is("f1=1");
308 		client(A2b.class)
309 			.beansRequireSerializable()
310 			.build()
311 			.post("/test",new A7("1"))
312 			.header("X",new A7("1"))
313 			.run()
314 			.assertContent("'1'")
315 			.assertHeader("X").is("1");
316 	}
317 
318 	public static class A9 {
319 		private int f1;
320 		public int getF1() { return f1; }
321 		public void setF1(int v) { f1 = v; }
322 
323 		private int f2;
324 		public int getF2() { return f2; }
325 
326 		static A9 get() {
327 			var x = new A9();
328 			x.f1 = 1;
329 			x.f2 = 2;
330 			return x;
331 		}
332 
333 		@Override
334 		public String toString() {
335 			return f1 + "/" + f2;
336 		}
337 	}
338 
339 	@Test void a09_beansRequireSettersForGetters() throws Exception {
340 		client(A2b.class)
341 			.build()
342 			.post("/test",A9.get())
343 			.header("X",A9.get())
344 			.run()
345 			.assertContent("{f1:1,f2:2}")
346 			.assertHeader("X").is("f1=1,f2=2");
347 		client(A2b.class)
348 			.beansRequireSettersForGetters()
349 			.build()
350 			.post("/test",A9.get())
351 			.header("X",A9.get())
352 			.run()
353 			.assertContent("{f1:1}")
354 			.assertHeader("X").is("f1=1");
355 	}
356 
357 	@Test void a10_bpi() throws Exception {
358 		client(A2b.class)
359 			.beanProperties(JsonMap.of("A9","f2"))
360 			.build()
361 			.post("/test",A9.get())
362 			.header("X",A9.get())
363 			.run()
364 			.assertContent("{f2:2}")
365 			.assertHeader("X").is("f2=2");
366 		client(A2b.class)
367 			.beanProperties(A9.class,"f2")
368 			.build()
369 			.post("/test",A9.get())
370 			.header("X",A9.get())
371 			.run()
372 			.assertContent("{f2:2}")
373 			.assertHeader("X").is("f2=2");
374 		client(A2b.class)
375 			.beanProperties("A9","f2")
376 			.build()
377 			.post("/test",A9.get())
378 			.header("X",A9.get())
379 			.run()
380 			.assertContent("{f2:2}")
381 			.assertHeader("X").is("f2=2");
382 		client(A2b.class)
383 			.beanProperties(A9.class.getName(),"f2")
384 			.build()
385 			.post("/test",A9.get())
386 			.header("X",A9.get())
387 			.run()
388 			.assertContent("{f2:2}")
389 			.assertHeader("X").is("f2=2");
390 	}
391 
392 	@Test void a11_bpro() throws Exception {
393 		var x = client(A2b.class)
394 			.beanPropertiesReadOnly(JsonMap.of("09","f2"))
395 			.build()
396 			.post("/test",A9.get())
397 			.header("X",A9.get())
398 			.run()
399 			.cacheContent()
400 			.assertContent("{f1:1,f2:2}")
401 			.assertHeader("X").is("f1=1,f2=2");
402 		assertEquals("1/0",x.getContent().as(A9.class).toString());
403 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
404 
405 		x = client(A2b.class)
406 			.beanPropertiesReadOnly(A9.class,"f2")
407 			.build()
408 			.post("/test",A9.get())
409 			.header("X",A9.get())
410 			.run()
411 			.cacheContent()
412 			.assertContent("{f1:1,f2:2}")
413 			.assertHeader("X").is("f1=1,f2=2");
414 		assertEquals("1/0",x.getContent().as(A9.class).toString());
415 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
416 
417 		x = client(A2b.class)
418 			.beanPropertiesReadOnly("O9","f2")
419 			.build()
420 			.post("/test",A9.get())
421 			.header("X",A9.get())
422 			.run()
423 			.cacheContent()
424 			.assertContent("{f1:1,f2:2}")
425 			.assertHeader("X").is("f1=1,f2=2");
426 		assertEquals("1/0",x.getContent().as(A9.class).toString());
427 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
428 	}
429 
430 	@Test void a12_bpwo() throws Exception {
431 		var x = client(A2b.class)
432 			.beanPropertiesWriteOnly(JsonMap.of("A9","f2"))
433 			.build()
434 			.post("/test",A9.get())
435 			.header("X",A9.get())
436 			.run()
437 			.cacheContent()
438 			.assertContent("{f1:1}")
439 			.assertHeader("X").is("f1=1");
440 		assertEquals("1/0",x.getContent().as(A9.class).toString());
441 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
442 
443 		x = client(A2b.class)
444 			.beanPropertiesWriteOnly(A9.class,"f2")
445 			.build()
446 			.post("/test",A9.get())
447 			.header("X",A9.get())
448 			.run()
449 			.cacheContent()
450 			.assertContent("{f1:1}")
451 			.assertHeader("X").is("f1=1");
452 		assertEquals("1/0",x.getContent().as(A9.class).toString());
453 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
454 
455 		x = client(A2b.class)
456 			.beanPropertiesWriteOnly("A9","f2")
457 			.build()
458 			.post("/test",A9.get())
459 			.header("X",A9.get())
460 			.run()
461 			.cacheContent()
462 			.assertContent("{f1:1}")
463 			.assertHeader("X").is("f1=1");
464 		assertEquals("1/0",x.getContent().as(A9.class).toString());
465 		assertEquals("1/0",x.getHeader("X").as(A9.class).get().toString());
466 	}
467 
468 	@Test void a13_bpx() throws Exception {
469 		client(A2b.class)
470 			.beanPropertiesExcludes(JsonMap.of("A9","f1"))
471 			.build()
472 			.post("/test",A9.get())
473 			.header("X",A9.get()).
474 			run()
475 			.assertContent("{f2:2}")
476 			.assertHeader("X").is("f2=2");
477 		client(A2b.class)
478 			.beanPropertiesExcludes(A9.class,"f1")
479 			.build()
480 			.post("/test",A9.get())
481 			.header("X",A9.get())
482 			.run()
483 			.assertContent("{f2:2}")
484 			.assertHeader("X").is("f2=2");
485 		client(A2b.class)
486 			.beanPropertiesExcludes("A9","f1")
487 			.build()
488 			.post("/test",A9.get())
489 			.header("X",A9.get())
490 			.run()
491 			.assertContent("{f2:2}")
492 			.assertHeader("X").is("f2=2");
493 		client(A2b.class)
494 			.beanPropertiesExcludes(A9.class.getName(),"f1")
495 			.build()
496 			.post("/test",A9.get())
497 			.header("X",A9.get())
498 			.run()
499 			.assertContent("{f2:2}")
500 			.assertHeader("X").is("f2=2");
501 	}
502 
503 	public static class A14 {
504 		public Object f;
505 	}
506 
507 	@Test void a14_debug() {
508 		var x = new A14();
509 		x.f = x;
510 		assertThrowsWithMessage(Exception.class, "Recursion occurred", ()->client().debug().build().post("/echo",x).run());
511 	}
512 
513 	@org.apache.juneau.annotation.Bean(typeName="foo")
514 	public static class A15a {
515 		public String foo;
516 		static A15a get() {
517 			var x = new A15a();
518 			x.foo = "1";
519 			return x;
520 		}
521 	}
522 
523 	@org.apache.juneau.annotation.Bean(typeName="bar")
524 	public static class A15b {
525 		public String foo;
526 		static A15b get() {
527 			var x = new A15b();
528 			x.foo = "2";
529 			return x;
530 		}
531 	}
532 
533 	public static class A15c {
534 		public Object foo;
535 		static A15c get() {
536 			var x = new A15c();
537 			x.foo = A15a.get();
538 			return x;
539 		}
540 	}
541 
542 	@Test void a15_dictionary() throws Exception {
543 		var o = client().beanDictionary(A15a.class,A15b.class).addRootType().addBeanTypes().build().post("/echoBody",A15a.get()).run().cacheContent().assertContent().isContains("{_type:'foo',foo:'1'}").getContent().as(Object.class);
544 		assertTrue(o instanceof A15a);
545 
546 		var m = JsonMap.of("x",A15a.get(),"y",A15b.get());
547 		m = client().beanDictionary(A15a.class,A15b.class).addRootType().addBeanTypes().build().post("/echoBody",m).run().cacheContent().assertContent("{x:{_type:'foo',foo:'1'},y:{_type:'bar',foo:'2'}}").getContent().as(JsonMap.class);
548 		assertTrue(m.get("x") instanceof A15a);
549 		assertTrue(m.get("y") instanceof A15b);
550 
551 		var x = client().dictionaryOn(A15c.class,A15a.class,A15b.class).addRootType().addBeanTypes().build().post("/echoBody",A15c.get()).run().cacheContent().assertContent("{foo:{_type:'foo',foo:'1'}}").getContent().as(A15c.class);
552 		assertTrue(x.foo instanceof A15a);
553 	}
554 
555 	public static class A16 {
556 		private String foo;
557 		public String getFoo() { return foo; }
558 
559 		static A16 get() {
560 			var x = new A16();
561 			x.foo = "foo";
562 			return x;
563 		}
564 	}
565 
566 	@Test void a16_disableIgnorePropertiesWithoutSetters() throws Exception {
567 		var x = client().build().post("/echoBody",A16.get()).run().cacheContent().assertContent().isContains("{foo:'foo'}").getContent().as(A16.class);
568 		assertNull(x.foo);
569 		assertThrowsWithMessage(Exception.class, "Setter or public field not defined", ()->client().disableIgnoreMissingSetters().build().post("/echoBody",A16.get()).run().cacheContent().assertContent().isContains("{foo:'foo'}").getContent().as(A16.class));
570 	}
571 
572 	public static class A17 {
573 		public String foo;
574 		public transient String bar;
575 		static A17 get() {
576 			var x = new A17();
577 			x.foo = "1";
578 			x.bar = "2";
579 			return x;
580 		}
581 	}
582 
583 	@Test void a17_disableIgnoreTransientFields() throws Exception {
584 		var x = client().build().post("/echoBody",A17.get()).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A17.class);
585 		assertNull(x.bar);
586 		x = client().disableIgnoreTransientFields().build().post("/echoBody",A17.get()).run().cacheContent().assertContent().isContains("{bar:'2',foo:'1'}").getContent().as(A17.class);
587 		assertEquals("2",x.bar);
588 	}
589 
590 	public static class A18 {
591 		public String foo;
592 	}
593 
594 	@Test void a18_disableIgnoreUnknownNullBeanProperties() throws Exception {
595 		client().build().post("/echoBody",reader("{foo:'1',bar:null}")).run().cacheContent().assertContent().isContains("{foo:'1',bar:null}").getContent().as(A18.class);
596 		assertThrowsWithMessage(Exception.class, "Unknown property 'bar'", ()->client().disableIgnoreUnknownNullBeanProperties().build().post("/echoBody",reader("{foo:'1',bar:null}")).run().cacheContent().assertContent().isContains("{foo:'1',bar:null}").getContent().as(A18.class));
597 	}
598 
599 	public interface A19 {
600 		String getFoo();
601 		void setFoo(String foo);
602 	}
603 
604 	@Test void a19_disableInterfaceProxies() throws Exception {
605 		var x = client().build().post("/echoBody",reader("{foo:'1'}")).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A19.class);
606 		assertEquals("1",x.getFoo());
607 		assertThrowsWithMessage(Exception.class, "could not be instantiated", ()->client().disableInterfaceProxies().build().post("/echoBody",reader("{foo:'1'}")).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A19.class));
608 	}
609 
610 	public static class A20 {
611 		private String foo;
612 		public String getFoo() { return foo; }
613 
614 		public A20 foo(String foo) {
615 			this.foo = foo;
616 			return this;
617 		}
618 	}
619 
620 	@Test void a20_fluentSetters() throws Exception {
621 		var x = client().findFluentSetters().build().post("/echoBody",reader("{foo:'1'}")).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A20.class);
622 		assertEquals("1",x.getFoo());
623 		x = client().findFluentSetters(A20.class).build().post("/echoBody",reader("{foo:'1'}")).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A20.class);
624 		assertEquals("1",x.getFoo());
625 	}
626 
627 	public static class A21 {
628 		private String foo;
629 		public String getFoo() { return foo; }
630 		public void setFoo(String v) { foo = v; }
631 
632 		@SuppressWarnings("unused") private String bar;
633 		public String getBar() { throw new RuntimeException("xxx"); }
634 
635 		static A21 get() {
636 			var x = new A21();
637 			x.foo = "1";
638 			x.bar = "2";
639 			return x;
640 		}
641 	}
642 
643 	@Test void a21_ignoreInvocationExceptionsOnGetters() throws Exception {
644 		assertThrowsWithMessage(Exception.class, "xxx", ()->client().build().post("/echoBody",A21.get()).run());
645 		var x = client().ignoreInvocationExceptionsOnGetters().build().post("/echoBody",A21.get()).run().cacheContent().assertContent().isContains("{foo:'1'}").getContent().as(A21.class);
646 		assertEquals("1",x.getFoo());
647 	}
648 
649 	public static class A22 {
650 		private String foo;
651 		public String getFoo() { return foo; }
652 		public void setFoo(String v) { foo = v; }
653 
654 		private String bar;
655 		public String getBar() { return bar; }
656 		public void setBar(String v) { throw new RuntimeException("xxx"); }
657 
658 		static A22 get() {
659 			var x = new A22();
660 			x.foo = "1";
661 			x.bar = "2";
662 			return x;
663 		}
664 	}
665 
666 	@Test void a22_ignoreInvocationExceptionsOnSetters() throws Exception {
667 		assertThrowsWithMessage(Exception.class, "Error occurred trying to set property 'bar'", ()->client().build().post("/echoBody",A22.get()).run().getContent().as(A22.class));
668 		var x = client().ignoreInvocationExceptionsOnSetters().build().post("/echoBody",A22.get()).run().cacheContent().getContent().as(A22.class);
669 		assertEquals("1",x.getFoo());
670 	}
671 
672 	public static class A23 {
673 		public String foo;
674 	}
675 
676 	@Test void a23_ignoreUnknownBeanProperties() throws Exception {
677 		assertThrowsWithMessage(Exception.class, "Unknown property 'bar' encountered", ()->client().build().post("/echoBody",reader("{foo:'1',bar:'2'}")).run().getContent().as(A23.class));
678 		var x = client().ignoreUnknownBeanProperties().build().post("/echoBody",reader("{foo:'1',bar:'2'}")).run().cacheContent().getContent().as(A23.class);
679 		assertEquals("1",x.foo);
680 	}
681 
682 	public interface A24a {
683 		void setFoo(int foo);
684 		int getFoo();
685 	}
686 
687 	public static class A24b implements A24a {
688 		private int foo;
689 		@Override public int getFoo() { return foo; }
690 		@Override public void setFoo(int v) { foo = v; }
691 	}
692 
693 	@Test void a24_implClass() throws Exception {
694 		var x = client().implClass(A24a.class,A24b.class).build().post("/echoBody",reader("{foo:1}")).run().getContent().as(A24a.class);
695 		assertEquals(1,x.getFoo());
696 		assertTrue(x instanceof A24b);
697 
698 		x = client().implClasses(map(A24a.class,A24b.class)).build().post("/echoBody",reader("{foo:1}")).run().getContent().as(A24a.class);
699 		assertEquals(1,x.getFoo());
700 		assertTrue(x instanceof A24b);
701 	}
702 
703 	public interface A25a {
704 		void setFoo(int foo);
705 		int getFoo();
706 	}
707 
708 	public static class A25b implements A25a {
709 		private int foo;
710 		@Override public int getFoo() { return foo; }
711 		@Override public void setFoo(int v) { foo = v; }
712 
713 		private int bar;
714 		public int getBar() { return bar; }  // Not executed
715 		public void setBar(int v) { bar = v; }  // Not executed
716 
717 		static A25b get() {
718 			var x = new A25b();
719 			x.foo = 1;
720 			x.bar = 2;
721 			return x;
722 		}
723 	}
724 
725 	@Test void a25_interfaceClass() throws Exception {
726 		var x = client().interfaceClass(A25b.class,A25a.class).build().post("/echoBody",A25b.get()).run().cacheContent().assertContent("{foo:1}").getContent().as(A25b.class);
727 		assertEquals(1,x.getFoo());
728 		x = client().interfaces(A25a.class).build().post("/echoBody",A25b.get()).run().assertContent("{foo:1}").getContent().as(A25b.class);
729 		assertEquals(1,x.getFoo());
730 	}
731 
732 	public static class A26 {
733 		public int foo;
734 		static A26 get() {
735 			var x = new A26();
736 			x.foo = 1;
737 			return x;
738 		}
739 	}
740 
741 	@Test void a26_locale() throws Exception {
742 		var x = client().locale(Locale.UK).build().post("/echoBody",A26.get()).run().cacheContent().assertContent("{foo:1}").getContent().as(A26.class);
743 		assertEquals(1,x.foo);
744 	}
745 
746 	@Test void a27_mediaType() throws Exception {
747 		var x = client().mediaType(MediaType.JSON).build().post("/echoBody",A26.get()).run().cacheContent().assertContent("{foo:1}").getContent().as(A26.class);
748 		assertEquals(1,x.foo);
749 	}
750 
751 	public static class A28 {
752 		public int foo;
753 		static A28 get() {
754 			var x = new A28();
755 			x.foo = 1;
756 			return x;
757 		}
758 		@Override
759 		public String toString() {
760 			return String.valueOf(foo);
761 		}
762 		public static A28 fromString(String foo) throws ParseException {
763 			var x = new A28();
764 			x.foo = JsonParser.DEFAULT.parse(foo,int.class);
765 			return x;
766 		}
767 	}
768 
769 	@Test void a28_notBeanClasses() throws Exception {
770 		var x = client().notBeanClasses(A28.class).build().post("/echoBody",A28.get()).run().cacheContent().assertContent("'1'").getContent().as(A28.class);
771 		assertEquals(1,x.foo);
772 	}
773 
774 	@Test void a29_notBeanPackages() throws Exception {
775 		var x = client().notBeanPackages(A28.class.getPackage().getName()).build().post("/echoBody",A28.get()).run().cacheContent().assertContent("'1'").getContent().as(A28.class);
776 		assertEquals(1,x.foo);
777 	}
778 
779 	public static class A30a {
780 		private String foo;
781 		public String getFoo() { return foo; }
782 		public void setFoo(String v) { foo = v; }
783 
784 		static A30a get() {
785 			var x = new A30a();
786 			x.foo = "foo";
787 			return x;
788 		}
789 	}
790 
791 	public static class A30b extends BeanInterceptor<A30a> {
792 		static boolean getterCalled,setterCalled;
793 		@Override
794 		public Object readProperty(A30a bean,String name,Object value) {
795 			getterCalled = true;
796 			return "x" + value;
797 		}
798 		@Override
799 		public Object writeProperty(A30a bean,String name,Object value) {
800 			setterCalled = true;
801 			return value.toString().substring(1);
802 		}
803 	}
804 
805 	@Test void a30_beanInterceptor() throws Exception {
806 		var x = client().beanInterceptor(A30a.class,A30b.class).build().post("/echoBody",A30a.get()).run().cacheContent().assertContent("{foo:'xfoo'}").getContent().as(A30a.class);
807 		assertEquals("foo",x.foo);
808 		assertTrue(A30b.getterCalled);
809 		assertTrue(A30b.setterCalled);
810 	}
811 
812 	public static class A31 {
813 		private String fooBar;
814 		public String getFooBar() { return fooBar; }
815 		public void setFooBar(String v) { fooBar = v; }
816 
817 		static A31 get() {
818 			var x = new A31();
819 			x.fooBar = "fooBar";
820 			return x;
821 		}
822 	}
823 
824 	@Test void a31_propertyNamer() throws Exception {
825 		var x = client().propertyNamer(PropertyNamerDLC.class).build().post("/echoBody",A31.get()).run().cacheContent().assertContent("{'foo-bar':'fooBar'}").getContent().as(A31.class);
826 		assertEquals("fooBar",x.fooBar);
827 		x = client().propertyNamer(A31.class,PropertyNamerDLC.class).build().post("/echoBody",A31.get()).run().cacheContent().assertContent("{'foo-bar':'fooBar'}").getContent().as(A31.class);
828 		assertEquals("fooBar",x.fooBar);
829 	}
830 
831 	public static class A32 {
832 		public int foo, bar, baz;
833 		static A32 get() {
834 			var x = new A32();
835 			x.foo = 1;
836 			x.bar = 2;
837 			x.baz = 3;
838 			return x;
839 		}
840 	}
841 
842 	@Test void a32_sortProperties() throws Exception {
843 		var x = client().sortProperties().build().post("/echoBody",A32.get()).run().cacheContent().assertContent("{bar:2,baz:3,foo:1}").getContent().as(A32.class);
844 		assertEquals(1,x.foo);
845 		x = client().sortProperties(A32.class).build().post("/echoBody",A32.get()).run().cacheContent().assertContent("{bar:2,baz:3,foo:1}").getContent().as(A32.class);
846 		assertEquals(1,x.foo);
847 	}
848 
849 	public static class A33a {
850 		public int foo;
851 	}
852 
853 	public static class A33b extends A33a {
854 		public int bar;
855 		static A33b get() {
856 			var x = new A33b();
857 			x.foo = 1;
858 			x.bar = 2;
859 			return x;
860 		}
861 	}
862 
863 	@Test void a33_stopClass() throws Exception {
864 		var x = client().stopClass(A33b.class,A33a.class).build().post("/echoBody",A33b.get()).run().cacheContent().assertContent("{bar:2}").getContent().as(A33b.class);
865 		assertEquals(0,x.foo);
866 		assertEquals(2,x.bar);
867 	}
868 
869 	public static class A34a {
870 		public int foo;
871 		static A34a get() {
872 			var x = new A34a();
873 			x.foo = 1;
874 			return x;
875 		}
876 	}
877 
878 	public static class A34b extends ObjectSwap<A34a,Integer> {
879 		@Override public Integer swap(BeanSession session,A34a o) { return o.foo; }
880 		@Override public A34a unswap(BeanSession session,Integer f,ClassMeta<?> hint) {return A34a.get(); }
881 	}
882 
883 	@Test void a34_swaps() throws Exception {
884 		var x = client().swaps(A34b.class).build().post("/echoBody",A34a.get()).run().cacheContent().assertContent("1").getContent().as(A34a.class);
885 		assertEquals(1,x.foo);
886 	}
887 
888 	public static class A35 {
889 		public int foo;
890 		static A35 get() {
891 			var x = new A35();
892 			x.foo = 1;
893 			return x;
894 		}
895 	}
896 
897 	@Test void a35_timeZone() throws Exception {
898 		var x = client().timeZone(TimeZone.getTimeZone("Z")).build().post("/echoBody",A35.get()).run().cacheContent().assertContent("{foo:1}").getContent().as(A35.class);
899 		assertEquals(1,x.foo);
900 	}
901 
902 	public static class A36 {
903 		public int foo;
904 		static A36 get() {
905 			var x = new A36();
906 			x.foo = 1;
907 			return x;
908 		}
909 	}
910 
911 	@Test void a36_typeName() throws Exception {
912 		var x = client().typeName(A36.class,"foo").addRootType().build().post("/echoBody",A36.get()).run().cacheContent().assertContent("{_type:'foo',foo:1}").getContent().as(A36.class);
913 		assertEquals(1,x.foo);
914 	}
915 
916 	@Test void a37_typePropertyName() throws Exception {
917 		var x = client().typeName(A36.class,"foo").typePropertyName("X").addRootType().build().post("/echoBody",A36.get()).run().cacheContent().assertContent("{X:'foo',foo:1}").getContent().as(A36.class);
918 		assertEquals(1,x.foo);
919 		x = client().typeName(A36.class,"foo").typePropertyName(A36.class,"X").addRootType().build().post("/echoBody",A36.get()).run().cacheContent().assertContent("{X:'foo',foo:1}").getContent().as(A36.class);
920 		assertEquals(1,x.foo);
921 	}
922 
923 	public enum A38a {
924 		ONE(1),TWO(2);
925 		private int value;
926 		A38a(int value) {
927 			this.value = value;
928 		}
929 		@Override
930 		public String toString() {
931 			return String.valueOf(value);  // Not executed
932 		}
933 	}
934 
935 	public static class A38b {
936 		public A38a foo;
937 		static A38b get() {
938 			var x = new A38b();
939 			x.foo = A38a.ONE;
940 			return x;
941 		}
942 	}
943 
944 	@Test void a38_useEnumNames() throws Exception {
945 		var x = client().useEnumNames().build().post("/echoBody",A38b.get()).run().cacheContent().assertContent("{foo:'ONE'}").getContent().as(A38b.class);
946 		assertEquals(A38a.ONE,x.foo);
947 	}
948 
949 	public static class A39 {
950 		private int foo;
951 		public int getFoo() { return foo; }
952 		public void setFoo(int v) { foo = v; }
953 
954 		public int bar;
955 
956 		static A39 get() {
957 			var x = new A39();
958 			x.foo = 1;
959 			x.bar = 2;
960 			return x;
961 		}
962 	}
963 
964 	@Test void a39_useJavaIntrospector() throws Exception {
965 		var x = client().useJavaBeanIntrospector().build().post("/echoBody",A39.get()).run().cacheContent().assertContent("{foo:1}").getContent().as(A39.class);
966 		assertEquals(1,x.foo);
967 	}
968 
969 	//------------------------------------------------------------------------------------------------------------------
970 	// Helper methods.
971 	//------------------------------------------------------------------------------------------------------------------
972 
973 	private static RestClient.Builder client() {
974 		return MockRestClient.create(A.class).json5();
975 	}
976 
977 	private static RestClient.Builder client(Class<?> c) {
978 		return MockRestClient.create(c).json5();
979 	}
980 }