diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1c45fb5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# Created by https://www.gitignore.io/api/eclipse,java,maven + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +### Vault ### +.vlt + +### IntelliJ ### +.idea/ +*.iml + +.DS_Store diff --git a/LICENSE b/LICENSE index d168c1ff..99873d35 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 valtech.se +Copyright (c) 2018 Valtech GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Readme.md b/Readme.md new file mode 100644 index 00000000..a22e85b1 --- /dev/null +++ b/Readme.md @@ -0,0 +1,307 @@ +# AEM Easy Content Upgrade (AECU) + +AECU simplifies content migrations by executing migration scripts during package installation. It is built on top of [Groovy Console](https://github.com/OlsonDigital/aem-groovy-console). + + +Features: + +* GUI to run scripts and see history of runs +* Run mode support +* Fallback scripts in case of errors +* Extension of Groovy Console bindings +* Service API +* Health Checks + +Table of contents +1. [Requirements](#requirements) +2. [Installation](#installation) +3. [Execution of Migration Scripts](#execution) + 1. [Install Hook](#installHook) + 2. [Manual Execution](#manualExecution) +4. [History of Past Runs](#history) +5. [Extension to Groovy Console](#groovy) +6. [JMX Interface](#jmx) +7. [Health Checks](#healthchecks) +8. [License](#license) + + + + +# Requirements + +AECU requires Java 8 and AEM 6.3 or above. Groovy Console can be installed manually if [bundle install](#bundleInstall) is not used. + + + +# Installation + +TODO + + + +## Bundle Installation + +To simplify installation we provide a bundle package that already includes the Groovy Console. This makes sure there are no compatibility issues. + +TODO + + + + +# Execution of Migration Scripts + + + +## Install Hook + +This is the preferred method to execute your scripts. It allows to run them without any user interaction. Just package them with a content package and do a regular deployment. + +You can add the install hook by adding de.valtech.aecu.core.installhook.AecuInstallHook as a hook to your package properties. The AECU package and Groovy Console need to be installed beforehand. + +```xml + + com.day.jcr.vault + content-package-maven-plugin + true + + src/main/content/META-INF/vault/filter.xml + true + true + Valtech + + de.valtech.aecu.core.installhook.AecuInstallHook + + + +``` + + + +## Manual Execution + +Manual script execution is useful in case you want to manually rerun a script (e.g. because it failed before). You can find the execute feature in AECU's tools menu. + + + +Execution is done in two simple steps: + +1. Select the base path and run the search. This will show a list of runnable scripts. +2. Run all scripts in batch or just single ones. If you run all you can change the order before (drag and drop with marker at the right). + +Once execution is done you will see if the script(s) succeeded. Click on the history link to see the details. + + + + + +# History of Past Runs + +You can find the history in AECU's tools menu. + + + +The history shows all runs that were executed via package install hook, manual run and JMX. It will not display scripts that were executed directly via Groovy Console. + + + +You can click on any run to see the full details. This will show the status for each script. You can also see the output of all scripts. + + + + + +# Extension to Groovy Console + +AECU adds its own binding to Groovy Console. You can reach it using "aecu" in your script. This provides methods to perform common tasks like property modification or node deletion. + +It follows a collect, filter, execute process. + +## Collect Options +In the collect phase you define which nodes should be checked for a migration. + +* forResources(String[] paths): use the given paths without any subnodes +* forChildResourcesOf(String path): use all direct childs of the given path (but no grandchilds) +* forDescendantResourcesOf(String path): use the whole subtree under this path + +You can call these methods multiple times and combine them. They will be merged together. + +Example: + +```java +println aecu.contentUpgradeBuilder() + .forResources((String[])["/content/we-retail/ca/en"]) + .forChildResourcesOf("/content/we-retail/us/en") + .forDescendantResourcesOf("/content/we-retail/us/en/experience") + .doSetProperty("name", "value") + .run() +``` + +## Filter options +These methods can be used to filter the nodes that were collected above. Multiple filters can be applied for one run. + +### Filter by Properties +Use this to filter by a list of property values (e.g. sling:resourceType). + +```java +filterByProperties(Map properties) +``` + +Example: + +```java +def conditionMap = [:] +conditionMap["sling:resourceType"] = "weretail/components/structure/page" + +println aecu.contentUpgradeBuilder() + .forChildResourcesOf("/content/we-retail/ca/en") + .filterByProperties(conditionMap) + .doSetProperty("name", "value") + .run() +``` + + + +### Filter by Node Name + +You can also filter nodes by their name. + +* filterByNodeName(String name): process only nodes which have this exact name +* filterByNodeNameRegex(String regex): process nodes that have a name that matches the given regular expression + +```java +println aecu.contentUpgradeBuilder() + .forChildResourcesOf("/content/we-retail/ca/en") + .filterByNodeName("jcr:content") + .filterByNodeNameRegex("jcr.*") + .doSetProperty("name", "value") + .run() +``` + + + +### Combine Multiple Filters +You can combine filters with AND and OR to build more complex filters. + +```java +def conditionMap_type = [:] +conditionMap_type['sling:resourceType'] = "weretail/components/content/heroimage" +def conditionMap_file = [:] +conditionMap_file['fileReference'] = "/content/dam/we-retail/en/activities/running/fitness-woman.jpg" +def conditionMap_page = [:] +conditionMap_page['jcr:primaryType'] = "cq:PageContent" + +def complexFilter = new ORFilter( + [ new FilterByProperties(conditionMap_page), + new ANDFilter( [ + new FilterByProperties(conditionMap_type), + new FilterByProperties(conditionMap_file) + ] ) + ]) + +println aecu.contentUpgradeBuilder() + .forDescendantResourcesOf("/content/we-retail/ca/en") + .filterWith(complexFilter) + .doSetProperty("name", "value") + .run() +``` + +## Execute Options + +### Update Single-value Properies + +* doSetProperty(String name, Object value): sets the given property to the value. Any existing value is overwritten. +* doDeleteProperty(String name): removes the property with the given name if existing. +* doRenameProperty(String oldName, String newName): renames the given property if existing. If the new property name already exists it will be overwritten. + +```java +println aecu.contentUpgradeBuilder() + .forChildResourcesOf("/content/we-retail/ca/en") + .filterByNodeName("jcr:content") + .doSetProperty("name", "value") + .doDeleteProperty("nameToDelete") + .doRenameProperty("oldName", "newName") + .run() +``` + +### Update Multi-value Properties + +* doAddValuesToMultiValueProperty(String name, String[] values): adds the list of values to a property. The property is created if it does not yet exist. +* doRemoveValuesOfMultiValueProperty(String name, String[] values): removes the list of values from a given property. +* doReplaceValuesOfMultiValueProperty(String name, String[] oldValues, String[] newValues): removes the old values and adds the new values in a given property. + +```java +println aecu.contentUpgradeBuilder() + .forChildResourcesOf("/content/we-retail/ca/en") + .filterByNodeName("jcr:content") + .doAddValuesToMultiValueProperty("name", (String[])["value1", "value2"]) + .doRemoveValuesOfMultiValueProperty("name", (String[])["value1", "value2"]) + .doReplaceValuesOfMultiValueProperty("name", (String[])["old1", "old2"], (String[])["new1", "new2"]) + .run() +``` + +### Copy and Move Properties + +This will copy or move a property to a subnode. You can also change the property name. + +* doCopyPropertyToRelativePath(String name, String newName, String relativeResourcePath): copy the property to the given path under the new name. +* doMovePropertyToRelativePath(String name, String newName, String relativeResourcePath): move the property to the given path under the new name. + +```java +println aecu.contentUpgradeBuilder() + .forChildResourcesOf("/content/we-retail/ca/en") + .filterByNodeName("jcr:content") + .doCopyPropertyToRelativePath("name", "newName", "subnode") + .doMovePropertyToRelativePath("name", "newName", "subnode") + .run() +``` + +TODO + +# JMX Interface + + + +AECU provides JMX methods for executing scripts and reading the history. You can also check the version here. + +## Execute + +This will execute the given script or folder. If a folder is specified then all files (incl. any subfolders) are executed. AECU will respect runmodes during execution. + +Parameters: + * Path: file or folder to execute + +## GetHistory + +Prints the history of the specified last runs. The entries are sorted by date and start with the last run. + +Parameters: + * Start index: starts with 0 (= latest history entry) + * Count: number of entries to print + +## GetFiles + +This will print all files that are executable for a given path. You can use this to check which scripts of a given folder would be executed. + +Parameters: +* Path: file or folder to check + + + + +# Health Checks + +Health checks show you the status of AECU itself and the last migration run. +You can access them on the [status page](http://localhost:4502/libs/granite/operations/content/healthreports/healthreportlist.html/system/sling/monitoring/mbeans/org/apache/sling/healthcheck/HealthCheck/aecuHealthCheckmBean). +For the status of older runs use AECU's history page. + + + + + +# License + +The AC Tool is licensed under the [MIT LICENSE](LICENSE). + +# Developers + +See our [developer zone](docs/developers.md) \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 00000000..d1053df9 --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + de.valtech.aecu + aecu + 0.9 + + + aecu.api + bundle + AECU - API + Api bundle for AECU + + + + + org.apache.sling + maven-sling-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + + javax.inject;version=0.0.0,* + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + org.osgi + osgi.core + + + org.osgi + osgi.cmpn + + + org.osgi + osgi.annotation + + + org.slf4j + slf4j-api + + + javax.jcr + jcr + + + javax.servlet + servlet-api + + + com.adobe.aem + uber-jar + apis + + + org.apache.sling + org.apache.sling.models.api + + + org.apache.commons + commons-lang3 + + + junit + junit + + + org.mockito + mockito-core + + + junit-addons + junit-addons + + + diff --git a/api/src/main/java/de/valtech/aecu/service/AecuException.java b/api/src/main/java/de/valtech/aecu/service/AecuException.java new file mode 100644 index 00000000..02a8746e --- /dev/null +++ b/api/src/main/java/de/valtech/aecu/service/AecuException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.service; + +/** + * Thrown when the AECU service faces an error. + * + * @author Roland Gruber + */ +public class AecuException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Constructor + * + * @param message error message + * @param e original exception + */ + public AecuException(String message, Throwable e) { + super(message, e); + } + + /** + * Constructor + * + * @param message error message + */ + public AecuException(String message) { + super(message); + } + +} diff --git a/api/src/main/java/de/valtech/aecu/service/AecuService.java b/api/src/main/java/de/valtech/aecu/service/AecuService.java new file mode 100644 index 00000000..c39151a7 --- /dev/null +++ b/api/src/main/java/de/valtech/aecu/service/AecuService.java @@ -0,0 +1,124 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.service; + +import java.util.List; + +import org.osgi.annotation.versioning.ProviderType; + +/** + * Service interface for AECU. Use this to execute scripts or query the history.
+ *
+ * How to perform an execution: + *
    + *
  1. Get a list of files to execute using {@link #getFiles(String) getFiles}. This will filter all + * files that do not match the run mode and any fallback scripts.
  2. + *
  3. Start a new history entry to store your results using {@link #createHistoryEntry() + * createHistoryEntry}. This store a new run with in-progress state.
  4. + *
  5. Execute your files one by one with {@link #execute(String) execute}
  6. + *
  7. Store each script run in history using + * {@link #storeExecutionInHistory(HistoryEntry, ExecutionResult) storeExecutionInHistory}
  8. + *
  9. Mark the run as done by closing the history with {@link #finishHistoryEntry(HistoryEntry) + * finishHistoryEntry}
  10. + *
+ * + * @author Roland Gruber + */ +@ProviderType +public interface AecuService { + + /** + * Returns the AECU version. + * + * @return version + */ + String getVersion(); + + /** + * Returns a list of files that can be executed in the given path. + * + * @param path file or folder + * @return list of files that are executable + * @throws AecuException error finding files (e.g. invalid path) + */ + List getFiles(String path) throws AecuException; + + /** + * Checks if the folder matches the system's run modes if specified in folder name. + * + * @param name resource name + * @return matches run modes + */ + boolean matchesRunmodes(String name); + + /** + * Checks if the name is a valid script. + * + * @param name file name + * @return is valid + */ + boolean isValidScriptName(String name); + + /** + * Executes the script at the given position. + * + * @param path path of script + * @return execution result + * @throws AecuException error during execution + */ + ExecutionResult execute(String path) throws AecuException; + + /** + * Starts a new history entry. + * + * @return history entry + * @throws AecuException error setting up entry + */ + HistoryEntry createHistoryEntry() throws AecuException; + + /** + * Stores an execution run in existing history. + * + * @param history history entry + * @param result script execution result + * @return updated history + * @throws AecuException error inserting history entry + */ + HistoryEntry storeExecutionInHistory(HistoryEntry history, ExecutionResult result) throws AecuException; + + /** + * Finishes the history entry. + * + * @param history open history entry + * @return history entry + * @throws AecuException error saving state + */ + HistoryEntry finishHistoryEntry(HistoryEntry history) throws AecuException; + + /** + * Returns the last history entries. The search starts at the newest entry. + * + * @param startIndex start reading at this index (first is 0) + * @param count number of entries to read + * @return history entries (newest first) + * @throws AecuException error reading history + */ + List getHistory(int startIndex, int count) throws AecuException; + +} diff --git a/api/src/main/java/de/valtech/aecu/service/ExecutionResult.java b/api/src/main/java/de/valtech/aecu/service/ExecutionResult.java new file mode 100644 index 00000000..d23bd6fd --- /dev/null +++ b/api/src/main/java/de/valtech/aecu/service/ExecutionResult.java @@ -0,0 +1,127 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.service; + +import org.apache.commons.lang3.StringUtils; + +/** + * Result of a script execution. + * + * @author Roland Gruber + */ +public class ExecutionResult { + + private boolean success; + private String output; + private String time; + private String result; + private ExecutionResult fallbackResult; + private String path; + + /** + * Constructor + * + * @param success execution was successful + * @param time execution time + * @param result result + * @param output script output + * @param fallbackResult fallback script result + * @param path script path + */ + public ExecutionResult(boolean success, String time, String result, String output, ExecutionResult fallbackResult, + String path) { + this.success = success; + this.output = output; + this.time = time; + this.result = result; + this.fallbackResult = fallbackResult; + this.path = path; + } + + /** + * Returns if execution was successful. + * + * @return successful + */ + public boolean isSuccess() { + return success; + } + + /** + * Returns the script result. + * + * @return output + */ + public String getResult() { + return result; + } + + /** + * Returns the script output. + * + * @return output + */ + public String getOutput() { + return output; + } + + /** + * Returns the execution time. + * + * @return time + */ + public String getTime() { + return time; + } + + /** + * Returns the fallback script result if any. + * + * @return result + */ + public ExecutionResult getFallbackResult() { + return fallbackResult; + } + + /** + * Returns the script path. + * + * @return path + */ + public String getPath() { + return path; + } + + @Override + public String toString() { + StringBuilder stringVal = new StringBuilder("Successful: " + Boolean.toString(success) + "Path: " + path); + if (StringUtils.isNotBlank(time)) { + stringVal.append("\n" + "Execution time: " + time); + } + if (StringUtils.isNotBlank(result)) { + stringVal.append("\n" + "Result: " + result); + } + stringVal.append("\n" + "Output: " + output); + if (fallbackResult != null) { + stringVal.append("Fallback script executed:\n" + fallbackResult.toString()); + } + return stringVal.toString(); + } + +} diff --git a/api/src/main/java/de/valtech/aecu/service/HistoryEntry.java b/api/src/main/java/de/valtech/aecu/service/HistoryEntry.java new file mode 100644 index 00000000..e3940e98 --- /dev/null +++ b/api/src/main/java/de/valtech/aecu/service/HistoryEntry.java @@ -0,0 +1,139 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.service; + +import java.util.Date; +import java.util.List; + +/** + * History entry for an execution run. + * + * @author Roland Gruber + */ +public interface HistoryEntry { + + /** + * Execution state (e.g. running) + */ + public enum STATE { + /** Execution ongoing */ + RUNNING("Running"), + /** Execution finished */ + FINISHED("Finished"); + + private String label; + + /** + * Constructor + * + * @param label label + */ + private STATE(String label) { + this.label = label; + } + + /** + * Returns the human-readable label for this result. + * + * @return label + */ + public String getLabel() { + return label; + } + } + + ; + + /** + * Execution result (e.g. successful) + */ + public enum RESULT { + /** All scripts executed successfully */ + SUCCESS("Success"), + /** Execution of one or more scripts failed */ + FAILURE("Failed"), + /** Execution not yet finished */ + UNKNOWN("Unknown"); + + private String label; + + /** + * Constructor + * + * @param label label + */ + private RESULT(String label) { + this.label = label; + } + + /** + * Returns the human-readable label for this result. + * + * @return label + */ + public String getLabel() { + return label; + } + } + + ; + + /** + * Returns the start time of the execution. + * + * @return start + */ + Date getStart(); + + /** + * Returns the end time of the execution. + * + * @return end + */ + Date getEnd(); + + /** + * Returns the single script runs. + * + * @return single results + */ + List getSingleResults(); + + /** + * Returns the current state of the run. + * + * @return state + */ + STATE getState(); + + /** + * Returns the global result of the run. + * + * @return result + */ + RESULT getResult(); + + /** + * Returns the path in repository where the history is stored. + * + * @return path + */ + String getRepositoryPath(); + +} diff --git a/api/src/main/java/de/valtech/aecu/service/package-info.java b/api/src/main/java/de/valtech/aecu/service/package-info.java new file mode 100644 index 00000000..e514cdd1 --- /dev/null +++ b/api/src/main/java/de/valtech/aecu/service/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * This package contains the service API for AEM Easy Content Upgrade (AECU). You can use this to + * integrate AECU into your own software. See {@link de.valtech.aecu.service.AecuService} for a + * starting point. + * + * @author Roland Gruber + */ +@Version("1.0") +package de.valtech.aecu.service; + +import org.osgi.annotation.versioning.Version; diff --git a/bundle/pom.xml b/bundle/pom.xml new file mode 100644 index 00000000..aa33e8b8 --- /dev/null +++ b/bundle/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + + de.valtech.aecu + aecu + 0.9 + + + aecu.bundle + content-package + AECU - Bundle + Bundle package for AECU that includes Groovy Console + + + src/main/content/jcr_root + + + + ${basedir}/src/main/content/jcr_root + + + **/.vlt + **/.vltignore + **/.gitignore + **/*.iml + **/.classpath + **/.project + **/.settings + **/.DS_Store + **/target/** + **/pom.xml + + + + src/main/content/META-INF/vault/definition + ../vault-work/META-INF/vault/definition + + + + + maven-resources-plugin + + true + + + + + com.day.jcr.vault + content-package-maven-plugin + true + + src/main/content/META-INF/vault/filter.xml + true + true + Valtech + + + com.icfolson.aem.groovy.console + aem-groovy-console + + + + + + + org.apache.sling + htl-maven-plugin + + + + + + + com.icfolson.aem.groovy.console + aem-groovy-console + zip + + + diff --git a/bundle/src/main/content/META-INF/vault/definition/thumbnail.png b/bundle/src/main/content/META-INF/vault/definition/thumbnail.png new file mode 100644 index 00000000..c4548ee8 Binary files /dev/null and b/bundle/src/main/content/META-INF/vault/definition/thumbnail.png differ diff --git a/bundle/src/main/content/jcr_root/.gitignore b/bundle/src/main/content/jcr_root/.gitignore new file mode 100644 index 00000000..3385916d --- /dev/null +++ b/bundle/src/main/content/jcr_root/.gitignore @@ -0,0 +1 @@ +/META-INF/ diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 00000000..f8bcd30d --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + de.valtech.aecu + aecu + 0.9 + + + aecu.core + bundle + AECU - Core + Core bundle for AECU + + + + + org.apache.sling + maven-sling-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + + javax.inject;version=0.0.0,* + + de.valtech.aecu.core + + + + + + + + + + de.valtech.aecu + aecu.api + ${project.version} + + + org.codehaus.groovy + groovy-all + + + com.icfolson.aem.groovy.console + aem-groovy-console + + + org.osgi + osgi.core + + + org.osgi + osgi.cmpn + + + org.osgi + osgi.annotation + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + javax.jcr + jcr + + + javax.servlet + servlet-api + + + com.adobe.aem + uber-jar + apis + + + org.apache.sling + org.apache.sling.models.api + + + org.apache.commons + commons-lang3 + + + junit + junit + + + org.mockito + mockito-core + + + junit-addons + junit-addons + + + com.google.code.gson + gson + + + com.google.code.findbugs + jsr305 + + + diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/ContentUpgrade.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/ContentUpgrade.java new file mode 100644 index 00000000..daf5db84 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/ContentUpgrade.java @@ -0,0 +1,214 @@ +package de.valtech.aecu.core.groovy.console.bindings; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.ResourceResolver; +import org.scribe.utils.MapUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; +import de.valtech.aecu.core.groovy.console.bindings.actions.PrintPath; +import de.valtech.aecu.core.groovy.console.bindings.actions.multivalue.AddMultiValues; +import de.valtech.aecu.core.groovy.console.bindings.actions.multivalue.RemoveMultiValues; +import de.valtech.aecu.core.groovy.console.bindings.actions.multivalue.ReplaceMultiValues; +import de.valtech.aecu.core.groovy.console.bindings.actions.properties.CopyPropertyToRelativePath; +import de.valtech.aecu.core.groovy.console.bindings.actions.properties.DeleteProperty; +import de.valtech.aecu.core.groovy.console.bindings.actions.properties.MovePropertyToRelativePath; +import de.valtech.aecu.core.groovy.console.bindings.actions.properties.RenameProperty; +import de.valtech.aecu.core.groovy.console.bindings.actions.properties.SetProperty; +import de.valtech.aecu.core.groovy.console.bindings.actions.resource.CopyResourceToRelativePath; +import de.valtech.aecu.core.groovy.console.bindings.actions.resource.DeleteResource; +import de.valtech.aecu.core.groovy.console.bindings.actions.resource.MoveResourceToRelativePath; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterBy; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterByNodeName; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterByNodeNameRegex; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterByProperties; +import de.valtech.aecu.core.groovy.console.bindings.traversers.ForChildResourcesOf; +import de.valtech.aecu.core.groovy.console.bindings.traversers.ForDescendantResourcesOf; +import de.valtech.aecu.core.groovy.console.bindings.traversers.ForResources; +import de.valtech.aecu.core.groovy.console.bindings.traversers.TraversData; + +public class ContentUpgrade { + + private static Logger LOG = LoggerFactory.getLogger(ContentUpgrade.class); + + private ResourceResolver resourceResolver = null; + + private List traversals = new ArrayList<>(); + private FilterBy filter = null; + private List actions = new ArrayList<>(); + + + public ContentUpgrade(@Nonnull ResourceResolver resourceResolver) { + this.resourceResolver = resourceResolver; + } + + /** + * Loops for given list of resources. + * + * @param paths list of paths + * @return upgrade object + **/ + public ContentUpgrade forResources(@Nonnull String[] paths) { + LOG.debug("forResources: {}", paths.toString()); + traversals.add(new ForResources(paths)); + return this; + } + + public ContentUpgrade forChildResourcesOf(@Nonnull String path) { + LOG.debug("forChildResourcesOf: {}", path); + traversals.add(new ForChildResourcesOf(path)); + return this; + } + + public ContentUpgrade forDescendantResourcesOf(@Nonnull String path) { + LOG.debug("forDescendantResourcesOf: {}", path); + traversals.add(new ForDescendantResourcesOf(path)); + return this; + } + + /** + * Filters by properties. + * + * @param conditionProperties properties to filter + * @return upgrade object + **/ + public ContentUpgrade filterByProperties(@Nonnull Map conditionProperties) { + LOG.debug("filterByProperties: {}", MapUtils.toString(conditionProperties)); + filter = new FilterByProperties(conditionProperties); + return this; + } + + public ContentUpgrade filterByNodeName(@Nonnull String nodeName) { + LOG.debug("filterByNodeName: {}", nodeName); + filter = new FilterByNodeName(nodeName); + return this; + } + + public ContentUpgrade filterByNodeNameRegex(@Nonnull String regex) { + LOG.debug("filterByNodeNameRegex: {}", regex); + filter = new FilterByNodeNameRegex(regex); + return this; + } + + public ContentUpgrade filterWith(@Nonnull FilterBy filter) { + LOG.debug("filterWith: {}", filter); + this.filter = filter; + return this; + } + + /** + * Sets a property value. + * + * @param name property name + * @param value property value + * @return upgrade object + **/ + public ContentUpgrade doSetProperty(@Nonnull String name, Object value) { + LOG.debug("doSetProperty: {} = {}", name, value); + actions.add(new SetProperty(name, value)); + return this; + } + + public ContentUpgrade doDeleteProperty(@Nonnull String name) { + LOG.debug("doDeleteProperty: {}", name); + actions.add(new DeleteProperty(name)); + return this; + } + + public ContentUpgrade doRenameProperty(@Nonnull String oldName, @Nonnull String newName) { + LOG.debug("doRenameProperty: {} to {}", oldName, newName); + actions.add(new RenameProperty(oldName, newName)); + return this; + } + + public ContentUpgrade doCopyPropertyToRelativePath(@Nonnull String name, String newName, + @Nonnull String relativeResourcePath) { + LOG.debug("doCopyProperty: {} to {}", name, relativeResourcePath); + actions.add(new CopyPropertyToRelativePath(name, newName, resourceResolver, relativeResourcePath)); + return this; + } + + public ContentUpgrade doMovePropertyToRelativePath(@Nonnull String name, String newName, + @Nonnull String relativeResourcePath) { + LOG.debug("doMoveProperty: {} to {}", name, relativeResourcePath); + actions.add(new MovePropertyToRelativePath(name, newName, resourceResolver, relativeResourcePath)); + return this; + } + + public ContentUpgrade doAddValuesToMultiValueProperty(@Nonnull String name, @Nonnull String[] values) { + LOG.debug("doAddToMultiValueProperty: {} + {}", name, Arrays.toString(values)); + actions.add(new AddMultiValues(name, values)); + return this; + } + + public ContentUpgrade doRemoveValuesOfMultiValueProperty(@Nonnull String name, @Nonnull String[] values) { + LOG.debug("doRemoveValuesFromMultiValueProperty: {} - {}", name, Arrays.toString(values)); + actions.add(new RemoveMultiValues(name, values)); + return this; + } + + public ContentUpgrade doReplaceValuesOfMultiValueProperty(@Nonnull String name, @Nonnull String[] oldValues, + @Nonnull String[] newValues) { + LOG.debug("doReplaceValuesOfMultiValueProperty: {} - {}", name, + Arrays.toString(oldValues) + " + " + Arrays.toString(newValues)); + actions.add(new ReplaceMultiValues(name, oldValues, newValues)); + return this; + } + + public ContentUpgrade doCopyResourceToRelativePath(@Nonnull String relativePath) { + LOG.debug("doCopyResource to {}", relativePath); + actions.add(new CopyResourceToRelativePath(relativePath, resourceResolver)); + return this; + } + + public ContentUpgrade doMoveResourceToRelativePath(@Nonnull String relativePath) { + LOG.debug("doMoveResource to {}", relativePath); + actions.add(new MoveResourceToRelativePath(relativePath, resourceResolver)); + return this; + } + + public ContentUpgrade doDeleteResource() { + LOG.debug("doDeleteResource"); + actions.add(new DeleteResource(resourceResolver)); + return this; + } + + /** + * Print path + * + * @return upgrade object + */ + public ContentUpgrade printPath() { + LOG.debug("printPath"); + actions.add(new PrintPath()); + return this; + } + + public StringBuffer run() throws PersistenceException { + LOG.debug("apply content upgrade"); + return run(false); + } + + public StringBuffer dryRun() throws PersistenceException { + LOG.debug("apply content upgrade dry"); + return run(true); + } + + private StringBuffer run(boolean dryRun) throws PersistenceException { + StringBuffer stringBuffer = new StringBuffer("Running content upgrade " + (dryRun ? "DRY" : "") + "...\n"); + for (TraversData traversal : traversals) { + for (Action action : actions) { + traversal.traverse(resourceResolver, filter, action, stringBuffer, dryRun); + } + } + return stringBuffer; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdate.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdate.java new file mode 100644 index 00000000..e3019216 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdate.java @@ -0,0 +1,41 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings; + +import org.apache.sling.api.resource.ResourceResolver; + +/** + * Groovy Console Bindings: Simple Content Update + * + * @author Roxana Muresan + */ +public class SimpleContentUpdate { + + private ResourceResolver resourceResolver; + + + public SimpleContentUpdate(ResourceResolver resourceResolver) { + this.resourceResolver = resourceResolver; + } + + public ContentUpgrade contentUpgradeBuilder() { + return new ContentUpgrade(resourceResolver); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/Action.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/Action.java new file mode 100644 index 00000000..9be0adba --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/Action.java @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public interface Action { + + String doAction(@Nonnull Resource resource) throws PersistenceException; + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/PrintPath.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/PrintPath.java new file mode 100644 index 00000000..b527af8d --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/PrintPath.java @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class PrintPath implements Action { + + @Override + public String doAction(@Nonnull Resource resource) { + return resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/AddMultiValues.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/AddMultiValues.java new file mode 100644 index 00000000..90cb88df --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/AddMultiValues.java @@ -0,0 +1,65 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.multivalue; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class AddMultiValues implements Action { + + private String name; + private String[] values; + + public AddMultiValues(@Nonnull String name, @Nonnull String[] values) { + this.name = name; + this.values = Arrays.stream(values).filter(f -> f != null).collect(Collectors.toList()).toArray(new String[] {}); + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + if (properties != null) { + String[] currentValues = properties.get(name, String[].class); + List valuesList = new ArrayList<>(); + if (currentValues != null && currentValues.length > 0) { + Collections.addAll(valuesList, currentValues); + } + Collections.addAll(valuesList, values); + properties.put(name, valuesList.toArray(new String[] {})); + + return "Adding values " + Arrays.toString(values) + " to multi-value property " + name + ": " + + Arrays.toString(currentValues) + " results in " + valuesList + " for resource " + resource.getPath(); + } + return "WARNING: could not get ModifiableValueMap for resource " + resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/RemoveMultiValues.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/RemoveMultiValues.java new file mode 100644 index 00000000..93928cd0 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/RemoveMultiValues.java @@ -0,0 +1,66 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.multivalue; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class RemoveMultiValues implements Action { + + private String name; + private String[] values; + + public RemoveMultiValues(@Nonnull String name, @Nonnull String[] values) { + this.name = name; + this.values = Arrays.stream(values).filter(f -> f != null).collect(Collectors.toList()).toArray(new String[] {}); + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + if (properties != null) { + String[] currentValues = properties.get(name, String[].class); + List valuesList = new ArrayList<>(); + if (currentValues != null && currentValues.length > 0) { + Collections.addAll(valuesList, currentValues); + } + valuesList.removeAll(Arrays.asList(values)); + properties.put(name, valuesList.toArray(new String[] {})); + + return "Removing values " + Arrays.toString(values) + " from multi-value property " + name + ": " + + Arrays.toString(currentValues) + " results in " + valuesList + " for resource " + resource.getPath(); + } + return "WARNING: could not get ModifiableValueMap for resource " + resource.getPath(); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/ReplaceMultiValues.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/ReplaceMultiValues.java new file mode 100644 index 00000000..b975158e --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/multivalue/ReplaceMultiValues.java @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.multivalue; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class ReplaceMultiValues implements Action { + + private String name; + private String[] oldValues; + private String[] newValues; + + public ReplaceMultiValues(@Nonnull String name, @Nonnull String[] oldValues, @Nonnull String[] newValues) { + this.name = name; + this.oldValues = Arrays.stream(oldValues).filter(f -> f != null).collect(Collectors.toList()).toArray(new String[] {}); + this.newValues = Arrays.stream(newValues).filter(f -> f != null).collect(Collectors.toList()).toArray(new String[] {}); + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + if (properties != null) { + String[] currentValues = properties.get(name, String[].class); + List valuesList = new ArrayList<>(); + if (currentValues != null && currentValues.length > 0) { + Collections.addAll(valuesList, currentValues); + } + + String warning = (oldValues.length != newValues.length) + ? "WARNING: old values and new values length mismatch (old: " + Arrays.toString(oldValues) + " , new: " + + Arrays.toString(newValues) + ")" + " -> the smaller length will be considered\n" + : ""; + + for (int i = 0; i < oldValues.length && i < newValues.length; i++) { + Collections.replaceAll(valuesList, oldValues[i], newValues[i]); + } + properties.put(name, valuesList.toArray(new String[] {})); + + return warning + "Replacing values " + Arrays.toString(oldValues) + " with values " + Arrays.toString(newValues) + + " for multi-value property " + name + ": " + Arrays.toString(currentValues) + " results in " + valuesList + + " for resource " + resource.getPath(); + } + return "WARNING: could not get ModifiableValueMap for resource " + resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/CopyPropertyToRelativePath.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/CopyPropertyToRelativePath.java new file mode 100644 index 00000000..74719c2b --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/CopyPropertyToRelativePath.java @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.properties; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ValueMap; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class CopyPropertyToRelativePath implements Action { + + private String relativeResourcePath; + private String name; + private String newName; + private ResourceResolver resourceResolver; + + public CopyPropertyToRelativePath(@Nonnull String name, String newName, @Nonnull ResourceResolver resourceResolver, + @Nonnull String relativeResourcePath) { + this.name = name; + this.newName = newName; + this.resourceResolver = resourceResolver; + this.relativeResourcePath = relativeResourcePath; + } + + @Override + public String doAction(@Nonnull Resource resource) { + ValueMap sourceProperties = resource.adaptTo(ValueMap.class); + + Resource destinationResource = resourceResolver.getResource(resource, relativeResourcePath);// TODO + // null + // check!!!! + ModifiableValueMap destinationProperties = destinationResource.adaptTo(ModifiableValueMap.class);// TODO + // null + // check!!!! + + Object propValue = sourceProperties.get(name); + String key = (newName != null) ? newName : name; + destinationProperties.put(key, propValue); + + return "Coping property " + name + " from " + resource.getPath() + " to resource " + destinationResource.getPath() + + " as " + key; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/DeleteProperty.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/DeleteProperty.java new file mode 100644 index 00000000..5a391702 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/DeleteProperty.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.properties; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class DeleteProperty implements Action { + + private String name; + + public DeleteProperty(@Nonnull String name) { + this.name = name; + } + + @Override + public String doAction(@Nonnull Resource resource) { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + properties.remove(name); + return "Deleting property " + name + " for resource " + resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/MovePropertyToRelativePath.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/MovePropertyToRelativePath.java new file mode 100644 index 00000000..60f5c818 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/MovePropertyToRelativePath.java @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.properties; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class MovePropertyToRelativePath implements Action { + + private String relativeResourcePath; + private String name; + private String newName; + private ResourceResolver resourceResolver; + + public MovePropertyToRelativePath(@Nonnull String name, String newName, @Nonnull ResourceResolver resourceResolver, + @Nonnull String relativeResourcePath) { + this.name = name; + this.newName = newName; + this.resourceResolver = resourceResolver; + this.relativeResourcePath = relativeResourcePath; + } + + @Override + public String doAction(@Nonnull Resource resource) { + ModifiableValueMap sourceProperties = resource.adaptTo(ModifiableValueMap.class); + + Resource targetResource = resourceResolver.getResource(resource, relativeResourcePath); + ModifiableValueMap targetProperties = targetResource.adaptTo(ModifiableValueMap.class); + + Object propValue = sourceProperties.get(name); + String key = (newName != null) ? newName : name; + targetProperties.put(key, propValue); + sourceProperties.remove(name); + + return "Moving property " + name + " from " + resource.getPath() + " to resource " + targetResource.getPath() + " as " + + key; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/RenameProperty.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/RenameProperty.java new file mode 100644 index 00000000..dc284eed --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/RenameProperty.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.properties; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class RenameProperty implements Action { + + private String oldName; + private String newName; + + public RenameProperty(@Nonnull String oldName, @Nonnull String newName) { + this.oldName = oldName; + this.newName = newName; + } + + @Override + public String doAction(@Nonnull Resource resource) { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + Object value = properties.remove(oldName); + properties.put(newName, value); + return "Renaming property " + oldName + " to " + newName + " for resource " + resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/SetProperty.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/SetProperty.java new file mode 100644 index 00000000..f3471682 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/properties/SetProperty.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.properties; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class SetProperty implements Action { + + protected String name; + protected Object value; + + public SetProperty(@Nonnull String name, Object value) { + this.name = name; + this.value = value; + } + + @Override + public String doAction(@Nonnull Resource resource) { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + properties.put(name, value); + return "Setting " + value.getClass().getSimpleName() + " property " + name + "=" + value + " for resource " + + resource.getPath(); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/CopyResourceToRelativePath.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/CopyResourceToRelativePath.java new file mode 100644 index 00000000..1d44c3fc --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/CopyResourceToRelativePath.java @@ -0,0 +1,51 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.resource; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class CopyResourceToRelativePath implements Action { + + private String relativePath; + private ResourceResolver resourceResolver; + + public CopyResourceToRelativePath(@Nonnull String relativePath, @Nonnull ResourceResolver resourceResolver) { + this.relativePath = relativePath; + this.resourceResolver = resourceResolver; + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + Resource destinationResource = resourceResolver.getResource(resource, relativePath); + String sourceAbsPAth = resource.getPath(); + String destinationAsPath = destinationResource.getPath(); + resourceResolver.copy(sourceAbsPAth, destinationAsPath); + + return "Copied " + sourceAbsPAth + " to path " + destinationAsPath; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/DeleteResource.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/DeleteResource.java new file mode 100644 index 00000000..fd04374d --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/DeleteResource.java @@ -0,0 +1,46 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.resource; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class DeleteResource implements Action { + + private ResourceResolver resourceResolver; + + public DeleteResource(@Nonnull ResourceResolver resourceResolver) { + this.resourceResolver = resourceResolver; + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + String path = resource.getPath(); + resourceResolver.delete(resource); + return "Deleted resource " + path; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/MoveResourceToRelativePath.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/MoveResourceToRelativePath.java new file mode 100644 index 00000000..1b094705 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/actions/resource/MoveResourceToRelativePath.java @@ -0,0 +1,51 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.actions.resource; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class MoveResourceToRelativePath implements Action { + + private String relativePath; + private ResourceResolver resourceResolver; + + public MoveResourceToRelativePath(@Nonnull String relativePath, @Nonnull ResourceResolver resourceResolver) { + this.relativePath = relativePath; + this.resourceResolver = resourceResolver; + } + + @Override + public String doAction(@Nonnull Resource resource) throws PersistenceException { + Resource destinationResource = resourceResolver.getResource(resource, relativePath); + String sourceAbsPAth = resource.getPath(); + String destinationAsPath = destinationResource.getPath(); + resourceResolver.move(sourceAbsPAth, destinationAsPath); + + return "Moved " + sourceAbsPAth + " to path " + destinationAsPath; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ANDFilter.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ANDFilter.java new file mode 100644 index 00000000..154eaea8 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ANDFilter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import java.util.List; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class ANDFilter implements FilterBy { + + private List filters; + + + public ANDFilter(@Nonnull List filters) { + this.filters = filters; + } + + @Override + public boolean filter(@Nonnull Resource resource) { + boolean foundFalse = filters.parallelStream().filter(f -> f.filter(resource) == false).findAny().isPresent(); + return !foundFalse; + + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterBy.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterBy.java new file mode 100644 index 00000000..99f316f1 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterBy.java @@ -0,0 +1,32 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public interface FilterBy { + + boolean filter(@Nonnull Resource resource); + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeName.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeName.java new file mode 100644 index 00000000..d66cc60c --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeName.java @@ -0,0 +1,40 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import org.apache.sling.api.resource.Resource; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class FilterByNodeName implements FilterBy { + + private String name; + + public FilterByNodeName(@Nonnull String name) { + this.name = name; + } + + @Override + public boolean filter(@Nonnull Resource resource) { + return resource.getName().equals(this.name); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeNameRegex.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeNameRegex.java new file mode 100644 index 00000000..1bba5942 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByNodeNameRegex.java @@ -0,0 +1,40 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import org.apache.sling.api.resource.Resource; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class FilterByNodeNameRegex implements FilterBy { + + private String regex; + + public FilterByNodeNameRegex(@Nonnull String regex) { + this.regex = regex; + } + + @Override + public boolean filter(@Nonnull Resource resource) { + return resource.getName().matches(regex); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByProperties.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByProperties.java new file mode 100644 index 00000000..6c11bd8b --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/FilterByProperties.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class FilterByProperties implements FilterBy { + + private Map conditionProperties = new HashMap<>(); + + public FilterByProperties(@Nonnull Map conditionProperties) { + this.conditionProperties.putAll(conditionProperties); + } + + @Override + public boolean filter(@Nonnull Resource resource) { + ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class); + for (String key : conditionProperties.keySet()) { + String conditionValue = conditionProperties.get(key); + String propertiesValue = properties.get(key, String.class); + + if ((conditionValue == null && propertiesValue != null) + || (conditionValue != null && !conditionValue.equals(propertiesValue))) { + return false; + } + } + return true; + } +} + diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/NOTFilter.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/NOTFilter.java new file mode 100644 index 00000000..ba87296b --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/NOTFilter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class NOTFilter implements FilterBy { + + private FilterBy filter; + + + public NOTFilter(@Nonnull FilterBy filter) { + this.filter = filter; + } + + @Override + public boolean filter(@Nonnull Resource resource) { + return !filter.filter(resource); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ORFilter.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ORFilter.java new file mode 100644 index 00000000..bb637d02 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/filters/ORFilter.java @@ -0,0 +1,44 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.filters; + +import java.util.List; + +import javax.annotation.Nonnull; + +import org.apache.sling.api.resource.Resource; + +/** + * @author Roxana Muresan + */ +public class ORFilter implements FilterBy { + + private List filters; + + + public ORFilter(List filters) { + this.filters = filters; + } + + @Override + public boolean filter(@Nonnull Resource resource) { + boolean foundTrue = filters.parallelStream().filter(f -> f.filter(resource)).findAny().isPresent(); + return foundTrue; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuBindingExtensionProvider.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuBindingExtensionProvider.java new file mode 100644 index 00000000..de08676b --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuBindingExtensionProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.provider; + +import com.icfolson.aem.groovy.console.api.BindingExtensionProvider; + +import de.valtech.aecu.core.groovy.console.bindings.SimpleContentUpdate; +import de.valtech.aecu.core.serviceuser.ServiceResourceResolverService; + +import groovy.lang.Binding; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.LoginException; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides additional AECU Bindings for the Groovy Console + * + * @author Roxana Muresan + */ +@Component(immediate = true) +public class AecuBindingExtensionProvider implements BindingExtensionProvider { + + private static final Logger LOG = LoggerFactory.getLogger(AecuBindingExtensionProvider.class); + + @Reference + private BindingExtensionProvider defaultBindingExtensionProvider; + @Reference + private ServiceResourceResolverService resourceResolverService; + + + @Override + public Binding getBinding(SlingHttpServletRequest request) { + Binding binding = defaultBindingExtensionProvider.getBinding(request); + try { + binding.setVariable("aecu", new SimpleContentUpdate(resourceResolverService.getContentMigratorResourceResolver())); + } catch (LoginException e) { + LOG.error( + "Failed to get resource resolver for aecu-content-migrator, make sure you all the configurations needed for this system user are deployed."); + } + return binding; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuStarImportExtensionProvider.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuStarImportExtensionProvider.java new file mode 100644 index 00000000..15147d97 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/provider/AecuStarImportExtensionProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.provider; + +import com.icfolson.aem.groovy.console.api.StarImportExtensionProvider; +import com.icfolson.aem.groovy.console.constants.GroovyConsoleConstants; + +import org.osgi.service.component.annotations.Component; + +import java.util.Set; + +@Component(immediate = true) +public class AecuStarImportExtensionProvider implements StarImportExtensionProvider { + + @Override + public Set getStarImports() { + Set imports = GroovyConsoleConstants.DEFAULT_STAR_IMPORTS; + imports.add("de.valtech.aecu.core.groovy.console.bindings.filters"); + return imports; + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForChildResourcesOf.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForChildResourcesOf.java new file mode 100644 index 00000000..86f7d690 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForChildResourcesOf.java @@ -0,0 +1,61 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.traversers; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterBy; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +import java.util.Iterator; + +/** + * @author Roxana Muresan + */ +public class ForChildResourcesOf implements TraversData { + + private String path; + + public ForChildResourcesOf(@Nonnull String path) { + this.path = path; + } + + + @Override + public void traverse(@Nonnull ResourceResolver resourceResolver, FilterBy filter, @Nonnull Action action, + @Nonnull StringBuffer stringBuffer, boolean dryRun) throws PersistenceException { + Resource parentResource = resourceResolver.getResource(path); + if (parentResource != null) { + Iterator resourceIterator = resourceResolver.listChildren(parentResource); + while (resourceIterator.hasNext()) { + Resource resource = resourceIterator.next(); + if (filter == null || filter.filter(resource)) { + stringBuffer.append(action.doAction(resource) + "\n"); + } + } + if (!dryRun) { + resourceResolver.commit(); + } + } + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForDescendantResourcesOf.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForDescendantResourcesOf.java new file mode 100644 index 00000000..71cf7dce --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForDescendantResourcesOf.java @@ -0,0 +1,70 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.traversers; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterBy; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +import java.util.Iterator; + +/** + * @author Roxana Muresan + */ +public class ForDescendantResourcesOf implements TraversData { + + private String path; + + public ForDescendantResourcesOf(@Nonnull String path) { + this.path = path; + } + + + @Override + public void traverse(@Nonnull ResourceResolver resourceResolver, FilterBy filter, @Nonnull Action action, + @Nonnull StringBuffer stringBuffer, boolean dryRun) throws PersistenceException { + Resource parentResource = resourceResolver.getResource(path); + if (parentResource != null) { + traverseChildResourcesRecursive(resourceResolver, parentResource, filter, action, stringBuffer, dryRun); + } + } + + private void traverseChildResourcesRecursive(ResourceResolver resourceResolver, Resource resource, FilterBy filter, + Action action, StringBuffer stringBuffer, boolean dryRun) throws PersistenceException { + if (resource != null && resource.hasChildren()) { + Iterator childResources = resource.listChildren(); + while (childResources.hasNext()) { + Resource child = childResources.next(); + if (filter == null || filter.filter(child)) { + stringBuffer.append(action.doAction(child) + "\n"); + } + traverseChildResourcesRecursive(resourceResolver, child, filter, action, stringBuffer, dryRun); + } + if (!dryRun) { + resourceResolver.commit(); + } + } + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForResources.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForResources.java new file mode 100644 index 00000000..a4f2fe41 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/ForResources.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.traversers; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterBy; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public class ForResources implements TraversData { + + private String[] paths; + + public ForResources(@Nonnull String[] paths) { + this.paths = paths; + } + + @Override + public void traverse(@Nonnull ResourceResolver resourceResolver, FilterBy filter, @Nonnull Action action, + @Nonnull StringBuffer stringBuffer, boolean dryRun) throws PersistenceException { + for (String path : paths) { + if (path != null) { + Resource resource = resourceResolver.getResource(path); + if (filter == null || filter.filter(resource)) { + stringBuffer.append(action.doAction(resource) + "\n"); + } + } + } + if (!dryRun) { + resourceResolver.commit(); + } + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/TraversData.java b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/TraversData.java new file mode 100644 index 00000000..5f0e75e1 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/groovy/console/bindings/traversers/TraversData.java @@ -0,0 +1,37 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.groovy.console.bindings.traversers; + +import de.valtech.aecu.core.groovy.console.bindings.actions.Action; +import de.valtech.aecu.core.groovy.console.bindings.filters.FilterBy; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.annotation.Nonnull; + +/** + * @author Roxana Muresan + */ +public interface TraversData { + + void traverse(@Nonnull ResourceResolver resourceResolver, FilterBy filter, @Nonnull Action action, + @Nonnull StringBuffer stringBuffer, boolean dryRun) throws PersistenceException; + +} diff --git a/core/src/main/java/de/valtech/aecu/core/healthcheck/LastRunHealthCheck.java b/core/src/main/java/de/valtech/aecu/core/healthcheck/LastRunHealthCheck.java new file mode 100644 index 00000000..76fa6f92 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/healthcheck/LastRunHealthCheck.java @@ -0,0 +1,72 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.healthcheck; + +import java.util.List; + +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.util.FormattingResultLog; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.HistoryEntry; + +/** + * Checks if the last script run was ok. + * + * @author Roland Gruber + */ +@Component(immediate = true, service = HealthCheck.class, property = {HealthCheck.TAGS + "=aecu", + HealthCheck.NAME + "=AECU Last Run", HealthCheck.MBEAN_NAME + "=aecuLastRunHCmBean"}) +public class LastRunHealthCheck implements HealthCheck { + + @Reference + private AecuService aecuService; + + @Override + public Result execute() { + final FormattingResultLog resultLog = new FormattingResultLog(); + try { + List history = aecuService.getHistory(0, 1); + if (history.isEmpty()) { + resultLog.info("No runs found"); + } else { + HistoryEntry entry = history.get(0); + switch (entry.getResult()) { + case FAILURE: + resultLog.critical("Last execution failed"); + break; + case SUCCESS: + resultLog.info("Last run was successful"); + break; + case UNKNOWN: + resultLog.warn("Last execution is still running"); + break; + } + } + } catch (AecuException e) { + resultLog.critical(e.getMessage()); + } + return new Result(resultLog); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/healthcheck/SelfCheckHealthCheck.java b/core/src/main/java/de/valtech/aecu/core/healthcheck/SelfCheckHealthCheck.java new file mode 100644 index 00000000..74d2a107 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/healthcheck/SelfCheckHealthCheck.java @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.healthcheck; + +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.util.FormattingResultLog; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import de.valtech.aecu.core.serviceuser.ServiceResourceResolverService; + +/** + * Checks if the internal service user is ok. + * + * @author Roland Gruber + */ +@Component(immediate = true, service = HealthCheck.class, property = {HealthCheck.TAGS + "=aecu", + HealthCheck.NAME + "=AECU Self Check", HealthCheck.MBEAN_NAME + "=aecuSelfCheckHCmBean"}) +public class SelfCheckHealthCheck implements HealthCheck { + + @Reference + private ServiceResourceResolverService resolverService; + + @Override + public Result execute() { + final FormattingResultLog resultLog = new FormattingResultLog(); + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + resultLog.info("Ok"); + } catch (LoginException e) { + resultLog.critical("Unable to open service resource resolver {}", e.getMessage()); + } + return new Result(resultLog); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/history/HistoryUtil.java b/core/src/main/java/de/valtech/aecu/core/history/HistoryUtil.java new file mode 100644 index 00000000..3f76d869 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/history/HistoryUtil.java @@ -0,0 +1,441 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.history; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.api.resource.ResourceUtil.BatchResourceRemover; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.jcr.resource.api.JcrResourceConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.valtech.aecu.core.service.HistoryEntryImpl; +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; +import de.valtech.aecu.service.HistoryEntry.RESULT; +import de.valtech.aecu.service.HistoryEntry.STATE; + +/** + * Reads and writes history entries. + * + * @author Roland Gruber + */ +public class HistoryUtil { + + private static final Logger LOG = LoggerFactory.getLogger(HistoryUtil.class); + + private static final String HISTORY_BASE = "/var/aecu"; + + private static final String NODE_FALLBACK = "fallback"; + + private static final String ATTR_PATH = "path"; + private static final String ATTR_RUN_OUTPUT = "runOutput"; + private static final String ATTR_RUN_SUCCESS = "runSuccess"; + private static final String ATTR_RUN_RESULT = "runResult"; + private static final String ATTR_RUN_TIME = "runTime"; + private static final String ATTR_RESULT = "result"; + private static final String ATTR_STATE = "state"; + private static final String ATTR_START = "start"; + private static final String ATTR_END = "end"; + + /** + * Starts a new history entry. + * + * @param resolver resource resolver + * @return history entry + * @throws AecuException error setting up entry + */ + public HistoryEntry createHistoryEntry(ResourceResolver resolver) throws AecuException { + HistoryEntryImpl history = new HistoryEntryImpl(); + Calendar start = new GregorianCalendar(); + String basePath = HISTORY_BASE + "/" + start.get(Calendar.YEAR) + "/" + (start.get(Calendar.MONTH) + 1) + "/" + + start.get(Calendar.DAY_OF_MONTH); + String nodeName = generateHistoryNodeName(); + String nodePath = basePath + "/" + nodeName; + createPath(basePath, resolver, JcrResourceConstants.NT_SLING_ORDERED_FOLDER); + createPath(nodePath, resolver, JcrConstants.NT_UNSTRUCTURED); + Resource resource = resolver.getResource(nodePath); + ModifiableValueMap values = resource.adaptTo(ModifiableValueMap.class); + values.put(ATTR_START, start); + values.put(ATTR_STATE, STATE.RUNNING.name()); + values.put(ATTR_RESULT, RESULT.UNKNOWN.name()); + history.setStart(start.getTime()); + history.setRepositoryPath(nodePath); + history.setState(STATE.RUNNING); + return history; + } + + /** + * Stores an execution run in existing history. + * + * @param history history entry + * @param result script execution result + * @param resolver resource resolver + * @throws AecuException error inserting history entry + */ + public void storeExecutionInHistory(HistoryEntry history, ExecutionResult result, ResourceResolver resolver) + throws AecuException { + String path = history.getRepositoryPath() + "/" + history.getSingleResults().size(); + saveExecutionResultInHistory(result, path, resolver); + } + + /** + * Finishes the history entry. + * + * @param history open history entry + * @param resolver resource resolver + */ + public void finishHistoryEntry(HistoryEntry history, ResourceResolver resolver) { + Resource resource = resolver.getResource(history.getRepositoryPath()); + ModifiableValueMap values = resource.adaptTo(ModifiableValueMap.class); + Calendar end = new GregorianCalendar(); + values.put(ATTR_END, end); + values.put(ATTR_STATE, STATE.FINISHED.name()); + values.put(ATTR_RESULT, history.getResult().name()); + ((HistoryEntryImpl) history).setEnd(end.getTime()); + ((HistoryEntryImpl) history).setState(STATE.FINISHED); + } + + /** + * Returns the last history entries. The search starts at the newest entry. + * + * @param startIndex start reading at this index (first is 0) + * @param count number of entries to read + * @param resolver resource resolver + * @return history entries (newest first) + */ + public List getHistory(int startIndex, int count, ResourceResolver resolver) { + List entries = new ArrayList<>(); + if (count == 0) { + return entries; + } + Resource base = resolver.getResource(HISTORY_BASE); + Resource current = getLatestHistoryEntry(base); + if (current == null) { + return entries; + } + // skip up to start index + for (int i = 0; i < startIndex; i++) { + current = getPreviousHistoryEntry(current); + } + for (int i = 0; i < count; i++) { + if (current == null) { + break; + } + entries.add(readHistoryEntry(current)); + current = getPreviousHistoryEntry(current); + } + return entries; + } + + /** + * Returns the run before the given one. + * + * @param current current run + * @return previous run + */ + private Resource getPreviousHistoryEntry(Resource current) { + // check if the parent has a sibling before the current node + Resource previous = getPreviousSibling(current); + if (previous != null) { + return previous; + } + // go down till we find an earlier sibling + Resource base = descendToPreviousSiblingInHistory(current.getParent()); + // go back up the folders + return ascendToLastRun(base); + } + + /** + * Gos up the folders to last run. + * + * @param resource current node + * @return last run + */ + private Resource ascendToLastRun(Resource resource) { + if (resource == null) { + return null; + } + Resource last = getLastChild(resource); + if (last == null) { + // stop if there is no child at all + return null; + } + ValueMap values = last.adaptTo(ValueMap.class); + if (JcrResourceConstants.NT_SLING_ORDERED_FOLDER.equals(values.get(JcrConstants.JCR_PRIMARYTYPE, String.class))) { + return ascendToLastRun(last); + } + return last; + } + + /** + * Descends in history till a previous sibling is found. Descending stops at history base level + * + * @param current current resource + * @return previous sibling + */ + private Resource descendToPreviousSiblingInHistory(Resource current) { + if ((current == null) || HISTORY_BASE.equals(current.getPath())) { + return null; + } + Resource previous = getPreviousSibling(current); + if (previous != null) { + return previous; + } + previous = descendToPreviousSiblingInHistory(current.getParent()); + return previous; + } + + /** + * Returns the previous sibling of the given node. + * + * @param resource current node + * @return last sibling or null + */ + private Resource getPreviousSibling(Resource resource) { + Iterator siblings = resource.getParent().listChildren(); + Resource previous = null; + while (siblings.hasNext()) { + Resource sibling = siblings.next(); + if (sibling.getName().equals(resource.getName())) { + break; + } + if (!sibling.getName().equals(AccessControlConstants.REP_POLICY)) { + previous = sibling; + } + } + return previous; + } + + /** + * Returns the latest history entry. + * + * @param base base resource + * @return latest run resource + */ + private Resource getLatestHistoryEntry(Resource base) { + if (base == null) { + return null; + } + return ascendToLastRun(base); + } + + /** + * Returns the last child of the given resource. + * + * @param resource resource + * @return last child + */ + private Resource getLastChild(Resource resource) { + if (resource == null) { + return null; + } + Resource last = null; + Iterator lastIterator = resource.listChildren(); + while (lastIterator.hasNext()) { + Resource candidate = lastIterator.next(); + if (!AccessControlConstants.REP_POLICY.equals(candidate.getName())) { + last = candidate; + } + } + return last; + } + + /** + * Reads a history entry from JCR. + * + * @param resource history resource + * @return history entry + */ + public HistoryEntry readHistoryEntry(Resource resource) { + HistoryEntryImpl entry = new HistoryEntryImpl(); + entry.setRepositoryPath(resource.getPath()); + ValueMap values = resource.adaptTo(ValueMap.class); + if (values.containsKey(ATTR_STATE)) { + entry.setState(STATE.valueOf(values.get(ATTR_STATE, String.class))); + } + if (values.containsKey(ATTR_START)) { + entry.setStart(values.get(ATTR_START, Calendar.class).getTime()); + } + if (values.containsKey(ATTR_END)) { + entry.setEnd(values.get(ATTR_END, Calendar.class).getTime()); + } + Iterable children = resource.getChildren(); + for (Resource child : children) { + entry.getSingleResults().add(readHistorySingleResult(child)); + } + return entry; + } + + /** + * Reads a single script run from history. + * + * @param resource resource + * @return result + */ + private ExecutionResult readHistorySingleResult(Resource resource) { + ExecutionResult fallback = null; + Resource fallbackResource = resource.getChild(NODE_FALLBACK); + if (fallbackResource != null) { + fallback = readHistorySingleResult(fallbackResource); + } + ValueMap values = resource.adaptTo(ValueMap.class); + String output = values.get(ATTR_RUN_OUTPUT, ""); + String time = values.get(ATTR_RUN_TIME, ""); + Boolean success = values.get(ATTR_RUN_SUCCESS, Boolean.FALSE); + String runResult = values.get(ATTR_RUN_RESULT, ""); + String path = values.get(ATTR_PATH, ""); + ExecutionResult result = new ExecutionResult(success, time, runResult, output, fallback, path); + return result; + } + + private void saveExecutionResultInHistory(ExecutionResult result, String path, ResourceResolver resolver) + throws AecuException { + createPath(path, resolver, "nt:unstructured"); + Resource entry = resolver.getResource(path); + ModifiableValueMap values = entry.adaptTo(ModifiableValueMap.class); + values.put(ATTR_RUN_SUCCESS, result.isSuccess()); + values.put(ATTR_PATH, result.getPath()); + if (StringUtils.isNotBlank(result.getOutput())) { + values.put(ATTR_RUN_OUTPUT, result.getOutput()); + } + if (StringUtils.isNotBlank(result.getResult())) { + values.put(ATTR_RUN_RESULT, result.getResult()); + } + if (StringUtils.isNotBlank(result.getTime())) { + values.put(ATTR_RUN_TIME, result.getTime()); + } + if (result.getFallbackResult() != null) { + String fallbackPath = path + "/" + NODE_FALLBACK; + saveExecutionResultInHistory(result.getFallbackResult(), fallbackPath, resolver); + } + } + + /** + * Creates the folder at the given path if not yet existing. + * + * @param path path + * @param resolver resource resolver + * @param primaryType primary type + * @throws AecuException error creating folder + */ + protected void createPath(String path, ResourceResolver resolver, String primaryType) throws AecuException { + Resource folder = resolver.getResource(path); + if (folder == null) { + String parent = path.substring(0, path.lastIndexOf("/")); + String name = path.substring(path.lastIndexOf("/") + 1); + if (resolver.getResource(parent) == null) { + createPath(parent, resolver, primaryType); + } + Map properties = new HashMap<>(); + properties.put(JcrConstants.JCR_PRIMARYTYPE, primaryType); + try { + resolver.create(resolver.getResource(parent), name, properties); + } catch (PersistenceException e) { + throw new AecuException("Unable to create " + path, e); + } + } + } + + /** + * Generates the node name for a history entry. + * + * @return name + */ + private String generateHistoryNodeName() { + Random random = new Random(); + return System.currentTimeMillis() + "" + random.nextInt(100000); + } + + /** + * Purges the history by keeping only entries within the set number of days. + * + * @param resolver resource resolver + * @param daysToKeep number of days to keep + * @throws PersistenceException error deleting node + */ + public void purgeHistory(ResourceResolver resolver, int daysToKeep) throws PersistenceException { + Resource base = resolver.getResource(HISTORY_BASE); + Calendar calendar = new GregorianCalendar(); + calendar.add(Calendar.DAY_OF_MONTH, -daysToKeep); + LOG.debug("Starting purge with limit " + calendar.getTime().toString()); + deleteRecursive(base.listChildren(), calendar, new int[] {Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH}); + } + + /** + * Deletes the year resources that are too old. + * + * @param resources resources + * @param calendar time limit + * @param fields calendar fields + * @throws PersistenceException error deleting node + */ + private void deleteRecursive(Iterator resources, Calendar calendar, int[] fields) throws PersistenceException { + int currentField = fields[0]; + while (resources.hasNext()) { + Resource resource = resources.next(); + String name = resource.getName(); + // skip extra nodes such as ACLs + if (!StringUtils.isNumeric(name)) { + LOG.debug("Skipping purge of other node: " + resource.getPath()); + continue; + } + int nodeValue = Integer.parseInt(name); + int limit = calendar.get(currentField); + if (currentField == Calendar.MONTH) { + // months start with 0 but are stored beginning with 1 in CRX + limit++; + } + if (nodeValue > limit) { + LOG.debug("Skipping purge of too young node: " + resource.getPath()); + } else if (nodeValue == limit) { + LOG.debug("Skipping purge of too young node: " + resource.getPath()); + // check next level + if (fields.length == 1) { + return; + } + int[] fieldsNew = new int[fields.length - 1]; + System.arraycopy(fields, 1, fieldsNew, 0, fieldsNew.length); + deleteRecursive(resource.listChildren(), calendar, fieldsNew); + } else { + LOG.debug("Purging node: " + resource.getPath()); + BatchResourceRemover remover = ResourceUtil.getBatchResourceRemover(1000); + remover.delete(resource); + } + } + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/installhook/AecuInstallHook.java b/core/src/main/java/de/valtech/aecu/core/installhook/AecuInstallHook.java new file mode 100644 index 00000000..697bb09d --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/installhook/AecuInstallHook.java @@ -0,0 +1,181 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.installhook; + +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; + +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener; +import org.apache.jackrabbit.vault.fs.io.Archive; +import org.apache.jackrabbit.vault.packaging.InstallContext; +import org.apache.jackrabbit.vault.packaging.InstallHook; +import org.apache.jackrabbit.vault.packaging.PackageException; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * InstallHook handling installation of groovy scripts. The InstallHook gathers groovy scripts + * contained in the installed vault package and executes them depending on active runmodes and if + * the script has been added, modified or never executed.
+ * Example usage in content-package-maven-plugin: + * + *
+ * {@code
+ * 
+ *     com.day.jcr.vault
+ *     content-package-maven-plugin
+ *     true
+ *     
+ *         src/main/content/META-INF/vault/filter.xml
+ *         true
+ *         true
+ *         Valtech
+ *         
+ *             de.valtech.aecu.core.installhook.AecuInstallHook
+ *         
+ *     
+ * 
+ * }
+ * 
+ */ +public class AecuInstallHook implements InstallHook { + + private static final Logger LOG = LoggerFactory.getLogger(AecuInstallHook.class); + + private final OsgiServiceProvider osgiServiceProvider; + private AecuTrackerListener listener; + + public AecuInstallHook() { + this.osgiServiceProvider = new OsgiServiceProvider(this.getClass()); + } + + @Override + public void execute(InstallContext installContext) throws PackageException { + LOG.info("Executing in phase {}", installContext.getPhase()); + ServiceReference aecuServiceReference = osgiServiceProvider.getServiceReference(AecuService.class); + AecuService aecuService = osgiServiceProvider.getService(aecuServiceReference); + + try { + switch (installContext.getPhase()) { + case PREPARE: + ProgressTrackerListener originalListener = installContext.getOptions().getListener(); + listener = new AecuTrackerListener(originalListener, aecuService); + installContext.getOptions().setListener(listener); + break; + case INSTALLED: + + Archive archive = installContext.getPackage().getArchive(); + List allValidScriptCandidatesInArchive = findCandidates("", archive.getJcrRoot(), aecuService); + List scriptsForInstallation = + getScriptsForExecution(allValidScriptCandidatesInArchive, installContext); + + if (!scriptsForInstallation.isEmpty()) { + HistoryEntry installationHistory = executeScripts(scriptsForInstallation, aecuService, installContext); + if (!HistoryEntry.RESULT.SUCCESS.equals(installationHistory.getResult())) { + throw new PackageException("Failed installation, check installation history at " + + installationHistory.getRepositoryPath()); + } + } + break; + default: + break; + } + } catch (IOException | AecuException e) { + throw new PackageException(e); + } finally { + osgiServiceProvider.ungetService(aecuServiceReference); + } + } + + private List getScriptsForExecution(List allValidScriptCandidatesInArchive, InstallContext installContext) { + List scriptsForExecution = new ArrayList<>(); + List modifiedOrAddedScriptPaths = listener.getModifiedOrAddedPaths(); + for (String groovyScriptPath : allValidScriptCandidatesInArchive) { + try { + HookExecutionHistory hookExecutionHistory = + new HookExecutionHistory(installContext.getSession(), groovyScriptPath); + if (shouldExecute(modifiedOrAddedScriptPaths, groovyScriptPath, hookExecutionHistory)) { + scriptsForExecution.add(groovyScriptPath); + } + } catch (AecuException e) { + listener.logError("Could not obtain execution history for " + groovyScriptPath, e); + } + + } + return scriptsForExecution; + } + + private boolean shouldExecute(List modifiedOrAddedScriptPaths, String groovyScriptPath, + HookExecutionHistory hookExecutionHistory) { + return modifiedOrAddedScriptPaths.contains(groovyScriptPath) || !hookExecutionHistory.hasBeenExecutedBefore(); + } + + private HistoryEntry executeScripts(List scriptsForExecution, AecuService aecuService, InstallContext installContext) + throws AecuException, IOException { + HistoryEntry installationHistory = aecuService.createHistoryEntry(); + for (String groovyScriptPath : scriptsForExecution) { + HookExecutionHistory hookExecutionHistory = new HookExecutionHistory(installContext.getSession(), groovyScriptPath); + try { + installationHistory = executeScript(aecuService, installationHistory, groovyScriptPath); + hookExecutionHistory.setExecuted(); + } catch (AecuException e) { + listener.logError("Error executing script " + groovyScriptPath, e); + } + } + installationHistory = aecuService.finishHistoryEntry(installationHistory); + return installationHistory; + } + + private HistoryEntry executeScript(AecuService aecuService, HistoryEntry installationHistory, String groovyScriptPath) + throws AecuException { + listener.logMessage("Executing script " + groovyScriptPath); + ExecutionResult result = aecuService.execute(groovyScriptPath); + installationHistory = aecuService.storeExecutionInHistory(installationHistory, result); + listener.logMessage("Executed script " + groovyScriptPath + ", output: " + result.getOutput()); + return installationHistory; + } + + // mildly duplicated + private List findCandidates(String parent, Archive.Entry entry, AecuService aecuService) { + List candidates = new ArrayList<>(); + if (entry == null) { + return candidates; + } + String entryName = entry.getName(); + String entryPath = parent + "/" + entryName; + + if (entry.isDirectory() && aecuService.matchesRunmodes(entryName)) { + for (Archive.Entry childEntry : entry.getChildren()) { + candidates.addAll(findCandidates(entryPath, childEntry, aecuService)); + } + } else if (aecuService.isValidScriptName(entryName)) { + candidates.add(StringUtils.substringAfter(entryPath, "/jcr_root")); + } + return candidates; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/installhook/AecuTrackerListener.java b/core/src/main/java/de/valtech/aecu/core/installhook/AecuTrackerListener.java new file mode 100644 index 00000000..bc3c3d14 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/installhook/AecuTrackerListener.java @@ -0,0 +1,122 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.installhook; + +import de.valtech.aecu.service.AecuService; + +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; + +/** + * Collects groovy script paths to potentially execute based on the given actions. + */ +public class AecuTrackerListener implements ProgressTrackerListener { + + private static final Logger LOG = LoggerFactory.getLogger(AecuTrackerListener.class); + + private static final Set ACTIONS = new HashSet<>(Arrays.asList("A", "M", "U")); + + private static final int VALID_ACTION_LENGTH = 1; + + private static final String LOG_PREFIX = "AECU InstallHook: "; + + private final ProgressTrackerListener originalListener; + private final AecuService aecuService; + private final List paths; + + /** + * Constructor. + * + * @param originalListener the original ProgressTrackerListener. + * @param aecuService an AecuService instance. + */ + public AecuTrackerListener(ProgressTrackerListener originalListener, AecuService aecuService) { + this.originalListener = originalListener; + this.aecuService = aecuService; + this.paths = new LinkedList<>(); + logMessage("Starting install hook..."); + } + + /** + * Returns an unmodifiable list of the modified or added paths encountered during the + * installation phase. + * + * @return a list of modified or added paths, can be empty. + */ + @Nonnull + public List getModifiedOrAddedPaths() { + return Collections.unmodifiableList(paths); + } + + @Override + public void onMessage(Mode mode, String action, String path) { + originalListener.onMessage(mode, action, path); + + if (StringUtils.length(action) != VALID_ACTION_LENGTH) { + // skip actions like 'Collecting import information... ', 'Package imported.' etc. + return; + } + + if (StringUtils.endsWith(path, "always.groovy")) { + logMessage(String.format("Adding %s due to having 'always' in name.", path)); + paths.add(path); + return; + } + + if (!ACTIONS.contains(action)) { + logMessage(String.format("Skipping %s due to non matching action '%s'", path, action)); + return; + } + + // in case a script was updated the update will actually be shown on jcr:content and not on + // the groovy script node + if (StringUtils.endsWith(path, "/jcr:content")) { + path = StringUtils.substringBefore(path, "/jcr:content"); + } + + if (aecuService.isValidScriptName(path)) { + logMessage(String.format("Found valid script path '%s'", path)); + paths.add(path); + } + } + + @Override + public void onError(Mode mode, String action, Exception e) { + originalListener.onError(mode, action, e); + } + + public void logMessage(String message) { + onMessage(Mode.TEXT, LOG_PREFIX + message, ""); + } + + public void logError(String message, Exception e) { + onError(Mode.TEXT, LOG_PREFIX + message, e); + } +} diff --git a/core/src/main/java/de/valtech/aecu/core/installhook/HookExecutionHistory.java b/core/src/main/java/de/valtech/aecu/core/installhook/HookExecutionHistory.java new file mode 100644 index 00000000..39a96583 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/installhook/HookExecutionHistory.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.installhook; + +import de.valtech.aecu.service.AecuException; + +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.commons.JcrUtils; +import org.apache.jackrabbit.value.DateValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +/** + * Execution history for groovy scripts executed during {@link AecuInstallHook} invocation. + */ +public class HookExecutionHistory { + + private static final Logger LOG = LoggerFactory.getLogger(HookExecutionHistory.class); + + private static final String HISTORY_BASE_PATH = "/var/aecu-installhook"; + + private static final String PN_EXECUTED = "executed"; + + private final Node hookHistory; + + /** + * Constructor. + * + * @param session a session with write permissons on {@value HISTORY_BASE_PATH}. + * @param groovyScriptPath the groovy script to instantiate the execution history for. + * @throws AecuException in case the call to JcrUtils.getOrCreateByPath fails. + */ + public HookExecutionHistory(Session session, String groovyScriptPath) throws AecuException { + try { + hookHistory = JcrUtils.getOrCreateByPath(HISTORY_BASE_PATH + groovyScriptPath, false, JcrConstants.NT_UNSTRUCTURED, + JcrConstants.NT_UNSTRUCTURED, session, true); + } catch (RepositoryException e) { + throw new AecuException("Error getting or creating node at " + HISTORY_BASE_PATH + groovyScriptPath, e); + } + } + + /** + * Returns if the script has been executed before. This is determined by checking existence of + * the property {@value PN_EXECUTED} on the history node. + * + * @return true if it has been executed previously, false otherwise. + */ + public boolean hasBeenExecutedBefore() { + boolean hasBeenExecuted = false; + try { + hasBeenExecuted = hookHistory.hasProperty(PN_EXECUTED); + } catch (RepositoryException e) { + LOG.error(e.getMessage(), e); + } + return hasBeenExecuted; + } + + /** + * Sets {@value PN_EXECUTED} on the history node to the current date. + * + * @throws AecuException in case the property could not be saved. + */ + public void setExecuted() throws AecuException { + try { + hookHistory.setProperty(PN_EXECUTED, new DateValue(Calendar.getInstance())); + hookHistory.getSession().save(); + } catch (RepositoryException e) { + throw new AecuException("Could not set property " + PN_EXECUTED, e); + } + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/installhook/OsgiServiceProvider.java b/core/src/main/java/de/valtech/aecu/core/installhook/OsgiServiceProvider.java new file mode 100644 index 00000000..67da6787 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/installhook/OsgiServiceProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.installhook; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +import javax.annotation.Nonnull; + +/** + * Service Provider for InstallHook. + */ +public class OsgiServiceProvider { + + private final BundleContext bundleContext; + + /** + * Constructor. + * + * @param clazz the class that was loaded over a bundle classloader. + */ + public OsgiServiceProvider(@Nonnull Class clazz) { + Bundle currentBundle = FrameworkUtil.getBundle(clazz); + if (currentBundle == null) { + throw new IllegalStateException("The class " + clazz + " was not loaded through a bundle classloader"); + } + bundleContext = currentBundle.getBundleContext(); + if (bundleContext == null) { + throw new IllegalStateException("Could not get bundle context for bundle " + currentBundle); + } + } + + /** + * Retrieves a {@link ServiceReference} for the given class. + * + * @see org.osgi.framework.BundleContext#getServiceReference(Class) + * @param clazz the class to retrieve a {@link ServiceReference} for. + * @param the type of the service. + * @return a {@link ServiceReference} for the requested service. + */ + @Nonnull + ServiceReference getServiceReference(@Nonnull Class clazz) { + ServiceReference serviceReference = bundleContext.getServiceReference(clazz); + if (serviceReference == null) { + throw new IllegalStateException("Could not retrieve service reference for class " + clazz); + } + return serviceReference; + } + + /** + * Retrieves the service object the {@link ServiceReference} is pointing to. + * + * @see org.osgi.framework.BundleContext#getService(ServiceReference) + * @param serviceReference the {@link ServiceReference}. + * @param the service type. + * @return the service instance. + */ + @Nonnull + T getService(@Nonnull ServiceReference serviceReference) { + T service = bundleContext.getService(serviceReference); + if (service == null) { + throw new IllegalStateException("Could not get the service for reference " + serviceReference + + ", verify that the bundle was installed correctly!"); + } + return service; + } + + /** + * @see org.osgi.framework.BundleContext#ungetService(ServiceReference) + */ + boolean ungetService(@Nonnull ServiceReference serviceReference) { + return bundleContext.ungetService(serviceReference); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBean.java b/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBean.java new file mode 100644 index 00000000..7cc6eed6 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBean.java @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.jmx; + +import java.util.List; + +import com.adobe.granite.jmx.annotation.Description; +import com.adobe.granite.jmx.annotation.Name; + +import de.valtech.aecu.service.AecuException; + +/** + * JMX service interface. + * + * @author Roland Gruber + */ +@Description("AEM Easy Content Upgrade") +public interface AecuServiceMBean { + + /** + * Returns the AECU version. + * + * @return version + */ + @Description("Version") + public String getVersion(); + + /** + * Returns a list of files that can be executed in the given path. + * + * @param path file or folder + * @return list of files that are executable + * @throws AecuException error finding files (e.g. invalid path) + */ + @Description("Returns a list of files that can be executed in the given path") + List getFiles(@Name("Path") @Description("File or folder") String path) throws AecuException; + + /** + * Executes the script at the given position. + * + * @param path path of script + * @return execution result + * @throws AecuException error during execution + */ + @Description("Executes a single file or all files of a folder structure") + String execute(@Name("Path") @Description("Path to file/folder that should be executed") String path) throws AecuException; + + /** + * Returns history entries. + * + * @param start start index (0 is last run) + * @param count number of entries to return + * @return history entries + * @throws AecuException error reading history + */ + @Description("Returns the last history entries") + String getHistory(@Name("Start index") int start, @Name("Count") int count) throws AecuException; + +} diff --git a/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBeanImpl.java b/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBeanImpl.java new file mode 100644 index 00000000..a34241fc --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/jmx/AecuServiceMBeanImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.jmx; + +import java.util.List; + +import javax.management.NotCompliantMBeanException; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import com.adobe.granite.jmx.annotation.AnnotatedStandardMBean; + +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; + +@Component(service = {AecuServiceMBean.class}, immediate = true, + property = {"jmx.objectname=de.valtech:type=AECU", "pattern=/.*"}) +public class AecuServiceMBeanImpl extends AnnotatedStandardMBean implements AecuServiceMBean { + + @Reference + AecuService aecuService; + + /** + * Constructor + * + * @throws NotCompliantMBeanException error setting up mbean + */ + public AecuServiceMBeanImpl() throws NotCompliantMBeanException { + super(AecuServiceMBean.class); + } + + @Override + public String getVersion() { + return aecuService.getVersion(); + } + + @Override + public List getFiles(String path) throws AecuException { + return aecuService.getFiles(path); + } + + @Override + public String execute(String path) throws AecuException { + HistoryEntry history = aecuService.createHistoryEntry(); + List files = aecuService.getFiles(path); + StringBuilder result = new StringBuilder("Found " + files.size() + " files to execute\n\n"); + for (String file : files) { + result.append(file + "\n"); + ExecutionResult singleResult = aecuService.execute(file); + aecuService.storeExecutionInHistory(history, singleResult); + result.append(singleResult.toString()); + result.append("\n\n"); + } + aecuService.finishHistoryEntry(history); + return result.toString(); + } + + @Override + public String getHistory(int start, int count) throws AecuException { + List entries = aecuService.getHistory(start, count); + StringBuilder output = new StringBuilder(); + for (HistoryEntry entry : entries) { + output.append(entry.toString() + "\n\n"); + } + return output.toString(); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryConfiguration.java b/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryConfiguration.java new file mode 100644 index 00000000..e49dbf77 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.maintenance; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.AttributeType; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +/** + * Configuration for purge task. + * + * @author Roland Gruber + */ +@ObjectClassDefinition(name = "AECU Purge history configuration") +public @interface PurgeHistoryConfiguration { + + @AttributeDefinition(type = AttributeType.INTEGER, name = "Days to keep", + description = "Entries younger than this will not be removed") + int daysToKeep(); + +} diff --git a/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryTask.java b/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryTask.java new file mode 100644 index 00000000..d47cd755 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/maintenance/PurgeHistoryTask.java @@ -0,0 +1,78 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.maintenance; + +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.event.jobs.Job; +import org.apache.sling.event.jobs.consumer.JobExecutionContext; +import org.apache.sling.event.jobs.consumer.JobExecutionResult; +import org.apache.sling.event.jobs.consumer.JobExecutor; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.Designate; + +import com.adobe.granite.maintenance.MaintenanceConstants; + +import de.valtech.aecu.core.history.HistoryUtil; +import de.valtech.aecu.core.serviceuser.ServiceResourceResolverService; + +/** + * Purges old entries from the history. + * + * @author Roland Gruber + */ +@Component(property = {MaintenanceConstants.PROPERTY_TASK_NAME + "=AECUPurgeHistory", + MaintenanceConstants.PROPERTY_TASK_TITLE + "=AECU Purge History", + JobExecutor.PROPERTY_TOPICS + "=" + MaintenanceConstants.TASK_TOPIC_PREFIX + "AECUPurgeHistory",}) +@Designate(ocd = PurgeHistoryConfiguration.class) +public class PurgeHistoryTask implements JobExecutor { + + private PurgeHistoryConfiguration config; + + @Reference + private ServiceResourceResolverService resolverService; + + /** + * Activates the service. + * + * @param config configuration + */ + @Activate + public void activate(PurgeHistoryConfiguration config) { + this.config = config; + } + + @Override + public JobExecutionResult process(Job job, JobExecutionContext context) { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + HistoryUtil historyUtil = new HistoryUtil(); + historyUtil.purgeHistory(resolver, config.daysToKeep()); + resolver.commit(); + return context.result().message("Purged AECU history entries").succeeded(); + } catch (LoginException e) { + return context.result().message("Service resolver failed with " + e.getMessage()).failed(); + } catch (PersistenceException e) { + return context.result().message("Purge failed with " + e.getMessage()).failed(); + } + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/model/execute/ExecuteDataSource.java b/core/src/main/java/de/valtech/aecu/core/model/execute/ExecuteDataSource.java new file mode 100644 index 00000000..459da67d --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/model/execute/ExecuteDataSource.java @@ -0,0 +1,78 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.model.execute; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import com.adobe.granite.ui.components.ds.ValueMapResource; + +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.SlingObject; + +import com.adobe.granite.ui.components.ds.DataSource; +import com.adobe.granite.ui.components.ds.SimpleDataSource; + + +/** + * Datasource model for execute page. + * + * @author Bryan Chavez + */ +@Model(adaptables = SlingHttpServletRequest.class) +public class ExecuteDataSource { + + private static final String ITEM_TYPE = "valtech/aecu/tools/execute/dataitem"; + private static final String ALLOWED_PATH = "/etc/groovyconsole/scripts"; + + @SlingObject + SlingHttpServletRequest request; + + @Inject + private AecuService aecuService; + + @PostConstruct + public void setup() throws AecuException { + + String path = request.getParameter("searchPath"); + List entries = new ArrayList<>(); + + if (path != null && StringUtils.isNotEmpty(path) && path.startsWith(ALLOWED_PATH)) { + List allowedScripts = aecuService.getFiles(path); + ResourceResolver resourceResolver = request.getResourceResolver(); + for (String scriptPath : allowedScripts) { + entries.add(new ValueMapResource(resourceResolver, scriptPath, ITEM_TYPE, null)); + } + } + + DataSource ds = new SimpleDataSource(entries.iterator()); + request.setAttribute(DataSource.class.getName(), ds); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataItem.java b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataItem.java new file mode 100644 index 00000000..1a353ee2 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataItem.java @@ -0,0 +1,121 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.model.history; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; + +import javax.annotation.PostConstruct; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.SlingObject; + +import de.valtech.aecu.service.HistoryEntry; +import de.valtech.aecu.service.HistoryEntry.RESULT; +import de.valtech.aecu.service.HistoryEntry.STATE; + +/** + * Model class for a single history item. + * + * @author Roland Gruber + */ +@Model(adaptables = Resource.class) +public class HistoryDataItem { + + private final DateFormat format = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); + + @SlingObject + private Resource resource; + + private HistoryEntry history = null; + + @PostConstruct + public void setup() { + history = resource.adaptTo(ValueMap.class).get(HistoryDataSource.ATTR_HISTORY, HistoryEntry.class); + } + + /** + * Returns the date of the run. + * + * @return date + */ + public String getDate() { + return format.format(history.getEnd()); + } + + /** + * Returns the duration of the run. + * + * @return duration + */ + public String getDuration() { + if (!STATE.FINISHED.equals(history.getState())) { + return ""; + } + Duration duration = Duration.between(history.getStart().toInstant(), history.getEnd().toInstant()); + long seconds = duration.getSeconds(); + if (seconds > 0) { + return duration.getSeconds() + "s"; + } + return (duration.getNano() / 1000000) + "ms"; + } + + /** + * Returns the status icon of the run. + * + * @return icon + */ + public String getStatusIcon() { + if (RESULT.FAILURE.equals(history.getResult())) { + return "closeCircle"; + } + if (RESULT.SUCCESS.equals(history.getResult())) { + return "checkCircle"; + } + return "clock"; + } + + /** + * Returns the status color of the run. + * + * @return icon + */ + public String getStatusColor() { + if (RESULT.FAILURE.equals(history.getResult())) { + return "fail"; + } + if (RESULT.SUCCESS.equals(history.getResult())) { + return "ok"; + } + return "inprogress"; + } + + /** + * Returns the path of the run. + * + * @return path + */ + public String getPath() { + return history.getRepositoryPath(); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataSource.java b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataSource.java new file mode 100644 index 00000000..1a53dba2 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryDataSource.java @@ -0,0 +1,107 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.model.history; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.OSGiService; +import org.apache.sling.models.annotations.injectorspecific.SlingObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.adobe.cq.commerce.common.ValueMapDecorator; +import com.adobe.granite.ui.components.ds.AbstractDataSource; +import com.adobe.granite.ui.components.ds.DataSource; +import com.adobe.granite.ui.components.ds.ValueMapResource; + +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.HistoryEntry; + +/** + * Datasource model for history overview page. + * + * @author Roland Gruber + */ +@Model(adaptables = SlingHttpServletRequest.class) +public class HistoryDataSource { + + private static final String ITEM_TYPE = "valtech/aecu/tools/history/dataitem"; + public static final String ATTR_HISTORY = "history"; + + private Logger LOG = LoggerFactory.getLogger(DataSource.class); + + @SlingObject + SlingHttpServletRequest request; + + @OSGiService + AecuService aecuService; + + @PostConstruct + public void setup() { + String[] selectors = request.getRequestPathInfo().getSelectors(); + int offset = 0; + int limit = 50; + if (selectors.length > 1) { + offset = Integer.parseInt(selectors[0]); + limit = Integer.parseInt(selectors[1]); + } + request.setAttribute(DataSource.class.getName(), getResourceIterator(offset, limit)); + } + + /** + * Returns the history entries. + * + * @param offset offset where to start reading + * @param limit maximum number of entries to return + * @return entries + */ + private DataSource getResourceIterator(int offset, int limit) { + return new AbstractDataSource() { + + @Override + public Iterator iterator() { + List entries = new ArrayList<>(); + try { + List historyEntries = aecuService.getHistory(offset, limit + 1); + for (HistoryEntry historyEntry : historyEntries) { + ValueMap vm = new ValueMapDecorator(new HashMap()); + vm.put(ATTR_HISTORY, historyEntry); + entries.add(new ValueMapResource(request.getResourceResolver(), historyEntry.getRepositoryPath(), + ITEM_TYPE, vm)); + } + } catch (AecuException e) { + LOG.error("Unable to read history entries", e); + } + return entries.iterator(); + } + + }; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/model/history/HistoryOverview.java b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryOverview.java new file mode 100644 index 00000000..02db4c7a --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/model/history/HistoryOverview.java @@ -0,0 +1,151 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.model.history; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.request.RequestParameter; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.SlingObject; + +import de.valtech.aecu.core.history.HistoryUtil; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; + +/** + * Sling model for history overview area. + * + * @author Roland Gruber + */ +@Model(adaptables = SlingHttpServletRequest.class) +public class HistoryOverview { + + @SlingObject + private SlingHttpServletRequest request; + + @SlingObject + private ResourceResolver resolver; + + private HistoryEntry historyEntry; + + private final DateFormat format = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); + + /** + * Reads the history entry from CRX. + */ + @PostConstruct + public void init() { + RequestParameter entryParam = request.getRequestParameter("entry"); + if (entryParam == null) { + return; + } + String path = entryParam.getString(); + Resource historyResource = resolver.getResource(path); + if (historyResource == null) { + return; + } + HistoryUtil historyUtil = new HistoryUtil(); + historyEntry = historyUtil.readHistoryEntry(historyResource); + } + + /** + * Returns the history entry. + * + * @return history + */ + public HistoryEntry getHistory() { + return historyEntry; + } + + /** + * Returns the start as formatted string. + * + * @return start date + */ + public String getStart() { + if ((historyEntry == null) || (historyEntry.getStart() == null)) { + return StringUtils.EMPTY; + } + return format.format(historyEntry.getStart()); + } + + /** + * Returns the end as formatted string. + * + * @return end date + */ + public String getEnd() { + if ((historyEntry == null) || (historyEntry.getEnd() == null)) { + return StringUtils.EMPTY; + } + return format.format(historyEntry.getEnd()); + } + + /** + * Returns the duration. + * + * @return duration + */ + public String getDuration() { + Duration duration = Duration.between(historyEntry.getStart().toInstant(), historyEntry.getEnd().toInstant()); + long seconds = duration.getSeconds(); + if (seconds > 0) { + return duration.getSeconds() + "s"; + } + return (duration.getNano() / 1000000) + "ms"; + } + + /** + * Returns the percentages of successful and failed scripts. + * + * @return percentages (successful, failed) + */ + public Pair getPercentages() { + int countAll = historyEntry.getSingleResults().size(); + if (countAll == 0) { + return Pair.of("0", "0"); + } + double countOk = 0; + double countFailed = 0; + for (ExecutionResult result : historyEntry.getSingleResults()) { + if (result.isSuccess()) { + countOk++; + } else { + countFailed++; + } + } + BigDecimal percentageOk = new BigDecimal((countOk / countAll) * 100); + BigDecimal percentageFailed = new BigDecimal((countFailed / countAll) * 100); + String valueOk = percentageOk.round(new MathContext(2)).toPlainString(); + String valueFailed = percentageFailed.round(new MathContext(2)).toPlainString(); + return Pair.of(valueOk, valueFailed); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/service/AecuServiceImpl.java b/core/src/main/java/de/valtech/aecu/core/service/AecuServiceImpl.java new file mode 100644 index 00000000..0d2af5b2 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/service/AecuServiceImpl.java @@ -0,0 +1,261 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.JcrConstants; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.jcr.resource.api.JcrResourceConstants; +import org.apache.sling.settings.SlingSettingsService; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import com.icfolson.aem.groovy.console.GroovyConsoleService; +import com.icfolson.aem.groovy.console.response.RunScriptResponse; + +import de.valtech.aecu.core.history.HistoryUtil; +import de.valtech.aecu.core.serviceuser.ServiceResourceResolverService; +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; +import de.valtech.aecu.service.HistoryEntry.STATE; + +/** + * AECU service. + * + * @author Roland Gruber + */ +@Component(service = AecuService.class) +public class AecuServiceImpl implements AecuService { + + @Reference + private ServiceResourceResolverService resolverService; + + @Reference + private SlingSettingsService slingSettings; + + @Reference + private GroovyConsoleService groovyConsoleService; + + @Override + public String getVersion() { + return FrameworkUtil.getBundle(AecuServiceImpl.class).getVersion().toString(); + } + + @Override + public List getFiles(String path) throws AecuException { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + return findCandidates(resolver, path); + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } + } + + /** + * Finds all candidates for scripts to run. + * + * @param resolver service resource resolver + * @param path starting path + * @return candidate list + * @throws AecuException error finding candidates + */ + private List findCandidates(ResourceResolver resolver, String path) throws AecuException { + if (path == null) { + throw new AecuException("Path is null"); + } + Resource resource = resolver.getResource(path); + if (resource == null) { + throw new AecuException("Path is invalid"); + } + List candidates = new ArrayList<>(); + if (isFolder(resource) && matchesRunmodes(resource.getName())) { + for (Resource child : resource.getChildren()) { + candidates.addAll(findCandidates(resolver, child.getPath())); + } + } else if (isValidScriptName(resource.getName())) { + candidates.add(path); + } + return candidates; + } + + /** + * Checks if the resource is a folder. + * + * @param resource resource + * @return is folder + */ + private boolean isFolder(Resource resource) { + String type = resource.getValueMap().get(JcrConstants.JCR_PRIMARYTYPE, String.class); + return JcrResourceConstants.NT_SLING_FOLDER.equals(type) || JcrResourceConstants.NT_SLING_ORDERED_FOLDER.equals(type) + || JcrConstants.NT_FOLDER.equals(type); + } + + @Override + public boolean matchesRunmodes(String name) { + if (!name.contains(".")) { + return true; + } + Set runModes = slingSettings.getRunModes(); + String runModeString = name.substring(name.indexOf(".") + 1); + String[] combinations = runModeString.split(";"); + for (String combination : combinations) { + String[] modes = combination.split("\\."); + if (runModes.containsAll(Arrays.asList(modes))) { + return true; + } + } + return false; + } + + @Override + public boolean isValidScriptName(String name) { + if (!name.endsWith(".groovy")) { + return false; + } + if (name.contains(".fallback.")) { + return false; + } + return true; + } + + @Override + public ExecutionResult execute(String path) throws AecuException { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + Resource resource = resolver.getResource(path); + if (resource == null) { + throw new AecuException("Path is invalid"); + } + if (!isValidScriptName(resource.getName())) { + throw new AecuException("Invalid script name"); + } + ExecutionResult result = executeScript(resolver, path); + return result; + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } + } + + /** + * Executes the script. + * + * @param resolver resource resolver + * @param path path + * @return result + */ + private ExecutionResult executeScript(ResourceResolver resolver, String path) { + GroovyConsoleRequest request = new GroovyConsoleRequest(resolver); + RunScriptResponse response = groovyConsoleService.runScript(request, path); + boolean success = StringUtils.isBlank(response.getExceptionStackTrace()); + String result = response.getResult(); + ExecutionResult fallbackResult = null; + if (!success && (getFallbackScript(resolver, path) != null)) { + fallbackResult = executeScript(resolver, getFallbackScript(resolver, path)); + } + return new ExecutionResult(success, response.getRunningTime(), result, + response.getOutput() + response.getExceptionStackTrace(), fallbackResult, path); + } + + /** + * Returns the fallback script name if any exists. + * + * @param resolver resource resolver + * @param path original script path + * @return fallback script path + */ + protected String getFallbackScript(ResourceResolver resolver, String path) { + String name = path.substring(path.lastIndexOf("/") + 1); + if (name.contains(".fallback.")) { + // skip if script is a fallback script itself + return null; + } + String baseName = name.substring(0, name.indexOf(".")); + String fallbackPath = path.substring(0, path.lastIndexOf("/") + 1) + baseName + ".fallback.groovy"; + if (resolver.getResource(fallbackPath) != null) { + return fallbackPath; + } + return null; + } + + @Override + public HistoryEntry createHistoryEntry() throws AecuException { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + HistoryUtil historyUtil = new HistoryUtil(); + HistoryEntry entry = historyUtil.createHistoryEntry(resolver); + resolver.commit(); + return entry; + } catch (PersistenceException e) { + throw new AecuException("Unable to create history", e); + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } + } + + @Override + public HistoryEntry finishHistoryEntry(HistoryEntry history) throws AecuException { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + HistoryUtil historyUtil = new HistoryUtil(); + historyUtil.finishHistoryEntry(history, resolver); + resolver.commit(); + return history; + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } catch (PersistenceException e) { + throw new AecuException("Unable to finish history " + history.getRepositoryPath(), e); + } + } + + @Override + public HistoryEntry storeExecutionInHistory(HistoryEntry history, ExecutionResult result) throws AecuException { + if ((history == null) || !STATE.RUNNING.equals(history.getState())) { + throw new AecuException("Invalid history entry."); + } + history.getSingleResults().add(result); + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + HistoryUtil historyUtil = new HistoryUtil(); + historyUtil.storeExecutionInHistory(history, result, resolver); + resolver.commit(); + return history; + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } catch (PersistenceException e) { + throw new AecuException("Unable to add history entry " + history.getRepositoryPath(), e); + } + } + + @Override + public List getHistory(int startIndex, int count) throws AecuException { + try (ResourceResolver resolver = resolverService.getServiceResourceResolver()) { + HistoryUtil historyUtil = new HistoryUtil(); + return historyUtil.getHistory(startIndex, count, resolver); + } catch (LoginException e) { + throw new AecuException("Unable to get service resource resolver", e); + } + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/service/GroovyConsoleRequest.java b/core/src/main/java/de/valtech/aecu/core/service/GroovyConsoleRequest.java new file mode 100644 index 00000000..097634a2 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/service/GroovyConsoleRequest.java @@ -0,0 +1,412 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpSession; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.request.RequestDispatcherOptions; +import org.apache.sling.api.request.RequestParameter; +import org.apache.sling.api.request.RequestParameterMap; +import org.apache.sling.api.request.RequestPathInfo; +import org.apache.sling.api.request.RequestProgressTracker; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; + +/** + * Dummy request that is used to execute the script. + * + * @author Roland Gruber + */ +public class GroovyConsoleRequest implements SlingHttpServletRequest { + + private ResourceResolver resolver; + + /** + * Constructor + * + * @param resolver resource resolver + */ + public GroovyConsoleRequest(ResourceResolver resolver) { + this.resolver = resolver; + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public Cookie[] getCookies() { + return null; + } + + @Override + public long getDateHeader(String arg0) { + return 0; + } + + @Override + public String getHeader(String arg0) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return null; + } + + @Override + public Enumeration getHeaders(String arg0) { + return null; + } + + @Override + public int getIntHeader(String arg0) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public HttpSession getSession(boolean arg0) { + return null; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isUserInRole(String arg0) { + return false; + } + + @Override + public Object getAttribute(String arg0) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public String getParameter(String arg0) { + return null; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String arg0) { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRealPath(String arg0) { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public RequestDispatcher getRequestDispatcher(String arg0) { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public void removeAttribute(String arg0) {} + + @Override + public void setAttribute(String arg0, Object arg1) {} + + @Override + public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException {} + + @Override + public AdapterType adaptTo(Class arg0) { + return null; + } + + @Override + public Cookie getCookie(String arg0) { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(Resource arg0) { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String arg0, RequestDispatcherOptions arg1) { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(Resource arg0, RequestDispatcherOptions arg1) { + return null; + } + + @Override + public RequestParameter getRequestParameter(String arg0) { + return null; + } + + @Override + public List getRequestParameterList() { + return null; + } + + @Override + public RequestParameterMap getRequestParameterMap() { + return null; + } + + @Override + public RequestParameter[] getRequestParameters(String arg0) { + return null; + } + + @Override + public RequestPathInfo getRequestPathInfo() { + return null; + } + + @Override + public RequestProgressTracker getRequestProgressTracker() { + return null; + } + + @Override + public Resource getResource() { + return null; + } + + @Override + public ResourceBundle getResourceBundle(Locale arg0) { + return null; + } + + @Override + public ResourceBundle getResourceBundle(String arg0, Locale arg1) { + return null; + } + + @Override + public ResourceResolver getResourceResolver() { + return resolver; + } + + @Override + public String getResponseContentType() { + return null; + } + + @Override + public Enumeration getResponseContentTypes() { + return null; + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/service/HistoryEntryImpl.java b/core/src/main/java/de/valtech/aecu/core/service/HistoryEntryImpl.java new file mode 100644 index 00000000..815b078f --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/service/HistoryEntryImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; + +/** + * Implementation of history entry. + * + * @author Roland Gruber + */ +public class HistoryEntryImpl implements HistoryEntry { + + private STATE state; + private String path; + private Date start; + private Date end; + private List singleResults = new ArrayList<>(); + + @Override + public Date getStart() { + return start; + } + + @Override + public Date getEnd() { + return end; + } + + @Override + public List getSingleResults() { + return singleResults; + } + + @Override + public STATE getState() { + return state; + } + + @Override + public RESULT getResult() { + if (singleResults.isEmpty()) { + return RESULT.UNKNOWN; + } + RESULT result = RESULT.SUCCESS; + for (ExecutionResult singleResult : singleResults) { + if (!singleResult.isSuccess()) { + result = RESULT.FAILURE; + break; + } + } + return result; + } + + @Override + public String getRepositoryPath() { + return path; + } + + /** + * Sets the start date. + * + * @param start start date + */ + public void setStart(Date start) { + this.start = start; + } + + /** + * Sets the end date. + * + * @param end end date + */ + public void setEnd(Date end) { + this.end = end; + } + + /** + * Sets the node path. + * + * @param path node path + */ + public void setRepositoryPath(String path) { + this.path = path; + } + + public void setState(STATE state) { + this.state = state; + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("Path: " + getRepositoryPath() + "\n"); + output.append("Start: " + getStart() + "\n"); + output.append("End: " + getEnd() + "\n"); + output.append("State: " + getState() + "\n"); + output.append("Result: " + getResult() + "\n\n"); + for (ExecutionResult singleResult : singleResults) { + output.append(singleResult.toString() + "\n"); + } + return output.toString(); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/serviceuser/ServiceResourceResolverService.java b/core/src/main/java/de/valtech/aecu/core/serviceuser/ServiceResourceResolverService.java new file mode 100644 index 00000000..f84c28ce --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/serviceuser/ServiceResourceResolverService.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.serviceuser; + +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import java.util.HashMap; +import java.util.Map; + +/** + * Provides the service resource resolver. + * + * @author Roland Gruber + */ +@Component(service = ServiceResourceResolverService.class) +public class ServiceResourceResolverService { + + private static final String SUBSERVICE_AECU = "aecu"; + private static final String SUBSERVICE_AECU_CONTENT_MIGRATION = "aecu-content-migrator"; + + @Reference + ResourceResolverFactory resolverFactory; + + /** + * Returns a resource resolver of the AECU service user. + * + * @return service resource resolver + * @throws LoginException error opening resource resolver + */ + public ResourceResolver getServiceResourceResolver() throws LoginException { + final Map authenticationInfo = new HashMap<>(); + authenticationInfo.put(ResourceResolverFactory.SUBSERVICE, SUBSERVICE_AECU); + return resolverFactory.getServiceResourceResolver(authenticationInfo); + } + + /** + * Returns a resource resolver of the AECU content migrator user. + * + * @return service resource resolver + * @throws LoginException error opening resource resolver + */ // TODO: add /apps write rights!!! + public ResourceResolver getContentMigratorResourceResolver() throws LoginException { + final Map authenticationInfo = new HashMap<>(); + authenticationInfo.put(ResourceResolverFactory.SUBSERVICE, SUBSERVICE_AECU_CONTENT_MIGRATION); + return resolverFactory.getServiceResourceResolver(authenticationInfo); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/servlets/BaseServlet.java b/core/src/main/java/de/valtech/aecu/core/servlets/BaseServlet.java new file mode 100644 index 00000000..4be2c234 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/servlets/BaseServlet.java @@ -0,0 +1,64 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.servlets; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.servlets.SlingAllMethodsServlet; + +/** + * @author Bryan Chavez + */ +public class BaseServlet extends SlingAllMethodsServlet { + + private static final long serialVersionUID = -5240930544859160292L; + + protected static final String ERROR_MESSAGE_INTERNAL_SERVER = "Internal Server Error"; + + + protected void setNoCache(HttpServletResponse response) { + response.setHeader("Cache-control", "no-cache, no-store"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "-1"); + } + + protected void writeResult(SlingHttpServletResponse response, String json, int status) throws IOException { + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.setStatus(status); + response.getWriter().write(json); + } + + protected void writeResult(SlingHttpServletResponse response, String json) throws IOException { + writeResult(response, json, HttpServletResponse.SC_OK); + } + + protected void sendInternalServerError(SlingHttpServletResponse response) throws IOException { + writeResult(response, ERROR_MESSAGE_INTERNAL_SERVER, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + protected boolean validateParameter(String param) { + return param != null && StringUtils.isNotEmpty(param); + } + +} diff --git a/core/src/main/java/de/valtech/aecu/core/servlets/ExecutionServlet.java b/core/src/main/java/de/valtech/aecu/core/servlets/ExecutionServlet.java new file mode 100644 index 00000000..9983cae5 --- /dev/null +++ b/core/src/main/java/de/valtech/aecu/core/servlets/ExecutionServlet.java @@ -0,0 +1,144 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.servlets; + + +import java.io.IOException; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +import de.valtech.aecu.core.history.HistoryUtil; +import de.valtech.aecu.service.AecuException; +import de.valtech.aecu.service.AecuService; +import de.valtech.aecu.service.ExecutionResult; +import de.valtech.aecu.service.HistoryEntry; + +/** + * @author Bryan Chavez + */ + +@Component(immediate = true, service = {Servlet.class}, property = {"sling.servlet.paths=/bin/public/valtech/aecu/execute", + "sling.servlet.extensions=json", "sling.servlet.methods=GET"}) +public class ExecutionServlet extends BaseServlet { + + private static final long serialVersionUID = 1L; + private static final Logger LOG = LoggerFactory.getLogger(ExecutionServlet.class); + + protected static final String ERROR_MESSAGE_MANDATORY = + "ExecutionServlet :: Make sure your are sending the correct parameters."; + + @Reference + AecuService aecuService; + + + @Override + protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) + throws ServletException, IOException { + + this.setNoCache(response); + + String historyEntryAction = request.getParameter("historyEntryAction"); + String aecuScriptPath = request.getParameter("aecuScriptPath"); + if (!this.validateParameter(aecuScriptPath) || !this.validateParameter(historyEntryAction)) { + writeResult(response, ERROR_MESSAGE_MANDATORY); + return; + } + + try { + HistoryEntry historyEntry = this.getHistoryEntry(request, response, historyEntryAction); + ExecutionResult executionResult = aecuService.execute(aecuScriptPath); + aecuService.storeExecutionInHistory(historyEntry, executionResult); + this.finishHistoryEntry(historyEntry, historyEntryAction); + writeResult(response, this.prepareJson(executionResult.isSuccess(), historyEntry.getRepositoryPath())); + + } catch (AecuException e) { + this.sendInternalServerError(response); + } + + } + + protected HistoryEntry getHistoryEntry(SlingHttpServletRequest request, SlingHttpServletResponse response, + String historyEntryAction) throws AecuException, IOException { + + HistoryEntry historyEntry; + + switch (historyEntryAction.toLowerCase()) { + case "use": + case "close": + // Used for "use" and "close" + String historyEntryPath = request.getParameter("historyEntryPath"); + if (!this.validateParameter(historyEntryPath)) { + writeResult(response, ERROR_MESSAGE_MANDATORY); + return null; + } + + ResourceResolver resolver = request.getResourceResolver(); + HistoryUtil historyUtil = new HistoryUtil(); + historyEntry = historyUtil.readHistoryEntry(resolver.getResource(historyEntryPath)); + break; + default: + // Used for "single" and "create" + historyEntry = aecuService.createHistoryEntry(); + break; + } + + return historyEntry; + } + + protected HistoryEntry finishHistoryEntry(HistoryEntry historyEntry, String historyEntryAction) throws AecuException { + + switch (historyEntryAction.toLowerCase()) { + case "single": + case "close": + // Used for "single" and "close" + aecuService.finishHistoryEntry(historyEntry); + break; + } + + return historyEntry; + + } + + /** + * This method builds the JSON String for the response. Eg: {"success": + * true,"historyEntryPath":"/var/aecu/2018/6/13/152892696338961314"} + * + * @param status success or fail + * @param historyEntryPath path to history node + * @return json String + */ + protected String prepareJson(boolean status, String historyEntryPath) { + JsonObject json = new JsonObject(); + json.addProperty("success", status); + json.addProperty("historyEntryPath", historyEntryPath); + return json.toString(); + } + +} diff --git a/core/src/test/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdateTest.java b/core/src/test/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdateTest.java new file mode 100644 index 00000000..95173f52 --- /dev/null +++ b/core/src/test/java/de/valtech/aecu/core/groovy/console/bindings/SimpleContentUpdateTest.java @@ -0,0 +1,28 @@ +package de.valtech.aecu.core.groovy.console.bindings; + +import org.apache.sling.api.resource.ResourceResolver; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SimpleContentUpdateTest { + + @Mock + private ResourceResolver resourceResolverMock; + + private SimpleContentUpdate simpleContentUpdate; + + + @Before + public void setUp() throws Exception { + simpleContentUpdate = new SimpleContentUpdate(resourceResolverMock); + } + + @Test + public void toDo() { + // TODO!! + } +} diff --git a/core/src/test/java/de/valtech/aecu/core/history/HistoryUtilTest.java b/core/src/test/java/de/valtech/aecu/core/history/HistoryUtilTest.java new file mode 100644 index 00000000..a32d1e25 --- /dev/null +++ b/core/src/test/java/de/valtech/aecu/core/history/HistoryUtilTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.history; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.jcr.resource.api.JcrResourceConstants; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import de.valtech.aecu.service.AecuException; + +/** + * Tests HistoryUtil + * + * @author Roland Gruber + */ +@RunWith(value = MockitoJUnitRunner.class) +public class HistoryUtilTest { + + @Spy + private HistoryUtil historyUtil; + + @Mock + private ResourceResolver resolver; + + @Test + public void createPath_Existing() throws AecuException { + String path = "/var/aecu/2018/5"; + when(resolver.getResource(path)).thenReturn(mock(Resource.class)); + + historyUtil.createPath(path, resolver, JcrResourceConstants.NT_SLING_FOLDER); + + verify(historyUtil, times(1)).createPath(anyString(), eq(resolver), eq(JcrResourceConstants.NT_SLING_FOLDER)); + } + + @Test + public void createPath_NotExisting() throws AecuException, PersistenceException { + String path = "/var/aecu/2018/5"; + when(resolver.getResource("/var/aecu/2018")).thenReturn(mock(Resource.class)); + + historyUtil.createPath(path, resolver, JcrResourceConstants.NT_SLING_FOLDER); + + verify(historyUtil, times(1)).createPath(anyString(), eq(resolver), eq(JcrResourceConstants.NT_SLING_FOLDER)); + verify(resolver, times(1)).create(any(Resource.class), eq("5"), any()); + } + +} diff --git a/core/src/test/java/de/valtech/aecu/core/service/AecuServiceImplTest.java b/core/src/test/java/de/valtech/aecu/core/service/AecuServiceImplTest.java new file mode 100644 index 00000000..be233116 --- /dev/null +++ b/core/src/test/java/de/valtech/aecu/core/service/AecuServiceImplTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.valtech.aecu.core.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.settings.SlingSettingsService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Tests AecuServiceImpl + * + * @author Roland Gruber + */ +@RunWith(value = MockitoJUnitRunner.class) +public class AecuServiceImplTest { + + @InjectMocks + @Spy + private AecuServiceImpl service; + + @Mock + private SlingSettingsService settingsService; + + @Mock + private ResourceResolver resolver; + + @Before + public void setup() { + Set runModes = new HashSet<>(); + runModes.add("author"); + runModes.add("test"); + runModes.add("test2"); + runModes.add("test3"); + when(settingsService.getRunModes()).thenReturn(runModes); + } + + @Test + public void matchesRunmodes_noMode() { + assertTrue(service.matchesRunmodes("name")); + } + + @Test + public void matchesRunmodes_nonMatching() { + assertFalse(service.matchesRunmodes("name.publish")); + } + + @Test + public void matchesRunmodes_matching() { + assertTrue(service.matchesRunmodes("name.author")); + assertTrue(service.matchesRunmodes("name.test")); + } + + @Test + public void matchesRunmodes_matchingMulti() { + assertTrue(service.matchesRunmodes("name.author.test")); + assertTrue(service.matchesRunmodes("name.author.test;testNO")); + assertTrue(service.matchesRunmodes("name.authorNO.test;test2.author")); + } + + @Test + public void matchesRunmodes_nonMatchingMulti() { + assertFalse(service.matchesRunmodes("name.author.testNO")); + assertFalse(service.matchesRunmodes("name.author.testNO;testNO")); + assertFalse(service.matchesRunmodes("name.author.testNO;test2NO.author")); + } + + @Test + public void isValidScriptName_ok() { + assertTrue(service.isValidScriptName("test.groovy")); + } + + @Test + public void isValidScriptName_invalidExtension() { + assertFalse(service.isValidScriptName("test.txt")); + } + + @Test + public void isValidScriptName_fallback() { + assertFalse(service.isValidScriptName("test.fallback.groovy")); + } + + @Test + public void getFallbackScript_Exists() { + when(resolver.getResource("/path/to/script.fallback.groovy")).thenReturn(mock(Resource.class)); + + assertEquals("/path/to/script.fallback.groovy", service.getFallbackScript(resolver, "/path/to/script.always.groovy")); + assertEquals("/path/to/script.fallback.groovy", service.getFallbackScript(resolver, "/path/to/script.groovy")); + } + + @Test + public void getFallbackScript_NotExists() { + assertNull(service.getFallbackScript(resolver, "/path/to/script.always.groovy")); + assertNull(service.getFallbackScript(resolver, "/path/to/script.groovy")); + } + + @Test + public void getFallbackScript_Fallback() { + verify(resolver, never()).getResource("/path/to/script.fallback.groovy"); + + assertNull(service.getFallbackScript(resolver, "/path/to/script.fallback.groovy")); + } + +} diff --git a/docs/developers.md b/docs/developers.md new file mode 100644 index 00000000..15247c1f --- /dev/null +++ b/docs/developers.md @@ -0,0 +1,27 @@ +# AEM Server Setup + +By default AEM is expected to listen on localhost on port 5702. This setting can be overridden by adding parameters: +* -Daem.port=4502 +* -Daem.host=localhost + +You need AEM 6.3 with service pack 2. + +# Build and Deploy + +To build and deploy run this in the base (aem-easy-content-upgrade) or ui.apps/examples folder: + +```bash +mvn clean install -PautoInstallPackage +``` + +In case you want to deploy core only you can use this command in core folder: + +```bash +mvn clean install -PautoInstallBundle +``` + + +# Code Formatting + +Please use our standard code formatters for [Eclipse](formatter/eclipse-aecu.xml) +and [IntelliJ](formatter/intellij-aecu.xml). diff --git a/docs/formatter/eclipse-aecu.xml b/docs/formatter/eclipse-aecu.xml new file mode 100644 index 00000000..a7317795 --- /dev/null +++ b/docs/formatter/eclipse-aecu.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/formatter/intellij-aecu.xml b/docs/formatter/intellij-aecu.xml new file mode 100644 index 00000000..49df1d87 --- /dev/null +++ b/docs/formatter/intellij-aecu.xml @@ -0,0 +1,476 @@ + + + + + + diff --git a/docs/images/healthCheck.png b/docs/images/healthCheck.png new file mode 100644 index 00000000..f4ce1a0e Binary files /dev/null and b/docs/images/healthCheck.png differ diff --git a/docs/images/historyDetails.png b/docs/images/historyDetails.png new file mode 100644 index 00000000..e09a7c20 Binary files /dev/null and b/docs/images/historyDetails.png differ diff --git a/docs/images/historyOverview.png b/docs/images/historyOverview.png new file mode 100644 index 00000000..910ee01a Binary files /dev/null and b/docs/images/historyOverview.png differ diff --git a/docs/images/jmx.png b/docs/images/jmx.png new file mode 100644 index 00000000..f1bb3d40 Binary files /dev/null and b/docs/images/jmx.png differ diff --git a/docs/images/run.png b/docs/images/run.png new file mode 100644 index 00000000..e83a0002 Binary files /dev/null and b/docs/images/run.png differ diff --git a/docs/images/tools.png b/docs/images/tools.png new file mode 100644 index 00000000..94c58025 Binary files /dev/null and b/docs/images/tools.png differ diff --git a/examples/pom.xml b/examples/pom.xml new file mode 100644 index 00000000..b8bd3e7d --- /dev/null +++ b/examples/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + de.valtech.aecu + aecu + 0.9 + + + aecu.examples + content-package + AECU - Examples + Examples package for AECU + + + src/main/content/jcr_root + + + + ${basedir}/src/main/content/jcr_root + + + **/.vlt + **/.vltignore + **/.gitignore + **/*.iml + **/.classpath + **/.project + **/.settings + **/.DS_Store + **/target/** + **/pom.xml + + + + src/main/content/META-INF/vault/definition + ../vault-work/META-INF/vault/definition + + + + + maven-resources-plugin + + true + + + + + com.day.jcr.vault + content-package-maven-plugin + true + + src/main/content/META-INF/vault/filter.xml + true + true + Valtech + + de.valtech.aecu.core.installhook.AecuInstallHook + + + + + + org.apache.sling + htl-maven-plugin + + + + + diff --git a/examples/src/main/content/META-INF/vault/definition/thumbnail.png b/examples/src/main/content/META-INF/vault/definition/thumbnail.png new file mode 100644 index 00000000..c4548ee8 Binary files /dev/null and b/examples/src/main/content/META-INF/vault/definition/thumbnail.png differ diff --git a/examples/src/main/content/META-INF/vault/filter.xml b/examples/src/main/content/META-INF/vault/filter.xml new file mode 100644 index 00000000..85e3bc87 --- /dev/null +++ b/examples/src/main/content/META-INF/vault/filter.xml @@ -0,0 +1,4 @@ + + + + diff --git a/examples/src/main/content/jcr_root/META-INF/MANIFEST.MF b/examples/src/main/content/jcr_root/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/examples/src/main/content/jcr_root/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/sayHello.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/sayHello.groovy new file mode 100644 index 00000000..20cba1b7 --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/sayHello.groovy @@ -0,0 +1,7 @@ +def checkResourceResolver = (resourceResolver != null); +def checkHelloWorld = (helloWorld != null); +def sayHello = (checkHelloWorld) ? helloWorld.sayHello() : "NooooOOOOoooooo!!!!!" + +println "resourceProvider is defined $checkResourceResolver" +println "helloWorld is defined $checkHelloWorld" +println "helloWorld says $sayHello" \ No newline at end of file diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script1.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script1.groovy new file mode 100644 index 00000000..63bec6eb --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script1.groovy @@ -0,0 +1,2 @@ +println "Sample project1 - script1" +Thread.sleep(2000); diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.fallback.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.fallback.groovy new file mode 100644 index 00000000..5a64c6f8 --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.fallback.groovy @@ -0,0 +1,2 @@ +// Scripts with extension ".fallback.groovy" will be executed if the non-fallback script fails +println "Executing fallback script for project1 - script2" \ No newline at end of file diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.groovy new file mode 100644 index 00000000..b7b40bf7 --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/script2.groovy @@ -0,0 +1,3 @@ +println "Executing project1 - script2" +This is +a syntax error \ No newline at end of file diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/tests_for_simpleContentUpdates.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/tests_for_simpleContentUpdates.groovy new file mode 100644 index 00000000..646cc3a5 --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project1/migrations.author/tests_for_simpleContentUpdates.groovy @@ -0,0 +1,49 @@ +def conditionMap_Hero1 = [:] +conditionMap_Hero1['sling:resourceType'] = "weretail/components/content/heroimage" +def conditionMap_Hero2 = [:] +conditionMap_Hero2['fileReference'] = "/content/dam/we-retail/en/activities/running/fitness-woman.jpg" + +def conditionMap_Page = [:] +conditionMap_Page['jcr:primaryType'] = "cq:PageContent" + + +println aecu.contentUpgradeBuilder() +// traversers + .forResources((String[])["/content/we-retail/ca/en/jcr:content", "/content/we-retail/ca/en/experience/jcr:content"]) //,"/invalid/path" + .forChildResourcesOf("/content/we-retail/ca/en/men") + .forDescendantResourcesOf("/content/we-retail/ca/en/women") +// filters + .filterByProperties(conditionMap_Page) +// actions + .doSetProperty("newStringProperty1", "aecu test with conditionMap_Page") + .doSetProperty("newBooleanProperty1", true) + .doSetProperty("newIntegerProperty1", 123) +// .doRenameProperty("newStringProperty1", "delete_me_later") +// .removeProperty("delete_me_later") + .dryRun() + + + + +def complexFilter = new ORFilter( + [ new FilterByProperties(conditionMap_Page), + new ANDFilter( [ + new FilterByProperties(conditionMap_Hero1), + new FilterByProperties(conditionMap_Hero2) + ] ) + ]) + +println aecu.contentUpgradeBuilder() +// traversers + .forResources((String[])["/content/we-retail/ca/en/jcr:content", "/content/we-retail/ca/en/experience/jcr:content"]) //,"/invalid/path" + .forChildResourcesOf("/content/we-retail/ca/en/men") + .forDescendantResourcesOf("/content/we-retail/ca/en/women") +// filters + .filterWith(complexFilter) +// actions + .doSetProperty("newStringProperty2", "aecu test with conditionMap_Hero") + .doSetProperty("newBooleanProperty2", false) + .doSetProperty("newIntegerProperty2", 789) +// .doRenameProperty("newStringProperty2", "delete_me_later2") +// .removeProperty("delete_me_later") + .dryRun() \ No newline at end of file diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script1.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script1.groovy new file mode 100644 index 00000000..aec4fa7f --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script1.groovy @@ -0,0 +1,3 @@ +println "Executing project2 - script1" +Thread.sleep(2000); +return "Done" diff --git a/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script2.always.groovy b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script2.always.groovy new file mode 100644 index 00000000..4e49522e --- /dev/null +++ b/examples/src/main/content/jcr_root/etc/groovyconsole/scripts/aecu/aecu-examples/project2/script2.always.groovy @@ -0,0 +1,3 @@ +// If you name your script ".always.groovy the it will be executed by install hook on each package deploy" +println "Executing a script with 'always' option" +Thread.sleep(2000); \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..dbc64fe8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,635 @@ + + + 4.0.0 + de.valtech.aecu + aecu + pom + 0.9 + AECU + AEM Easy COntent Upgrade + https://github.com/valtech/aem-easy-content-upgrade + + + api + core + ui.apps + bundle + examples + + + + localhost + 5702 + localhost + 5703 + admin + admin + admin + admin + + UTF-8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + true + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-maven + + enforce + + + + + [3.2.5,) + + + Project must be compiled with Java 8 or higher + 1.8.0 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-idea-plugin + 2.2.1 + + 1.8 + true + true + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.10 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.7 + + + org.apache.maven.plugins + maven-site-plugin + 3.3 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.20 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + external.atlassian.jgitflow + jgitflow-maven-plugin + 1.0-m5.1 + + true + true + true + true + rc + + + + org.apache.sling + maven-sling-plugin + 2.2.0 + + http://${aem.host}:${aem.port}/system/console + WebConsole + + + + org.apache.sling + htl-maven-plugin + 1.0.6 + + true + + + + + validate + + + + + + com.day.jcr.vault + content-package-maven-plugin + 0.5.1 + + http://${aem.host}:${aem.port}/crx/packmgr/service.jsp + true + true + ${vault.user} + ${vault.password} + + + + org.apache.felix + maven-bundle-plugin + 3.5.0 + true + + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.0 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + [1.0.0,) + + enforce + + + + + + + + + + org.apache.maven.plugins + + + maven-dependency-plugin + + + [2.2,) + + + copy-dependencies + unpack + + + + + + + + + + org.codehaus.mojo + + + build-helper-maven-plugin + + + [1.5,) + + + + reserve-network-port + + + + + + + + + + + + + + + + + + autoInstallBundle + + false + + + + + + org.apache.sling + maven-sling-plugin + + + install-bundle + + install + + + + + + + + + + + autoInstallPackage + + false + + + + + + com.day.jcr.vault + content-package-maven-plugin + + + install-package + + install + + + http://${aem.host}:${aem.port}/crx/packmgr/service.jsp + + + + + + + + + + + autoInstallPackagePublish + + false + + + + + + com.day.jcr.vault + content-package-maven-plugin + + + install-package-publish + + install + + + http://${aem.publish.host}:${aem.publish.port}/crx/packmgr/service.jsp + + + + + + + + + + + + release + + + performRelease + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + + + + org.osgi + osgi.core + 6.0.0 + provided + + + org.osgi + osgi.cmpn + 6.0.0 + provided + + + org.osgi + osgi.annotation + 6.0.1 + provided + + + org.slf4j + slf4j-api + 1.5.11 + provided + + + com.adobe.aem + uber-jar + 6.3.0 + apis + provided + + + com.adobe.cq + core.wcm.components.all + zip + 1.0.6 + + + org.apache.sling + org.apache.sling.models.api + 1.0.0 + provided + + + javax.servlet + servlet-api + 2.5 + provided + + + javax.servlet.jsp + jsp-api + 2.1 + provided + + + javax.jcr + jcr + 2.0 + provided + + + com.day.cq.wcm + cq-wcm-taglib + 5.7.4 + provided + + + org.codehaus.groovy + groovy-all + 2.4.10 + provided + + + com.icfolson.aem.groovy.console + aem-groovy-console + 11.3.0 + provided + + + + * + * + + + + + com.icfolson.aem.groovy.console + aem-groovy-console + 11.3.0 + zip + + + + * + * + + + + + org.apache.commons + commons-lang3 + 3.0.1 + + + junit + junit + 4.12 + test + + + org.slf4j + slf4j-simple + 1.5.11 + test + + + org.mockito + mockito-core + 2.7.22 + test + + + junit-addons + junit-addons + 1.4 + test + + + + com.google.code.gson + gson + 2.3 + + + com.google.code.findbugs + jsr305 + 2.0.1 + compile + + + + + + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + + Roland Gruber + roland.gruber@valtech.de + Valtech GmbH + https://www.valtech.de/ + + + Roxana Mureșan + roxana.muresan@valtech.de + Valtech GmbH + https://www.valtech.de/ + + + Bryan Chavez + bryan.chavez@valtech.de + Valtech GmbH + https://www.valtech.de/ + + + Christopher Piosecny + christopher.piosecny@valtech.de + Valtech GmbH + https://www.valtech.de/ + + + Robert Lingner + robert.lingner@valtech.de + Valtech GmbH + https://www.valtech.de/ + + + + + scm:git:https://github.com/valtech/aem-easy-content-upgrade.git + scm:git:git@github.com:valtech/aem-easy-content-upgrade.git + https://github.com/valtech/aem-easy-content-upgrade + + + + Github + https://github.com/valtech/aem-easy-content-upgrade/issues + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + adobe + Adobe Public Repository + http://repo.adobe.com/nexus/content/groups/public/ + default + + + + + adobe + Adobe Public Repository + http://repo.adobe.com/nexus/content/groups/public/ + default + + + + diff --git a/ui.apps/pom.xml b/ui.apps/pom.xml new file mode 100644 index 00000000..62c4ed8c --- /dev/null +++ b/ui.apps/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + + de.valtech.aecu + aecu + 0.9 + + + aecu.ui.apps + content-package + AECU - UI apps + UI apps package for AECU + + + src/main/content/jcr_root + + + + ${basedir}/src/main/content/jcr_root + + + **/.vlt + **/.vltignore + **/.gitignore + **/*.iml + **/.classpath + **/.project + **/.settings + **/.DS_Store + **/target/** + **/pom.xml + + + + src/main/content/META-INF/vault/definition + ../vault-work/META-INF/vault/definition + + + + + maven-resources-plugin + + true + + + + + com.day.jcr.vault + content-package-maven-plugin + true + + src/main/content/META-INF/vault/filter.xml + true + true + Valtech + + merge_preserve + + + + de.valtech.aecu + aecu.api + /apps/valtech/aecu/install + + + de.valtech.aecu + aecu.core + /apps/valtech/aecu/install + + + + + + + org.apache.sling + htl-maven-plugin + + + + + + + de.valtech.aecu + aecu.core + ${project.version} + + + de.valtech.aecu + aecu.api + ${project.version} + + + diff --git a/ui.apps/src/main/content/META-INF/vault/definition/thumbnail.png b/ui.apps/src/main/content/META-INF/vault/definition/thumbnail.png new file mode 100644 index 00000000..c4548ee8 Binary files /dev/null and b/ui.apps/src/main/content/META-INF/vault/definition/thumbnail.png differ diff --git a/ui.apps/src/main/content/META-INF/vault/filter.xml b/ui.apps/src/main/content/META-INF/vault/filter.xml new file mode 100644 index 00000000..ac60f294 --- /dev/null +++ b/ui.apps/src/main/content/META-INF/vault/filter.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/ui.apps/src/main/content/jcr_root/META-INF/MANIFEST.MF b/ui.apps/src/main/content/jcr_root/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/ui.apps/src/main/content/jcr_root/_rep_policy.xml b/ui.apps/src/main/content/jcr_root/_rep_policy.xml new file mode 100644 index 00000000..3b0dc6d0 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/_rep_policy.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/.content.xml b/ui.apps/src/main/content/jcr_root/apps/.content.xml new file mode 100644 index 00000000..84ee7452 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/.content.xml @@ -0,0 +1,4 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/.content.xml b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/.content.xml new file mode 100644 index 00000000..cd148fc1 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/.content.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/execute/.content.xml b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/execute/.content.xml new file mode 100644 index 00000000..850dabde --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/execute/.content.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/history/.content.xml b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/history/.content.xml new file mode 100644 index 00000000..10517193 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/cq/core/content/nav/tools/aecu/history/.content.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/.content.xml new file mode 100644 index 00000000..dfac52e3 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/.content.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/.content.xml new file mode 100644 index 00000000..3d0c31ab --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/.content.xml new file mode 100644 index 00000000..9ef5e220 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/.content.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/.content.xml new file mode 100644 index 00000000..4210640b --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/.content.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/aecu/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/aecu/.content.xml new file mode 100644 index 00000000..ef5c15d2 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/hc/aecu/.content.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/.content.xml new file mode 100755 index 00000000..9722c012 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/.content.xml @@ -0,0 +1,6 @@ + + + diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/.content.xml new file mode 100755 index 00000000..9722c012 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/.content.xml @@ -0,0 +1,6 @@ + + + diff --git a/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/aecu_history_purge/.content.xml b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/aecu_history_purge/.content.xml new file mode 100644 index 00000000..920997cf --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/settings/granite/operations/maintenance/granite_weekly/aecu_history_purge/.content.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/.content.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/.content.xml new file mode 100644 index 00000000..788411b1 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css.txt b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css.txt new file mode 100644 index 00000000..b10e4ed2 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css.txt @@ -0,0 +1,3 @@ +#base=css + +aecu.css diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css/aecu.css b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css/aecu.css new file mode 100644 index 00000000..950e601a --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/css/aecu.css @@ -0,0 +1,61 @@ + +.base-container { + margin: 2rem; +} + +.aecu-searchfield { + margin-top: 1rem; + margin-bottom: 1rem; +} + +table.aecu-history-overview td { + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.2rem; + padding-bottom: 0.2rem; +} + +table.aecu-history-detail td { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.icon-color-ok { + color: green; +} + +.icon-color-fail { + color: red; +} + +.icon-color-inprogress { + color: blue; +} + +.aecu-color-ok { + color: green; +} + +.aecu-color-fail { + color: red; +} + +.aecu-chart-text { + line-height: 1; + text-anchor: middle; + font-size: 0.5em; + transform: translateY(0.35em); +} + +.aecu-font-normal { + font-size: 1rem; +} + +.aecu-font-large { + font-size: 1.25rem; +} + +.aecu-padding-sides5 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js.txt b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js.txt new file mode 100644 index 00000000..6764754f --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js.txt @@ -0,0 +1,8 @@ +#base=js + +aecu.js +constants.js +requestHandler.js +executor.js +utils.js +history.js diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/aecu.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/aecu.js new file mode 100644 index 00000000..958cbf09 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/aecu.js @@ -0,0 +1,24 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Initialize of variable */ +var AECU = AECU || {}; \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/constants.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/constants.js new file mode 100644 index 00000000..72ade47d --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/constants.js @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +AECU.Constants = {}; + +AECU.Constants.Executor = { + servletPath: "/bin/public/valtech/aecu/execute.json?aecuScriptPath={0}&historyEntryAction={1}&historyEntryPath={2}", + historyPath: "/apps/valtech/aecu/tools/history/details.html?entry={0}&aecuScriptPath={1}" +} + +AECU.Constants.Executor.Status = { + ready: { + icon: "helpCircle", + className: "", + text: "Ready to run" + }, + inProgress: { + icon: "playCircle", + className: "icon-color-inprogress", + text: "In progress" + }, + fail: { + icon: "closeCircle", + className: "icon-color-fail", + text: "Failed" + }, + pending: { + icon: "pending", + className: "icon-color-inprogress", + text: "Pending" + }, + executed: { + icon: "checkCircle", + className: "icon-color-ok", + text: "Ok" + }, + internalError: { + icon: "sentimentNegative", + className: "icon-color-fail", + text: "Internal error" + } +} + +AECU.Constants.Executor.HistoryEntryActions = { + single: "single", + create: "create", + use: "use", + close: "close" +} \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/executor.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/executor.js new file mode 100644 index 00000000..4d2c477c --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/executor.js @@ -0,0 +1,153 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +AECU.Executor = {}; + +AECU.Executor.historyEntryPath; + +AECU.Executor.doGET = function (getProps) { + /* ADD HERE YOUR COMMON EXECUTOR AJAX PROPERTIES AND + MERGE THEM WITH getPros */ + var props = { + async: false + } + var executorGetProps = $.extend({}, props, getProps); + AECU.RequestHandler.GET(executorGetProps); +} + +AECU.Executor.executeAll = function (tableRows) { + AECU.Executor.changeAllStatus(AECU.Constants.Executor.Status.pending); + var historyEntryAction; + for (var i = 0, length = tableRows.length; i < length; i++) { + if (i == 0) { + historyEntryAction = AECU.Constants.Executor.HistoryEntryActions.create; + } else if (i == tableRows.length - 1) { + historyEntryAction = AECU.Constants.Executor.HistoryEntryActions.close; + } else { + historyEntryAction = AECU.Constants.Executor.HistoryEntryActions.use; + } + AECU.Executor.execute(tableRows[i], historyEntryAction, AECU.Executor.historyEntryPath); + } + AECU.Executor.disableButton(); + AECU.Executor.historyEntryPath = undefined; +} + +AECU.Executor.execute = function (row, historyEntryAction, historyEntryPath) { + this.doGET({ + url: AECU.Constants.Executor.servletPath.format(row.dataset.aecuExecuteScript, historyEntryAction, + historyEntryPath), + beforeSend: function () { + AECU.Executor.changeRowStatus(row, AECU.Constants.Executor.Status.inProgress); + AECU.Executor.disableButton(row); + }, + success: function (json) { + AECU.Executor.historyEntryPath = json.historyEntryPath; + AECU.Executor.addHistoryLink(row, json.historyEntryPath); + if (json.success) { + AECU.Executor.changeRowStatus(row, AECU.Constants.Executor.Status.executed); + } else { + AECU.Executor.changeRowStatus(row, AECU.Constants.Executor.Status.fail); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + AECU.Executor.changeRowStatus(row, AECU.Constants.Executor.Status.internalError); + } + }); +} + +AECU.Executor.addHistoryLink = function (row, historyEntryPath) { + var historyLink = $(row).find("[data-aecu-execute-script-history]")[0]; + historyLink.href = AECU.Constants.Executor.historyPath.format(historyEntryPath, row.dataset.aecuExecuteScript); + historyLink.text = "Go to history"; +} + +AECU.Executor.changeRowStatus = function (row, value) { + AECU.Executor.changeStatus($(row).find("[data-aecu-execute-script-status]"), value); + AECU.Executor.changeScriptColor($(row).find("[data-aecu-execute-script-path]"), value); +} + +AECU.Executor.changeAllStatus = function (value) { + AECU.Executor.changeStatus($("[data-aecu-execute-script-status]"), value); + AECU.Executor.changeScriptColor($("[data-aecu-execute-script-path]"), value); +} + +AECU.Executor.changeStatus = function (items, value) { + var icon = value.icon; + var title = value.text; + var className = value.className; + var iconTags = items.children("coral-icon"); + iconTags.each(function () { + this.set('icon', icon); + this.set('title', title); + this._syncDOM(); + }); + iconTags.removeClass('icon-color-inprogress'); + iconTags.addClass(className); +} + +AECU.Executor.changeScriptColor = function (items, value) { + var className = value.className; + items.removeClass('icon-color-inprogress'); + items.addClass(className); +} + +AECU.Executor.disableButton = function (row) { + if (row != undefined) { + $(row).find("[data-aecu-execute-script-button]").prop('disabled', true); + } else { + /* The only button not in a row. */ + $("#aecu-execute-button-all").prop('disabled', true); + } +} + +$(document).ready(function () { + + /* Disable executeAll button is there are no scripts displayed. */ + if ($('[data-aecu-execute-script]').length == 0) { + AECU.Executor.disableButton(); + } + + /* Event for executing all scrips displayed in screen. */ + $("#aecu-execute-button-all").on('click', function (e) { + var tableRows = $('[data-aecu-execute-script]'); + if (tableRows.length > 0) { + AECU.Executor.executeAll(tableRows); + } + }); + + /* Event for each row (script) displayed in screen. */ + $('[data-aecu-execute-script-button]').on('click', function (event) { + AECU.Executor.disableButton(); + AECU.Executor.execute( + this.closest('[data-aecu-execute-script]'), + AECU.Constants.Executor.HistoryEntryActions.single, null); + }); + + /* open rail tab */ + var button = $('coral-cyclebutton'); + if (button) { + Coral.commons.ready(button[0], function () { + button.find('button').click(); + }); + } + +}); \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/history.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/history.js new file mode 100644 index 00000000..12e29c7c --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/history.js @@ -0,0 +1,57 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +(function ($, ns, channel, window) { + "use strict"; + + AECU.History = {}; + + /** + * Select the accordion item based on the GET parameter. + */ + AECU.History.selectAccordion = function () { + var search = window.location.search; + if (search && search.includes('aecuScriptPath')) { + search = search.substring(1); + var params = search.split('&'); + for (var i = 0; i < params.length; i++) { + var param = params[i]; + var parts = param.split('='); + if (parts[0] == 'aecuScriptPath') { + var name = parts[1]; + var item = jQuery("coral-accordion-item[data-path='" + name + "']"); + Coral.commons.ready(item[0], function () { + item[0].selected = true; + }); + } + } + } + }; + + /** + * Initial actions + */ + $(document).ready(function () { + AECU.History.selectAccordion(); + }); + +})(jQuery, Granite.author, jQuery(document), this); diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/requestHandler.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/requestHandler.js new file mode 100644 index 00000000..b276aaa8 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/requestHandler.js @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +AECU.RequestHandler = {}; + +AECU.RequestHandler.getProps = { + type: 'GET', + cache: false, + success: function (data) { + }, + error: function (jqXHR, textStatus, errorThrown) { + }, + complete: function (jqXHR, textStatus) { + } +} + +AECU.RequestHandler.GET = function (props) { + var finalProps = $.extend({}, this.getProps, props); + $.ajax(finalProps); +} \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/utils.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/utils.js new file mode 100644 index 00000000..a0b9a712 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu.editor/js/utils.js @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Valtech GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Example: "Hello {0}!".format("World"); + * Output: "Hello World!" + * + */ +String.prototype.format = function () { + var content = this; + var length = arguments.length; + for (var i = 0; i < length; i++) { + var replacement = '{' + i + '}'; + content = content.replace(replacement, arguments[i]); + } + return content; +}; \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/.content.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/.content.xml new file mode 100644 index 00000000..c3011352 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/.content.xml @@ -0,0 +1,5 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js.txt b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js.txt new file mode 100644 index 00000000..86ac4e2a --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js.txt @@ -0,0 +1,2 @@ +#base=js +aecu_groovyconsole_extension.js diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js/aecu_groovyconsole_extension.js b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js/aecu_groovyconsole_extension.js new file mode 100644 index 00000000..9bf22eaf --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/clientlibs/aecu/groovyconsole/js/aecu_groovyconsole_extension.js @@ -0,0 +1,13 @@ +$(document).ready(function () { + + if ($("#bindings").length != 0 && $("#bindings ul li.aecu-binding").length == 0) { + // TODO url to some public available javadoc + $("#bindings ul").append("
  • aecu - de.valtech.aecu.core.groovy.console.bindings.SimpleContentUpdate
  • "); + } + + if ($("#imports").length != 0 && $("#bindings ul li.aecu-import").length == 0) { + // TODO url to some public available javadoc + $("#imports ul").append("
  • de.valtech.aecu.core.groovy.console.bindings.filters
  • "); + } + +}); \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/executionResults.html b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/executionResults.html new file mode 100644 index 00000000..31b63913 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/executionResults.html @@ -0,0 +1,29 @@ + + + +

    No scripts executed

    +
    + + + +

    Execution details

    + + + + + +

    Execution details for ${cmp.history.singleResults.size} scripts

    + + + + + ${item.path} + + + + + + + +
    +
    \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/templates/executionResult.html b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/templates/executionResult.html new file mode 100644 index 00000000..527e3603 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/executionResults/templates/executionResult.html @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/.content.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/.content.xml new file mode 100644 index 00000000..d1363777 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/overview.html b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/overview.html new file mode 100644 index 00000000..4b3c1566 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/components/content/history/overview/overview.html @@ -0,0 +1,28 @@ + +

    Run details

    + + + + + + + + + + + +
    State: ${cmp.history.state.label} + + + + + + + + + + ${cmp.percentages.left}% + + + +
    Result: ${cmp.history.result.label}
    Duration: ${cmp.duration}
    diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/de.valtech.aecu.core.maintenance.PurgeHistoryTask.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/de.valtech.aecu.core.maintenance.PurgeHistoryTask.xml new file mode 100644 index 00000000..db8d13a9 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/de.valtech.aecu.core.maintenance.PurgeHistoryTask.xml @@ -0,0 +1,5 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.hc.core.impl.CompositeHealthCheck-aecu.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.hc.core.impl.CompositeHealthCheck-aecu.xml new file mode 100644 index 00000000..f45a726e --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.hc.core.impl.CompositeHealthCheck-aecu.xml @@ -0,0 +1,8 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuContentMigratorUser.config b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuContentMigratorUser.config new file mode 100644 index 00000000..91811d89 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuContentMigratorUser.config @@ -0,0 +1 @@ +user.mapping=["de.valtech.aecu.core:aecu-content-migrator\=aecu-content-migrator"] \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuSystemUser.config b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuSystemUser.config new file mode 100644 index 00000000..cc15e955 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-valtechAecuSystemUser.config @@ -0,0 +1 @@ +user.mapping=["de.valtech.aecu.core:aecu\=aecu-service"] \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/.content.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/.content.xml new file mode 100644 index 00000000..56d6dc70 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/.content.xml @@ -0,0 +1,5 @@ + + diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/dataitem/dataitem.html b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/dataitem/dataitem.html new file mode 100644 index 00000000..2dbd93cf --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/dataitem/dataitem.html @@ -0,0 +1,16 @@ + + + + + ${resource.path} + * not executed * + + + + + + + + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/datasource/datasource.html b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/datasource/datasource.html new file mode 100644 index 00000000..d76dd992 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/datasource/datasource.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/page/.content.xml b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/page/.content.xml new file mode 100644 index 00000000..7d69c927 --- /dev/null +++ b/ui.apps/src/main/content/jcr_root/apps/valtech/aecu/tools/execute/page/.content.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + +
    + + + + + + +
    +
    +
    +
    +
    + + + +