| /* |
| * 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 freemarker.ext.beans; |
| |
| import java.lang.ref.ReferenceQueue; |
| import java.lang.ref.WeakReference; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| import freemarker.template.Configuration; |
| import freemarker.template.DefaultObjectWrapperBuilder; |
| import freemarker.template.TemplateModel; |
| import freemarker.template.Version; |
| |
| /** |
| * Gets/creates a {@link BeansWrapper} singleton instance that's already configured as specified in the properties of |
| * this object; this is recommended over using the {@link BeansWrapper} constructors. The returned instance can't be |
| * further configured (it's write protected). |
| * |
| * <p>The builder meant to be used as a drop-away object (not stored in a field), like in this example: |
| * <pre> |
| * BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_2_3_21).build(); |
| * </pre> |
| * |
| * <p>Or, a more complex example:</p> |
| * <pre> |
| * // Create the builder: |
| * BeansWrapperBuilder builder = new BeansWrapperBuilder(Configuration.VERSION_2_3_21); |
| * // Set desired BeansWrapper configuration properties: |
| * builder.setUseModelCache(true); |
| * builder.setExposeFields(true); |
| * |
| * // Get the singleton: |
| * BeansWrapper beansWrapper = builder.build(); |
| * // You don't need the builder anymore. |
| * </pre> |
| * |
| * <p>Despite that builders aren't meant to be used as long-lived objects (singletons), the builder is thread-safe after |
| * you have stopped calling its setters and it was safely published (see JSR 133) to other threads. This can be useful |
| * if you have to put the builder into an IoC container, rather than the singleton it produces. |
| * |
| * <p>The main benefit of using a builder instead of a {@link BeansWrapper} constructor is that this way the |
| * internal object wrapping-related caches (most notably the class introspection cache) will come from a global, |
| * JVM-level (more precisely, {@code freemarker.jar}-class-loader-level) cache. Also the {@link BeansWrapper} singletons |
| * themselves are stored in this global cache. Some of the wrapping-related caches are expensive to build and can take |
| * significant amount of memory. Using builders, components that use FreeMarker will share {@link BeansWrapper} |
| * instances and said caches even if they use separate FreeMarker {@link Configuration}-s. (Many Java libraries use |
| * FreeMarker internally, so {@link Configuration} sharing is not an option.) |
| * |
| * <p>Note that the returned {@link BeansWrapper} instances are only weak-referenced from inside the builder mechanism, |
| * so singletons are garbage collected when they go out of usage, just like non-singletons. |
| * |
| * <p>About the object wrapping-related caches: |
| * <ul> |
| * <li><p>Class introspection cache: Stores information about classes that once had to be wrapped. The cache is |
| * stored in the static fields of certain FreeMarker classes. Thus, if you have two {@link BeansWrapper} |
| * instances, they might share the same class introspection cache. But if you have two |
| * {@code freemarker.jar}-s (typically, in two Web Application's {@code WEB-INF/lib} directories), those won't |
| * share their caches (as they don't share the same FreeMarker classes). |
| * Also, currently there's a separate cache for each permutation of the property values that influence class |
| * introspection: |
| * {@link BeansWrapperBuilder#setExposeFields(boolean) expose_fields}, and |
| * {@link BeansWrapperBuilder#setExposureLevel(int) exposure_level}, and |
| * {@link BeansWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)} member_access_policy}. |
| * So only {@link BeansWrapper} where those properties are the same may share class introspection caches among each |
| * other. |
| * </li> |
| * <li><p>Model caches: These are local to a {@link BeansWrapper}. {@link BeansWrapperBuilder} returns the same |
| * {@link BeansWrapper} instance for equivalent properties (unless the existing instance was garbage collected |
| * and thus a new one had to be created), hence these caches will be re-used too. {@link BeansWrapper} instances |
| * are cached in the static fields of FreeMarker too, but there's a separate cache for each |
| * Thread Context Class Loader, which in a servlet container practically means a separate cache for each Web |
| * Application (each servlet context). (This is like so because for resolving class names to classes FreeMarker |
| * uses the Thread Context Class Loader, so the result of the resolution can be different for different |
| * Thread Context Class Loaders.) The model caches are: |
| * <ul> |
| * <li><p> |
| * Static model caches: These are used by the hash returned by {@link BeansWrapper#getEnumModels()} and |
| * {@link BeansWrapper#getStaticModels()}, for caching {@link TemplateModel}-s for the static methods/fields |
| * and Java enums that were accessed through them. To use said hashes, you have to put them |
| * explicitly into the data-model or expose them to the template explicitly otherwise, so in most applications |
| * these caches aren't unused. |
| * </li> |
| * <li><p> |
| * Instance model cache: By default off (see {@link BeansWrapper#setUseCache(boolean)}). Caches the |
| * {@link TemplateModel}-s for all Java objects that were accessed from templates. |
| * </li> |
| * </ul> |
| * </li> |
| * </ul> |
| * |
| * <p>Note that what this method documentation says about {@link BeansWrapper} also applies to |
| * {@link DefaultObjectWrapperBuilder}. |
| * |
| * @since 2.3.21 |
| */ |
| public class BeansWrapperBuilder extends BeansWrapperConfiguration { |
| |
| private final static Map<ClassLoader, Map<BeansWrapperConfiguration, WeakReference<BeansWrapper>>> |
| INSTANCE_CACHE = new WeakHashMap<>(); |
| private final static ReferenceQueue<BeansWrapper> INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue<>(); |
| |
| private static class BeansWrapperFactory |
| implements _BeansAPI._BeansWrapperSubclassFactory<BeansWrapper, BeansWrapperConfiguration> { |
| |
| private static final BeansWrapperFactory INSTANCE = new BeansWrapperFactory(); |
| |
| @Override |
| public BeansWrapper create(BeansWrapperConfiguration bwConf) { |
| return new BeansWrapper(bwConf, true); |
| } |
| |
| } |
| |
| /** |
| * See {@link BeansWrapperConfiguration#BeansWrapperConfiguration(Version)}. |
| */ |
| public BeansWrapperBuilder(Version incompatibleImprovements) { |
| super(incompatibleImprovements); |
| } |
| |
| /** For unit testing only */ |
| static void clearInstanceCache() { |
| synchronized (INSTANCE_CACHE) { |
| INSTANCE_CACHE.clear(); |
| } |
| } |
| |
| /** |
| * For unit testing only |
| */ |
| static Map<ClassLoader, Map<BeansWrapperConfiguration, WeakReference<BeansWrapper>>> getInstanceCache() { |
| return INSTANCE_CACHE; |
| } |
| |
| /** |
| * Returns a {@link BeansWrapper} instance that matches the settings of this builder. This will be possibly a |
| * singleton that is also in use elsewhere, not necessarily a new object. |
| */ |
| public BeansWrapper build() { |
| return _BeansAPI.getBeansWrapperSubclassSingleton( |
| this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, BeansWrapperFactory.INSTANCE); |
| } |
| |
| } |