| // *************************************************************************************************************************** |
| // * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * |
| // * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * |
| // * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * |
| // * with the License. You may obtain a copy of the License at * |
| // * * |
| // * http://www.apache.org/licenses/LICENSE-2.0 * |
| // * * |
| // * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * |
| // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * |
| // * specific language governing permissions and limitations under the License. * |
| // *************************************************************************************************************************** |
| package org.apache.juneau; |
| |
| import java.lang.annotation.*; |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| import org.apache.juneau.annotation.*; |
| import org.apache.juneau.internal.*; |
| import org.apache.juneau.json.*; |
| import org.apache.juneau.utils.*; |
| |
| /** |
| * A reusable stateless thread-safe read-only configuration, typically used for creating one-time use {@link Session} |
| * objects. |
| * |
| * <p> |
| * Contexts are created through the {@link ContextBuilder#build()} method (and subclasses of {@link ContextBuilder}). |
| * |
| * <p> |
| * Subclasses MUST implement the following constructor: |
| * |
| * <p class='bcode w800'> |
| * <jk>public</jk> T(PropertyStore); |
| * </p> |
| * |
| * <p> |
| * Besides that restriction, a context object can do anything you desire. |
| * <br>However, it MUST be thread-safe and all fields should be declared final to prevent modification. |
| * <br>It should NOT be used for storing temporary or state information. |
| * |
| * @see PropertyStore |
| */ |
| @ConfigurableContext |
| public abstract class Context implements MetaProvider { |
| |
| static final String PREFIX = "Context"; |
| |
| /** |
| * Configuration property: Annotations. |
| * |
| * <h5 class='section'>Property:</h5> |
| * <ul> |
| * <li><b>Name:</b> <js>"Context.annotations.lo"</js> |
| * <li><b>Data type:</b> <c>List<Annotation></c> |
| * <li><b>Default:</b> Empty list. |
| * <li><b>Methods:</b> |
| * <ul> |
| * <li class='jm'>{@link ContextBuilder#annotations(Annotation...)} |
| * </ul> |
| * </ul> |
| * |
| * <h5 class='section'>Description:</h5> |
| * <p> |
| * Defines annotations to apply to specific classes and methods. |
| * |
| * <p> |
| * Allows you to dynamically apply Juneau annotations typically applied directly to classes and methods. |
| * Useful in cases where you want to use the functionality of the annotation on beans and bean properties but |
| * do not have access to the code to do so. |
| * |
| * <p> |
| * As a rule, any Juneau annotation with an <c>on()</c> method can be used with this property. |
| * |
| * <p> |
| * The following example shows the equivalent methods for applying the {@link Bean @Bean} annotation: |
| * <p class='bpcode w800'> |
| * <jc>// Class with explicit annotation.</jc> |
| * <ja>@Bean</ja>(bpi=<jk>"street,city,state"</js>) |
| * <jk>public class</jk> A {...} |
| * |
| * <jc>// Class with annotation applied via @BeanConfig</jc> |
| * <jk>public class</jk> B {...} |
| * |
| * <jc>// Java REST method with @BeanConfig annotation.</jc> |
| * <ja>@RestMethod</ja>(...) |
| * <ja>@BeanConfig</ja>( |
| * annotations={ |
| * <ja>@Bean</ja>(on=<js>"B"</js>, bpi=<jk>"street,city,state"</js>) |
| * } |
| * ) |
| * <jk>public void</jk> doFoo() {...} |
| * </p> |
| */ |
| public static final String CONTEXT_annotations = PREFIX + ".annotations.lo"; |
| |
| |
| private final PropertyStore propertyStore; |
| private final int identityCode; |
| private final ReflectionMap<Annotation> annotations; |
| |
| /** |
| * Constructor for this class. |
| * |
| * <p> |
| * Subclasses MUST implement the same public constructor. |
| * |
| * @param ps The read-only configuration for this context object. |
| * @param allowReuse If <jk>true</jk>, subclasses that share the same property store values can be reused. |
| */ |
| public Context(PropertyStore ps, boolean allowReuse) { |
| this.propertyStore = ps == null ? PropertyStore.DEFAULT : ps; |
| this.identityCode = allowReuse ? new HashCode().add(getClass().getName()).add(ps).get() : System.identityHashCode(this); |
| |
| ReflectionMap.Builder<Annotation> rmb = ReflectionMap.create(Annotation.class); |
| for (Annotation a : propertyStore.getListProperty(CONTEXT_annotations, Annotation.class)) { |
| try { |
| Method m = a.getClass().getMethod("on"); |
| String on = (String)m.invoke(a); |
| rmb.append(on, a); |
| } catch (Exception e) { |
| throw new ConfigException("Invalid annotation @{0} used in CONTEXT_annotations property. Annotation must define an on() method.", a.getClass().getSimpleName()); |
| } |
| } |
| this.annotations = rmb.build(); |
| } |
| |
| /** |
| * Returns the raw property value with the specified name. |
| * |
| * @param key The property name. |
| * @return The property value, or <jk>null</jk> if it doesn't exist. |
| */ |
| public Object getProperty(String key) { |
| return propertyStore.getProperty(key); |
| } |
| |
| /** |
| * Returns the property value with the specified name. |
| * |
| * @param key The property name. |
| * @param c The class to cast or convert the value to. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final <T> T getProperty(String key, Class<T> c, T def) { |
| return propertyStore.getProperty(key, c, def); |
| } |
| |
| /** |
| * Shortcut for calling <code>getProperty(key, Boolean.<jk>class</jk>, def)</code>. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final Boolean getBooleanProperty(String key, Boolean def) { |
| return getProperty(key, Boolean.class, def); |
| } |
| |
| /** |
| * Shortcut for calling <code>getProperty(key, Integer.<jk>class</jk>, def)</code>. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final Integer getIntegerProperty(String key, Integer def) { |
| return getProperty(key, Integer.class, def); |
| } |
| |
| /** |
| * Shortcut for calling <code>getProperty(key, Long.<jk>class</jk>, def)</code>. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final Long getLongProperty(String key, Long def) { |
| return getProperty(key, Long.class, def); |
| } |
| |
| /** |
| * Shortcut for calling <code>getProperty(key, String.<jk>class</jk>, def)</code>. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final String getStringProperty(String key, String def) { |
| return getProperty(key, String.class, def); |
| } |
| |
| /** |
| * Returns a property as a parsed comma-delimited list of strings. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final String[] getCdlProperty(String key, String def) { |
| return StringUtils.split(StringUtils.emptyIfNull(getProperty(key, String.class, def))); |
| } |
| |
| /** |
| * Same as {@link #getStringProperty(String, String)} but returns a blank instead of the default value if it resolves to <js>"NONE"</js>. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final String getStringPropertyWithNone(String key, String def) { |
| String s = getProperty(key, String.class, def); |
| return "NONE".equalsIgnoreCase(s) ? "" : s; |
| } |
| |
| /** |
| * Returns the class property with the specified name. |
| * |
| * @param key The property name. |
| * @param type The class type of the property. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final <T> Class<? extends T> getClassProperty(String key, Class<T> type, Class<? extends T> def) { |
| return propertyStore.getClassProperty(key, type, def); |
| } |
| |
| /** |
| * Returns the array property value with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final <T> T[] getArrayProperty(String key, Class<T> eType) { |
| return propertyStore.getArrayProperty(key, eType); |
| } |
| |
| /** |
| * Returns the array property value with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @param def The default value. |
| * @return The property value, or the default value if it doesn't exist. |
| */ |
| public final <T> T[] getArrayProperty(String key, Class<T> eType, T[] def) { |
| return propertyStore.getArrayProperty(key, eType, def); |
| } |
| |
| /** |
| * Returns the class array property with the specified name. |
| * |
| * @param key The property name. |
| * @return The property value, or an empty array if it doesn't exist. |
| */ |
| public final Class<?>[] getClassArrayProperty(String key) { |
| return propertyStore.getClassArrayProperty(key); |
| } |
| |
| /** |
| * Returns the class array property with the specified name. |
| * |
| * @param key The property name. |
| * @param def The default value. |
| * @return The property value, or an empty array if it doesn't exist. |
| */ |
| public final Class<?>[] getClassArrayProperty(String key, Class<?>[] def) { |
| return propertyStore.getClassArrayProperty(key, def); |
| } |
| |
| /** |
| * Returns the class array property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value, or an empty array if it doesn't exist. |
| */ |
| public final <T> Class<T>[] getClassArrayProperty(String key, Class<T> eType) { |
| return propertyStore.getClassArrayProperty(key, eType); |
| } |
| |
| /** |
| * Returns the set property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist. |
| */ |
| public final <T> Set<T> getSetProperty(String key, Class<T> eType) { |
| return propertyStore.getSetProperty(key, eType); |
| } |
| |
| /** |
| * Returns the set property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @param def The default value if the property doesn't exist or is empty. |
| * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or the default value if it doesn't exist or is empty. |
| */ |
| public final <T> Set<T> getSetProperty(String key, Class<T> eType, Set<T> def) { |
| return propertyStore.getSetProperty(key, eType, def); |
| } |
| |
| /** |
| * Returns the class set property with the specified name. |
| * |
| * @param key The property name. |
| * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist. |
| */ |
| public final Set<Class<?>> getClassSetProperty(String key) { |
| return propertyStore.getClassSetProperty(key); |
| } |
| |
| /** |
| * Returns the class set property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist. |
| */ |
| public final <T> Set<Class<T>> getClassSetProperty(String key, Class<T> eType) { |
| return propertyStore.getClassSetProperty(key, eType); |
| } |
| |
| /** |
| * Returns the list property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist. |
| */ |
| public final <T> List<T> getListProperty(String key, Class<T> eType) { |
| return propertyStore.getListProperty(key, eType); |
| } |
| |
| /** |
| * Returns the list property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @param def The default value if the property doesn't exist or is empty. |
| * @return The property value as an unmodifiable <c>ArrayList</c>, or the default value if it doesn't exist or is empty. |
| */ |
| public final <T> List<T> getListProperty(String key, Class<T> eType, List<T> def) { |
| return propertyStore.getListProperty(key, eType, def); |
| } |
| |
| /** |
| * Returns the class list property with the specified name. |
| * |
| * @param key The property name. |
| * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist. |
| */ |
| public final List<Class<?>> getClassListProperty(String key) { |
| return propertyStore.getClassListProperty(key); |
| } |
| |
| /** |
| * Returns the class list property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist. |
| */ |
| public final <T> List<Class<T>> getClassListProperty(String key, Class<T> eType) { |
| return propertyStore.getClassListProperty(key, eType); |
| } |
| |
| /** |
| * Returns the map property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist. |
| */ |
| public final <T> Map<String,T> getMapProperty(String key, Class<T> eType) { |
| return propertyStore.getMapProperty(key, eType); |
| } |
| |
| /** |
| * Returns the class map property with the specified name. |
| * |
| * @param key The property name. |
| * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist. |
| */ |
| public final Map<String,Class<?>> getClassMapProperty(String key) { |
| return propertyStore.getClassMapProperty(key); |
| } |
| |
| /** |
| * Returns the class map property with the specified name. |
| * |
| * @param key The property name. |
| * @param eType The class type of the elements in the property. |
| * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist. |
| */ |
| public final <T> Map<String,Class<T>> getClassMapProperty(String key, Class<T> eType) { |
| return propertyStore.getClassMapProperty(key, eType); |
| } |
| |
| /** |
| * Returns an instance of the specified class, string, or object property. |
| * |
| * <p> |
| * If instantiating a class, assumes the class has a no-arg constructor. |
| * Otherwise, throws a runtime exception. |
| * |
| * @param key The property name. |
| * @param type The class type of the property. |
| * @param def |
| * The default value if the property doesn't exist. |
| * <br>Can either be an instance of <c>T</c>, or a <code>Class<? <jk>extends</jk> T></code>, or <jk>null</jk>. |
| * @return A new property instance. |
| */ |
| public <T> T getInstanceProperty(String key, Class<T> type, Object def) { |
| return propertyStore.getInstanceProperty(key, type, def); |
| } |
| |
| /** |
| * Returns an instance of the specified class, string, or object property. |
| * |
| * @param key The property name. |
| * @param type The class type of the property. |
| * @param def |
| * The default value if the property doesn't exist. |
| * <br>Can either be an instance of <c>T</c>, or a <code>Class<? <jk>extends</jk> T></code>. |
| * @param resolver |
| * The resolver to use for instantiating objects. |
| * @param args |
| * Arguments to pass to the constructor. |
| * Constructors matching the arguments are always used before no-arg constructors. |
| * @return A new property instance. |
| */ |
| public <T> T getInstanceProperty(String key, Class<T> type, Object def, ResourceResolver resolver, Object...args) { |
| return propertyStore.getInstanceProperty(key, type, def, resolver, args); |
| } |
| |
| /** |
| * Returns an instance of the specified class, string, or object property. |
| * |
| * @param key The property name. |
| * @param outer The outer object if the class we're instantiating is an inner class. |
| * @param type The class type of the property. |
| * @param def |
| * The default value if the property doesn't exist. |
| * <br>Can either be an instance of <c>T</c>, or a <code>Class<? <jk>extends</jk> T></code>. |
| * @param resolver |
| * The resolver to use for instantiating objects. |
| * @param args |
| * Arguments to pass to the constructor. |
| * Constructors matching the arguments are always used before no-arg constructors. |
| * @return A new property instance. |
| */ |
| public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, ResourceResolver resolver, Object...args) { |
| return propertyStore.getInstanceProperty(key, outer, type, def, resolver, args); |
| } |
| |
| /** |
| * Returns the specified property as an array of instantiated objects. |
| * |
| * @param key The property name. |
| * @param type The class type of the property. |
| * @param def The default object to return if the property doesn't exist. |
| * @return A new property instance. |
| */ |
| public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def) { |
| return propertyStore.getInstanceArrayProperty(key, type, def); |
| } |
| |
| /** |
| * Returns the specified property as an array of instantiated objects. |
| * |
| * @param key The property name. |
| * @param type The class type of the property. |
| * @param def The default object to return if the property doesn't exist. |
| * @param resolver |
| * The resolver to use for instantiating objects. |
| * @param args |
| * Arguments to pass to the constructor. |
| * Constructors matching the arguments are always used before no-arg constructors. |
| * @return A new property instance. |
| */ |
| public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, ResourceResolver resolver, Object...args) { |
| return propertyStore.getInstanceArrayProperty(key, type, def, resolver, args); |
| } |
| |
| /** |
| * Returns the specified property as an array of instantiated objects. |
| * |
| * @param key The property name. |
| * @param outer The outer object if the class we're instantiating is an inner class. |
| * @param type The class type of the property. |
| * @param def The default object to return if the property doesn't exist. |
| * @param resolver |
| * The resolver to use for instantiating objects. |
| * @param args |
| * Arguments to pass to the constructor. |
| * Constructors matching the arguments are always used before no-arg constructors. |
| * @return A new property instance. |
| */ |
| public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, ResourceResolver resolver, Object...args) { |
| return propertyStore.getInstanceArrayProperty(key, outer, type, def, resolver, args); |
| } |
| |
| /** |
| * Returns the keys found in the specified property group. |
| * |
| * <p> |
| * The keys are NOT prefixed with group names. |
| * |
| * @param group The group name. |
| * @return The set of property keys, or an empty set if the group was not found. |
| */ |
| public Set<String> getPropertyKeys(String group) { |
| return propertyStore.getPropertyKeys(group); |
| } |
| |
| /** |
| * Returns the property store associated with this context. |
| * |
| * @return The property store associated with this context. |
| */ |
| @BeanIgnore |
| public final PropertyStore getPropertyStore() { |
| return propertyStore; |
| } |
| |
| /** |
| * Creates a builder from this context object. |
| * |
| * <p> |
| * Builders are used to define new contexts (e.g. serializers, parsers) based on existing configurations. |
| * |
| * @return A new ContextBuilder object. |
| */ |
| public ContextBuilder builder() { |
| return null; |
| } |
| |
| /** |
| * Create a new bean session based on the properties defined on this context. |
| * |
| * <p> |
| * Use this method for creating sessions if you don't need to override any |
| * properties or locale/timezone currently set on this context. |
| * |
| * @return A new session object. |
| */ |
| public Session createSession() { |
| return createSession(createDefaultSessionArgs()); |
| } |
| |
| /** |
| * Create a new session based on the properties defined on this context combined with the specified |
| * runtime args. |
| * |
| * <p> |
| * Use this method for creating sessions if you don't need to override any |
| * properties or locale/timezone currently set on this context. |
| * |
| * @param args |
| * The session arguments. |
| * @return A new session object. |
| */ |
| public abstract Session createSession(SessionArgs args); |
| |
| /** |
| * Defines default session arguments used when calling the {@link #createSession()} method. |
| * |
| * @return A SessionArgs object, possibly a read-only reusable instance. |
| */ |
| public abstract SessionArgs createDefaultSessionArgs(); |
| |
| @Override /* Object */ |
| public int hashCode() { |
| return identityCode; |
| } |
| |
| /** |
| * Returns a uniqueness identity code for this context. |
| * |
| * @return A uniqueness identity code. |
| */ |
| public int identityCode() { |
| return identityCode; |
| } |
| |
| @Override /* Object */ |
| public boolean equals(Object o) { |
| // Context objects are considered equal if they're the same class and have the same set of properties. |
| if (o == null) |
| return false; |
| if (o.getClass() != this.getClass()) |
| return false; |
| Context c = (Context)o; |
| return (c.propertyStore.equals(propertyStore)); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // MetaProvider methods |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| @SuppressWarnings("unchecked") |
| @Override /* MetaProvider */ |
| public <A extends Annotation> A getAnnotation(Class<A> a, Class<?> c) { |
| return a == null || c == null ? null : (A)annotations.findFirst(c, a).orElse(c.getAnnotation(a)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override /* MetaProvider */ |
| public <A extends Annotation> A getDeclaredAnnotation(Class<A> a, Class<?> c) { |
| return a == null || c == null ? null : (A)annotations.findFirst(c, a).orElse(c.getAnnotation(a)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override /* MetaProvider */ |
| public <A extends Annotation> A getAnnotation(Class<A> a, Method m) { |
| return a == null || m == null ? null : (A)annotations.findFirst(m, a).orElse(m.getAnnotation(a)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override /* MetaProvider */ |
| public <A extends Annotation> A getAnnotation(Class<A> a, Field f) { |
| return a == null || f == null ? null : (A)annotations.findFirst(f, a).orElse(f.getAnnotation(a)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override /* MetaProvider */ |
| public <A extends Annotation> A getAnnotation(Class<A> a, Constructor<?> c) { |
| return a == null || c == null ? null : (A)annotations.findFirst(c, a).orElse(c.getAnnotation(a)); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Other methods |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| @Override /* Object */ |
| public String toString() { |
| return SimpleJsonSerializer.DEFAULT_READABLE.toString(toMap()); |
| } |
| |
| /** |
| * Returns the properties defined on this bean context as a simple map for debugging purposes. |
| * |
| * @return A new map containing the properties defined on this context. |
| */ |
| public ObjectMap toMap() { |
| return new DefaultFilteringObjectMap() |
| .append("Context", new DefaultFilteringObjectMap() |
| .append("identityCode", identityCode) |
| .append("propertyStore", propertyStore) |
| ); |
| } |
| } |