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.cp;
18  
19  import static java.lang.annotation.ElementType.*;
20  import static java.lang.annotation.RetentionPolicy.*;
21  import static org.apache.juneau.TestUtils.*;
22  import static org.apache.juneau.junit.bct.BctAssertions.*;
23  import static org.junit.jupiter.api.Assertions.*;
24  
25  import java.lang.annotation.*;
26  import java.util.*;
27  import java.util.function.*;
28  
29  import org.apache.juneau.*;
30  import org.apache.juneau.annotation.*;
31  import org.apache.juneau.annotation.Named;
32  import org.apache.juneau.commons.reflect.*;
33  import org.apache.juneau.commons.utils.*;
34  import org.junit.jupiter.api.*;
35  
36  class BeanStore_Test extends TestBase {
37  
38  	@Documented
39  	@Target({PARAMETER})
40  	@Retention(RUNTIME)
41  	@Inherited
42  	public @interface Name {
43  		String value();
44  	}
45  
46  	//-----------------------------------------------------------------------------------------------------------------
47  	// Basic tests
48  	//-----------------------------------------------------------------------------------------------------------------
49  
50  	public static class A1 {}
51  	public static class A2 {}
52  
53  	public static final String A1n = A1.class.getSimpleName();  // NOSONAR
54  
55  	public static class A3 extends BeanStore {
56  		protected A3(Builder builder) {
57  			super(builder);
58  		}
59  	}
60  
61  	private static A1 a1a = new A1(), a1b = new A1(), a1c = new A1(), a1d = new A1(), a1e = new A1();
62  	private static A2 a2a = new A2();
63  
64  	@Test void a00_dummy() {
65  		assertDoesNotThrow(BeanStore.Void::new);
66  	}
67  
68  	@Test void a01_builderCopyConstructor() {
69  		var b1p = BeanStore.create().readOnly().threadSafe().build();
70  		var b1c = BeanStore.create().parent(b1p).build();
71  		assertContains("readOnly=true", b1c.toString());
72  		assertContains("threadSafe=true", b1c.toString());
73  	}
74  
75  	@Test void a02_readOnly() {
76  		var b1p = BeanStore.create().readOnly().build();
77  		var b1c = BeanStore.create().parent(b1p).build();
78  		var b2p = BeanStore.create().readOnly().threadSafe().build();
79  		var b2c = BeanStore.create().parent(b1p).threadSafe().build();
80  
81  		for (var b : array(b1p, b2p)) {
82  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.add(A1.class, a1a));
83  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.add(A1.class, a1a, "foo"));
84  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addBean(A1.class, a1a));
85  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addBean(A1.class, a1a, "foo"));
86  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addSupplier(A1.class, ()->a1a));
87  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addSupplier(A1.class, ()->a1a, "foo"));
88  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", b::clear);
89  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.removeBean(A1.class));
90  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.removeBean(A1.class, "foo"));
91  		}
92  
93  		for (var b : array(b1c, b2c)) {
94  			b.add(A1.class, a1a);
95  			b.add(A1.class, a1a, "foo");
96  			b.addBean(A1.class, a1a);
97  			b.addBean(A1.class, a1a, "foo");
98  			b.addSupplier(A1.class, ()->a1a);
99  			b.addSupplier(A1.class, ()->a1a, "foo");
100 			b.clear();
101 			b.removeBean(A1.class);
102 			b.removeBean(A1.class, "foo");
103 		}
104 	}
105 
106 	@Test void a04_addBean() {
107 		var b1p = BeanStore.create().build();
108 		var b1c = BeanStore.of(b1p);
109 		var b2p = BeanStore.create().threadSafe().build();
110 		var b2c = BeanStore.create().threadSafe().parent(b2p).build();
111 
112 		for (var b : array(b1p, b1c, b2p, b2c)) {
113 			assertFalse(b.hasBean(A1.class));
114 			assertEmpty(b.getBean(A1.class));
115 		}
116 
117 		b1p.addBean(A1.class, a1a);
118 		b2p.addBean(A1.class, a1a);
119 		for (var b : array(b1p, b1c, b2p, b2c)) {
120 			assertTrue(b.hasBean(A1.class));
121 			assertEquals(a1a, b.getBean(A1.class).get());
122 		}
123 
124 		b1p.clear();
125 		b2p.clear();
126 		for (var b : array(b1p, b1c, b2p, b2c)) {
127 			assertFalse(b.hasBean(A1.class));
128 			assertEmpty(b.getBean(A1.class));
129 		}
130 
131 		b1p.addBean(A1.class, null);
132 		b2p.addBean(A1.class, null);
133 		for (var b : array(b1p, b1c, b2p, b2c)) {
134 			assertTrue(b.hasBean(A1.class));
135 			assertEmpty(b.getBean(A1.class));
136 		}
137 
138 		b1p.clear().addSupplier(A1.class, ()->a1a);
139 		b2p.clear().addSupplier(A1.class, ()->a1a);
140 		for (var b : array(b1p, b1c, b2p, b2c)) {
141 			assertTrue(b.hasBean(A1.class));
142 			assertEquals(a1a, b.getBean(A1.class).get());
143 		}
144 
145 		b1p.add(A1.class, a1b);
146 		b2p.add(A1.class, a1b);
147 		for (var b : array(b1p, b1c, b2p, b2c)) {
148 			assertTrue(b.hasBean(A1.class));
149 			assertEquals(a1b, b.getBean(A1.class).get());
150 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1b, a1a);
151 		}
152 
153 		b1c.add(A2.class, a2a);
154 		b2c.add(A2.class, a2a);
155 		for (var b : array(b1p, b2p)) {
156 			assertFalse(b.hasBean(A2.class));
157 			assertEmpty(b.getBean(A2.class));
158 			assertEmpty(b.stream(A2.class));
159 		}
160 		for (var b : array(b1c, b2c)) {
161 			assertTrue(b.hasBean(A2.class));
162 			assertEquals(a2a, b.getBean(A2.class).get());
163 			assertList(b.stream(A2.class).map(BeanStoreEntry::get), a2a);
164 		}
165 
166 		assertMatchesGlob("{entries=[{type=A1,bean="+Utils.id(a1b)+"},{type=A1,bean="+Utils.id(a1a)+"}],identity=*}", b1p);
167 		assertMatchesGlob("{entries=[{type=A2,bean="+Utils.id(a2a)+"}],identity=*,parent={entries=[{type=A1,bean="+Utils.id(a1b)+"},{type=A1,bean="+Utils.id(a1a)+"}],identity=*}}", b1c);
168 		assertMatchesGlob("{entries=[{type=A1,bean="+Utils.id(a1b)+"},{type=A1,bean="+Utils.id(a1a)+"}],identity=*,threadSafe=true}", b2p);
169 		assertMatchesGlob("{entries=[{type=A2,bean="+Utils.id(a2a)+"}],identity=*,parent={entries=[{type=A1,bean="+Utils.id(a1b)+"},{type=A1,bean="+Utils.id(a1a)+"}],identity=*,threadSafe=true},threadSafe=true}", b2c);
170 
171 		b1p.removeBean(A1.class);
172 		b1c.clear().addBean(A1.class, a1a);
173 		b2p.removeBean(A1.class);
174 		b2c.clear().addBean(A1.class, a1a);
175 
176 		for (var b : array(b1p, b2p)) {
177 			assertFalse(b.hasBean(A1.class));
178 			assertEmpty(b.getBean(A1.class));
179 			assertEmpty(b.stream(A1.class));
180 		}
181 		for (var b : array(b1c, b2c)) {
182 			assertTrue(b.hasBean(A1.class));
183 			assertEquals(a1a, b.getBean(A1.class).get());
184 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1a);
185 		}
186 
187 		b1c.removeBean(A1.class);
188 		b2c.removeBean(A1.class);
189 		for (var b : array(b1p, b1c, b2p, b2c)) {
190 			assertFalse(b.hasBean(A1.class));
191 			assertEmpty(b.getBean(A1.class));
192 			assertEmpty(b.stream(A1.class));
193 		}
194 	}
195 
196 	@Test void a05_addNamedBeans() {
197 		var b1p = BeanStore.create().build();
198 		var b1c = BeanStore.of(b1p);
199 		var b2p = BeanStore.create().threadSafe().build();
200 		var b2c = BeanStore.create().threadSafe().parent(b2p).build();
201 
202 		for (var b : array(b1p, b2p)) {
203 			b.addBean(A1.class, a1a).addBean(A1.class, a1b, "foo").addBean(A1.class, a1c, "bar").addBean(A1.class, a1d, "bar").addBean(A2.class, a2a, "foo");
204 		}
205 		for (var b : array(b1c, b2c)) {
206 			b.addBean(A1.class, a1e);
207 		}
208 
209 		for (var b : array(b1p, b2p)) {
210 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1d,a1c,a1b,a1a);
211 		}
212 		for (var b : array(b1c, b2c)) {
213 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1e,a1d,a1c,a1b,a1a);
214 		}
215 
216 		for (var b : array(b1p, b1c, b2p, b2c)) {
217 			assertEquals(a1b, b.getBean(A1.class, "foo").get());
218 			assertEquals(a1d, b.getBean(A1.class, "bar").get());
219 			assertEmpty(b.getBean(A1.class, "baz"));
220 		}
221 		for (var b : array(b1p, b2p)) {
222 			assertEquals(a1a, b.getBean(A1.class, null).get());
223 		}
224 		for (var b : array(b1c, b2c)) {
225 			assertEquals(a1e, b.getBean(A1.class, null).get());
226 		}
227 	}
228 
229 	//-----------------------------------------------------------------------------------------------------------------
230 	// Parameter matching
231 	//-----------------------------------------------------------------------------------------------------------------
232 
233 	public static class B1 {
234 		A1 a1;
235 		Optional<A2> a2;
236 		BeanStore a3;
237 
238 		public B1(A1 a1, Optional<A2> a2, BeanStore a3) {
239 			this.a1 = a1;
240 			this.a2 = a2;
241 			this.a3 = a3;
242 		}
243 
244 		public B1(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
245 			this.a1 = a1;
246 			this.a2 = a2;
247 		}
248 
249 		public void m1(A1 a1, Optional<A2> a2, BeanStore a3) {
250 			this.a1 = a1;
251 			this.a2 = a2;
252 			this.a3 = a3;
253 		}
254 
255 		public void m2(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
256 			this.a1 = a1;
257 			this.a2 = a2;
258 			this.a3 = null;
259 		}
260 
261 		public static B1 m3(A1 a1, Optional<A2> a2, BeanStore a3) {
262 			return new B1(a1, a2, a3);
263 		}
264 	}
265 
266 	@Test void b01_getParams() {
267 
268 		Predicate<Object> pEmptyOptional = x -> !((Optional<?>)x).isPresent();
269 		Predicate<Object> pIsBeanStore = BeanStore.class::isInstance;
270 		Predicate<Object> pNull = x -> x == null;
271 		Predicate<Object> pA1a = x -> x==a1a;
272 		Predicate<Object> pA2a = x -> ((Optional<?>)x).get()==a2a;
273 
274 		var outer = new B1(null, null, null);
275 
276 		var b1p = BeanStore.create().outer(outer).build();
277 		var b1c = BeanStore.create().outer(outer).parent(b1p).build();
278 		var b2p = BeanStore.create().outer(outer).threadSafe().build();
279 		var b2c = BeanStore.create().outer(outer).parent(b1p).threadSafe().build();
280 
281 		var ci = ClassInfo.of(B1.class);
282 		var c1 = ci.getPublicConstructor(x -> x.hasParameterTypes(A1.class, Optional.class, BeanStore.class)).get();
283 		var c2 = ci.getPublicConstructor(x -> x.hasParameterTypes(A1.class, Optional.class)).get();
284 		var m1 = ci.getPublicMethod(x-> x.hasName("m1")).get();
285 		var m2 = ci.getPublicMethod(x-> x.hasName("m2")).get();
286 		var m3 = ci.getPublicMethod(x-> x.hasName("m3")).get();
287 
288 		for (var b : array(b1p, b1c, b2p, b2c)) {
289 			for (var e : array(c1, m1, m3)) {
290 				assertString(A1n, b.getMissingParams(e));
291 				assertFalse(b.hasAllParams(e));
292 			}
293 			for (var e : array(c2, m2)) {
294 				assertString(A1n+"@foo", b.getMissingParams(e));
295 				assertFalse(b.hasAllParams(e));
296 			}
297 		}
298 
299 		for (var b : array(b1p, b1c, b2p, b2c)) {
300 			assertList(b.getParams(c1), pNull, pEmptyOptional, pIsBeanStore);
301 			assertList(b.getParams(c2), pNull, pEmptyOptional);
302 			assertList(b.getParams(m1), pNull, pEmptyOptional, pIsBeanStore);
303 			assertList(b.getParams(m2), pNull, pEmptyOptional);
304 			assertList(b.getParams(m3), pNull, pEmptyOptional, pIsBeanStore);
305 		}
306 
307 		b1p.add(A1.class, a1a);
308 		b2p.add(A1.class, a1a);
309 		for (var b : array(b1p, b1c, b2p, b2c)) {
310 			assertNull(b.getMissingParams(c1));
311 			assertString(A1n+"@foo", b.getMissingParams(c2));
312 			assertNull(b.getMissingParams(m1));
313 			assertString(A1n+"@foo", b.getMissingParams(m2));
314 			assertNull(b.getMissingParams(m3));
315 			assertTrue(b.hasAllParams(c1));
316 			assertFalse(b.hasAllParams(c2));
317 			assertTrue(b.hasAllParams(m1));
318 			assertFalse(b.hasAllParams(m2));
319 			assertTrue(b.hasAllParams(m3));
320 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
321 			assertList(b.getParams(c2), pNull, pEmptyOptional);
322 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
323 			assertList(b.getParams(m2), pNull, pEmptyOptional);
324 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
325 		}
326 
327 		b1p.add(A1.class, a1a, "foo");
328 		b2p.add(A1.class, a1a, "foo");
329 		for (var b : array(b1p, b1c, b2p, b2c)) {
330 			for (var e : array(c1, c2, m1, m2, m3)) {
331 				assertNull(b.getMissingParams(e));
332 				assertTrue(b.hasAllParams(e));
333 			}
334 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
335 			assertList(b.getParams(c2), pA1a, pEmptyOptional);
336 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
337 			assertList(b.getParams(m2), pA1a, pEmptyOptional);
338 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
339 		}
340 
341 		b1p.add(A1.class, a1b, "bar");
342 		b2p.add(A1.class, a1b, "bar");
343 		for (var b : array(b1p, b1c, b2p, b2c)) {
344 			for (var e : array(c1, c2, m1, m2, m3)) {
345 				assertNull(b.getMissingParams(e));
346 				assertTrue(b.hasAllParams(e));
347 			}
348 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
349 			assertList(b.getParams(c2), pA1a, pEmptyOptional);
350 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
351 			assertList(b.getParams(m2), pA1a, pEmptyOptional);
352 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
353 		}
354 
355 		b1p.add(A2.class, a2a, "bar");
356 		b2p.add(A2.class, a2a, "bar");
357 		for (var b : array(b1p, b1c, b2p, b2c)) {
358 			for (var e : array(c1, c2, m1, m2, m3)) {
359 				assertNull(b.getMissingParams(e));
360 				assertTrue(b.hasAllParams(e));
361 			}
362 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
363 			assertList(b.getParams(c2), pA1a, pA2a);
364 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
365 			assertList(b.getParams(m2), pA1a, pA2a);
366 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
367 		}
368 
369 		b1p.add(A2.class, a2a, null);
370 		b2p.add(A2.class, a2a, null);
371 		for (var b : array(b1p, b1c, b2p, b2c)) {
372 			for (var e : array(c1, c2, m1, m2, m3)) {
373 				assertNull(b.getMissingParams(e));
374 				assertTrue(b.hasAllParams(e));
375 			}
376 			assertList(b.getParams(c1), pA1a, pA2a, pIsBeanStore);
377 			assertList(b.getParams(c2), pA1a, pA2a);
378 			assertList(b.getParams(m1), pA1a, pA2a, pIsBeanStore);
379 			assertList(b.getParams(m2), pA1a, pA2a);
380 			assertList(b.getParams(m3), pA1a, pA2a, pIsBeanStore);
381 		}
382 	}
383 
384 	public class B2 {
385 		A1 a1;
386 		Optional<A2> a2;
387 		BeanStore a3;
388 
389 		public B2(A1 a1, Optional<A2> a2, BeanStore a3) {
390 			this.a1 = a1;
391 			this.a2 = a2;
392 			this.a3 = a3;
393 		}
394 
395 		public B2(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
396 			this.a1 = a1;
397 			this.a2 = a2;
398 		}
399 	}
400 
401 	@Test void b02_getParams_innerClass() {
402 
403 		Predicate<Object> pEmptyOptional = x -> !((Optional<?>)x).isPresent();
404 		Predicate<Object> pIsBeanStore = BeanStore.class::isInstance;
405 		Predicate<Object> pNull = x -> x == null;
406 		Predicate<Object> pThis = x -> x == this;
407 		Predicate<Object> pA1a = x -> x==a1a;
408 		Predicate<Object> pA2a = x -> ((Optional<?>)x).get()==a2a;
409 
410 		var b1p = BeanStore.create().outer(this).build();
411 		var b1c = BeanStore.create().outer(this).parent(b1p).build();
412 		var b2p = BeanStore.create().outer(this).threadSafe().build();
413 		var b2c = BeanStore.create().outer(this).parent(b1p).threadSafe().build();
414 
415 		var ci = ClassInfo.of(B2.class);
416 		var c1 = ci.getPublicConstructor(x -> x.hasParameterTypes(BeanStore_Test.class, A1.class, Optional.class, BeanStore.class)).get();
417 		var c2 = ci.getPublicConstructor(x -> x.hasParameterTypes(BeanStore_Test.class, A1.class, Optional.class)).get();
418 
419 		for (var b : array(b1p, b1c, b2p, b2c)) {
420 			assertString(A1n, b.getMissingParams(c1));
421 			assertString(A1n+"@foo", b.getMissingParams(c2));
422 			assertFalse(b.hasAllParams(c1));
423 			assertFalse(b.hasAllParams(c2));
424 		}
425 
426 		for (var b : array(b1p, b1c, b2p, b2c)) {
427 			assertList(b.getParams(c1), pThis, pNull, pEmptyOptional, pIsBeanStore);
428 			assertList(b.getParams(c2), pThis, pNull, pEmptyOptional);
429 		}
430 
431 		b1p.add(A1.class, a1a);
432 		b2p.add(A1.class, a1a);
433 		for (var b : array(b1p, b1c, b2p, b2c)) {
434 			assertNull(b.getMissingParams(c1));
435 			assertString(A1n+"@foo", b.getMissingParams(c2));
436 			assertTrue(b.hasAllParams(c1));
437 			assertFalse(b.hasAllParams(c2));
438 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
439 			assertList(b.getParams(c2), pThis, pNull, pEmptyOptional);
440 		}
441 
442 		b1p.add(A1.class, a1a, "foo");
443 		b2p.add(A1.class, a1a, "foo");
444 		for (var b : array(b1p, b1c, b2p, b2c)) {
445 			for (var e : array(c1, c2)) {
446 				assertNull(b.getMissingParams(e));
447 				assertTrue(b.hasAllParams(e));
448 			}
449 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
450 			assertList(b.getParams(c2), pThis, pA1a, pEmptyOptional);
451 		}
452 
453 		b1p.add(A1.class, a1b, "bar");
454 		b2p.add(A1.class, a1b, "bar");
455 		for (var b : array(b1p, b1c, b2p, b2c)) {
456 			for (var e : array(c1, c2)) {
457 				assertNull(b.getMissingParams(e));
458 				assertTrue(b.hasAllParams(e));
459 			}
460 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
461 			assertList(b.getParams(c2), pThis, pA1a, pEmptyOptional);
462 		}
463 
464 		b1p.add(A2.class, a2a, "bar");
465 		b2p.add(A2.class, a2a, "bar");
466 		for (var b : array(b1p, b1c, b2p, b2c)) {
467 			for (var e : array(c1, c2)) {
468 				assertNull(b.getMissingParams(e));
469 				assertTrue(b.hasAllParams(e));
470 			}
471 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
472 			assertList(b.getParams(c2), pThis, pA1a, pA2a);
473 		}
474 
475 		b1p.add(A2.class, a2a, null);
476 		b2p.add(A2.class, a2a, null);
477 		for (var b : array(b1p, b1c, b2p, b2c)) {
478 			for (var e : array(c1, c2)) {
479 				assertNull(b.getMissingParams(e));
480 				assertTrue(b.hasAllParams(e));
481 			}
482 			assertList(b.getParams(c1), pThis, pA1a, pA2a, pIsBeanStore);
483 			assertList(b.getParams(c2), pThis, pA1a, pA2a);
484 		}
485 	}
486 
487 	//-----------------------------------------------------------------------------------------------------------------
488 	// createMethodFinder()
489 	//-----------------------------------------------------------------------------------------------------------------
490 
491 	public static class C {
492 		public A1 a;
493 	}
494 
495 	@Test void c00_createMethodFinder_invalidArgs() {
496 		var b = BeanStore.create().build();
497 		assertThrowsWithMessage(IllegalArgumentException.class, "Method cannot be used without outer bean definition.", ()->b.createMethodFinder(null));
498 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'beanType' cannot be null.", ()->b.createMethodFinder((Class<?>)null,""));
499 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'resourceClass' cannot be null.", ()->b.createMethodFinder(String.class,null));
500 	}
501 
502 	// Instance methods.
503 	public static class C1 {
504 		public C createA1() { return new C(); }
505 		public A1 createA2() { return new A1(); }
506 		protected C createA3() { return new C(); }
507 		@Deprecated public C createA4() { return new C(); }
508 		@BeanIgnore public C createA5() { return new C(); }
509 		public C createA6() { return null; }
510 		public C createA7() { throw new RuntimeException("foo"); }
511 	}
512 
513 	// Static methods.
514 	public static class C2 {
515 		public static C createB1() { return new C(); }
516 		public static A1 createB2() { return new A1(); }
517 		protected static C createB3() { return new C(); }
518 		@Deprecated public static C createB4() { return new C(); }
519 		@BeanIgnore public static C createB5() { return new C(); }
520 		public static C createB6() { return null; }
521 		public static C createB7() { throw new RuntimeException("foo"); }
522 	}
523 
524 	// Bean matching.
525 	public static class C3 {
526 		public C createC1(A1 a) { return new C(); }
527 		public static C createC2(A1 a) { return new C(); }
528 		public static C createC3(Optional<A1> a) { C e = new C(); e.a = a.orElse(null); return e; }
529 		public static C createC4(@Named("Foo") A1 a) { return new C(); }
530 		public static C createC5(@Named("Foo") Optional<A1> a) { C e = new C(); e.a = a.orElse(null); return e; }
531 		public static C createC6(BeanStore bs) { assertNotNull(bs); return new C(); }
532 	}
533 
534 
535 	//-----------------------------------------------------------------------------------------------------------------
536 	// createBean()
537 	//-----------------------------------------------------------------------------------------------------------------
538 
539 	public static class D1a {}
540 	public class D1b {}
541 
542 	@Test void d01_createBean_basic() {
543 		var bs = BeanStore.create().outer(new BeanStore_Test()).build();
544 		assertNotNull(bs.createBean(D1a.class).run());
545 		assertNotNull(bs.createBean(D1b.class).run());
546 		assertThrows(IllegalArgumentException.class, () -> bs.createBean(null).run());
547 	}
548 
549 	public static class D2 {
550 		public static D2 create() { return d2; }
551 	}
552 	public static D2 d2 = new D2();
553 
554 	@Test void d02_createBean_staticCreator_create() {
555 		var bs = BeanStore.INSTANCE;
556 		assertEquals(d2, bs.createBean(D2.class).run());
557 	}
558 
559 	public abstract static class D3 {
560 		public static D3 getInstance() { return d3; }
561 	}
562 	public static D3 d3 = new D3() {};
563 
564 	@Test void d03_createBean_staticCreator_getInstance() {
565 		var bs = BeanStore.INSTANCE;
566 		assertEquals(d3, bs.createBean(D3.class).run());
567 	}
568 
569 	public static class D4a {
570 		public static D4a getFoo() { return d4a1; }
571 		protected static D4a create() { return d4a2; }
572 		public static D4a create(String foo) { return d4a3; }
573 		@Deprecated protected static D4a getInstance() { return d4a4; }
574 		protected D4a() {}
575 	}
576 	public static D4a d4a1 = new D4a(), d4a2 = new D4a(), d4a3 = new D4a(), d4a4 = new D4a();
577 
578 	public static class D4b {
579 		public D4b create() { return d4b1; }
580 		@BeanIgnore public static D4b getInstance() { return d4b2; }
581 		protected D4b() {}
582 	}
583 	public static D4b d4b1 = new D4b(), d4b2 = new D4b();
584 
585 	public static class D4c {
586 		public static String create() { return null; }
587 		protected D4c() {}
588 	}
589 
590 	@Test void d04_createBean_staticCreator_invalidSignatures() {
591 		var bs = BeanStore.INSTANCE;
592 		assertNotEqualsAny(bs.createBean(D4a.class).run(), d4a1, d4a2, d4a3, d4a4);
593 		assertNotEqualsAny(bs.createBean(D4b.class).run(), d4b1, d4b2);
594 		assertNotNull(bs.createBean(D4c.class).run());
595 	}
596 
597 	public static class D5 {
598 		public static D5 create() { return d5a; }
599 		public static D5 create(Integer i, String s) { return d5b; }
600 		public static D5 create(Integer i) { return d5c; }
601 		protected D5() {}
602 	}
603 	public static D5 d5a = new D5(), d5b = new D5(), d5c = new D5();
604 
605 	@Test void d05_createBean_staticCreator_withBeans() {
606 		var bs = BeanStore.create().build();
607 		assertEquals(d5a, bs.createBean(D5.class).run());
608 		bs.add(Integer.class, 1);
609 		assertEquals(d5c, bs.createBean(D5.class).run());
610 		bs.add(String.class, "x");
611 		assertEquals(d5b, bs.createBean(D5.class).run());
612 		bs.removeBean(Integer.class);
613 		assertEquals(d5a, bs.createBean(D5.class).run());
614 	}
615 
616 	public static class D6 {
617 		public String s;
618 		public static D6 create() { return new D6("0"); }
619 		protected D6(String s) {
620 			this.s = s;
621 		}
622 	}
623 
624 	@Test void d06_createBean_staticCreator_ignoredWithBuilder() {
625 		var bs = BeanStore.INSTANCE;
626 		assertString("1", bs.createBean(D6.class).builder(String.class, "1").run().s);
627 	}
628 
629 	public static class D7 {
630 		public String a;
631 		public static D7 create(Optional<String> s) { return new D7(s.orElse("X")); }
632 		protected D7(String s) { a = s; }
633 	}
634 
635 	@Test void d07_createBean_staticCreator_withOptional() {
636 		var bs = BeanStore.create().build();
637 		assertString("X", bs.createBean(D7.class).run().a);
638 		bs.add(String.class, "bar");
639 		assertString("bar", bs.createBean(D7.class).run().a);
640 	}
641 
642 	public static class D8 {
643 		public String a = "foo";
644 		public static D8 create(Optional<String> s, Integer i) { return new D8(s.orElse(null) + "," + i); }
645 		private D8(String s) { a = s; }
646 	}
647 
648 	@Test void d08_createBean_staticCreator_missingPrereqs() {
649 		var bs = BeanStore.create().build();
650 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class org.apache.juneau.cp.BeanStore_Test$D8: Static creator found but could not find prerequisites: Integer.", ()->bs.createBean(D8.class).run());
651 		bs.add(Integer.class, 1);
652 		assertString("null,1", bs.createBean(D8.class).run().a);
653 		bs.add(String.class, "bar");
654 		assertString("bar,1", bs.createBean(D8.class).run().a);
655 	}
656 
657 	public abstract static class D9a {
658 		public D9a() {}
659 	}
660 
661 	public interface D9b {}
662 
663 	@Test void d09_createBean_staticCreator_withBeans() {
664 		var bs = BeanStore.INSTANCE;
665 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D9a.class.getName()+": Class is abstract.", ()->bs.createBean(D9a.class).run());
666 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D9b.class.getName()+": Class is an interface.", ()->bs.createBean(D9b.class).run());
667 	}
668 
669 	public static class D10 {
670 		public String a;
671 		public D10(String s) { a = "s="+s; }
672 		public D10(Integer i) { a = "i="+i; }
673 		public D10(String s, Integer i) { a = "s="+s+",i="+i; }
674 	}
675 
676 	@Test void d10_createBean_constructors_public() {
677 		var bs = BeanStore.create().build();
678 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D10.class.getName()+": Public constructor found but could not find prerequisites: Integer or Integer,String or String.", ()->bs.createBean(D10.class).run());
679 		bs.add(String.class, "foo");
680 		assertString("s=foo", bs.createBean(D10.class).run().a);
681 		bs.add(Integer.class, 1);
682 		assertString("s=foo,i=1", bs.createBean(D10.class).run().a);
683 		bs.removeBean(String.class);
684 		assertString("i=1", bs.createBean(D10.class).run().a);
685 	}
686 
687 	public static class D11 {
688 		public String a;
689 		protected D11(String s) { a = "s="+s; }
690 		protected D11(Integer i) { a = "i="+i; }
691 		protected D11(String s, Integer i) { a = "s="+s+",i="+i; }
692 	}
693 
694 	@Test void d11_createBean_constructors_protected() {
695 		var bs = BeanStore.create().build();
696 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D11.class.getName()+": Protected constructor found but could not find prerequisites: Integer or Integer,String or String.", ()->bs.createBean(D11.class).run());
697 		bs.add(String.class, "foo");
698 		assertString("s=foo", bs.createBean(D11.class).run().a);
699 		bs.add(Integer.class, 1);
700 		assertString("s=foo,i=1", bs.createBean(D11.class).run().a);
701 		bs.removeBean(String.class);
702 		assertString("i=1", bs.createBean(D11.class).run().a);
703 	}
704 
705 	public static class D12 {
706 		public String a;
707 		public D12(String s) { a = "s="+s; }
708 		protected D12(String s, Integer i) { a = "s="+s+",i="+i; }
709 	}
710 
711 	@Test void d12_createBean_constructors_publicOverProtected() {
712 		var bs = BeanStore.create().build();
713 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D12.class.getName()+": Public constructor found but could not find prerequisites: String.", ()->bs.createBean(D12.class).run());
714 		bs.add(String.class, "foo");
715 		bs.add(Integer.class, 1);
716 		assertString("s=foo", bs.createBean(D12.class).run().a);
717 	}
718 
719 	public static class D13 {
720 		private D13() {}
721 	}
722 
723 	@Test void d13_createBean_constructors_private() {
724 		var bs = BeanStore.INSTANCE;
725 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D13.class.getName()+": No public/protected constructors found.", ()->bs.createBean(D13.class).run());
726 	}
727 
728 	public static class D14 {
729 		public String a;
730 		public D14(@Named("foo") String o) { a = o; }
731 		public D14(@Named("foo") String o, Integer i) { a = o + "," + i; }
732 	}
733 
734 	@Test void d14_createBean_constructors_namedBean() {
735 		var bs = BeanStore.create().build();
736 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D14.class.getName()+": Public constructor found but could not find prerequisites: Integer,String@foo or String@foo.", ()->bs.createBean(D14.class).run());
737 		bs.add(String.class, "bar", "foo");
738 		assertString("bar", bs.createBean(D14.class).run().a);
739 	}
740 
741 	public class D15 {
742 		public String a;
743 		public D15(@Named("foo") String o) { a = o; }
744 		public D15(@Named("foo") String o, Integer i) { a = o + "," + i; }
745 	}
746 
747 	@Test void d15_createBean_constructors_namedBean_withOuter() {
748 		var bs = BeanStore.create().outer(new BeanStore_Test()).build();
749 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D15.class.getName()+": Public constructor found but could not find prerequisites: Integer,String@foo or String@foo.", ()->bs.createBean(D15.class).run());
750 		bs.add(String.class, "bar", "foo");
751 		assertString("bar", bs.createBean(D15.class).run().a);
752 	}
753 
754 	public static class D16 {
755 		public String a;
756 		public static Builder create() { return new Builder(); }
757 		public static class Builder {
758 			public String b;
759 		}
760 		protected D16(Builder b) { a = b.b; }
761 	}
762 
763 	@Test void d16_createBean_builders() {
764 		var bs = BeanStore.create().build();
765 		var b = D16.create();
766 		b.b = "foo";
767 		assertString("foo", bs.createBean(D16.class).builder(D16.Builder.class, b).run().a);
768 	}
769 
770 	public static class D17 {
771 		public String a;
772 		public static Builder create() { return new Builder(); }
773 		public static class Builder {
774 			public String b;
775 		}
776 		protected D17(Builder b, Integer i) { a = b.b; }
777 		protected D17(Integer i) {}  // NOSONAR
778 		D17(String s) {}  // NOSONAR
779 		protected D17(Builder b) { a = b.b; }
780 	}
781 
782 	@Test void d17_createBean_builders_inherent() {
783 		var bs = BeanStore.create().build();
784 		assertNull(bs.createBean(D17.class).run().a);
785 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D17.class.getName()+": Protected constructor found but could not find prerequisites: Builder or Builder,Integer or Integer.", ()->bs.createBean(D17.class).builder(Boolean.class, true).run());
786 	}
787 
788 	//-----------------------------------------------------------------------------------------------------------------
789 	// Helpers
790 	//-----------------------------------------------------------------------------------------------------------------
791 
792 	@SafeVarargs
793 	private static <T> T[] array(T...t) {
794 		return t;
795 	}
796 }