001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau; 014 015import static org.apache.juneau.internal.ClassUtils.*; 016import static org.apache.juneau.internal.CollectionUtils.*; 017import static org.apache.juneau.internal.ConsumerUtils.*; 018import static org.apache.juneau.collections.JsonMap.*; 019import static org.apache.juneau.common.internal.ThrowableUtils.*; 020 021import java.lang.annotation.Annotation; 022import java.lang.reflect.*; 023import java.util.*; 024import java.util.concurrent.*; 025import java.util.function.*; 026 027import org.apache.juneau.annotation.*; 028import org.apache.juneau.collections.*; 029import org.apache.juneau.csv.annotation.*; 030import org.apache.juneau.html.annotation.*; 031import org.apache.juneau.internal.*; 032import org.apache.juneau.json.annotation.*; 033import org.apache.juneau.jsonschema.annotation.*; 034import org.apache.juneau.msgpack.annotation.*; 035import org.apache.juneau.oapi.annotation.*; 036import org.apache.juneau.parser.annotation.*; 037import org.apache.juneau.plaintext.annotation.*; 038import org.apache.juneau.reflect.*; 039import org.apache.juneau.serializer.annotation.*; 040import org.apache.juneau.soap.annotation.*; 041import org.apache.juneau.svl.*; 042import org.apache.juneau.uon.annotation.*; 043import org.apache.juneau.urlencoding.annotation.*; 044import org.apache.juneau.utils.*; 045import org.apache.juneau.xml.annotation.*; 046 047/** 048 * Base class for all Context beans. 049 * 050 * <p> 051 * Context beans follow the convention of havinTg the following parts: 052 * <ul> 053 * <li>A {@link Builder} class for configuring the context bean. 054 * <ul> 055 * <li>This bean is non-thread-safe and meant for one-time use. 056 * </ul> 057 * <li>A {@link Context#Context(Builder)} constructor that takes in a builder object. 058 * <ul> 059 * <li>This bean is thread-safe and cacheable/reusable. 060 * </ul> 061 * <li>A {@link ContextSession} class for doing work. 062 * <ul> 063 * <li>This bean is non-thread-safe and meant for one-time use. 064 * </ul> 065 * 066 * <h5 class='section'>Notes:</h5><ul> 067 * <li class='note'>This class is thread safe and reusable. 068 * </ul> 069 * 070 * <h5 class='section'>See Also:</h5><ul> 071 * </ul> 072 */ 073public abstract class Context implements AnnotationProvider { 074 075 //----------------------------------------------------------------------------------------------------------------- 076 // Static 077 //----------------------------------------------------------------------------------------------------------------- 078 079 private static final Map<Class<?>,MethodInfo> BUILDER_CREATE_METHODS = new ConcurrentHashMap<>(); 080 081 /** 082 * Predicate for annotations that themselves are annotated with {@link ContextApply}. 083 */ 084 public static final Predicate<AnnotationInfo<?>> CONTEXT_APPLY_FILTER = x -> x.hasAnnotation(ContextApply.class); 085 086 /** 087 * Instantiates a builder of the specified context class. 088 * 089 * <p> 090 * Looks for a public static method called <c>create</c> that returns an object that can be passed into a public 091 * or protected constructor of the class. 092 * 093 * @param type The builder to create. 094 * @return A new builder. 095 */ 096 public static Builder createBuilder(Class<? extends Context> type) { 097 try { 098 MethodInfo mi = BUILDER_CREATE_METHODS.get(type); 099 if (mi == null) { 100 ClassInfo c = ClassInfo.of(type); 101 for (ConstructorInfo ci : c.getPublicConstructors()) { 102 if (ci.matches(x -> x.hasNumParams(1) && ! x.getParam(0).getParameterType().is(type))) { 103 mi = c.getPublicMethod( 104 x -> x.isStatic() 105 && x.isNotDeprecated() 106 && x.hasName("create") 107 && x.hasReturnType(ci.getParam(0).getParameterType()) 108 ); 109 if (mi != null) 110 break; 111 } 112 } 113 if (mi == null) 114 throw new BasicRuntimeException("Could not find builder create method on class {0}", type); 115 BUILDER_CREATE_METHODS.put(type, mi); 116 } 117 Builder b = (Builder)mi.invoke(null); 118 b.type(type); 119 return b; 120 } catch (ExecutableException e) { 121 throw asRuntimeException(e); 122 } 123 } 124 125 //----------------------------------------------------------------------------------------------------------------- 126 // Builder 127 //----------------------------------------------------------------------------------------------------------------- 128 129 /** 130 * Builder class. 131 */ 132 @FluentSetters 133 public static abstract class Builder { 134 135 private static final Map<Class<?>,ConstructorInfo> CONTEXT_CONSTRUCTORS = new ConcurrentHashMap<>(); 136 137 boolean debug; 138 Class<? extends Context> type; 139 Context impl; 140 List<Annotation> annotations; 141 Cache<HashKey,? extends Context> cache; 142 143 private final List<Object> builders = list(); 144 private final AnnotationWorkList applied = AnnotationWorkList.create(); 145 146 /** 147 * Constructor. 148 * Default settings. 149 */ 150 @SuppressWarnings("unchecked") 151 protected Builder() { 152 debug = env("Context.debug", false); 153 annotations = null; 154 registerBuilders(this); 155 156 // By default, the type being created should be the class declaring the builder. 157 Class<?> dc = getClass().getDeclaringClass(); 158 if (Context.class.isAssignableFrom(dc)) 159 type((Class<? extends Context>)dc); 160 } 161 162 /** 163 * Copy constructor. 164 * 165 * @param copyFrom The bean to copy from. 166 */ 167 protected Builder(Context copyFrom) { 168 debug = copyFrom.debug; 169 type = copyFrom.getClass(); 170 annotations = listFrom(copyFrom.annotations, true); 171 registerBuilders(this); 172 } 173 174 /** 175 * Copy constructor. 176 * 177 * @param copyFrom The builder to copy from. 178 */ 179 protected Builder(Builder copyFrom) { 180 debug = copyFrom.debug; 181 type = copyFrom.type; 182 annotations = listFrom(copyFrom.annotations, true); 183 registerBuilders(this); 184 } 185 186 private Context innerBuild() { 187 if (type == null) 188 throw new BasicRuntimeException("Type not specified for context builder {0}", getClass().getName()); 189 if (impl != null && type.isInstance(impl)) 190 return type.cast(impl); 191 if (cache != null) 192 return cache.get(hashKey(), ()->getContextConstructor().invoke(this)); 193 return getContextConstructor().invoke(this); 194 } 195 196 private ConstructorInfo getContextConstructor() { 197 ConstructorInfo cci = CONTEXT_CONSTRUCTORS.get(type); 198 if (cci == null) { 199 cci = ClassInfo.of(type).getPublicConstructor( 200 x -> x.hasNumParams(1) 201 && x.getParam(0).canAccept(this) 202 ); 203 if (cci == null) 204 throw new BasicRuntimeException("Public constructor not found: {0}({1})", className(type), className(this)); 205 CONTEXT_CONSTRUCTORS.put(type, cci); 206 } 207 return cci; 208 } 209 210 /** 211 * Copy creator. 212 * 213 * @return A new mutable copy of this builder. 214 */ 215 public abstract Builder copy(); 216 217 /** 218 * Build the object. 219 * 220 * @return The built object. 221 */ 222 public Context build() { 223 return innerBuild(); 224 } 225 226 /** 227 * Returns the hashkey of this builder. 228 * 229 * <p> 230 * Used to return previously instantiated context beans that have matching hashkeys. 231 * The {@link HashKey} object is suitable for use as a hashmap key of a map of context beans. 232 * A context bean is considered equivalent if the {@link HashKey#equals(Object)} method is the same. 233 * 234 * @return The hashkey of this builder. 235 */ 236 public HashKey hashKey() { 237 return HashKey.of(debug, type, annotations); 238 } 239 240 /** 241 * Specifies a cache to use for hashkey-based caching. 242 * 243 * @param value The cache. 244 * @return This object. 245 */ 246 @FluentSetter 247 public Builder cache(Cache<HashKey,? extends Context> value) { 248 this.cache = value; 249 return this; 250 } 251 252 /** 253 * Convenience method for calling {@link #build()} while avoiding a cast. 254 * 255 * @param <T> The type to cast the built object to. 256 * @param c The type to cast the built object to. 257 * @return The built context bean. 258 */ 259 @SuppressWarnings("unchecked") 260 public final <T extends Context> T build(Class<T> c) { 261 if (type == null || ! c.isAssignableFrom(type)) 262 type = c; 263 return (T)innerBuild(); 264 } 265 266 /** 267 * Apply a consumer to this builder. 268 * 269 * @param <T> The builder subtype that this consumer can be applied to. 270 * @param subtype The builder subtype that this consumer can be applied to. 271 * @param consumer The consumer. 272 * @return This object. 273 */ 274 public <T extends Builder> Builder apply(Class<T> subtype, Consumer<T> consumer) { 275 if (subtype.isInstance(this)) 276 consumer.accept(subtype.cast(this)); 277 return this; 278 } 279 280 /** 281 * Associates a context class with this builder. 282 * 283 * <p> 284 * This is the type of object that this builder creates when the {@link #build()} method is called. 285 * 286 * <p> 287 * By default, it's the outer class of where the builder class is defined. 288 * 289 * @param value The context class that this builder should create. 290 * @return This object. 291 */ 292 @FluentSetter 293 public Builder type(Class<? extends Context> value) { 294 this.type = value; 295 return this; 296 } 297 298 /** 299 * Returns the context class that this builder should create. 300 * 301 * @return The context class if it was specified. 302 */ 303 public Optional<Class<?>> getType() { 304 return optional(type); 305 } 306 307 /** 308 * Specifies a pre-instantiated bean for the {@link #build()} method to return. 309 * 310 * @param value The value for this setting. 311 * @return This object. 312 */ 313 @FluentSetter 314 public Builder impl(Context value) { 315 impl = value; 316 return this; 317 } 318 319 /** 320 * Returns <jk>true</jk> if any of the annotations/appliers can be applied to this builder. 321 * 322 * @param work The work to check. 323 * @return <jk>true</jk> if any of the annotations/appliers can be applied to this builder. 324 */ 325 public boolean canApply(AnnotationWorkList work) { 326 Flag f = Flag.create(); 327 work.forEach(x -> builders.forEach(b -> f.setIf(x.canApply(b)))); 328 return f.isSet(); 329 } 330 331 /** 332 * Applies a set of applied to this builder. 333 * 334 * <p> 335 * An {@link AnnotationWork} consists of a single pair of {@link AnnotationInfo} that represents an annotation instance, 336 * and {@link AnnotationApplier} which represents the code used to apply the values in that annotation to a specific builder. 337 * 338 * <h5 class='section'>Example:</h5> 339 * <p class='bjava'> 340 * <jc>// A class annotated with a config annotation.</jc> 341 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 342 * <jk>public class</jk> MyClass {...} 343 * 344 * <jc>// Find all annotations that themselves are annotated with @ContextPropertiesApply.</jc> 345 * AnnotationList <jv>annotations</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getAnnotationList(<jsf>CONTEXT_APPLY_FILTER</jsf>); 346 * VarResolverSession <jv>vrs</jv> = VarResolver.<jsf>DEFAULT</jsf>.createSession(); 347 * AnnotationWorkList <jv>work</jv> = AnnotationWorkList.of(<jv>vrs</jv>, <jv>annotations</jv>); 348 * 349 * <jc>// Apply any settings found on the annotations.</jc> 350 * WriterSerializer <jv>serializer</jv> = JsonSerializer 351 * .<jsm>create</jsm>() 352 * .apply(<jv>work</jv>) 353 * .build(); 354 * </p> 355 * 356 * @param work The list of annotations and appliers to apply to this builder. 357 * @return This object. 358 */ 359 @FluentSetter 360 public Builder apply(AnnotationWorkList work) { 361 applied.addAll(work); 362 work.forEach(x -> builders.forEach(y -> x.apply(y))); 363 return this; 364 } 365 366 /** 367 * Returns all the annotations that have been applied to this builder. 368 * 369 * @return All the annotations that have been applied to this builder. 370 */ 371 public AnnotationWorkList getApplied() { 372 return applied; 373 } 374 375 /** 376 * Registers the specified secondary builders with this context builder. 377 * 378 * <p> 379 * When {@link #apply(AnnotationWorkList)} is called, it gets called on all registered builders. 380 * 381 * @param builders The builders to add to the list of builders. 382 */ 383 protected void registerBuilders(Object...builders) { 384 for (Object b : builders) { 385 if (b == this) 386 this.builders.add(b); 387 else if (b instanceof Builder) 388 this.builders.addAll(((Builder)b).builders); 389 else 390 this.builders.add(b); 391 } 392 } 393 394 /** 395 * Applies any of the various <ja>@XConfig</ja> annotations on the specified class to this context. 396 * 397 * <p> 398 * Any annotations found that themselves are annotated with {@link ContextApply} will be resolved and 399 * applied as properties to this builder. These annotations include: 400 * <ul class='javatreec'> 401 * <li class ='ja'>{@link BeanConfig} 402 * <li class ='ja'>{@link CsvConfig} 403 * <li class ='ja'>{@link HtmlConfig} 404 * <li class ='ja'>{@link HtmlDocConfig} 405 * <li class ='ja'>{@link JsonConfig} 406 * <li class ='ja'>{@link JsonSchemaConfig} 407 * <li class ='ja'>{@link MsgPackConfig} 408 * <li class ='ja'>{@link OpenApiConfig} 409 * <li class ='ja'>{@link ParserConfig} 410 * <li class ='ja'>{@link PlainTextConfig} 411 * <li class ='ja'>{@link SerializerConfig} 412 * <li class ='ja'>{@link SoapXmlConfig} 413 * <li class ='ja'>{@link UonConfig} 414 * <li class ='ja'>{@link UrlEncodingConfig} 415 * <li class ='ja'>{@link XmlConfig} 416 * <li class ='ja'><c>RdfConfig</c> 417 * </ul> 418 * 419 * <p> 420 * Annotations on classes are appended in the following order: 421 * <ol> 422 * <li>On the package of this class. 423 * <li>On interfaces ordered parent-to-child. 424 * <li>On parent classes ordered parent-to-child. 425 * <li>On this class. 426 * </ol> 427 * 428 * <p> 429 * The default var resolver {@link VarResolver#DEFAULT} is used to resolve any variables in annotation field values. 430 * 431 * <h5 class='section'>Example:</h5> 432 * <p class='bjava'> 433 * <jc>// A class annotated with a config annotation.</jc> 434 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 435 * <jk>public class</jk> MyClass {...} 436 * 437 * <jc>// Apply any settings found on the annotations.</jc> 438 * WriterSerializer <jv>serializer</jv> = JsonSerializer 439 * .<jsm>create</jsm>() 440 * .applyAnnotations(MyClass.<jk>class</jk>) 441 * .build(); 442 * </p> 443 * 444 * @param fromClasses The classes on which the annotations are defined. 445 * @return This object. 446 */ 447 @FluentSetter 448 public Builder applyAnnotations(Class<?>...fromClasses) { 449 AnnotationWorkList work = AnnotationWorkList.create(); 450 for (Class<?> c : fromClasses) 451 work.add(ClassInfo.of(c).getAnnotationList(CONTEXT_APPLY_FILTER)); 452 return apply(work); 453 } 454 455 /** 456 * Applies any of the various <ja>@XConfig</ja> annotations on the specified method to this context. 457 * 458 * <p> 459 * Any annotations found that themselves are annotated with {@link ContextApply} will be resolved and 460 * applied as properties to this builder. These annotations include: 461 * <ul class='javatreec'> 462 * <li class ='ja'>{@link BeanConfig} 463 * <li class ='ja'>{@link CsvConfig} 464 * <li class ='ja'>{@link HtmlConfig} 465 * <li class ='ja'>{@link HtmlDocConfig} 466 * <li class ='ja'>{@link JsonConfig} 467 * <li class ='ja'>{@link JsonSchemaConfig} 468 * <li class ='ja'>{@link MsgPackConfig} 469 * <li class ='ja'>{@link OpenApiConfig} 470 * <li class ='ja'>{@link ParserConfig} 471 * <li class ='ja'>{@link PlainTextConfig} 472 * <li class ='ja'>{@link SerializerConfig} 473 * <li class ='ja'>{@link SoapXmlConfig} 474 * <li class ='ja'>{@link UonConfig} 475 * <li class ='ja'>{@link UrlEncodingConfig} 476 * <li class ='ja'>{@link XmlConfig} 477 * <li class ='ja'><c>RdfConfig</c> 478 * </ul> 479 * 480 * <p> 481 * Annotations on methods are appended in the following order: 482 * <ol> 483 * <li>On the package of the method class. 484 * <li>On interfaces ordered parent-to-child. 485 * <li>On parent classes ordered parent-to-child. 486 * <li>On the method class. 487 * <li>On this method and matching methods ordered parent-to-child. 488 * </ol> 489 * 490 * <p> 491 * The default var resolver {@link VarResolver#DEFAULT} is used to resolve any variables in annotation field values. 492 * 493 * <h5 class='section'>Example:</h5> 494 * <p class='bjava'> 495 * <jc>// A method annotated with a config annotation.</jc> 496 * <jk>public class</jk> MyClass { 497 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 498 * <jk>public void</jk> myMethod() {...} 499 * } 500 * 501 * <jc>// Apply any settings found on the annotations.</jc> 502 * WriterSerializer <jv>serializer</jv> = JsonSerializer 503 * .<jsm>create</jsm>() 504 * .applyAnnotations(MyClass.<jk>class</jk>.getMethod(<js>"myMethod"</js>)) 505 * .build(); 506 * </p> 507 * 508 * @param fromMethods The methods on which the annotations are defined. 509 * @return This object. 510 */ 511 @FluentSetter 512 public Builder applyAnnotations(Method...fromMethods) { 513 AnnotationWorkList work = AnnotationWorkList.create(); 514 for (Method m : fromMethods) 515 work.add(MethodInfo.of(m).getAnnotationList(CONTEXT_APPLY_FILTER)); 516 return apply(work); 517 } 518 519 //----------------------------------------------------------------------------------------------------------------- 520 // Properties 521 //----------------------------------------------------------------------------------------------------------------- 522 523 /** 524 * Defines annotations to apply to specific classes and methods. 525 * 526 * <p> 527 * Allows you to dynamically apply Juneau annotations typically applied directly to classes and methods. 528 * Useful in cases where you want to use the functionality of the annotation on beans and bean properties but 529 * do not have access to the code to do so. 530 * 531 * <p> 532 * As a rule, any Juneau annotation with an <l>on()</l> method can be used with this setting. 533 * 534 * <p> 535 * The following example shows the equivalent methods for applying the {@link Bean @Bean} annotation: 536 * <p class='bjava'> 537 * <jc>// Class with explicit annotation.</jc> 538 * <ja>@Bean</ja>(properties=<js>"street,city,state"</js>) 539 * <jk>public class</jk> A {...} 540 * 541 * <jc>// Class with annotation applied via @BeanConfig</jc> 542 * <jk>public class</jk> B {...} 543 * 544 * <jc>// Java REST method with @BeanConfig annotation.</jc> 545 * <ja>@RestGet</ja>(...) 546 * <ja>@Bean</ja>(on=<js>"B"</js>, properties=<js>"street,city,state"</js>) 547 * <jk>public void</jk> doFoo() {...} 548 * </p> 549 * 550 * <p> 551 * In general, the underlying framework uses this method when it finds dynamically applied annotations on 552 * config annotations. However, concrete implementations of annotations are also provided that can be passed 553 * directly into builder classes like so: 554 * <p class='bjava'> 555 * <jc>// Create a concrete @Bean annotation.</jc> 556 * <ja>Bean</ja> <jv>annotation</jv> = BeanAnnotation.<jsm>create</jsm>(B.<jk>class</jk>).properties(<js>"street,city,state"</js>).build(); 557 * 558 * <jc>// Apply it to a serializer.</jc> 559 * WriterSerializer <jv>serializer</jv> = JsonSerializer.<jsm>create</jsm>().annotations(<jv>annotation</jv>).build(); 560 * 561 * <jc>// Serialize a bean with the dynamically applied annotation.</jc> 562 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> B()); 563 * </p> 564 * 565 * <p> 566 * The following is the list of annotations builders provided that can be constructed 567 * and passed into the builder class: 568 * <ul class='javatreec'> 569 * <li class='ja'>{@link org.apache.juneau.annotation.BeanAnnotation} 570 * <li class='ja'>{@link org.apache.juneau.annotation.BeancAnnotation} 571 * <li class='ja'>{@link org.apache.juneau.annotation.BeanIgnoreAnnotation} 572 * <li class='ja'>{@link org.apache.juneau.annotation.BeanpAnnotation} 573 * <li class='ja'>{@link org.apache.juneau.annotation.ExampleAnnotation} 574 * <li class='ja'>{@link org.apache.juneau.annotation.NamePropertyAnnotation} 575 * <li class='ja'>{@link org.apache.juneau.annotation.ParentPropertyAnnotation} 576 * <li class='ja'>{@link org.apache.juneau.annotation.SwapAnnotation} 577 * <li class='ja'>{@link org.apache.juneau.annotation.UriAnnotation} 578 * <li class='ja'>{@link org.apache.juneau.csv.annotation.CsvAnnotation} 579 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlAnnotation} 580 * <li class='ja'>{@link org.apache.juneau.json.annotation.JsonAnnotation} 581 * <li class='ja'>{@link org.apache.juneau.annotation.SchemaAnnotation} 582 * <li class='ja'>{@link org.apache.juneau.msgpack.annotation.MsgPackAnnotation} 583 * <li class='ja'>{@link org.apache.juneau.oapi.annotation.OpenApiAnnotation} 584 * <li class='ja'>{@link org.apache.juneau.plaintext.annotation.PlainTextAnnotation} 585 * <li class='ja'>{@link org.apache.juneau.soap.annotation.SoapXmlAnnotation} 586 * <li class='ja'>{@link org.apache.juneau.uon.annotation.UonAnnotation} 587 * <li class='ja'>{@link org.apache.juneau.urlencoding.annotation.UrlEncodingAnnotation} 588 * <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlAnnotation} 589 * </ul> 590 * 591 * <p> 592 * The syntax for the <l>on()</l> pattern match parameter depends on whether it applies to a class, method, field, or constructor. 593 * The valid pattern matches are: 594 * <ul class='spaced-list'> 595 * <li>Classes: 596 * <ul> 597 * <li>Fully qualified: 598 * <ul> 599 * <li><js>"com.foo.MyClass"</js> 600 * </ul> 601 * <li>Fully qualified inner class: 602 * <ul> 603 * <li><js>"com.foo.MyClass$Inner1$Inner2"</js> 604 * </ul> 605 * <li>Simple: 606 * <ul> 607 * <li><js>"MyClass"</js> 608 * </ul> 609 * <li>Simple inner: 610 * <ul> 611 * <li><js>"MyClass$Inner1$Inner2"</js> 612 * <li><js>"Inner1$Inner2"</js> 613 * <li><js>"Inner2"</js> 614 * </ul> 615 * </ul> 616 * <li>Methods: 617 * <ul> 618 * <li>Fully qualified with args: 619 * <ul> 620 * <li><js>"com.foo.MyClass.myMethod(String,int)"</js> 621 * <li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js> 622 * <li><js>"com.foo.MyClass.myMethod()"</js> 623 * </ul> 624 * <li>Fully qualified: 625 * <ul> 626 * <li><js>"com.foo.MyClass.myMethod"</js> 627 * </ul> 628 * <li>Simple with args: 629 * <ul> 630 * <li><js>"MyClass.myMethod(String,int)"</js> 631 * <li><js>"MyClass.myMethod(java.lang.String,int)"</js> 632 * <li><js>"MyClass.myMethod()"</js> 633 * </ul> 634 * <li>Simple: 635 * <ul> 636 * <li><js>"MyClass.myMethod"</js> 637 * </ul> 638 * <li>Simple inner class: 639 * <ul> 640 * <li><js>"MyClass$Inner1$Inner2.myMethod"</js> 641 * <li><js>"Inner1$Inner2.myMethod"</js> 642 * <li><js>"Inner2.myMethod"</js> 643 * </ul> 644 * </ul> 645 * <li>Fields: 646 * <ul> 647 * <li>Fully qualified: 648 * <ul> 649 * <li><js>"com.foo.MyClass.myField"</js> 650 * </ul> 651 * <li>Simple: 652 * <ul> 653 * <li><js>"MyClass.myField"</js> 654 * </ul> 655 * <li>Simple inner class: 656 * <ul> 657 * <li><js>"MyClass$Inner1$Inner2.myField"</js> 658 * <li><js>"Inner1$Inner2.myField"</js> 659 * <li><js>"Inner2.myField"</js> 660 * </ul> 661 * </ul> 662 * <li>Constructors: 663 * <ul> 664 * <li>Fully qualified with args: 665 * <ul> 666 * <li><js>"com.foo.MyClass(String,int)"</js> 667 * <li><js>"com.foo.MyClass(java.lang.String,int)"</js> 668 * <li><js>"com.foo.MyClass()"</js> 669 * </ul> 670 * <li>Simple with args: 671 * <ul> 672 * <li><js>"MyClass(String,int)"</js> 673 * <li><js>"MyClass(java.lang.String,int)"</js> 674 * <li><js>"MyClass()"</js> 675 * </ul> 676 * <li>Simple inner class: 677 * <ul> 678 * <li><js>"MyClass$Inner1$Inner2()"</js> 679 * <li><js>"Inner1$Inner2()"</js> 680 * <li><js>"Inner2()"</js> 681 * </ul> 682 * </ul> 683 * <li>A comma-delimited list of anything on this list. 684 * </ul> 685 * 686 * <h5 class='section'>See Also:</h5><ul> 687 * <li class='ja'>{@link BeanConfig} 688 * </ul> 689 * 690 * @param values 691 * The annotations to register with the context. 692 * @return This object. 693 */ 694 @FluentSetter 695 public Builder annotations(Annotation...values) { 696 annotations = addAll(annotations, values); 697 return this; 698 } 699 700 /** 701 * <i><l>Context</l> configuration property: </i> Debug mode. 702 * 703 * <p> 704 * Enables the following additional information during serialization: 705 * <ul class='spaced-list'> 706 * <li> 707 * When bean getters throws exceptions, the exception includes the object stack information 708 * in order to determine how that method was invoked. 709 * <li> 710 * Enables {@link BeanTraverseContext.Builder#detectRecursions()}. 711 * </ul> 712 * 713 * <p> 714 * Enables the following additional information during parsing: 715 * <ul class='spaced-list'> 716 * <li> 717 * When bean setters throws exceptions, the exception includes the object stack information 718 * in order to determine how that method was invoked. 719 * </ul> 720 * 721 * <h5 class='section'>Example:</h5> 722 * <p class='bjava'> 723 * <jc>// Create a serializer with debug enabled.</jc> 724 * WriterSerializer <jv>serializer</jv> = JsonSerializer 725 * .<jsm>create</jsm>() 726 * .debug() 727 * .build(); 728 * 729 * <jc>// Create a POJO model with a recursive loop.</jc> 730 * <jk>public class</jk> MyBean { 731 * <jk>public</jk> Object <jf>f</jf>; 732 * } 733 * MyBean <jv>bean</jv> = <jk>new</jk> MyBean(); 734 * <jv>bean</jv>.<jf>f</jf> = <jv>bean</jv>; 735 * 736 * <jc>// Throws a SerializeException and not a StackOverflowError</jc> 737 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>bean</jv>); 738 * </p> 739 * 740 * <h5 class='section'>See Also:</h5><ul> 741 * <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#debug()} 742 * <li class='jm'>{@link org.apache.juneau.ContextSession.Builder#debug(Boolean)} 743 * </ul> 744 * 745 * @return This object. 746 */ 747 @FluentSetter 748 public Builder debug() { 749 return debug(true); 750 } 751 752 /** 753 * Same as {@link #debug()} but allows you to explicitly specify the value. 754 * 755 * @param value The value for this setting. 756 * @return This object. 757 */ 758 @FluentSetter 759 public Builder debug(boolean value) { 760 debug = value; 761 return this; 762 } 763 764 /** 765 * Returns <jk>true</jk> if debug is enabled. 766 * 767 * @return <jk>true</jk> if debug is enabled. 768 */ 769 public boolean isDebug() { 770 return debug; 771 } 772 773 /** 774 * Looks up a system property or environment variable. 775 * 776 * <p> 777 * First looks in system properties. Then converts the name to env-safe and looks in the system environment. 778 * Then returns the default if it can't be found. 779 * 780 * @param <T> The type to convert to. 781 * @param name The property name. 782 * @param def The default value if not found. 783 * @return The default value. 784 */ 785 protected <T> T env(String name, T def) { 786 return SystemEnv.env(name, def); 787 } 788 789 /** 790 * Looks up a system property or environment variable. 791 * 792 * <p> 793 * First looks in system properties. Then converts the name to env-safe and looks in the system environment. 794 * Then returns the default if it can't be found. 795 * 796 * @param name The property name. 797 * @return The value if found. 798 */ 799 protected Optional<String> env(String name) { 800 return SystemEnv.env(name); 801 } 802 // <FluentSetters> 803 804 // </FluentSetters> 805 } 806 807 //----------------------------------------------------------------------------------------------------------------- 808 // Instance 809 //----------------------------------------------------------------------------------------------------------------- 810 811 final List<Annotation> annotations; 812 final boolean debug; 813 814 private final ReflectionMap<Annotation> annotationMap; 815 private final TwoKeyConcurrentCache<Class<?>,Class<? extends Annotation>,Annotation[]> classAnnotationCache; 816 private final TwoKeyConcurrentCache<Class<?>,Class<? extends Annotation>,Annotation[]> declaredClassAnnotationCache; 817 private final TwoKeyConcurrentCache<Method,Class<? extends Annotation>,Annotation[]> methodAnnotationCache; 818 private final TwoKeyConcurrentCache<Field,Class<? extends Annotation>,Annotation[]> fieldAnnotationCache; 819 private final TwoKeyConcurrentCache<Constructor<?>,Class<? extends Annotation>,Annotation[]> constructorAnnotationCache; 820 821 /** 822 * Copy constructor. 823 * 824 * @param copyFrom The context to copy from. 825 */ 826 protected Context(Context copyFrom) { 827 annotationMap = copyFrom.annotationMap; 828 annotations = copyFrom.annotations; 829 debug = copyFrom.debug; 830 classAnnotationCache = copyFrom.classAnnotationCache; 831 declaredClassAnnotationCache = copyFrom.declaredClassAnnotationCache; 832 methodAnnotationCache = copyFrom.methodAnnotationCache; 833 fieldAnnotationCache = copyFrom.fieldAnnotationCache; 834 constructorAnnotationCache = copyFrom.constructorAnnotationCache; 835 } 836 837 /** 838 * Constructor for this class. 839 * 840 * @param builder The builder for this class. 841 */ 842 protected Context(Builder builder) { 843 init(builder); 844 debug = builder.debug; 845 annotations = optional(builder.annotations).orElseGet(CollectionUtils::emptyList); 846 847 ReflectionMap.Builder<Annotation> rmb = ReflectionMap.create(Annotation.class); 848 849 annotations.forEach(a -> { 850 try { 851 ClassInfo ci = ClassInfo.of(a.getClass()); 852 853 MethodInfo mi = ci.getPublicMethod(x -> x.hasName("onClass")); 854 if (mi != null) { 855 if (! mi.getReturnType().is(Class[].class)) 856 throw new ConfigException("Invalid annotation @{0} used in BEAN_annotations property. Annotation must define an onClass() method that returns a Class array.", a.getClass().getSimpleName()); 857 for (Class<?> c : (Class<?>[])mi.accessible().invoke(a)) 858 rmb.append(c.getName(), a); 859 } 860 861 mi = ci.getPublicMethod(x -> x.hasName("on")); 862 if (mi != null) { 863 if (! mi.getReturnType().is(String[].class)) 864 throw new ConfigException("Invalid annotation @{0} used in BEAN_annotations property. Annotation must define an on() method that returns a String array.", a.getClass().getSimpleName()); 865 for (String s : (String[])mi.accessible().invoke(a)) 866 rmb.append(s, a); 867 } 868 869 } catch (Exception e) { 870 throw new ConfigException(e, "Invalid annotation @{0} used in BEAN_annotations property.", className(a)); 871 } 872 }); 873 this.annotationMap = rmb.build(); 874 boolean disabled = Boolean.getBoolean("juneau.disableAnnotationCaching"); 875 classAnnotationCache = new TwoKeyConcurrentCache<>(disabled, (k1,k2) -> annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))); 876 declaredClassAnnotationCache = new TwoKeyConcurrentCache<>(disabled, (k1,k2) -> annotationMap.appendAll(k1, k2, k1.getDeclaredAnnotationsByType(k2))); 877 methodAnnotationCache = new TwoKeyConcurrentCache<>(disabled, (k1,k2) -> annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))); 878 fieldAnnotationCache = new TwoKeyConcurrentCache<>(disabled, (k1,k2) -> annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))); 879 constructorAnnotationCache = new TwoKeyConcurrentCache<>(disabled, (k1,k2) -> annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))); 880 } 881 882 /** 883 * Perform optional initialization on builder before it is used. 884 * 885 * <p> 886 * Default behavior is a no-op. 887 * 888 * @param builder The builder to initialize. 889 */ 890 protected void init(Builder builder) {} 891 892 /** 893 * Creates a builder from this context object. 894 * 895 * <p> 896 * Builders are used to define new contexts (e.g. serializers, parsers) based on existing configurations. 897 * 898 * @return A new Builder object. 899 */ 900 public Builder copy() { 901 throw new UnsupportedOperationException("Not implemented."); 902 } 903 904 /** 905 * Create a session builder based on the properties defined on this context. 906 * 907 * <p> 908 * Use this method for creating sessions where you want to override basic settings. 909 * Otherwise, use {@link #getSession()} directly. 910 * 911 * @return A new session builder. 912 */ 913 public ContextSession.Builder createSession() { 914 throw new UnsupportedOperationException("Not implemented."); 915 } 916 917 /** 918 * Returns a session to use for this context. 919 * 920 * <p> 921 * Note that subclasses may opt to return a reusable non-modifiable session. 922 * 923 * @return A new session object. 924 */ 925 public ContextSession getSession() { 926 return createSession().build(); 927 } 928 929 //----------------------------------------------------------------------------------------------------------------- 930 // Properties 931 //----------------------------------------------------------------------------------------------------------------- 932 933 /** 934 * Debug mode. 935 * 936 * @see Context.Builder#debug() 937 * @return 938 * <jk>true</jk> if debug mode is enabled. 939 */ 940 public boolean isDebug() { 941 return debug; 942 } 943 944 //----------------------------------------------------------------------------------------------------------------- 945 // MetaProvider methods 946 //----------------------------------------------------------------------------------------------------------------- 947 948 @Override /* MetaProvider */ 949 public <A extends Annotation> void forEachAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter, Consumer<A> action) { 950 if (type != null && onClass != null) 951 for (A a : annotations(type, onClass)) 952 consume(filter, action, a); 953 } 954 955 @Override /* MetaProvider */ 956 public <A extends Annotation> A firstAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter) { 957 if (type != null && onClass != null) 958 for (A a : annotations(type, onClass)) 959 if (test(filter, a)) 960 return a; 961 return null; 962 } 963 964 @Override /* MetaProvider */ 965 public <A extends Annotation> A lastAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter) { 966 A x = null; 967 if (type != null && onClass != null) 968 for (A a : annotations(type, onClass)) 969 if (test(filter, a)) 970 x = a; 971 return x; 972 } 973 974 @Override /* MetaProvider */ 975 public <A extends Annotation> void forEachDeclaredAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter, Consumer<A> action) { 976 if (type != null && onClass != null) 977 for (A a : declaredAnnotations(type, onClass)) 978 consume(filter, action, a); 979 } 980 981 @Override /* MetaProvider */ 982 public <A extends Annotation> A firstDeclaredAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter) { 983 if (type != null && onClass != null) 984 for (A a : declaredAnnotations(type, onClass)) 985 if (test(filter, a)) 986 return a; 987 return null; 988 } 989 990 @Override /* MetaProvider */ 991 public <A extends Annotation> A lastDeclaredAnnotation(Class<A> type, Class<?> onClass, Predicate<A> filter) { 992 A x = null; 993 if (type != null && onClass != null) 994 for (A a : declaredAnnotations(type, onClass)) 995 if (test(filter, a)) 996 x = a; 997 return x; 998 } 999 1000 @Override /* MetaProvider */ 1001 public <A extends Annotation> void forEachAnnotation(Class<A> type, Method onMethod, Predicate<A> filter, Consumer<A> action) { 1002 if (type != null && onMethod != null) 1003 for (A a : annotations(type, onMethod)) 1004 consume(filter, action, a); 1005 } 1006 1007 @Override /* MetaProvider */ 1008 public <A extends Annotation> A firstAnnotation(Class<A> type, Method onMethod, Predicate<A> filter) { 1009 if (type != null && onMethod != null) 1010 for (A a : annotations(type, onMethod)) 1011 if (test(filter, a)) 1012 return a; 1013 return null; 1014 } 1015 1016 @Override /* MetaProvider */ 1017 public <A extends Annotation> A lastAnnotation(Class<A> type, Method onMethod, Predicate<A> filter) { 1018 A x = null; 1019 if (type != null && onMethod != null) 1020 for (A a : annotations(type, onMethod)) 1021 if (test(filter, a)) 1022 x = a; 1023 return x; 1024 } 1025 1026 @Override /* MetaProvider */ 1027 public <A extends Annotation> void forEachAnnotation(Class<A> type, Field onField, Predicate<A> filter, Consumer<A> action) { 1028 if (type != null && onField != null) 1029 for (A a : annotations(type, onField)) 1030 consume(filter, action, a); 1031 } 1032 1033 @Override /* MetaProvider */ 1034 public <A extends Annotation> A firstAnnotation(Class<A> type, Field onField, Predicate<A> filter) { 1035 if (type != null && onField != null) 1036 for (A a : annotations(type, onField)) 1037 if (test(filter, a)) 1038 return a; 1039 return null; 1040 } 1041 1042 @Override /* MetaProvider */ 1043 public <A extends Annotation> A lastAnnotation(Class<A> type, Field onField, Predicate<A> filter) { 1044 A x = null; 1045 if (type != null && onField != null) 1046 for (A a : annotations(type, onField)) 1047 if (test(filter, a)) 1048 x = a; 1049 return x; 1050 } 1051 1052 @Override /* MetaProvider */ 1053 public <A extends Annotation> void forEachAnnotation(Class<A> type, Constructor<?> onConstructor, Predicate<A> filter, Consumer<A> action) { 1054 if (type != null && onConstructor != null) 1055 for (A a : annotations(type, onConstructor)) 1056 consume(filter, action, a); 1057 } 1058 1059 @Override /* MetaProvider */ 1060 public <A extends Annotation> A firstAnnotation(Class<A> type, Constructor<?> onConstructor, Predicate<A> filter) { 1061 if (type != null && onConstructor != null) 1062 for (A a : annotations(type, onConstructor)) 1063 if (test(filter, a)) 1064 return a; 1065 return null; 1066 } 1067 1068 @Override /* MetaProvider */ 1069 public <A extends Annotation> A lastAnnotation(Class<A> type, Constructor<?> onConstructor, Predicate<A> filter) { 1070 A x = null; 1071 if (type != null && onConstructor != null) 1072 for (A a : annotations(type, onConstructor)) 1073 if (test(filter, a)) 1074 x = a; 1075 return x; 1076 } 1077 1078 /** 1079 * Returns <jk>true</jk> if <c>getAnnotation(a,c)</c> returns a non-null value. 1080 * 1081 * @param <A> The annotation being checked for. 1082 * @param type The annotation being checked for. 1083 * @param onClass The class being checked on. 1084 * @return <jk>true</jk> if the annotation exists on the specified class. 1085 */ 1086 public <A extends Annotation> boolean hasAnnotation(Class<A> type, Class<?> onClass) { 1087 return annotations(type, onClass).length > 0; 1088 } 1089 1090 /** 1091 * Returns <jk>true</jk> if <c>getAnnotation(a,m)</c> returns a non-null value. 1092 * 1093 * @param <A> The annotation being checked for. 1094 * @param type The annotation being checked for. 1095 * @param onMethod The method being checked on. 1096 * @return <jk>true</jk> if the annotation exists on the specified method. 1097 */ 1098 public <A extends Annotation> boolean hasAnnotation(Class<A> type, Method onMethod) { 1099 return annotations(type, onMethod).length > 0; 1100 } 1101 1102 /** 1103 * Returns <jk>true</jk> if <c>getAnnotation(a,f)</c> returns a non-null value. 1104 * 1105 * @param <A> The annotation being checked for. 1106 * @param type The annotation being checked for. 1107 * @param onField The field being checked on. 1108 * @return <jk>true</jk> if the annotation exists on the specified field. 1109 */ 1110 public <A extends Annotation> boolean hasAnnotation(Class<A> type, Field onField) { 1111 return annotations(type, onField).length > 0; 1112 } 1113 1114 /** 1115 * Returns <jk>true</jk> if <c>getAnnotation(a,c)</c> returns a non-null value. 1116 * 1117 * @param <A> The annotation being checked for. 1118 * @param type The annotation being checked for. 1119 * @param onConstructor The constructor being checked on. 1120 * @return <jk>true</jk> if the annotation exists on the specified field. 1121 */ 1122 public <A extends Annotation> boolean hasAnnotation(Class<A> type, Constructor<?> onConstructor) { 1123 return annotations(type, onConstructor).length > 0; 1124 } 1125 1126 @SuppressWarnings("unchecked") 1127 private <A extends Annotation> A[] annotations(Class<A> type, Class<?> onClass) { 1128 return (A[])classAnnotationCache.get(onClass, type); 1129 } 1130 1131 @SuppressWarnings("unchecked") 1132 private <A extends Annotation> A[] declaredAnnotations(Class<A> type, Class<?> onClass) { 1133 return (A[])declaredClassAnnotationCache.get(onClass, type); 1134 } 1135 1136 @SuppressWarnings("unchecked") 1137 private <A extends Annotation> A[] annotations(Class<A> type, Method onMethod) { 1138 return (A[])methodAnnotationCache.get(onMethod, type); 1139 } 1140 1141 @SuppressWarnings("unchecked") 1142 private <A extends Annotation> A[] annotations(Class<A> type, Field onField) { 1143 return (A[])fieldAnnotationCache.get(onField, type); 1144 } 1145 1146 @SuppressWarnings("unchecked") 1147 private <A extends Annotation> A[] annotations(Class<A> type, Constructor<?> onConstructor) { 1148 return (A[])constructorAnnotationCache.get(onConstructor, type); 1149 } 1150 1151 //----------------------------------------------------------------------------------------------------------------- 1152 // Other methods 1153 //----------------------------------------------------------------------------------------------------------------- 1154 1155 /** 1156 * Returns the properties on this bean as a map for debugging. 1157 * 1158 * @return The properties on this bean as a map for debugging. 1159 */ 1160 protected JsonMap properties() { 1161 return filteredMap("annotations", annotations, "debug", debug); 1162 } 1163 1164 @Override /* Object */ 1165 public String toString() { 1166 return ObjectUtils.toPropertyMap(this).asReadableString(); 1167 } 1168}