All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.orbinson.aem.groovy.console.impl.DefaultGroovyConsoleService.groovy Maven / Gradle / Ivy

The newest version!
package be.orbinson.aem.groovy.console.impl

import be.orbinson.aem.groovy.console.GroovyConsoleService
import be.orbinson.aem.groovy.console.api.ActiveJob
import be.orbinson.aem.groovy.console.api.JobProperties
import be.orbinson.aem.groovy.console.api.context.ScriptContext
import be.orbinson.aem.groovy.console.api.context.ScriptData
import be.orbinson.aem.groovy.console.audit.AuditService
import be.orbinson.aem.groovy.console.configuration.ConfigurationService
import be.orbinson.aem.groovy.console.constants.GroovyConsoleConstants
import be.orbinson.aem.groovy.console.extension.ExtensionService
import be.orbinson.aem.groovy.console.notification.NotificationService
import be.orbinson.aem.groovy.console.response.RunScriptResponse
import be.orbinson.aem.groovy.console.response.SaveScriptResponse
import be.orbinson.aem.groovy.console.response.impl.DefaultRunScriptResponse
import be.orbinson.aem.groovy.console.response.impl.DefaultSaveScriptResponse
import groovy.transform.Synchronized
import groovy.transform.TimedInterrupt
import groovy.util.logging.Slf4j
import org.apache.jackrabbit.JcrConstants
import org.apache.jackrabbit.util.Text
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.ResourceUtil
import org.apache.sling.event.jobs.JobManager
import org.apache.sling.jcr.resource.api.JcrResourceConstants
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.CompilationCustomizer
import org.osgi.service.component.annotations.Component
import org.osgi.service.component.annotations.Reference
import org.osgi.service.component.annotations.ReferenceCardinality
import org.osgi.service.component.annotations.ReferencePolicy

import java.util.concurrent.CopyOnWriteArrayList

import static be.orbinson.aem.groovy.console.constants.GroovyConsoleConstants.*

@Component(service = GroovyConsoleService, immediate = true)
@Slf4j("LOG")
class DefaultGroovyConsoleService implements GroovyConsoleService {


    @Reference
    private ConfigurationService configurationService

    private volatile List notificationServices = new CopyOnWriteArrayList<>()

    @Reference
    private AuditService auditService

    @Reference
    private ExtensionService extensionService

    @Reference
    private JobManager jobManager

    @Override
    RunScriptResponse runScript(ScriptContext scriptContext) {
        def binding = getBinding(scriptContext)

        def runScriptResponse = null

        try {
            def script = new GroovyShell(binding, configuration).parse(scriptContext.script)

            extensionService.getScriptMetaClasses(scriptContext).each { meta ->
                script.metaClass(meta)
            }

            def start = System.currentTimeMillis()
            def result = script.run()
            def date = new Date()

            date.time = System.currentTimeMillis() - start
            def runningTime = date.format(FORMAT_RUNNING_TIME, TimeZone.getTimeZone(TIME_ZONE_RUNNING_TIME))

            LOG.debug("script execution completed, running time : {}", runningTime)

            runScriptResponse = DefaultRunScriptResponse.fromResult(scriptContext, result,
                    scriptContext.outputStream.toString(CHARSET), runningTime)

            auditAndNotify(runScriptResponse)
        } catch (MultipleCompilationErrorsException e) {
            LOG.error("script compilation error", e)

            runScriptResponse = DefaultRunScriptResponse.fromException(scriptContext,
                    scriptContext.outputStream.toString(CHARSET), e)
        } catch (Throwable t) {
            LOG.error("error running script", t)

            runScriptResponse = DefaultRunScriptResponse.fromException(scriptContext,
                    scriptContext.outputStream.toString(CHARSET), t)

            auditAndNotify(runScriptResponse)
        } finally {
            scriptContext.outputStream.close()
        }

        runScriptResponse
    }

    @Override
    @Synchronized
    SaveScriptResponse saveScript(ScriptData scriptData) {
        def resourceResolver = scriptData.resourceResolver

        def folderResource = ResourceUtil.getOrCreateResource(resourceResolver, PATH_SCRIPTS_FOLDER, JcrResourceConstants.NT_SLING_FOLDER, JcrResourceConstants.NT_SLING_FOLDER, true);

        def fileName = scriptData.fileName

        if (folderResource.getChild(fileName)) {
            resourceResolver.delete(folderResource.getChild(fileName))
            resourceResolver.commit();
        }

        saveFile(resourceResolver, folderResource, scriptData.script, fileName, new Date(), "application/octet-stream")

        new DefaultSaveScriptResponse(fileName)
    }

    @Override
    List getActiveJobs() {
        jobManager.findJobs(JobManager.QueryType.ACTIVE, GroovyConsoleConstants.JOB_TOPIC, 0, null).collect { job ->
            new ActiveJob(job)
        }
    }

    @Override
    boolean addScheduledJob(JobProperties jobProperties) {
        if (jobProperties.cronExpression) {
            LOG.info("adding scheduled job with properties : {}", jobProperties.toMap())

            jobManager.createJob(GroovyConsoleConstants.JOB_TOPIC)
                    .properties(jobProperties.toMap())
                    .schedule()
                    .cron(jobProperties.cronExpression)
                    .add()
        } else {
            LOG.info("adding immediate job with properties : {}", jobProperties.toMap())

            jobManager.addJob(GroovyConsoleConstants.JOB_TOPIC, jobProperties.toMap())
        }
    }

    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
    @Synchronized
    void bindNotificationService(NotificationService notificationService) {
        notificationServices.add(notificationService)

        LOG.info("added notification service : {}", notificationService.class.name)
    }

    @Synchronized
    void unbindNotificationService(NotificationService notificationService) {
        notificationServices.remove(notificationService)

        LOG.info("removed notification service : {}", notificationService.class.name)
    }

    // internals

    private void auditAndNotify(RunScriptResponse response) {
        if (!configurationService.auditDisabled) {
            auditService.createAuditRecord(response)
        }

        notificationServices.each { notificationService ->
            notificationService.notify(response)
        }
    }

    private Binding getBinding(ScriptContext scriptContext) {
        def binding = new Binding()

        extensionService.getBindingVariables(scriptContext).each { name, variable ->
            binding.setVariable(name, variable.value)
        }

        binding
    }

    private CompilerConfiguration getConfiguration() {
        def configuration = new CompilerConfiguration()

        if (configurationService.threadTimeout > 0) {
            // add timed interrupt using configured timeout value
            configuration.addCompilationCustomizers(new ASTTransformationCustomizer(value: configurationService.threadTimeout, TimedInterrupt))
        }

        configuration.addCompilationCustomizers(extensionService.compilationCustomizers
                as CompilationCustomizer[])
    }

    private void saveFile(ResourceResolver resourceResolver, Resource folderResource, String script, String fileName, Date date,
                          String mimeType) {

        def fileResource = resourceResolver.create(folderResource, Text.escapeIllegalJcrChars(fileName),
                [(JcrConstants.JCR_PRIMARYTYPE): JcrConstants.NT_FILE] as Map)

        def fileContentResource = resourceResolver.create(fileResource, JcrConstants.JCR_CONTENT,
                [(JcrConstants.JCR_PRIMARYTYPE): JcrConstants.NT_RESOURCE] as Map)

        def stream = new ByteArrayInputStream(script.getBytes(CHARSET))
        def valueMap = fileContentResource.adaptTo(ModifiableValueMap.class)

        valueMap.put(JcrConstants.JCR_MIMETYPE, mimeType)
        valueMap.put(JcrConstants.JCR_ENCODING, CHARSET)
        valueMap.put(JcrConstants.JCR_DATA, stream)
        valueMap.put(JcrConstants.JCR_LASTMODIFIED, date.time)
        valueMap.put("jcr:lastModifiedBy", resourceResolver.getUserID())

        resourceResolver.commit()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy