{"id":21888605,"url":"https://github.com/swisscom/jcr-hopper","last_synced_at":"2025-07-18T15:33:08.859Z","repository":{"id":257949771,"uuid":"862942912","full_name":"swisscom/JCR-Hopper","owner":"swisscom","description":"Migrate AEM with Grace","archived":false,"fork":false,"pushed_at":"2025-02-10T18:17:00.000Z","size":2427,"stargazers_count":8,"open_issues_count":7,"forks_count":5,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-15T10:18:09.600Z","etag":null,"topics":["aem","crx","jcr"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swisscom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-25T12:59:19.000Z","updated_at":"2025-02-10T18:17:04.000Z","dependencies_parsed_at":"2024-11-19T11:35:45.886Z","dependency_job_id":"cf925497-d3cf-4d36-a28d-0b22752d4287","html_url":"https://github.com/swisscom/JCR-Hopper","commit_stats":null,"previous_names":["swisscom/jcr-hopper"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2FJCR-Hopper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2FJCR-Hopper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2FJCR-Hopper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2FJCR-Hopper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swisscom","download_url":"https://codeload.github.com/swisscom/JCR-Hopper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249048746,"owners_count":21204306,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aem","crx","jcr"],"created_at":"2024-11-28T11:16:06.133Z","updated_at":"2025-07-18T15:33:08.845Z","avatar_url":"https://github.com/swisscom.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JCR Hopper\n\n\u003cimg src=\"./docs/logo.svg\" width=200 height=200 alt=\"JCR Hopper Navy Hat\"\u003e\n\n_Migrate AEM with Grace_\n\nJCR Hopper is a migration and reporting tool for \u003cabbr title=\"Adobe Experience Manager\"\u003eAEM\u003c/abbr\u003e’s content repository.\nIt has its own script format that is valid JSON and consists of a series of actions,\neach of which “hops” over nodes in the repository.\n\nJCR Hopper also comes with a visual tool to create, preview and run the scripts.\n\n## Installation\n\nDownload the package (.zip file) from the “Releases” section in GitHub and install it using package manager.\n\nAlternatively, if you don’t want the script builder GUI, you can also only deploy the bundle (.jar file).\n\n## Usage\n\n### Java API\n\nRunning a script requires a script (either as a JSON-encoded string or as an instance of `com.swisscom.aem.tools.jcrhopper.config.Script`) and a runner (`com.swisscom.aem.tools.jcrhopper.Runner`).\n\n#### Creating a script\n\nJCR hopper scripts comprise the following:\n\n- A `log level` that determines which log messages are sent to the run handler.\n- A `list of hop configs` that are run against a given node. Hops can also have their own descendant pipelines of hop configs.\n- A `list of parameters` that the script supports. Parameters always have a default value, thus passing arguments to the script runner is always optional. Each parameter also has a name, a script builder input type hint and an evaluation type that determines how the default values／arguments are to be interpreted.\n\nHigh level structure of Script json content\n\n```json\n{\n\t\"logLevel\": \"info\",\n\t\"hops\": [],\n\t\"parameters\": []\n}\n```\n\n#### Configuring a builder\n\nTypically, a runner is created by means of a builder using `Runner#builder()`.\n\nThe builder offers various methods to configure the runner:\n\n- `#addUtil(String, Object)`, `#addUtils(Map\u003cString, Object\u003e)`: Register namespaces for utility classes／objects that are available in JEXL expressions and script blocks.\n- `#addVariable(String, Object)`, `#addVariables(Map\u003cString, Object\u003e)`: Make some variables known to the script ahead of time.\n- `#addHop(Hop\u003c?\u003e)`, `#addHops(Collection\u003cHop\u003c?\u003e\u003e)`: Make hop types known to the runner. Note: scripts in JSON format can only be parsed once the hop types they are using are registered.\n- `#registerFile(String, Function\u003cString, File\u003e)`, `#registerFiles(Map\u003cString, Function\u003cString, File\u003e\u003e)`: Register file creators that allow scripts to aggregate information into output files.\n- `#addDefaultUtils(boolean)`: if set to `true`, The script will know about the `arrays`, `stream`, `class`, and `collections` utility namespaces.\n- `#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).\n- `#scriptEngineManager(javax.script.ScriptEngineManager)`: Set the script engine manager to use when searching for non-JEXL scripting engines.\n\nOnce the builder is configured, it can be turned into a runner with either `#build(Script)` or `#build(String)` (for JSON scripts).\n\n#### Running a Script\n\nA configured runner offers several overloads to run the script:\n\n- `#run(javax.jcr.Node, boolean)`: Run the script on the given node. Save the session afterwards if the second argument is `true`.\n- `#run(javax.jcr.Session, boolean)`: Same as above but use the root node of the given session.\n- `#run(javax.jcr.Session, boolean, java.util.Map\u003cString, String\u003e)`, `#run(javax.jcr.Node, boolean, java.util.Map\u003cString, String\u003e)`: Same as above but with arguments to be passed to the script. Only arguments matching declared parameters on the script will be taken into account.\n\n#### Using the OSGi Service\n\nThe OSGi service `com.swisscom.aem.tools.jcrhopper.osgi.RunnerService` is used to return pre-configured runner builders that already know about the default hops, utils and files.\n\n#### Extending the OSGi Service\n\nTo extend runner builders returned by the OSGi service, create a component that provides the `com.swisscom.aem.tools.jcrhopper.osgi.RunnerBuilderExtension` service. Implement `#configure` to configure runner builders that are created via `RunnerService`.\n\n### HTTP API\n\n#### Use the Script Builder\n\n![Script builder screenshot](./docs/script-builder.png)\n\nThe script builder is a visual tool designed to help you build, prototype and execute your scripts. It is available at `/apps/jcr-hopper/script-builder.html`.\n\n#### Run the Script Manually\n\nThe `com.swisscom.aem.tools.impl.http.HopRunnerServlet` servlet listens at `/bin/servlets/jcr-hopper/run` (though that is configurable) for POST requests. Each request will trigger one script execution. The script, the commit flag and all script arguments are passed in the request.\n\nThe script will be executed with the JCR session that is associated with the privileges of the requesting HTTP user.\n\nThe servlet uses `RunnerService`, so any registered `RunnerBuilderExtension`s are available.\n\nThe response will be a new-line delimited list of JSON objects or strings.\n\nTo use cURL to execute a script, do the following:\n\n```sh\n# $DO_COMMIT is either \"true\" or \"false\"\n# $AEM_INSTANCE is the base URL of your AEM instance\ncurl -X POST -F \"_script=@/path/to/script.json\" -F \"_commit=$DO_COMMIT\" \"$AEM_INSTANCE/bin/servlets/jcr-hopper/run\"\n```\n\nAdditional arguments can also be passed with `-F`.\n\n\u003csmall\u003eNote: Usually, POST requests are subject to the CSRF filter, thus a `:cq_csrf_token` would need to be added. However, the default configuration allowlists the cURL user agent to not require that.\u003c/small\u003e\n\n#### Add a new sample script\n\nYou can add a new Sample script for your project as well. Follow below steps to add a new sample.\n\n- Add your script (see [Creating a script](#Creating a script) above) as a JSON file under `/apps/jcr-hopper/script-builder/scripts/\u003cyour project folder\u003e/`.\n- If you need a custom title then set a `jcr:title` \u0026 add `mix:title\" mixin` to your json file node.\n\n## Expression Syntax\n\nAll configurable fields are either JEXL expressions (when the result type is arbitrary) or JEXL string templates (when the result is `String`).\n\nConsult the [JEXL syntax reference](https://commons.apache.org/proper/commons-jexl/reference/syntax.html) for further details.\n\n## Action Types\n\n### \u003cimg src=\"./docs/icons/arrow_curve_down.svg\" width=20 height=20 alt=\"\"\u003e Get Child Nodes\n\nGets all child nodes of this node accessible through the current Session that match a given pattern. The pattern may be a full name, a partial name with one or more wildcard characters (\\*), or a disjunction of those (using the | character).\n\nFor more information, see [Node#getNodes(String)](https://adobe.ly/2YrfG1G).\n\nThe nested hop pipeline will run on each matching child node.\n\n### \u003cimg src=\"./docs/icons/pirate_flag.svg\" width=20 height=20 alt=\"\"\u003e Copy Node\n\nCopy the current node recursively to a new destination.\n\nThe nested hop pipeline will run on the copied node.\n\n### \u003cimg src=\"./docs/icons/sparkler.svg\" width=20 height=20 alt=\"\"\u003e Create Child Node\n\nCreate a new node. If the given path is relative, it is based on the current node’s path (usually used to create a child node).\n\nThe nested hop pipeline will run on the created node.\n\n### \u003cimg src=\"./docs/icons/control_knobs.svg\" width=20 height=20 alt=\"\"\u003e Declare Variables\n\nSets variables on the current scope, available in script runner hops and JEXL expressions of all subsequent hops and their nested pipelines.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/loop.svg\" width=20 height=20 alt=\"\"\u003e Iterate\n\nLoops over an arbitrary expression. Can be configured to assume the expression evaluates to an iterable of nodes, in which case that becomes the current node of the nested pipeline. Otherwise, the nested pipeline stays on the current node but the iteration value becomes usable.\n\n### \u003cimg src=\"./docs/icons/hole.svg\" width=20 height=20 alt=\"\"\u003e Filter\n\nRuns the nested pipeline only if the given JEXL expression is matched.\n\n### \u003cimg src=\"./docs/icons/desert_island.svg\" width=20 height=20 alt=\"\"\u003e Move Node\n\nMoves the current node to a new location. Must not be used on the root node.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/magnifying_glass_right.svg\" width=20 height=20 alt=\"\"\u003e Query JCR\n\nRuns an XPath or SQL2 query against the JCR.\n\nRuns the nested pipeline for each node found.\n\n### \u003cimg src=\"./docs/icons/speech_bubble.svg\" width=20 height=20 alt=\"\"\u003e Rename Property\n\nChanges the name of the given property on the current node.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/arrow_up_down.svg\" width=20 height=20 alt=\"\"\u003e Reorder Node\n\nChanges the order of the current node.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/heart_exclamation.svg\" width=20 height=20 alt=\"\"\u003e Resolve Specific Node\n\nResolves a node at a specific location.\n\nRuns the nested pipeline on the resolved node.\n\n### \u003cimg src=\"./docs/icons/pink_potion.svg\" width=20 height=20 alt=\"\"\u003e Run a Script\n\nRuns a script with arbitrary code. Currently, JEXL is supported as well as all languages known to the `javax.script.ScriptEngineManager`.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/pencil.svg\" width=20 height=20 alt=\"\"\u003e Set a Property\n\nSets a property on the current node.\n\nThis hop does not have a nested pipeline.\n\n### \u003cimg src=\"./docs/icons/bang.svg\" width=20 height=20 alt=\"\"\u003e Catch Thrown Errors\n\nCatches exceptions thrown on the nested pipeline and ensures hop execution of the current pipeline continues.\n\n## Acknowledgements\n\n[Mutant Standard emoji](https://mutant.tech/) are licensed CC BY-NC-SA 4.0 International.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fjcr-hopper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswisscom%2Fjcr-hopper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fjcr-hopper/lists"}