de.axelfaust.alfresco.nashorn.repo.loaders.ClasspathScriptFile.java Source code

Java tutorial

Introduction

Here is the source code for de.axelfaust.alfresco.nashorn.repo.loaders.ClasspathScriptFile.java

Source

/*
 * Copyright 2016 Axel Faust
 *
 * Licensed under the Eclipse Public License (EPL), Version 1.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the License at
 *
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * 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.
 */
package de.axelfaust.alfresco.nashorn.repo.loaders;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.UUID;

import org.alfresco.scripts.ScriptException;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ResourceUtils;

/**
 * @author Axel Faust
 */
public class ClasspathScriptFile implements ScriptFile {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClasspathScriptFile.class);

    private static final String CACHE_DIRECTORY_NAME = "Alfresco-Nashorn-ClasspathScriptCache";

    private static final File CACHE_DIRECTORY = TempFileProvider
            .getTempDir(CACHE_DIRECTORY_NAME + "-" + System.currentTimeMillis());
    static {
        CACHE_DIRECTORY.deleteOnExit();
    }

    protected static final long DEFAULT_EXISTENCE_CHECK_INTERVAL = 30000;

    protected static final long DEFAULT_LAST_MODIFIED_CHECK_INTERVAL = 30000;

    protected final String filePath;

    protected transient boolean exists = false;

    protected transient boolean existsInJarFile = false;

    protected transient long lastExistenceCheck = -1;

    protected transient long lastModified = -1;

    protected transient long lastModifiedCheck = -1;

    protected transient long size = -1;

    protected transient ClassPathResource resource;

    protected transient File cacheFile;

    public ClasspathScriptFile(final String filePath) {
        ParameterCheck.mandatoryString("filePath", filePath);

        this.filePath = filePath;
        this.resource = new ClassPathResource(filePath, this.getClass().getClassLoader());

        this.cacheFile = new File(CACHE_DIRECTORY,
                MessageFormat.format("{0}-{1}.js", this.resource.getFilename(), UUID.randomUUID().toString()));
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public boolean exists(final boolean force) {
        boolean exists = this.exists;

        // JAR files "should never" cease to exist during runtime
        if (!this.existsInJarFile || force) {
            final long currentTimeMillis = System.currentTimeMillis();
            if (force || currentTimeMillis - this.lastExistenceCheck > DEFAULT_EXISTENCE_CHECK_INTERVAL) {
                synchronized (this) {
                    exists = this.exists;
                    if (force || currentTimeMillis - this.lastExistenceCheck > DEFAULT_EXISTENCE_CHECK_INTERVAL) {
                        final boolean doesExist = this.resource.exists();

                        if (!doesExist && this.exists) {
                            this.lastModifiedCheck = -1;

                            this.cacheFile.delete();
                            this.size = -1;
                        } else if (doesExist && !this.existsInJarFile) {
                            try {
                                final URL url = this.resource.getURL();
                                this.existsInJarFile = ResourceUtils.isJarURL(url);
                            } catch (final IOException ioex) {
                                LOGGER.debug("Error retrieving resource URL despite proven existence", ioex);
                            }
                        }

                        exists = this.exists = doesExist;
                        this.lastExistenceCheck = currentTimeMillis;
                    }
                }
            }
        }

        return exists;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public long getSize(final boolean force) {
        long size = this.exists(force) ? this.size : -1;

        if (this.exists) {
            if (force || size == -1) {
                synchronized (this) {
                    if (force || this.size == -1) {
                        this.cacheScriptFile();
                    }
                    size = this.size;
                }

            }
        }

        return size;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public long getLastModified(final boolean force) {
        long lastModified = this.exists(force) ? this.lastModified : -1;
        if (this.exists) {
            final long currentTimeMillis = System.currentTimeMillis();
            if (force || currentTimeMillis - this.lastModifiedCheck > DEFAULT_LAST_MODIFIED_CHECK_INTERVAL) {
                synchronized (this) {
                    if (this.exists) {
                        lastModified = this.lastModified;
                        if (force || currentTimeMillis
                                - this.lastModifiedCheck > DEFAULT_LAST_MODIFIED_CHECK_INTERVAL) {
                            try {
                                lastModified = this.resource.lastModified();

                                if (this.lastModified != -1 && lastModified != this.lastModified) {
                                    this.cacheFile.delete();
                                    this.size = -1;
                                }
                            } catch (final IOException ioex) {
                                LOGGER.debug("Resource could not be resolved - treating as no longer existing",
                                        ioex);

                                lastModified = this.lastModified = -1;
                                this.exists = this.existsInJarFile = false;

                                this.cacheFile.delete();
                                this.size = -1;
                            }

                            this.lastModifiedCheck = currentTimeMillis;
                        }
                    }
                }
            }
        }

        return lastModified;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized InputStream getInputStream() {
        InputStream is;

        if (this.exists(false)) {
            if (!this.cacheFile.exists()) {
                this.cacheScriptFile();
            }

            if (!this.cacheFile.exists()) {
                throw new ScriptException("Script can't be loaded");
            }

            try {
                is = new FileInputStream(this.cacheFile);
            } catch (final IOException ioex) {
                throw new ScriptException("Error loading cached script file", ioex);
            }
        } else {
            throw new ScriptException("Script file does not exist");
        }

        return is;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public synchronized void reset() {
        this.exists = this.existsInJarFile = false;
        this.lastExistenceCheck = -1;
        this.lastModified = -1;
        this.lastModifiedCheck = -1;

        this.cacheFile.delete();
        this.size = -1;
    }

    protected synchronized void cacheScriptFile() {
        try {
            try (final InputStream is = new StrictScriptEnforcingSourceInputStream(
                    this.resource.getInputStream())) {
                try (final OutputStream os = new FileOutputStream(this.cacheFile, false)) {
                    IOUtils.copy(is, os);
                }
            }

            this.size = this.cacheFile.length();
        } catch (final IOException ioex) {
            LOGGER.debug("Error caching script file - treating as no longer existing", ioex);

            this.exists = this.existsInJarFile = false;

            this.cacheFile.delete();
            this.size = -1;

            throw new ScriptException("Script can't be loaded", ioex);
        }
    }
}