SLING-9320 - Allow precompiled units to access objects from the same bundle through the Use API
* extended the JavaUseProvider to use the precompiled unit's bundle classloader for
loading classes
* added IT
diff --git a/pom.xml b/pom.xml
index 9e40c9b..942cb77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,7 +122,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.scripting.bundle.tracker</artifactId>
- <version>0.1.0</version>
+ <version>0.1.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyScriptEngine.java b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyScriptEngine.java
index d11bdee..c9cab5c 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyScriptEngine.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyScriptEngine.java
@@ -70,7 +70,7 @@
try {
SightlyCompiledScript compiledScript = null;
if (precompiledUnitManager != null) {
- compiledScript = precompiledUnitManager.evaluate(this, scriptContext);
+ compiledScript = precompiledUnitManager.getSightlyCompiledScript(this, scriptContext);
} else if (slingHTLMasterCompiler != null) {
compiledScript = slingHTLMasterCompiler.compileHTLScript(this, reader, scriptContext);
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/JavaUseProvider.java b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/JavaUseProvider.java
index 6a45676..faad0de 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/JavaUseProvider.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/JavaUseProvider.java
@@ -18,6 +18,7 @@
******************************************************************************/
package org.apache.sling.scripting.sightly.impl.engine.extension.use;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
@@ -37,6 +38,8 @@
import org.apache.sling.scripting.sightly.render.RenderContext;
import org.apache.sling.scripting.sightly.use.ProviderOutcome;
import org.apache.sling.scripting.sightly.use.UseProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@@ -82,71 +85,49 @@
return ProviderOutcome.failure();
}
Bindings globalBindings = renderContext.getBindings();
- SlingScriptHelper sling = BindingsUtils.getHelper(globalBindings);
SlingHttpServletRequest request = BindingsUtils.getRequest(globalBindings);
Map<String, Object> overrides = setRequestAttributes(request, arguments);
- Object result = null;
try {
+ Exception failure = null;
+ if (precompiledUnitManager != null) {
+ ClassLoader unitClassLoader = precompiledUnitManager.getBundledRenderUnitClassloader(globalBindings);
+ if (unitClassLoader != null) {
+ try {
+ Class<?> clazz = unitClassLoader.loadClass(identifier);
+ return loadObject(clazz, cls -> precompiledUnitManager.getServiceForBundledRenderUnit(globalBindings, clazz),
+ globalBindings,
+ arguments);
+ } catch (Exception e) {
+ // maybe the class will actually come from the repository
+ failure = e;
+ }
+ }
+ }
if (slingHTLMasterCompiler != null) {
- result = slingHTLMasterCompiler.getResourceBackedUseObject(renderContext, identifier);
- }
- if (result != null) {
- if (result instanceof Use) {
- ((Use) result).init(BindingsUtils.merge(globalBindings, arguments));
- }
- return ProviderOutcome.success(result);
- } else {
- LOG.debug("Attempting to load class {} from the classloader cache.", identifier);
- if (precompiledUnitManager != null) {
- result = precompiledUnitManager.getBundledRenderUnitDependency(globalBindings, identifier);
- if (result != null) {
- return ProviderOutcome.success(result);
+ Object result = slingHTLMasterCompiler.getResourceBackedUseObject(renderContext, identifier);
+ if (result != null) {
+ if (result instanceof Use) {
+ ((Use) result).init(BindingsUtils.merge(globalBindings, arguments));
}
- }
- if (slingHTLMasterCompiler != null) {
- ClassLoader classLoader = slingHTLMasterCompiler.getClassLoader();
- // attempt OSGi service load
- if (classLoader != null) {
- Class<?> cls = classLoader.loadClass(identifier);
- // attempt OSGi service load
- result = sling.getService(cls);
- if (result != null) {
- return ProviderOutcome.success(result);
- }
- Object adaptableCandidate = arguments.get(ADAPTABLE);
- if (adaptableCandidate instanceof Adaptable) {
- Adaptable adaptable = (Adaptable) adaptableCandidate;
- result = adaptable.adaptTo(cls);
- if (result != null) {
- return ProviderOutcome.success(result);
- }
- }
- result = request.adaptTo(cls);
- if (result == null) {
- Resource resource = BindingsUtils.getResource(globalBindings);
- result = resource.adaptTo(cls);
- }
- if (result != null) {
- return ProviderOutcome.success(result);
- } else if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())) {
- LOG.debug("Wont attempt to instantiate an interface or abstract class {}", cls.getName());
- return ProviderOutcome.failure();
- } else {
- /*
- * the object was cached by the class loader but it's not adaptable from {@link Resource} or {@link
- * SlingHttpServletRequest}; attempt to load it like a regular POJO that optionally could implement {@link Use}
- */
- result = cls.newInstance();
- if (result instanceof Use) {
- ((Use) result).init(BindingsUtils.merge(globalBindings, arguments));
- }
- return ProviderOutcome.notNullOrFailure(result);
+ return ProviderOutcome.success(result);
+ } else {
+ SlingScriptHelper slingScriptHelper = BindingsUtils.getHelper(globalBindings);
+ if (slingScriptHelper != null) {
+ try {
+ Class<?> clazz = slingHTLMasterCompiler.getClassLoader().loadClass(identifier);
+ return loadObject(clazz, slingScriptHelper::getService, globalBindings, arguments);
+ } catch (Exception e) {
+ failure = e;
}
}
}
- return ProviderOutcome.failure();
+
}
+ if (failure != null) {
+ return ProviderOutcome.failure(failure);
+ }
+ return ProviderOutcome.failure();
} catch (Exception e) {
// any other exception is an error
return ProviderOutcome.failure(e);
@@ -155,6 +136,51 @@
}
}
+ private ProviderOutcome loadObject(@NotNull Class<?> cls, @NotNull ServiceLoader serviceLoader, @NotNull Bindings globalBindings,
+ @NotNull Bindings arguments)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
+ InstantiationException {
+ // attempt OSGi service load
+ Object result = serviceLoader.getService(cls);
+ if (result != null) {
+ return ProviderOutcome.success(result);
+ }
+ Object adaptableCandidate = arguments.get(ADAPTABLE);
+ if (adaptableCandidate instanceof Adaptable) {
+ Adaptable adaptable = (Adaptable) adaptableCandidate;
+ result = adaptable.adaptTo(cls);
+ if (result != null) {
+ return ProviderOutcome.success(result);
+ }
+ }
+ SlingHttpServletRequest request = BindingsUtils.getRequest(globalBindings);
+ if (request != null) {
+ result = request.adaptTo(cls);
+ }
+ if (result == null) {
+ Resource resource = BindingsUtils.getResource(globalBindings);
+ if (resource != null) {
+ result = resource.adaptTo(cls);
+ }
+ }
+ if (result != null) {
+ return ProviderOutcome.success(result);
+ } else if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())) {
+ LOG.debug("Won't attempt to instantiate an interface or abstract class {}", cls.getName());
+ return ProviderOutcome.failure();
+ } else {
+ /*
+ * the object was cached by the class loader but it's not adaptable from {@link Resource} or {@link
+ * SlingHttpServletRequest}; attempt to load it like a regular POJO that optionally could implement {@link Use}
+ */
+ result = cls.getDeclaredConstructor().newInstance();
+ if (result instanceof Use) {
+ ((Use) result).init(BindingsUtils.merge(globalBindings, arguments));
+ }
+ return ProviderOutcome.notNullOrFailure(result);
+ }
+ }
+
private Map<String, Object> setRequestAttributes(ServletRequest request, Bindings arguments) {
Map<String, Object> overrides = new HashMap<>();
for (Map.Entry<String, Object> entry : arguments.entrySet()) {
@@ -184,4 +210,8 @@
}
private static final Object NULL = new Object();
+
+ private interface ServiceLoader {
+ @Nullable Object getService(Class<?> cls);
+ }
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/UseRuntimeExtension.java b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/UseRuntimeExtension.java
index 242228b..d22f064 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/UseRuntimeExtension.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/UseRuntimeExtension.java
@@ -35,7 +35,6 @@
import org.apache.sling.scripting.sightly.render.RuntimeObjectModel;
import org.apache.sling.scripting.sightly.use.ProviderOutcome;
import org.apache.sling.scripting.sightly.use.UseProvider;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@@ -86,9 +85,8 @@
service = UseProvider.class,
cardinality = ReferenceCardinality.MULTIPLE
)
- private void bindUseProvider(ServiceReference serviceReference) {
- BundleContext bundleContext = serviceReference.getBundle().getBundleContext();
- providersMap.put(serviceReference, (UseProvider) bundleContext.getService(serviceReference));
+ private void bindUseProvider(ServiceReference<UseProvider> serviceReference, UseProvider provider) {
+ providersMap.put(serviceReference, provider);
}
private void unbindUseProvider(ServiceReference serviceReference) {
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/precompiled/PrecompiledUnitManager.java b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/precompiled/PrecompiledUnitManager.java
index f880a1d..ca4c8f4 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/precompiled/PrecompiledUnitManager.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/precompiled/PrecompiledUnitManager.java
@@ -25,8 +25,10 @@
import org.apache.sling.scripting.sightly.impl.engine.SightlyCompiledScript;
import org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngine;
import org.apache.sling.scripting.sightly.render.RenderUnit;
+import org.jetbrains.annotations.Nullable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -34,7 +36,6 @@
import org.slf4j.LoggerFactory;
@Component(
- immediate = true,
service = {}
/*
* this component will register itself as a service only if the org.apache.sling.scripting.bundle.tracker API is present
@@ -60,7 +61,7 @@
/**
- * Provides support for evaluating precompiled HTL scripts passed through the {@code scriptContext}. This feature works only when the
+ * Provides support for extracting precompiled HTL scripts passed through the {@code scriptContext}. This feature works only when the
* {@link org.apache.sling.scripting.bundle.tracker.BundledRenderUnit} API is deployed to the platform as well.
*
* @param sightlyScriptEngine the HTL script engine providing access to the HTL runtime
@@ -68,7 +69,8 @@
* @return an instance of the compiled script, if a precompiled {@link RenderUnit} was present in the {@link ScriptContext}, {@code
* null} otherwise
*/
- public SightlyCompiledScript evaluate(SightlyScriptEngine sightlyScriptEngine, ScriptContext scriptContext) {
+ @Nullable
+ public SightlyCompiledScript getSightlyCompiledScript(SightlyScriptEngine sightlyScriptEngine, ScriptContext scriptContext) {
Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
Object bundledRenderUnit = bindings.get(BundledRenderUnit.VARIABLE);
if (bundledRenderUnit instanceof BundledRenderUnit) {
@@ -80,11 +82,22 @@
return null;
}
- public Object getBundledRenderUnitDependency(Bindings bindings, String identifier) {
+ @Nullable
+ public ClassLoader getBundledRenderUnitClassloader(Bindings bindings) {
Object bru = bindings.get(BundledRenderUnit.VARIABLE);
if (bru instanceof BundledRenderUnit) {
BundledRenderUnit bundledRenderUnit = (BundledRenderUnit) bru;
- return bundledRenderUnit.getService(identifier);
+ return bundledRenderUnit.getBundle().adapt(BundleWiring.class).getClassLoader();
+ }
+ return null;
+ }
+
+ @Nullable
+ public <T> T getServiceForBundledRenderUnit(Bindings bindings, Class<?> clazz) {
+ Object bru = bindings.get(BundledRenderUnit.VARIABLE);
+ if (bru instanceof BundledRenderUnit) {
+ BundledRenderUnit bundledRenderUnit = (BundledRenderUnit) bru;
+ return bundledRenderUnit.getService(clazz.getName());
}
return null;
}
@@ -93,7 +106,7 @@
try {
PrecompiledUnitManager.class.getClassLoader().loadClass("org.apache.sling.scripting.bundle.tracker.BundledRenderUnit");
return bundleContext.registerService(PrecompiledUnitManager.class, this, null);
- } catch (Throwable e) {
+ } catch (ClassNotFoundException e) {
LOGGER.info("No support for precompiled scripts.");
}
return null;