diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java index 8fa21c9b..dc75ad28 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java @@ -295,6 +295,7 @@ public void installConfigurationFiles(PersistableInstallationLogger installLog, sw.stop(); long executionTime = sw.getTime(); installLog.setExecutionTime(executionTime); + installLog.addVerboseMessage(LOG, "Finished at bundle start level " + RuntimeHelper.getCurrentStartLevel()); installLog.addMessage(LOG, "Successfully applied AC Tool configuration in " + msHumanReadable(executionTime)); } catch (Exception e) { installLog.addError("Could not process yaml files", e); // ensure exception is added to installLog before it's persisted in log in finally clause diff --git a/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java b/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java index 0b6426d3..d7f05d2d 100644 --- a/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java +++ b/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java @@ -11,6 +11,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Dictionary; +import java.util.Hashtable; import java.util.List; import javax.jcr.Node; @@ -20,10 +22,15 @@ import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.sling.event.jobs.Job; +import org.apache.sling.event.jobs.JobManager; +import org.apache.sling.event.jobs.consumer.JobConsumer; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.AttributeDefinition; @@ -40,6 +47,8 @@ @Designate(ocd=AcToolStartupHookServiceImpl.Config.class) public class AcToolStartupHookServiceImpl { private static final Logger LOG = LoggerFactory.getLogger(AcToolStartupHookServiceImpl.class); + + private static final String JOB_TOPIC_ACTOOL = "biz/netcentric/actool/apply"; @ObjectClassDefinition(name = "AC Tool Startup Hook", description = "Applies AC Tool config automatically upon startup (depending on configuration/runtime)") public static @interface Config { @@ -49,13 +58,21 @@ public enum StartupHookActivation { @AttributeDefinition(name = "Activation Mode", description = "Apply on startup - CLOUD_ONLY autodetects the cloud (by missing OSGi installer bundle) and only runs on startup if deployed in the cloud. ALWAYS can be useful for local testing. NEVER disables AC Tool runs on startup entirely.") StartupHookActivation activationMode() default StartupHookActivation.CLOUD_ONLY; + + @AttributeDefinition(name = "Async for Mutable Content", description = "Will execute on the mutable content asynchronously using a Sling Job") + boolean runAsyncForMutableConent() default false; } - + @Reference(policyOption = ReferencePolicyOption.GREEDY) private AcInstallationService acInstallationService; @Reference(policyOption = ReferencePolicyOption.GREEDY) private SlingRepository repository; + + @Reference(policyOption = ReferencePolicyOption.GREEDY) + private JobManager jobManager; + + private ServiceRegistration jobHandlerRegistration; private boolean isCompositeNodeStore; @@ -64,36 +81,76 @@ public void activate(BundleContext bundleContext, Config config) { boolean isCloudReady = RuntimeHelper.isCloudReadyInstance(); Config.StartupHookActivation activationMode = config.activationMode(); - LOG.info("AcTool Startup Hook (start level: {} isCloudReady: {} activationMode: {})", + boolean runAsyncForMutableConent = config.runAsyncForMutableConent(); + LOG.info("AcTool Startup Hook (start level: {} isCloudReady: {} activationMode: {} runAsyncForMutableConent: {})", RuntimeHelper.getCurrentStartLevel(bundleContext), isCloudReady, - activationMode); + activationMode, + runAsyncForMutableConent); boolean applyOnStartup = (activationMode == Config.StartupHookActivation.ALWAYS) || (isCloudReady && activationMode == Config.StartupHookActivation.CLOUD_ONLY); if(applyOnStartup) { - - try { - - List relevantPathsForInstallation = getRelevantPathsForInstallation(); - LOG.info("Running AcTool with " - + (relevantPathsForInstallation.isEmpty() ? "all paths" : "paths " + relevantPathsForInstallation) + "..."); - acInstallationService.apply(null, relevantPathsForInstallation.toArray(new String[relevantPathsForInstallation.size()]), - true); - LOG.info("AC Tool Startup Hook done. (start level " + RuntimeHelper.getCurrentStartLevel(bundleContext) + ")"); - copyAcHistoryToOrFromApps(isCloudReady); - - } catch (RepositoryException e) { - LOG.error("Exception while triggering AC Tool on startup: " + e, e); + if (runAsyncForMutableConent && isCompositeNodeStore) { + runAcToolAsync(bundleContext, isCloudReady); + } else { + runAcTool(bundleContext, isCloudReady); } + } else { LOG.debug("Skipping AcTool Startup Hook: activationMode: {} isCloudReady: {}", activationMode, isCloudReady); } } + @Deactivate + public void deactivate() { + if (jobHandlerRegistration != null) { + jobHandlerRegistration.unregister(); + } + } + + private void runAcTool(BundleContext bundleContext, boolean isCloudReady) { + try { + + List relevantPathsForInstallation = getRelevantPathsForInstallation(); + LOG.info("Running AcTool with " + + (relevantPathsForInstallation.isEmpty() ? "all paths" : "paths " + relevantPathsForInstallation) + "..."); + acInstallationService.apply(null, relevantPathsForInstallation.toArray(new String[relevantPathsForInstallation.size()]), + true); + LOG.info("AC Tool Startup Hook done. (start level " + RuntimeHelper.getCurrentStartLevel(bundleContext) + ")"); + + copyAcHistoryToOrFromApps(isCloudReady); + + } catch (RepositoryException e) { + LOG.error("Exception while triggering AC Tool on startup: " + e, e); + } + } + + private void runAcToolAsync(final BundleContext bundleContext, final boolean isCloudReady) { + + final AcToolStartupHookServiceImpl startupHook = this; + JobConsumer acToolJobConsumer = new JobConsumer() { + + @Override + public JobResult process(Job job) { + startupHook.runAcTool(bundleContext, isCloudReady); + return JobResult.OK; + } + }; + + Dictionary serviceProps = new Hashtable<>(); + serviceProps.put(JobConsumer.PROPERTY_TOPICS, JOB_TOPIC_ACTOOL); + + // register handler + jobHandlerRegistration = bundleContext.registerService(JobConsumer.class, acToolJobConsumer, serviceProps); + + LOG.info("Running AcTool asynchronously..."); + jobManager.createJob(JOB_TOPIC_ACTOOL).add(); + } + private List getRelevantPathsForInstallation() throws RepositoryException { Session session = null; try {