001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.assets; 019 020import io.stallion.Context; 021import io.stallion.assetBundling.AssetBundle; 022import io.stallion.assetBundling.AssetFile; 023import io.stallion.exceptions.NotFoundException; 024import io.stallion.exceptions.UsageException; 025import io.stallion.plugins.PluginRegistry; 026import io.stallion.settings.Settings; 027import org.apache.commons.codec.digest.DigestUtils; 028import org.apache.commons.io.IOUtils; 029 030import java.io.File; 031import java.io.IOException; 032import java.io.UnsupportedEncodingException; 033import java.net.URISyntaxException; 034import java.net.URL; 035import java.net.URLEncoder; 036import java.text.MessageFormat; 037import java.util.List; 038import java.util.Map; 039 040import static io.stallion.utils.Literals.empty; 041import static io.stallion.utils.Literals.or; 042 043 044public class FileSystemAssetBundleRenderer { 045 private String fileSystemPath; 046 private String path; 047 private String extension; 048 private File bundleFile; 049 private String bundleRelativePath; 050 051 public FileSystemAssetBundleRenderer(String path) { 052 path = AssetsController.ensureSafeAssetsPath(path); 053 int i = path.indexOf(".bundle.") + 7; 054 if (i <= 6) { 055 throw new UsageException("The path - " + path + " - is not a valid bundle name. Expected to end with .bundle.js, .bundle.head.js, or .bundle.css"); 056 } 057 this.extension = path.substring(i); 058 path = path.substring(0, i); 059 if (path.startsWith("/")) { 060 path = path.substring(1); 061 } 062 if (path.contains("..")) { 063 throw new UsageException("Illegal assets path " + path); 064 } 065 if (!path.startsWith("assets/")) { 066 this.bundleRelativePath = path; 067 path = "assets/" + path; 068 } else { 069 this.bundleRelativePath = path.substring(7); 070 } 071 this.path = path; 072 fileSystemPath = Settings.instance().getTargetFolder() + "/" + path; 073 bundleFile = new File(fileSystemPath); 074 if (!bundleFile.exists()) { 075 throw new NotFoundException("The bundle for path " + path + " was not found in the site /assets folder."); 076 } 077 078 } 079 080 public String renderFile(String filePath) { 081 AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath()); 082 return bundle.renderPath(filePath, true); 083 } 084 085 public String renderDebugHtml() { 086 087 AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath()); 088 bundle.hydrateFilesIfNeeded(true); 089 StringBuilder builder = new StringBuilder(); 090 for (AssetFile af: bundle.getFiles()) { 091 af.hydrateIfNeeded(true); 092 String tag = "<script src=\"{0}\" type=\"text/javascript\"></script>"; 093 if (".head.js".equals(extension)) { 094 if (empty(af.getHeadJavaScript())) { 095 continue; 096 } 097 } else if (".css".equals(extension)) { 098 if (empty(af.getCss())) { 099 continue; 100 } 101 tag = "<link rel=\"stylesheet\" href=\"{0}\">"; 102 } else if (".js".equals(extension)) { 103 if (empty(af.getJavaScript())) { 104 continue; 105 } 106 } 107 String url; 108 String relativePath = af.getRelativePath(); 109 if (!relativePath.startsWith("/")) { 110 relativePath = "/" + relativePath; 111 } 112 try { 113 url = Settings.instance().getCdnUrl() + "/st-assets/" + bundleRelativePath + this.extension 114 + "?isBundleFile=true&bundleFilePath=" + URLEncoder.encode(relativePath + extension, "utf-8") 115 + "&ts=" + af.getHydratedAt(); 116 //url = Settings.instance().getCdnUrl() + "/st-file-bundle-assets" + relativePath + extension + "?bundlePath=" + URLEncoder.encode(this.path, "utf-8") + extension + "&ts=" + af.getHydratedAt(); 117 } catch (UnsupportedEncodingException e) { 118 throw new RuntimeException(e); 119 } 120 builder.append(" " + MessageFormat.format(tag, url) + "\n"); 121 } 122 return builder.toString(); 123 124 } 125 126 public String renderProductionHtml() { 127 String content = renderProductionContent(); 128 String hash = DigestUtils.md5Hex(content); 129 String tag = "<script src=\"{0}\" type=\"text/javascript\"></script>"; 130 String url = Settings.instance().getCdnUrl() + "/st-assets/" + bundleRelativePath + extension + "?isConcatenatedFileBundle=true&hash=" + hash; 131 if (".css".equals(extension)) { 132 tag = "<link rel=\"stylesheet\" href=\"{0}\">"; 133 } 134 return MessageFormat.format(tag, url); 135 } 136 137 138 139 public String renderProductionContent() { 140 AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath()); 141 bundle.hydrateAllFilesIfNeeded(true); 142 if (extension.equals(".head.js")) { 143 return bundle.getConcatenatedHeadJavaScript(); 144 } else if (extension.equals(".js")) { 145 return bundle.getConcatenatedJavaScript(); 146 } else if (extension.equals(".css")) { 147 return bundle.getConcatenatedCss(); 148 } 149 throw new NotFoundException("Could not find a bundle handler for extension " + extension); 150 151 } 152}