Skip to content

Commit

Permalink
SLING-9320 - Allow bundled render units to access objects from the sa…
Browse files Browse the repository at this point in the history
…me bundle through the Use API

* merged the code from the SlingModelsUseProvider into the JavaUseProvider, with an
optional import of the org.apache.sling.models API
* updated the RenderUnitProvider and ScriptUseProvider to work with bundled
render units
  • Loading branch information
raducotescu committed Apr 17, 2020
1 parent a0f0e77 commit bcd4602
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 66 deletions.
1 change: 1 addition & 0 deletions bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Import-Package: org.apache.sling.scripting.sightly.compiler.*;resolution:=op
org.apache.sling.commons.compiler.*;resolution:=optional, \\
org.apache.sling.commons.classloader.*;resolution:=optional, \\
org.apache.sling.scripting.bundle.tracker.*;resolution:=optional, \\
org.apache.sling.models.*;resolution:=optional, \\
*
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@
<version>2.2.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.models.api</artifactId>
<version>1.3.8</version>
<scope>provided</scope>
</dependency>

<!-- javax.servlet, remove if we do not need a response wrapper -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ 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.sling.scripting.sightly.engine;

import java.net.URL;

import javax.script.Bindings;

import org.apache.sling.scripting.bundle.tracker.BundledRenderUnit;
import org.apache.sling.scripting.sightly.render.RenderUnit;
import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;

@ProviderType
public interface BundledUnitManager {

/**
* <p>
* Given a {@link Bindings} map, this method will check if the {@code bindings} contain a value for the {@link
* BundledRenderUnit#VARIABLE} property and if the object provided by {@link BundledRenderUnit#getUnit()} is an instance of a {@link
* RenderUnit}. If so, this service will return the {@link ClassLoader} of the {@link org.osgi.framework.Bundle} providing the {@link
* BundledRenderUnit}.</p>
*
* @param bindings the bindings passed initially to the HTL Script Engine
* @return the {@link BundledRenderUnit}'s classloader if one is found, {@code null} otherwise
*/
@Nullable ClassLoader getBundledRenderUnitClassloader(Bindings bindings);

/**
* Given a {@link Bindings} map, this method will check if the {@code bindings} contain a value for the {@link
* BundledRenderUnit#VARIABLE} property and, if a {@link BundledRenderUnit} is found, attempt to return the URL of dependency that the
* {@link BundledRenderUnit} needs to load. This will take into account the bundle wirings of the unit's providing bundle (see {@link
* BundledRenderUnit#getBundle()}).
*
* @param bindings the bindings passed initially to the HTL Script Engine
* @param identifier the identifier of the dependency that a {@link BundledRenderUnit} from the {@link Bindings} needs to load
* @return the URL of the {@code identifier} dependency, if one was found
*/
URL getScript(Bindings bindings, String identifier);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
@Version("1.0.0")
@Version("1.1.0")
package org.apache.sling.scripting.sightly.engine;

import org.osgi.annotation.versioning.Version;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import javax.script.ScriptException;

import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
import org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManager;
import org.apache.sling.scripting.sightly.engine.BundledUnitManager;
import org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManagerImpl;
import org.apache.sling.scripting.sightly.impl.engine.compiled.SlingHTLMasterCompiler;
import org.apache.sling.scripting.sightly.render.RenderUnit;
import org.slf4j.Logger;
Expand All @@ -42,11 +43,11 @@ public class SightlyScriptEngine extends AbstractSlingScriptEngine implements Co
private static final Logger LOGGER = LoggerFactory.getLogger(SightlyScriptEngine.class);

private SlingHTLMasterCompiler slingHTLMasterCompiler;
private BundledUnitManager bundledUnitManager;
private BundledUnitManagerImpl bundledUnitManager;
private ExtensionRegistryService extensionRegistryService;

SightlyScriptEngine(SightlyScriptEngineFactory factory, ExtensionRegistryService extensionRegistryService,
SlingHTLMasterCompiler slingHTLMasterCompiler, BundledUnitManager bundledUnitManager) {
SlingHTLMasterCompiler slingHTLMasterCompiler, BundledUnitManagerImpl bundledUnitManager) {
super(factory);
this.extensionRegistryService = extensionRegistryService;
this.slingHTLMasterCompiler = slingHTLMasterCompiler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import javax.script.ScriptEngineFactory;

import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
import org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManager;
import org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManagerImpl;
import org.apache.sling.scripting.sightly.impl.engine.compiled.SlingHTLMasterCompiler;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
Expand All @@ -49,7 +49,7 @@ public class SightlyScriptEngineFactory extends AbstractScriptEngineFactory {
private SlingHTLMasterCompiler slingHTLMasterCompiler;

@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
private BundledUnitManager bundledUnitManager;
private BundledUnitManagerImpl bundledUnitManager;

@Reference
private ExtensionRegistryService extensionRegistryService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@

import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.charset.StandardCharsets;

import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

Expand All @@ -38,6 +38,7 @@
import org.apache.sling.scripting.core.ScriptNameAwareReader;
import org.apache.sling.scripting.sightly.impl.engine.SightlyCompiledScript;
import org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngine;
import org.apache.sling.scripting.sightly.engine.BundledUnitManager;
import org.apache.sling.scripting.sightly.impl.utils.BindingsUtils;
import org.apache.sling.scripting.sightly.render.RenderUnit;
import org.jetbrains.annotations.NotNull;
Expand All @@ -55,7 +56,7 @@
/**
* The {@code BundledUnitManager} is an optional service, which is made available only if the {@link
* org.apache.sling.scripting.bundle.tracker} APIs are available. This service allows various components to work with {@link
* org.apache.sling.scripting.bundle.tracker.BundledRenderUnit} instance and perform dependency resolution based on their availability in
* BundledRenderUnit} instance and perform dependency resolution based on their availability in
* the {@link Bindings} maps passed to the HTL Script Engine.
*/
@Component(
Expand All @@ -64,9 +65,9 @@
* this component will register itself as a service only if the org.apache.sling.scripting.bundle.tracker API is present
*/
)
public class BundledUnitManager {
public class BundledUnitManagerImpl implements BundledUnitManager {

private static final Logger LOGGER = LoggerFactory.getLogger(BundledUnitManager.class);
private static final Logger LOGGER = LoggerFactory.getLogger(BundledUnitManagerImpl.class);

private final ServiceRegistration<?> serviceRegistration;

Expand All @@ -77,7 +78,7 @@ public class BundledUnitManager {
private ScriptCache scriptCache;

@Activate
public BundledUnitManager(BundleContext bundleContext) {
public BundledUnitManagerImpl(BundleContext bundleContext) {
serviceRegistration = register(bundleContext);
}

Expand Down Expand Up @@ -129,35 +130,21 @@ public RenderUnit getRenderUnit(@NotNull Bindings bindings, @NotNull String iden
BundledRenderUnit bundledRenderUnit = getBundledRenderUnit(bindings);
Resource currentResource = BindingsUtils.getResource(bindings);
if (currentResource != null && bundledRenderUnit != null) {
boolean absolute = identifier.charAt(0) == '/';
for (TypeProvider provider : bundledRenderUnit.getTypeProviders()) {
for (ResourceType type : provider.getBundledRenderUnitCapability().getResourceTypes()) {
StringBuilder renderUnitIdentifier = new StringBuilder(identifier);
if (!absolute) {
renderUnitIdentifier = renderUnitIdentifier.insert(0, type.toString() + "/");
String renderUnitIdentifier = getResourceTypeQualifiedPath(identifier, type);
String renderUnitBundledPath = renderUnitIdentifier;
if (renderUnitBundledPath.startsWith("/")) {
renderUnitBundledPath = renderUnitBundledPath.substring(1);
}
if (provider.isPrecompiled()) {
String classResourcePath = renderUnitIdentifier.toString();
if (classResourcePath.startsWith("/")) {
classResourcePath = classResourcePath.substring(1);
String className = JavaEscapeHelper.makeJavaPackage(renderUnitBundledPath);
try {
Class<?> clazz = provider.getBundle().loadClass(className);
if (clazz.getSuperclass() == RenderUnit.class) {
return (RenderUnit) clazz.getDeclaredConstructor().newInstance();
}
String className = JavaEscapeHelper.makeJavaPackage(classResourcePath);
try {
Class<?> clazz = provider.getBundle().loadClass(className);
if (clazz.getSuperclass() == RenderUnit.class) {
return (RenderUnit) clazz.getDeclaredConstructor().newInstance();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception ignored) {
// do nothing here
}
} else {
String scriptResourcePath = renderUnitIdentifier.toString();
if (scriptResourcePath.startsWith("/")) {
scriptResourcePath = scriptResourcePath.substring(1);
}
URL bundledScriptURL = provider.getBundle().getEntry("javax.script" + "/" + scriptResourcePath);
} catch (ClassNotFoundException e) {
URL bundledScriptURL = provider.getBundle().getEntry("javax.script" + "/" + renderUnitBundledPath);
if (bundledScriptURL != null) {
try {
SightlyScriptEngine sightlyScriptEngine = (SightlyScriptEngine) scriptEngineManager.getEngineByName(
Expand All @@ -167,39 +154,61 @@ public RenderUnit getRenderUnit(@NotNull Bindings bindings, @NotNull String iden
if (cachedScript != null) {
return ((SightlyCompiledScript) cachedScript.getCompiledScript()).getRenderUnit();
} else {
final String finalRenderUnitIdentifier = renderUnitIdentifier.toString();
try (ScriptNameAwareReader reader =
new ScriptNameAwareReader(new InputStreamReader(bundledScriptURL.openStream(),
StandardCharsets.UTF_8),
finalRenderUnitIdentifier)) {
StandardCharsets.UTF_8), renderUnitIdentifier)) {
SightlyCompiledScript compiledScript =
(SightlyCompiledScript) sightlyScriptEngine.compile(reader);
scriptCache.putScript(new CachedScript() {
@Override
public String getScriptPath() {
return bundledScriptURL.toExternalForm();
}

@Override
public CompiledScript getCompiledScript() {
return compiledScript;
}
});
return compiledScript.getRenderUnit();
}
}
}
} catch (IOException | ScriptException ignored) {

} catch (IOException | ScriptException compileException) {
throw new IllegalStateException(compileException);
}
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
}
}
return null;
}

@Override
@Nullable
public URL getScript(Bindings bindings, String identifier) {
BundledRenderUnit bundledRenderUnit = getBundledRenderUnit(bindings);
Resource currentResource = BindingsUtils.getResource(bindings);
if (currentResource != null && bundledRenderUnit != null) {
for (TypeProvider provider : bundledRenderUnit.getTypeProviders()) {
for (ResourceType type : provider.getBundledRenderUnitCapability().getResourceTypes()) {
String scriptResourcePath = getResourceTypeQualifiedPath(identifier, type);
URL bundledScriptURL = provider.getBundle().getEntry("javax.script" + "/" + scriptResourcePath);
if (bundledScriptURL != null) {
return bundledScriptURL;
}
}
}
}
return null;
}

@NotNull
private String getResourceTypeQualifiedPath(String identifier, ResourceType type) {
boolean absolute = identifier.charAt(0) == '/';
StringBuilder renderUnitIdentifier = new StringBuilder(identifier);
if (!absolute) {
renderUnitIdentifier = renderUnitIdentifier.insert(0, type.toString() + "/");
}
String scriptResourcePath = renderUnitIdentifier.toString();
if (scriptResourcePath.startsWith("/")) {
scriptResourcePath = scriptResourcePath.substring(1);
}
return scriptResourcePath;
}

/**
* <p>
* Given a {@link Bindings} map, this method will check if the {@code bindings} contain a value for the {@link
Expand All @@ -210,6 +219,7 @@ public CompiledScript getCompiledScript() {
* @param bindings the bindings passed initially to the HTL Script Engine
* @return the {@link BundledRenderUnit}'s classloader if one is found, {@code null} otherwise
*/
@Override
@Nullable
public ClassLoader getBundledRenderUnitClassloader(Bindings bindings) {
Object bru = bindings.get(BundledRenderUnit.VARIABLE);
Expand Down Expand Up @@ -245,7 +255,9 @@ public <T> T getServiceForBundledRenderUnit(Bindings bindings, Class<T> clazz) {
private ServiceRegistration<?> register(BundleContext bundleContext) {
try {
BundledUnitManager.class.getClassLoader().loadClass("org.apache.sling.scripting.bundle.tracker.BundledRenderUnit");
return bundleContext.registerService(BundledUnitManager.class, this, null);
return bundleContext.registerService(new String[] {BundledUnitManager.class.getName(),
BundledUnitManagerImpl.class.getName()}, this,
null);
} catch (ClassNotFoundException e) {
LOGGER.info("No support for bundled RenderUnits.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.regex.Matcher;

import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
Expand All @@ -47,6 +48,8 @@
import org.apache.sling.commons.compiler.JavaCompiler;
import org.apache.sling.commons.compiler.Options;
import org.apache.sling.commons.compiler.source.JavaEscapeHelper;
import org.apache.sling.scripting.api.CachedScript;
import org.apache.sling.scripting.api.ScriptCache;
import org.apache.sling.scripting.api.ScriptNameAware;
import org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider;
import org.apache.sling.scripting.sightly.SightlyException;
Expand Down Expand Up @@ -100,6 +103,9 @@ public class SlingHTLMasterCompiler {
@Reference
private ResourceBackedPojoChangeMonitor resourceBackedPojoChangeMonitor;

@Reference
private ScriptCache scriptCache;

private static final String NO_SCRIPT = "NO_SCRIPT";
private static final String JAVA_EXTENSION = ".java";
static final String SIGHTLY_CONFIG_FILE = "/sightly.config";
Expand Down Expand Up @@ -204,6 +210,10 @@ public SightlyCompiledScript compileHTLScript(final SightlyScriptEngine engine,
sName = getScriptName(scriptContext);
}
final String scriptName = sName;
CachedScript cachedScript = scriptCache.getScript(scriptName);
if (cachedScript != null && cachedScript.getCompiledScript() instanceof SightlyCompiledScript) {
return (SightlyCompiledScript) cachedScript.getCompiledScript();
}
CompilationUnit compilationUnit = new CompilationUnit() {
@Override
public String getScriptName() {
Expand Down Expand Up @@ -241,7 +251,19 @@ public Reader getScriptReader() {
String javaSourceCode = javaClassBackendCompiler.build(sourceIdentifier);
Object renderUnit = compileSource(sourceIdentifier, javaSourceCode);
if (renderUnit instanceof RenderUnit) {
return new SightlyCompiledScript(engine, (RenderUnit) renderUnit);
SightlyCompiledScript compiledScript = new SightlyCompiledScript(engine, (RenderUnit) renderUnit);
scriptCache.putScript(new CachedScript() {
@Override
public String getScriptPath() {
return scriptName;
}

@Override
public CompiledScript getCompiledScript() {
return compiledScript;
}
});
return compiledScript;
} else {
throw new SightlyException("Expected a RenderUnit.");
}
Expand Down

0 comments on commit bcd4602

Please sign in to comment.