diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 25d1224f1b..9ba3af9985 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -88,6 +88,9 @@ jobs: - name: ${{ matrix.namePrefix }} Build with Maven ${{ env.STEP_NAME_SUFFIX }} run: mvn -e -B -V ${{ env.MVN_GOAL }} ${{ env.MVN_ADDITIONAL_OPTS }} + - name: ${{ matrix.namePrefix }} Build cloud all package with Maven ${{ env.STEP_NAME_SUFFIX }} + run: mvn -e -B -V -pl all -P cloud ${{ env.MVN_GOAL }} ${{ env.MVN_ADDITIONAL_OPTS }} + - name: Publish Test Report if: ${{ always() }} # make sure to run even if previous Maven execution failed (due to failed test) uses: scacap/action-surefire-report@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 67055693df..cb094b4da6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com) ## Unreleased ([details][unreleased changes details]) +## Added + +- #3147 - Allow usage of Dispatcher Flush Rules in AEMaaCS + ## 6.2.0 - 2023-09-14 ## Added diff --git a/all/pom.xml b/all/pom.xml index cc714d4a58..5c6ce01689 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -17,7 +17,8 @@ ~ limitations under the License. --> - + 4.0.0 @@ -44,32 +45,45 @@ org.apache.jackrabbit filevault-package-maven-plugin + false container - - - ${project.groupId} - acs-aem-commons-bundle - /apps/acs-commons/install - true - true - - - - - ${project.groupId} - acs-aem-commons-ui.apps - true - true - - - ${project.groupId} - acs-aem-commons-ui.content - true - true - - + + + default-metadata + + generate-metadata + + + acs-aem-commons-all + + + ${project.groupId} + acs-aem-commons-bundle + /apps/acs-commons/install + true + true + + + ${project.groupId} + acs-aem-commons-ui.apps + ~cloud + /apps/acs-commons/install + true + true + + + ${project.groupId} + acs-aem-commons-ui.content + /apps/acs-commons/install + true + true + + + + + @@ -200,6 +214,9 @@ com.day.jcr.vault content-package-maven-plugin + + ${contentPackageFile} + install-content-package @@ -213,5 +230,116 @@ + + + cloud + + false + + + ${project.build.directory}/${project.build.finalName}-cloud.zip + + + + + org.apache.jackrabbit + filevault-package-maven-plugin + false + + + generate-metadata-cloud + + generate-metadata + + + cloud + + all + + + + ${project.groupId} + acs-aem-commons-bundle + /apps/acs-commons/install + true + true + + + ${project.groupId} + acs-aem-commons-bundle-cloud + /apps/acs-commons/install + true + true + + + ${project.groupId} + acs-aem-commons-ui.apps + cloud + /apps/acs-commons/install + true + true + + + ${project.groupId} + acs-aem-commons-ui.content + /apps/acs-commons/install + true + true + + + + + + package-cloud + + package + + + cloud + + + + validate-package-cloud + + validate-package + + + cloud + ${contentPackageFile} + + + + + + com.adobe.aem + aemanalyser-maven-plugin + 1.5.8 + + + aem-analyser + + project-analyse + + + + + + + + + com.adobe.acs + acs-aem-commons-bundle-cloud + ${project.version} + jar + + + com.adobe.acs + acs-aem-commons-ui.apps + cloud + ${project.version} + zip + + + diff --git a/bundle-cloud/pom.xml b/bundle-cloud/pom.xml new file mode 100644 index 0000000000..e19168eea1 --- /dev/null +++ b/bundle-cloud/pom.xml @@ -0,0 +1,124 @@ + + + + 4.0.0 + + + + + + com.adobe.acs + acs-aem-commons + 6.2.1-SNAPSHOT + + + + + + acs-aem-commons-bundle-cloud + ACS AEM Commons Bundle - Core Cloud Fragment + OSGi Core bundle fragment for ACS AEM Commons (only AEMaaCS) + + + + biz.aQute.bnd + bnd-maven-plugin + + + + + + biz.aQute.bnd + bnd-baseline-maven-plugin + + + org.apache.sling + sling-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + + + com.google.code.findbugs + jsr305 + provided + + + com.adobe.acs + acs-aem-commons-bundle + ${project.version} + provided + + + + com.adobe.acs + acs-aem-commons-bundle + ${project.version} + tests + test-jar + test + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.sling + org.apache.sling.testing.osgi-mock.junit4 + test + + + org.apache.sling + org.apache.sling.testing.sling-mock.junit4 + test + + + io.wcm + io.wcm.testing.aem-mock.junit4 + test + + + + + com.adobe.aem + aem-sdk-api + provided + + + diff --git a/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlushRulesExecutor.java b/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlushRulesExecutor.java new file mode 100644 index 0000000000..5f62f3a8e4 --- /dev/null +++ b/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlushRulesExecutor.java @@ -0,0 +1,115 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2016 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.replication.dispatcher.impl; + +import com.adobe.acs.commons.replication.dispatcher.DispatcherFlushRules; +import com.adobe.acs.commons.util.RequireAem; +import com.day.cq.replication.ReplicationAction; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.apache.sling.discovery.DiscoveryService; +import org.apache.sling.distribution.DistributionRequestType; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; + +import static org.apache.sling.distribution.event.DistributionEventProperties.DISTRIBUTION_PATHS; +import static org.apache.sling.distribution.event.DistributionEventProperties.DISTRIBUTION_TYPE; +import static org.apache.sling.distribution.event.DistributionEventTopics.AGENT_PACKAGE_DISTRIBUTED; +import static org.osgi.service.event.EventConstants.EVENT_TOPIC; + +@Component( + immediate = true, + service = EventHandler.class, + property = { + EVENT_TOPIC + "=" + AGENT_PACKAGE_DISTRIBUTED + } +) +public class CloudDispatcherFlushRulesExecutor implements EventHandler { + + private static final Logger log = LoggerFactory.getLogger(CloudDispatcherFlushRulesExecutor.class); + + @Reference(target = "(distribution=cloud-ready)") + private RequireAem requireAem; + + @Reference + private DiscoveryService discoveryService; + + @Reference + private volatile List dispatcherFlushRules; + + @Override + public void handleEvent(Event event) { + String distributionType = (String) event.getProperty(DISTRIBUTION_TYPE); + + boolean isLeader = discoveryService.getTopology().getLocalInstance().isLeader(); + // process the OSGi event on the leader author instance + if (isLeader) { + String[] distributionPaths = (String[]) event.getProperty(DISTRIBUTION_PATHS); + if (distributionPaths == null || distributionPaths.length == 0) { + log.debug("Skipping processing because the distribution paths are empty"); + return; + } + ReplicationActionType actionType = getReplicationActionType(distributionType); + if (actionType != null) { + executeFlushRules(actionType, Arrays.asList(distributionPaths)); + } + } + } + + private void executeFlushRules(ReplicationActionType actionType, List distributionPaths) { + ReplicationAction action = new ReplicationAction(actionType, distributionPaths.toArray(new String[0]), 0L, "", null); + ReplicationOptions opts = new ReplicationOptions(); + log.debug("Executing dispatcher flush rules for distribution paths {}", distributionPaths); + for (DispatcherFlushRules dispatcherFlushRule : dispatcherFlushRules) { + try { + dispatcherFlushRule.preprocess(action, opts); + } catch (ReplicationException e) { + log.warn("Could not execute dispatcher flush rule for distribution paths [{}]", distributionPaths, e); + } + } + if (log.isInfoEnabled()) { + log.info("Executed flush rules for resources [{}]", distributionPaths); + } + } + + + private ReplicationActionType getReplicationActionType(String distributionType) { + DistributionRequestType requestType = DistributionRequestType.fromName(distributionType); + if (DistributionRequestType.ADD.equals(requestType)) { + return ReplicationActionType.ACTIVATE; + } else if (DistributionRequestType.DELETE.equals(requestType)) { + return ReplicationActionType.DEACTIVATE; + } else if (DistributionRequestType.TEST.equals(requestType)) { + return ReplicationActionType.TEST; + } + log.debug("Distribution request type {} not supported", requestType); + return null; + } + + +} diff --git a/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlusher.java b/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlusher.java new file mode 100644 index 0000000000..e494e510b8 --- /dev/null +++ b/bundle-cloud/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/CloudDispatcherFlusher.java @@ -0,0 +1,139 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2016 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.replication.dispatcher.impl; + +import com.adobe.acs.commons.replication.dispatcher.DispatcherFlushFilter; +import com.adobe.acs.commons.replication.dispatcher.DispatcherFlusher; +import com.day.cq.replication.Agent; +import com.day.cq.replication.AgentFilter; +import com.day.cq.replication.AgentManager; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationResult; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.distribution.DistributionRequest; +import org.apache.sling.distribution.DistributionRequestType; +import org.apache.sling.distribution.DistributionResponse; +import org.apache.sling.distribution.Distributor; +import org.apache.sling.distribution.SimpleDistributionRequest; +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.component.propertytypes.ServiceRanking; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@ServiceRanking(-5000) +@Designate(ocd = CloudDispatcherFlusher.Config.class) +public class CloudDispatcherFlusher implements DispatcherFlusher { + + private static final Logger log = LoggerFactory.getLogger(CloudDispatcherFlusher.class); + + @ObjectClassDefinition + @interface Config { + @AttributeDefinition(description = "Agent names to trigger when executing a distribute invalidate (ex. publish, preview)") + String[] agent_names() default {"publish"}; + } + + @Reference + private Distributor distributor; + + @Reference + private AgentManager agentManager; + + private String[] agentNames; + + @Activate + protected void activate(Config config) { + this.agentNames = config.agent_names(); + } + + @Override + public Map flush(ResourceResolver resourceResolver, String... paths) { + Map result = new HashMap<>(); + DistributionRequest distributionRequest = new SimpleDistributionRequest(DistributionRequestType.INVALIDATE, false, paths); + for (String agentName : agentNames) { + DistributionResponse distributionResponse = distributor.distribute(agentName, resourceResolver, distributionRequest); + Agent agent = agentManager.getAgents().get(agentName); + if (agent != null) { + result.put(agent, toReplicationResult(distributionResponse)); + } + } + log.debug("Executed dispatcher flush for paths {}", Arrays.asList(paths)); + return result; + } + + @Override + public Map flush(ResourceResolver resourceResolver, ReplicationActionType actionType, boolean synchronous, String... paths) throws ReplicationException { + // see https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/implementing/content-delivery/caching.html?lang=en#sling-distribution + log.warn( + "Dispatcher flusher in cloud should use INVALIDATE distribution types from author, no custom action type and synchronous should be set, " + + "refactor your code to use the DispatcherFlushRules.flush(resourceResolver, paths) method" + ); + return flush(resourceResolver, paths); + } + + @Override + public Map flush(ResourceResolver resourceResolver, ReplicationActionType actionType, boolean synchronous, AgentFilter agentFilter, String... paths) throws ReplicationException { + // see https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/implementing/content-delivery/caching.html?lang=en#sling-distribution + log.warn( + "Dispatcher flusher in cloud should use INVALIDATE distribution types from author, no custom action type and synchronous should be set, " + + "refactor your code to use the DispatcherFlushRules.flush(resourceResolver, paths) method" + ); + return flush(resourceResolver, paths); + } + + @Override + public final Agent[] getFlushAgents() { + return this.getAgents(new DispatcherFlushFilter()); + } + + /** + * {@inheritDoc} + */ + @Override + public final Agent[] getAgents(final AgentFilter agentFilter) { + final List flushAgents = new ArrayList(); + + for (final Agent agent : agentManager.getAgents().values()) { + if (agentFilter.isIncluded(agent)) { + flushAgents.add(agent); + } + } + return flushAgents.toArray(new Agent[flushAgents.size()]); + } + + private ReplicationResult toReplicationResult(DistributionResponse distributionResponse) { + if (distributionResponse.isSuccessful()) { + return ReplicationResult.OK; + } + return new ReplicationResult(false, 500, distributionResponse.getMessage()); + } +} diff --git a/bundle/pom.xml b/bundle/pom.xml index 9404054e8e..7c143df860 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -53,11 +53,15 @@ org.apache.maven.plugins maven-jar-plugin - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - + + + + generate-test-jar + + test-jar + + + biz.aQute.bnd @@ -66,14 +70,6 @@ org.apache.sling sling-maven-plugin - - ${crx.protocol}://${crx.host}:${crx.port}${crx.contextRoot} - /apps/acs-commons/install - SlingPostServlet - ${crx.user} - ${crx.password} - true - org.apache.maven.plugins diff --git a/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/DispatcherFlushRules.java b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/DispatcherFlushRules.java new file mode 100644 index 0000000000..5acb2e406e --- /dev/null +++ b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/DispatcherFlushRules.java @@ -0,0 +1,30 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2016 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.replication.dispatcher; + +import com.day.cq.replication.ReplicationAction; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; + +public interface DispatcherFlushRules { + + void preprocess(final ReplicationAction replicationAction, + final ReplicationOptions replicationOptions) throws ReplicationException; +} diff --git a/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlushRulesImpl.java b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlushRulesImpl.java index 5c7e5f67d5..5309028875 100644 --- a/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlushRulesImpl.java +++ b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlushRulesImpl.java @@ -20,6 +20,7 @@ import com.adobe.acs.commons.replication.dispatcher.DispatcherFlushFilter; import com.adobe.acs.commons.replication.dispatcher.DispatcherFlusher; +import com.adobe.acs.commons.replication.dispatcher.DispatcherFlushRules; import com.adobe.acs.commons.replication.dispatcher.DispatcherFlushFilter.FlushType; import com.adobe.acs.commons.util.ParameterUtil; import com.day.cq.replication.AgentManager; @@ -71,7 +72,7 @@ name = "webconsole.configurationFactory.nameHint", value = "Rule: {prop.replication-action-type}, for Hierarchy: [{prop.rules.hierarchical}] or Resources: [{prop.rules.resource-only}]") }) -public class DispatcherFlushRulesImpl implements Preprocessor { +public class DispatcherFlushRulesImpl implements Preprocessor, DispatcherFlushRules { private static final Logger log = LoggerFactory.getLogger(DispatcherFlushRulesImpl.class); private static final String OPTION_INHERIT = "INHERIT"; diff --git a/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlusherImpl.java b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlusherImpl.java index 7a9718773c..72f1dfbf7c 100644 --- a/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlusherImpl.java +++ b/bundle/src/main/java/com/adobe/acs/commons/replication/dispatcher/impl/DispatcherFlusherImpl.java @@ -32,6 +32,7 @@ import org.apache.sling.api.resource.ResourceResolver; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.propertytypes.ServiceRanking; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,12 +45,8 @@ * ACS AEM Commons - Dispatcher Flusher * Service used to issue flush requests to enabled Dispatcher Flush Agents. */ -@Component( - property = { - "service.ranking=-10000" - }, - service = {DispatcherFlusher.class} -) +@Component +@ServiceRanking(-10000) public class DispatcherFlusherImpl implements DispatcherFlusher { private static final Logger log = LoggerFactory.getLogger(DispatcherFlusherImpl.class); diff --git a/bundle/src/test/java/com/adobe/acs/commons/it/build/ScrMetadataIT.java b/bundle/src/test/java/com/adobe/acs/commons/it/build/ScrMetadataIT.java index 44e13fe4f2..9da815aeab 100644 --- a/bundle/src/test/java/com/adobe/acs/commons/it/build/ScrMetadataIT.java +++ b/bundle/src/test/java/com/adobe/acs/commons/it/build/ScrMetadataIT.java @@ -115,6 +115,7 @@ public class ScrMetadataIT { COMPONENT_PROPERTIES_TO_IGNORE_FOR_TYPE_CHANGE = new HashSet<>(); COMPONENT_PROPERTIES_TO_IGNORE.add("com.adobe.acs.commons.redirects.filter.RedirectFilter:mapUrls"); + COMPONENT_PROPERTIES_TO_IGNORE.add("com.adobe.acs.commons.replication.dispatcher.impl.DispatcherFlusherImpl:service.ranking"); ALLOWED_SCR_NS_URIS = new HashSet<>(); ALLOWED_SCR_NS_URIS.add("http://www.osgi.org/xmlns/scr/v1.0.0"); diff --git a/pom.xml b/pom.xml index 8e649b22aa..d5ffb4f5d6 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,11 @@ oakpal-checks/pom.xml + + 6.5.10.0002 + + 2023.1.10675.20230113T110236Z-220900 + 0.8.7 1.8 @@ -98,6 +103,11 @@ maven-jar-plugin 3.2.2 + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + maven-compiler-plugin @@ -207,6 +217,8 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ # support only DS 1.4 (https://github.com/bndtools/bnd/pull/3121/files) -dsannotations-options: version;maximum=1.4.0,inherit -metatypeannotations-options: version;maximum=1.4.0 +# also detect scr descriptors from bundle fragments (https://docs.osgi.org/specification/osgi.cmpn/8.0.0/service.component.html#d0e30931) +Service-Component: OSGI-INF/*.xml ]]> @@ -242,6 +254,14 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ org.apache.sling sling-maven-plugin 2.4.2 + + ${crx.protocol}://${crx.host}:${crx.port}${crx.contextRoot} + /apps/acs-commons/install + SlingPostServlet + ${crx.user} + ${crx.password} + true + org.apache.jackrabbit @@ -501,11 +521,17 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ ${project.version} test + + com.adobe.aem + aem-sdk-api + ${aem.sdk.api.version} + provided + io.wcm.maven io.wcm.maven.aem-dependencies - 6.5.10.0002 + ${aem.classic.api.version} pom import @@ -866,6 +892,15 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ + + + + cloud + + bundle-cloud + + @@ -878,6 +913,7 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ bundle + bundle-cloud oakpal-checks ui.apps ui.content diff --git a/ui.apps/pom.xml b/ui.apps/pom.xml index cef6f8f52f..3132f60e37 100644 --- a/ui.apps/pom.xml +++ b/ui.apps/pom.xml @@ -17,7 +17,8 @@ ~ limitations under the License. --> - + 4.0.0 @@ -47,15 +48,49 @@ merge application - - - day/cq60/product - cq-content - [6.5.6,) - - - + + + default-metadata + + generate-metadata + + + + + day/cq60/product + cq-content + [6.5.10,) + + + + + + cloud-metadata + + generate-metadata + + + cloud + + + day/cq60/product + cq-content + [6.6,) + + + + + + cloud-package + + package + + + cloud + + + @@ -84,8 +119,8 @@ - - + +