io.stallion.assets.AssetsController.java Source code

Java tutorial

Introduction

Here is the source code for io.stallion.assets.AssetsController.java

Source

/*
 * Stallion Core: A Modern Web Framework
 *
 * Copyright (C) 2015 - 2016 Stallion Software LLC.
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details. You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 *
 *
 *
 */

package io.stallion.assets;

import io.stallion.Context;
import io.stallion.assetBundling.BundleRegistry;
import io.stallion.exceptions.ClientException;
import io.stallion.exceptions.UsageException;
import io.stallion.fileSystem.FileSystemWatcherService;
import io.stallion.services.Log;
import io.stallion.services.PermaCache;
import io.stallion.settings.Settings;
import io.stallion.utils.GeneralUtils;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.File;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import static io.stallion.utils.Literals.*;
import static io.stallion.Context.*;

/**
 * Manages assets that can be included on web pages. Takes care of cache-busting URL's
 * and asset bundling
 */
public class AssetsController {

    private static AssetsController _instance;

    public static AssetsController instance() {
        if (_instance == null) {
            throw new UsageException("Must call load() before accessing the AssetsController instance");
        }
        return _instance;
    }

    public static String ensureSafeAssetsPath(String path) {
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        if (!path.startsWith("/assets/")) {
            path = "/assets" + path;
        }
        if (path.contains("..")) {
            throw new UsageException("Invalid asset path, illegal characters: " + path);
        }
        return path;
    }

    /**
     * Get a wrapper for the asset controller with limited methods. This is used by
     * the template context and other sandboxed situations.
     *
     * @return
     */
    public static AssetsControllerSafeWrapper wrapper() {
        return instance().getWrapper();
    }

    public static AssetsController load() {
        _instance = new AssetsController();
        if (new File(Settings.instance().getTargetFolder() + "/assets").isDirectory()) {
            /*
            FileSystemWatcherService.instance().registerWatcher(
                new AssetFileChangeEventHandler()
                        .setWatchedFolder(Settings.instance().getTargetFolder() + "/assets")
                        .setWatchTree(true)
            );*/
        }
        // Load the pre-processors;
        //ExternalCommandPreProcessorRegistry.instance();
        return _instance;
    }

    public static void shutdown() {
        _instance = null;
    }

    private AssetsControllerSafeWrapper wrapper;

    private static HashMap<String, Long> timeStampByPath = new HashMap<String, Long>();

    public static HashMap<String, Long> getTimeStampByPath() {
        return timeStampByPath;
    }

    public static void setTimeStampByPath(HashMap<String, Long> timeStampByPath) {
        AssetsController.timeStampByPath = timeStampByPath;
    }

    /**
     * Loads a resource from the main stallion jar.
     *
     * @param path
     * @return
     */
    public String resource(String path) {
        return resource(path, "stallion");
    }

    public String resource(String path, String plugin) {
        return resource(path, plugin, "");
    }

    /**
     * Get the URL to access an asset file that is bundled in the jar as a resource.
     *
     * @param path - the path, relative to the assets folder in side the resource directory
     * @param plugin - the plugin from which you are loading the resource, use "stallion" to load from the main jar
     * @param developerUrl - an alternative URL to use in development mode. Useful if you want to point
     *                     you local nginx directly at the file, so you can see your changes without recompiling.
     * @return
     */
    public String resource(String path, String plugin, String developerUrl) {
        if (Context.getSettings().getDevMode() && !empty(developerUrl)) {
            return developerUrl;
        }

        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        return Context.settings().getCdnUrl() + "/st-resource/" + plugin + "/" + path;
    }

    /**
     * Turn a list of additional strings that should be in the Footer section of the
     * page and return as a string
     *
     * @return
     */
    public String pageFooterLiterals() {
        return Context.getResponse().getPageFooterLiterals().stringify();
    }

    /**
     * Turn a list of additional strings that should be in the HEAD section of the
     * page and return as a string
     *
     * @return
     */
    public String pageHeadLiterals() {
        return Context.getResponse().getPageHeadLiterals().stringify();
    }

    public String bundle(String plugin, String path) {
        if (Settings.instance().getBundleDebug()) {
            return new ResourceAssetBundleRenderer(plugin, path).renderDebugHtml();
        } else {
            return new ResourceAssetBundleRenderer(plugin, path).renderProductionHtml();
        }
    }

    /**
     * Output the HTML required to render a bundle of assets.
     * *
     * @param fileName
     * @return
     */
    public String bundle(String fileName) {
        if (Settings.instance().getBundleDebug()) {
            return new FileSystemAssetBundleRenderer(fileName).renderDebugHtml();
        } else {
            return new FileSystemAssetBundleRenderer(fileName).renderProductionHtml();
        }
    }

    /**
     * Get the URL for an asset file, with a timestamp added for cache busting.
     * @param path
     * @return
     */
    public String url(String path) {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        String url = Context.settings().getCdnUrl() + "/st-assets/" + path;
        if (url.contains("?")) {
            url = url + "&";
        } else {
            url = url + "?";
        }
        url = url + "ts=" + getTimeStampForAssetFile(path).toString();
        return url;
    }

    public Long getTimeStampForAssetFile(String path) {
        String filePath = Context.settings().getTargetFolder() + "/assets/" + path;
        if (getTimeStampByPath().containsKey(filePath)) {
            Long ts = getTimeStampByPath().get(filePath);
            if (ts > 0) {
                return ts;
            }
        }

        Path pathObj = FileSystems.getDefault().getPath(filePath);
        File file = new File(filePath);
        Long ts = file.lastModified();
        getTimeStampByPath().put(filePath, ts);
        return ts;
    }

    public Long getCurrentTimeStampForAssetFile(String path) {
        String filePath = Context.settings().getTargetFolder() + "/assets/" + path;
        Path pathObj = FileSystems.getDefault().getPath(filePath);
        File file = new File(filePath);
        Long ts = file.lastModified();
        return ts;
    }

    private String getKeyStringForPathSource(String path, String source) {
        Long ts = getCurrentTimeStampForAssetFile(path);
        if (ts == 0 || ts == null) {
            return DigestUtils.md5Hex(source);
        } else {
            return ts.toString();
        }
    }

    public AssetsControllerSafeWrapper getWrapper() {
        if (wrapper == null) {
            wrapper = new AssetsControllerSafeWrapper(this);
        }
        return wrapper;
    }

}