From 7de3009ecf09d5a3467ac341300056065e1aa85e Mon Sep 17 00:00:00 2001 From: Raphael Schweikert Date: Thu, 31 Oct 2024 12:28:49 +0100 Subject: [PATCH 1/3] feat(runner): make ScriptEngineManager instance configurable on runner --- README.md | 1 + .../java/com/swisscom/aem/tools/impl/HopContextImpl.java | 6 ++++++ .../java/com/swisscom/aem/tools/impl/RunnerImpl.java | 4 ++++ .../java/com/swisscom/aem/tools/impl/hops/RunScript.java | 2 +- .../com/swisscom/aem/tools/jcrhopper/RunnerBuilder.java | 9 +++++++++ .../swisscom/aem/tools/jcrhopper/context/HopContext.java | 6 ++++++ 6 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dfbdc4c..d4ab5c0 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ The builder offers various methods to configure the runner: - `#registerFile(String, Function)`, `#registerFiles(Map>)`: Register file creators that allow scripts to aggregate information into output files. - `#addDefaultUtils(boolean)`: if set to `true`, The script will know about the `arrays`, `stream`, `class`, and `collections` utility namespaces. - `#runHandler(com.swisscom.aem.tools.jcrhopper.config.RunHandler)`: Set a listener that is informed about script output and events. The default run handler does nothing (but log messages are logged using Slf4j in any case). +- `#scriptEngineManager(javax.script.ScriptEngineManager)`: Set the script engine manager to use when searching for non-JEXL scripting engines. Once the builder is configured, it can be turned into a runner with either `#build(Script)` or `#build(String)` (for JSON scripts). diff --git a/src/main/java/com/swisscom/aem/tools/impl/HopContextImpl.java b/src/main/java/com/swisscom/aem/tools/impl/HopContextImpl.java index 0f6705b..1f2073d 100644 --- a/src/main/java/com/swisscom/aem/tools/impl/HopContextImpl.java +++ b/src/main/java/com/swisscom/aem/tools/impl/HopContextImpl.java @@ -11,6 +11,7 @@ import java.util.Map; import javax.jcr.Node; import javax.jcr.RepositoryException; +import javax.script.ScriptEngineManager; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -123,6 +124,11 @@ public Map getVariables() { return Collections.unmodifiableMap(variables); } + @Override + public ScriptEngineManager getScriptEngineManager() { + return runner.getScriptEngineManager(); + } + // region Logging functions @Override public String getName() { diff --git a/src/main/java/com/swisscom/aem/tools/impl/RunnerImpl.java b/src/main/java/com/swisscom/aem/tools/impl/RunnerImpl.java index 79316c7..0b3cca0 100644 --- a/src/main/java/com/swisscom/aem/tools/impl/RunnerImpl.java +++ b/src/main/java/com/swisscom/aem/tools/impl/RunnerImpl.java @@ -25,6 +25,7 @@ import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.script.ScriptEngineManager; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.commons.jexl3.JexlBuilder; @@ -43,6 +44,9 @@ public class RunnerImpl implements Runner { private final Map variables; private final Map> fileTypeSuppliers; + @Getter + private final ScriptEngineManager scriptEngineManager; + @Getter private final RunHandler runHandler; diff --git a/src/main/java/com/swisscom/aem/tools/impl/hops/RunScript.java b/src/main/java/com/swisscom/aem/tools/impl/hops/RunScript.java index fc168fb..481a11d 100644 --- a/src/main/java/com/swisscom/aem/tools/impl/hops/RunScript.java +++ b/src/main/java/com/swisscom/aem/tools/impl/hops/RunScript.java @@ -73,7 +73,7 @@ private static void runJexl(Config config, HopContext context, Map params, String extension, Writer writer) throws HopperException { - final ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); + final ScriptEngineManager scriptEngineManager = context.getScriptEngineManager(); final ScriptEngine engine = scriptEngineManager.getEngineByExtension(extension); if (engine == null) { throw new IllegalArgumentException("Script type " + extension + " not valid"); diff --git a/src/main/java/com/swisscom/aem/tools/jcrhopper/RunnerBuilder.java b/src/main/java/com/swisscom/aem/tools/jcrhopper/RunnerBuilder.java index 8cca803..a76a2ec 100644 --- a/src/main/java/com/swisscom/aem/tools/jcrhopper/RunnerBuilder.java +++ b/src/main/java/com/swisscom/aem/tools/jcrhopper/RunnerBuilder.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; +import javax.script.ScriptEngineManager; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -43,6 +44,13 @@ public class RunnerBuilder { private final Map variables = new HashMap<>(); private final Map> fileTypeSuppliers = new HashMap<>(); + /** + * The script engine manager for running script types other than JEXL. + */ + @Setter + @Getter + private ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); + private final Gson gson; /** @@ -237,6 +245,7 @@ public Runner build(Script script) { utils, new HashMap<>(variables), new HashMap<>(fileTypeSuppliers), + scriptEngineManager, runHandler, script ); diff --git a/src/main/java/com/swisscom/aem/tools/jcrhopper/context/HopContext.java b/src/main/java/com/swisscom/aem/tools/jcrhopper/context/HopContext.java index 3f84fa7..3117497 100644 --- a/src/main/java/com/swisscom/aem/tools/jcrhopper/context/HopContext.java +++ b/src/main/java/com/swisscom/aem/tools/jcrhopper/context/HopContext.java @@ -6,6 +6,7 @@ import java.util.Map; import javax.jcr.Node; import javax.jcr.RepositoryException; +import javax.script.ScriptEngineManager; import org.slf4j.Logger; public interface HopContext extends Logger { @@ -102,4 +103,9 @@ public interface HopContext extends Logger { * @return a helper for JCR property manipulation */ JcrFunctions getJcrFunctions(); + + /** + * @return the script engine manager for running various script types + */ + ScriptEngineManager getScriptEngineManager(); } From 38c42dcde6752a1b914a6b7888b44e97370dc2c0 Mon Sep 17 00:00:00 2001 From: Raphael Schweikert Date: Thu, 31 Oct 2024 12:37:01 +0100 Subject: [PATCH 2/3] feat(runner): use script engine from OSGi if available --- .../ScriptEngineProviderExtension.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/swisscom/aem/tools/impl/extension/ScriptEngineProviderExtension.java diff --git a/src/main/java/com/swisscom/aem/tools/impl/extension/ScriptEngineProviderExtension.java b/src/main/java/com/swisscom/aem/tools/impl/extension/ScriptEngineProviderExtension.java new file mode 100644 index 0000000..41cf190 --- /dev/null +++ b/src/main/java/com/swisscom/aem/tools/impl/extension/ScriptEngineProviderExtension.java @@ -0,0 +1,20 @@ +package com.swisscom.aem.tools.impl.extension; + +import com.swisscom.aem.tools.jcrhopper.RunnerBuilder; +import com.swisscom.aem.tools.jcrhopper.osgi.RunnerBuilderExtension; +import javax.script.ScriptEngineManager; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; + +@Component(service = RunnerBuilderExtension.class) +public class ScriptEngineProviderExtension implements RunnerBuilderExtension { + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private ScriptEngineManager scriptEngineManager; + + @Override + public void configure(RunnerBuilder builder) { + builder.scriptEngineManager(scriptEngineManager); + } +} From 7c427196c8c1c9985a0e14e3e9355eedab07b32c Mon Sep 17 00:00:00 2001 From: Raphael Schweikert Date: Thu, 31 Oct 2024 13:29:53 +0100 Subject: [PATCH 3/3] feat(editor): pass all possible scripting languages to the editor --- mock/script-builder.html | 1 + .../script-builder/build-area/build-area.html | 7 +++++- src/main/frontend/App.tsx | 11 +++++--- src/main/frontend/editor.tsx | 4 +-- src/main/frontend/model/hops/runScript.ts | 6 ++--- src/main/frontend/sections/RunControls.tsx | 13 +++++++--- src/main/frontend/sections/Runner.tsx | 6 ++--- .../sections/editor/types/RunScriptStep.tsx | 25 +++++++++++-------- src/main/frontend/widgets/CodeEditor.tsx | 4 ++- .../tools/jcrhopper/impl/HopRunnerInfo.java | 23 +++++++++++++++++ 10 files changed, 72 insertions(+), 28 deletions(-) diff --git a/mock/script-builder.html b/mock/script-builder.html index 88d704a..d2f45b5 100644 --- a/mock/script-builder.html +++ b/mock/script-builder.html @@ -42,6 +42,7 @@
diff --git a/src/main/content/jcr_root/apps/jcr-hopper/script-builder/build-area/build-area.html b/src/main/content/jcr_root/apps/jcr-hopper/script-builder/build-area/build-area.html index 1bba759..7dc5731 100644 --- a/src/main/content/jcr_root/apps/jcr-hopper/script-builder/build-area/build-area.html +++ b/src/main/content/jcr_root/apps/jcr-hopper/script-builder/build-area/build-area.html @@ -1 +1,6 @@ -
+
diff --git a/src/main/frontend/App.tsx b/src/main/frontend/App.tsx index 8ec40fc..a0991b9 100644 --- a/src/main/frontend/App.tsx +++ b/src/main/frontend/App.tsx @@ -9,7 +9,10 @@ import { HistoryUpdater, useHistoryImmutable } from './hooks/useHistoryImmutable import { useOnce } from './hooks/useOnce'; import { ScriptEditor } from './sections/ScriptEditor'; -export const RunEndpointContext = createContext(''); +export const EnvironmentContext = createContext({ + runEndpoint: '/', + validScriptingLanguages: {} as Record, +}); export const ScriptContext = createContext>(null!); const RootElement = styled('div')` @@ -78,7 +81,7 @@ const RootElement = styled('div')` } `; -export const App: FC<{ runEndpoint: string }> = props => { +export const App: FC<{ runEndpoint: string; validScriptingLanguages: Record }> = props => { const initialScript = useOnce(getInitialScript); const scriptContext = useHistoryImmutable(initialScript, current => { @@ -86,7 +89,7 @@ export const App: FC<{ runEndpoint: string }> = props => { }); return ( - + @@ -94,6 +97,6 @@ export const App: FC<{ runEndpoint: string }> = props => { - + ); }; diff --git a/src/main/frontend/editor.tsx b/src/main/frontend/editor.tsx index 2fb82ae..862ec5b 100644 --- a/src/main/frontend/editor.tsx +++ b/src/main/frontend/editor.tsx @@ -11,11 +11,11 @@ patchCoralUiCreateElement(); function init() { const target = document.querySelector('.jcr-hopper-builder')!; - const { runEndpoint } = target.dataset; + const { runEndpoint, validScriptingLanguages } = target.dataset; const root = createRoot(target.parentElement!.parentElement!); root.render( - + , ); } diff --git a/src/main/frontend/model/hops/runScript.ts b/src/main/frontend/model/hops/runScript.ts index d97e51c..ef2297f 100644 --- a/src/main/frontend/model/hops/runScript.ts +++ b/src/main/frontend/model/hops/runScript.ts @@ -1,6 +1,6 @@ import { AnyHop } from '.'; -export const SCRIPT_LANGUAGES = { +export const SCRIPT_LANGUAGES: Record = { jexl: 'JEXL', js: 'JavaScript', }; @@ -8,7 +8,7 @@ export const SCRIPT_LANGUAGES = { export interface Type extends AnyHop { type: 'runScript'; code: string; - extension: keyof typeof SCRIPT_LANGUAGES; + extension: string; putLocalsBackIntoScope?: boolean; } @@ -22,7 +22,7 @@ export const title = 'Run a Script'; export function shortDescription(config: Type) { const lang = config.extension ?? 'js'; - const name = SCRIPT_LANGUAGES[lang] ?? lang.toUpperCase(); + const name = lang in SCRIPT_LANGUAGES ? SCRIPT_LANGUAGES[lang] : lang.toUpperCase(); const lines = config.code?.split(/\n/).filter(Boolean).length; if (!lines) { diff --git a/src/main/frontend/sections/RunControls.tsx b/src/main/frontend/sections/RunControls.tsx index 8c920c3..5065836 100644 --- a/src/main/frontend/sections/RunControls.tsx +++ b/src/main/frontend/sections/RunControls.tsx @@ -1,7 +1,7 @@ import React, { FC, FormEvent, useContext, useRef } from 'react'; import { styled } from 'goober'; -import { RunEndpointContext, ScriptContext } from '../App'; +import { EnvironmentContext, ScriptContext } from '../App'; const Elm = styled('form', React.forwardRef)` grid-auto-flow: row; @@ -13,7 +13,7 @@ const Elm = styled('form', React.forwardRef)` export const RunControls: FC<{ runWith: (data: FormData) => Promise }> = ({ runWith }) => { const { current: script } = useContext(ScriptContext); - const endpoint = useContext(RunEndpointContext); + const environmentContext = useContext(EnvironmentContext); const formRef = useRef(null); @@ -31,7 +31,14 @@ export const RunControls: FC<{ runWith: (data: FormData) => Promise }> = ( } return ( - + {script.parameters.length ? (
Arguments diff --git a/src/main/frontend/sections/Runner.tsx b/src/main/frontend/sections/Runner.tsx index e0c9501..3d7fa81 100644 --- a/src/main/frontend/sections/Runner.tsx +++ b/src/main/frontend/sections/Runner.tsx @@ -1,17 +1,17 @@ import React, { FC, useContext, useState } from 'react'; -import { RunEndpointContext } from '../App'; +import { EnvironmentContext } from '../App'; import { Output } from './Output'; import { Run } from '../model/Run'; import { RunControls } from './RunControls'; export const Runner: FC = () => { - const endpoint = useContext(RunEndpointContext); + const environmentContext = useContext(EnvironmentContext); const [runs, setRuns] = useState([]); async function runWith(data: FormData) { - const response = fetch(endpoint, { + const response = fetch(environmentContext.runEndpoint, { method: 'POST', body: data, }); diff --git a/src/main/frontend/sections/editor/types/RunScriptStep.tsx b/src/main/frontend/sections/editor/types/RunScriptStep.tsx index ccb55e5..04eea38 100644 --- a/src/main/frontend/sections/editor/types/RunScriptStep.tsx +++ b/src/main/frontend/sections/editor/types/RunScriptStep.tsx @@ -3,26 +3,28 @@ import React, { forwardRef, useContext } from 'react'; import { Hop } from '../../../model/hops'; import { StepEditor } from '../../../widgets/StepEditor'; -import { SCRIPT_LANGUAGES, shortDescription, title, Type, iconFor } from '../../../model/hops/runScript'; +import { shortDescription, title, Type, iconFor } from '../../../model/hops/runScript'; import { Help } from '../../../widgets/Help'; import { Select } from '../../../widgets/Select'; -import { CodeEditor } from '../../../widgets/CodeEditor'; -import { ScriptContext } from '../../../App'; +import { CodeEditor, EditorLanguage } from '../../../widgets/CodeEditor'; +import { EnvironmentContext, ScriptContext } from '../../../App'; import { Switch } from '../../../widgets/Switch'; export const RunScriptStep = forwardRef(function RunScriptStep({ parentHops, hop }, ref) { const scriptContext = useContext(ScriptContext); + const { validScriptingLanguages } = useContext(EnvironmentContext); + const languageName = validScriptingLanguages[hop.extension]; return (