|
20 | 20 | package com.adobe.acs.commons.wcm.properties.shared.impl; |
21 | 21 |
|
22 | 22 | import com.adobe.acs.commons.wcm.properties.shared.SharedComponentProperties; |
23 | | -import org.apache.felix.scr.annotations.Activate; |
24 | 23 | import org.apache.felix.scr.annotations.Component; |
25 | 24 | import org.apache.felix.scr.annotations.Reference; |
26 | 25 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
|
29 | 28 | import org.apache.sling.api.SlingHttpServletRequest; |
30 | 29 | import org.apache.sling.api.resource.Resource; |
31 | 30 | import org.apache.sling.api.resource.ValueMap; |
| 31 | +import org.apache.sling.api.scripting.LazyBindings; |
32 | 32 | import org.apache.sling.api.scripting.SlingBindings; |
33 | 33 | import org.apache.sling.scripting.api.BindingsValuesProvider; |
34 | 34 | import org.slf4j.Logger; |
35 | 35 | import org.slf4j.LoggerFactory; |
36 | 36 |
|
37 | 37 | import javax.script.Bindings; |
38 | | -import java.lang.reflect.InvocationHandler; |
39 | | -import java.lang.reflect.Method; |
40 | | -import java.lang.reflect.Proxy; |
41 | 38 | import java.util.Optional; |
42 | 39 | import java.util.function.Supplier; |
43 | | -import java.util.stream.Stream; |
44 | 40 |
|
45 | 41 | /** |
46 | 42 | * Bindings Values Provider that adds bindings for globalProperties, |
|
62 | 58 | public class SharedComponentPropertiesBindingsValuesProvider implements BindingsValuesProvider { |
63 | 59 | private static final Logger log = LoggerFactory.getLogger(SharedComponentPropertiesBindingsValuesProvider.class); |
64 | 60 |
|
65 | | - /** |
66 | | - * The LazyBindings class, and its Supplier child interface, are introduced in org.apache.sling.api version 2.22.0, |
67 | | - * which is first included in AEM 6.5 SP7. |
68 | | - */ |
69 | | - protected static final String FQDN_LAZY_BINDINGS = "org.apache.sling.api.scripting.LazyBindings"; |
70 | | - protected static final String SUPPLIER_PROXY_LABEL = "ACS AEM Commons SCP BVP reflective Proxy for LazyBindings.Supplier"; |
71 | | - |
72 | 61 | /** |
73 | 62 | * Bind if available, check for null when reading. |
74 | 63 | */ |
75 | 64 | @Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL_UNARY) |
76 | 65 | SharedComponentProperties sharedComponentProperties; |
77 | 66 |
|
78 | | - /** |
79 | | - * Added for pre-6.5.7 support for LazyBindings. This holds the LazyBindings interface |
80 | | - * if it is discovered on activation, and is used to check if the {@link #addBindings(Bindings)} param |
81 | | - * is an instance of LazyBindings. This hack is necessary until this bundle can drop support for |
82 | | - * AEM versions prior to 6.5.7, at which point this variable can be removed, and the {@link #isLazy(Bindings)} |
83 | | - * method can be simplified to return {@code bindings instanceof LazyBindings}. |
84 | | - */ |
85 | | - private Class<? extends Bindings> lazyBindingsType; |
86 | | - |
87 | | - /** |
88 | | - * Added for pre-6.5.7 support for LazyBindings. This holds the LazyBindings.Supplier interface |
89 | | - * if it is discovered on activation, and is used to create reflection Proxy instances as a hack |
90 | | - * until this bundle can drop support for AEM versions prior to 6.5.7, at which point this variable |
91 | | - * can be removed, and the {@link #wrapSupplier(Supplier)} method can be simplified to accept a |
92 | | - * LazyBindings.Supplier instead of a java.util.function.Supplier and return it (for matching a |
93 | | - * lambda expression passed at the call site), or to simply return a lambda that calls the get() |
94 | | - * method on the java.util.function.Supplier argument. |
95 | | - */ |
96 | | - private Class<? extends Supplier> supplierType; |
97 | | - |
98 | | - /** |
99 | | - * This variable only exists to facilitate testing for pre-6.5.7 LazyBindings support, so that a non-classpath |
100 | | - * class loader can be injected, to provide the LazyBindings class. |
101 | | - */ |
102 | | - private ClassLoader lazyBindingsClassLoader = SlingBindings.class.getClassLoader(); |
103 | | - |
104 | | - /** |
105 | | - * Called by the unit test to inject a URL class loader that provides a LazyBindings instance |
106 | | - * at {@link #FQDN_LAZY_BINDINGS}. |
107 | | - * |
108 | | - * @param classLoader a new class loader |
109 | | - * @return the old class loader |
110 | | - */ |
111 | | - protected ClassLoader swapLazyBindingsClassLoaderForTesting(ClassLoader classLoader) { |
112 | | - if (classLoader != null) { |
113 | | - ClassLoader oldClassLoader = this.lazyBindingsClassLoader; |
114 | | - this.lazyBindingsClassLoader = classLoader; |
115 | | - return oldClassLoader; |
116 | | - } |
117 | | - return null; |
118 | | - } |
119 | | - |
120 | | - /** |
121 | | - * Return the resolved lazyBindingsType for testing. |
122 | | - * |
123 | | - * @return the lazyBindingsType |
124 | | - */ |
125 | | - protected Class<? extends Bindings> getLazyBindingsType() { |
126 | | - return this.lazyBindingsType; |
127 | | - } |
128 | | - |
129 | | - /** |
130 | | - * Return the resolved supplierType for testing. |
131 | | - * |
132 | | - * @return the supplierType |
133 | | - */ |
134 | | - protected Class<? extends Supplier> getSupplierType() { |
135 | | - return this.supplierType; |
136 | | - } |
137 | | - |
138 | 67 | /** |
139 | 68 | * This method ensures that the provided supplier is appropriately typed for insertion into a SlingBindings |
140 | 69 | * object. It primarily facilitates lambda type inference (i.e., {@code wrapSupplier(() -> something)} forces |
141 | | - * inference to the functional interface type of the method parameter). And so long as pre-6.5.7 AEMs are supported, |
142 | | - * this method is also responsible for constructing the {@link Proxy} instance when LazyBindings is present at |
143 | | - * runtime, and for immediately returning {@code Supplier.get()} when it is not present. |
144 | | - * After support for pre-6.5.7 AEMs is dropped, the method return type can be changed from {@code Object} to |
145 | | - * {@code <T> LazyBindings.Supplier<T>} to fully support lazy injection. |
| 70 | + * inference to the functional interface type of the method parameter). |
146 | 71 | * |
147 | 72 | * @param supplier the provided supplier |
148 | | - * @return the Supplier as a LazyBindings.Supplier if supported, or the value of the provided supplier if not |
149 | | - */ |
150 | | - protected Object wrapSupplier(final Supplier<?> supplier) { |
151 | | - if (this.supplierType != null) { |
152 | | - return Proxy.newProxyInstance(lazyBindingsClassLoader, new Class[]{this.supplierType}, |
153 | | - new SupplierWrapper(supplier)); |
154 | | - } |
155 | | - return supplier.get(); |
156 | | - } |
157 | | - |
158 | | - /** |
159 | | - * The only purpose of this class is to drive the pre-6.5.7 reflection-based Proxy instance returned |
160 | | - * by {@link #wrapSupplier(Supplier)}. |
161 | | - */ |
162 | | - protected static class SupplierWrapper implements InvocationHandler { |
163 | | - private final Supplier<?> wrapped; |
164 | | - |
165 | | - public SupplierWrapper(final Supplier<?> supplier) { |
166 | | - this.wrapped = supplier; |
167 | | - } |
168 | | - |
169 | | - @Override |
170 | | - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
171 | | - // we are implementing a @FunctionalInterface, so don't get carried away with implementing |
172 | | - // Object methods. |
173 | | - if ("get".equals(method.getName())) { |
174 | | - return wrapped.get(); |
175 | | - } else if ("toString".equals(method.getName())) { |
176 | | - // return this marker string for visibility in debugging tools. Otherwise, |
177 | | - // the default toString is "\"null\"", which is confusing |
178 | | - return SUPPLIER_PROXY_LABEL; |
179 | | - } |
180 | | - return method.getDefaultValue(); |
181 | | - } |
182 | | - } |
183 | | - |
184 | | - /** |
185 | | - * The purpose of this activate method is to determine if we are running in a 6.5.7+ AEM environment |
186 | | - * without having to explicitly require {@code org.apache.sling.api.scripting} package version 2.5.0. |
187 | | - */ |
188 | | - @Activate |
189 | | - protected void activate() { |
190 | | - // use SlingBindings class loader to check for LazyBindings class, |
191 | | - // to minimize risk involved with using reflection. |
192 | | - try { |
193 | | - this.checkAndSetLazyBindingsType(lazyBindingsClassLoader.loadClass(FQDN_LAZY_BINDINGS)); |
194 | | - } catch (ReflectiveOperationException cnfe) { |
195 | | - log.info("LazyBindings not found, will resort to injecting immediate Bindings values", cnfe); |
196 | | - } |
197 | | - } |
198 | | - |
199 | | - /** |
200 | | - * Check that the provided {@code lazyBindingsType} implements {@link Bindings} and defines an enclosed marker |
201 | | - * interface named {@code Supplier} that extends {@link Supplier}, and if so, set {@code this.lazyBindingsType} and |
202 | | - * {@code this.supplierType}. Otherwise, set both to {@code null}. |
| 73 | + * @return the Supplier as a LazyBindings.Supplier |
203 | 74 | */ |
204 | | - @SuppressWarnings({"squid:S1872", "unchecked"}) |
205 | | - protected void checkAndSetLazyBindingsType(final Class<?> lazyBindingsType) { |
206 | | - if (lazyBindingsType != null && Bindings.class.isAssignableFrom(lazyBindingsType)) { |
207 | | - this.supplierType = (Class<? extends Supplier>) Stream.of(lazyBindingsType.getDeclaredClasses()) |
208 | | - .filter(clazz -> Supplier.class.getSimpleName().equals(clazz.getSimpleName()) |
209 | | - && Supplier.class.isAssignableFrom(clazz)).findFirst().orElse(null); |
210 | | - this.lazyBindingsType = (Class<? extends Bindings>) lazyBindingsType; |
211 | | - } else { |
212 | | - log.info("Supplier interface not declared by lazyBindingsType: {}, will resort to immediate Bindings values", |
213 | | - lazyBindingsType); |
214 | | - this.supplierType = null; |
215 | | - this.lazyBindingsType = null; |
216 | | - } |
| 75 | + protected LazyBindings.Supplier wrapSupplier(final Supplier<?> supplier) { |
| 76 | + return () -> supplier != null ? supplier.get() : null; |
217 | 77 | } |
218 | 78 |
|
219 | 79 | /** |
220 | | - * Check if provided {@code bindings} implements LazyBindings. |
| 80 | + * Check if provided {@code bindings} is an instance of {@link LazyBindings}. |
221 | 81 | * |
222 | 82 | * @param bindings the parameter from {@link #addBindings(Bindings)} |
223 | 83 | * @return true if bindings implements LazyBindings |
224 | 84 | */ |
225 | 85 | private boolean isLazy(Bindings bindings) { |
226 | | - return Optional.ofNullable(this.lazyBindingsType) |
227 | | - .map(clazz -> clazz.isInstance(bindings)) |
228 | | - .orElse(false); |
| 86 | + return bindings instanceof LazyBindings; |
229 | 87 | } |
230 | 88 |
|
231 | 89 | /** |
|
0 commit comments