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