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

de.acosix.alfresco.utility.share.surf.ExternalLessCssThemeHandler Maven / Gradle / Ivy

There is a newer version: 1.4.4
Show newest version
package de.acosix.alfresco.utility.share.surf;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.config.ConfigElement;
import org.springframework.extensions.config.WebFrameworkConfigElement;
import org.springframework.extensions.surf.CssThemeHandler;
import org.springframework.extensions.surf.LessForJavaCssThemeHandler;
import org.springframework.extensions.surf.RequestContext;
import org.springframework.extensions.surf.support.ThreadLocalRequestContext;
import org.springframework.extensions.surf.types.Theme;
import org.springframework.extensions.webscripts.ScriptConfigModel;

import com.asual.lesscss.LessEngine;
import com.asual.lesscss.LessException;

/**
 * This class is a combined copy of the Alfresco {@code ExternalLessCssThemeHandler} and {@code LessCssThemeHandler} available from Alfresco
 * 5.1 onwards (Surf version 6.0+). This
 * copy was necessary to allow this module to compile against Alfresco 5.0 API baseline.
 *
 * This is a LESS CSS handler driven by a pre-configured external LESS process.
 * 

* Typically uses a Node "lessc" module preinstalled via NPM or similar. This is not * expected to be used in production environments where adding additional 3rd party * modules to the web-tier is not desired or permitted. * * @see http://lesscss.org/ * * @author Kevin Roast */ public class ExternalLessCssThemeHandler extends CssThemeHandler { public static final String LESS_TOKEN = "less-variables"; private static final Log logger = LogFactory.getLog(ExternalLessCssThemeHandler.class); /** * The default LESS configuration. This will be populated with the contents of a file referenced by the * web-framework > defaults > dojo-pages > default-less-configuration. */ private String defaultLessConfig = null; private String cmd; /** * @param cmd * The external cmd to execute. For example Node lessc this would be "lessc -". * The command must be able to accept LESS CSS as stdin and return output from stdout. */ public void setCmd(final String cmd) { this.cmd = cmd; } /** * Sets up a new instance. */ public ExternalLessCssThemeHandler() { } /** * Overrides the default implementation to add LESS processing capabilities. * * @param path * The path of the file being processed (used only for error output) * @param cssContents * The CSS to process * @throws IOException * when accessing file contents. */ @Override public String processCssThemes(final String path, final StringBuilder cssContents) throws IOException { if (this.cmd == null || this.cmd.length() == 0) { throw new IllegalArgumentException("External LESS 'cmd' not set correctly in bean config."); } // setup our external process and retrieve streams - IO exception is handled in caller final Process proc = Runtime.getRuntime().exec(this.cmd); // if we get here, retrieve the streams for processing final BufferedWriter stdIn = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream(), StandardCharsets.UTF_8.name())); final BufferedReader stdOut = new BufferedReader(new InputStreamReader(proc.getInputStream(), StandardCharsets.UTF_8.name())); final BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream(), StandardCharsets.UTF_8.name())); try { // push our CSS to the standard Input of the external process stdIn.append(this.getLessVariables()); stdIn.append(cssContents.toString()); stdIn.close(); // read the output from the command StringBuilder buf = new StringBuilder(1024); String s; while ((s = stdOut.readLine()) != null) { buf.append(s); } stdOut.close(); // read any errors from the attempted command if ((s = stdError.readLine()) != null) { // error occured, collect information and throw exception with the message buf = new StringBuilder("Error during external LESS compilation for path: ").append(path).append("\r\n"); do { buf.append(s); } while ((s = stdError.readLine()) != null); stdError.close(); throw new IOException(buf.toString()); } return buf.toString(); } finally { stdError.close(); stdOut.close(); stdIn.close(); } } /** * Looks for the LESS CSS token which should contain the LESS style variables that * can be applied to each CSS file. This will be prepended to each CSS file processed. * * @return The String of LESS variables. */ public String getLessVariables() { String variables = this.getDefaultLessConfig(); Theme currentTheme = ThreadLocalRequestContext.getRequestContext().getTheme(); if (currentTheme == null) { currentTheme = ThreadLocalRequestContext.getRequestContext().getObjectService().getTheme("default"); } final String themeVariables = currentTheme.getCssTokens().get(LessForJavaCssThemeHandler.LESS_TOKEN); if (themeVariables != null) { // Add a new line just to make sure the first theme specific variable isn't appended to // the end of the last default variable! variables += "\n" + themeVariables; } return variables; } /** * This function is used to log exceptions that occur during LESS compilation. Unfortunately the * {@link LessException} that is thrown from the {@link LessEngine} does not capture all exception * eventualities. When a JavaScript error occurs in Rhino this can result in a {@link ClassCastException} * which needs to be caught separately. Currently Surf still supports Java 6 so cannot process * multiple exceptions so the error handling has been abstracted to a helper method. * * @param e * The exception that has been thrown. * @param path * The path being processed that caused the exception * @return The error message generated */ public String logLessException(final Exception e, final String path) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); final String errorMsg = "LESS for Java Engine error compiling: '" + path + "': " + sw.toString(); if (logger.isErrorEnabled()) { logger.error(errorMsg); } return errorMsg; } /** * Returns the current default LESS configuration. If it has not previously been retrieved then it will * attempt to load it. * * @return A String containing the default LESS configuration variables. */ @SuppressWarnings("unchecked") protected String getDefaultLessConfig() { final RequestContext rc = ThreadLocalRequestContext.getRequestContext(); if (this.defaultLessConfig == null) { String defaultLessConfigPath = null; final ScriptConfigModel config = rc.getExtendedScriptConfigModel(null); final Map configs = (Map) config.getScoped().get("WebFramework"); if (configs != null) { final WebFrameworkConfigElement wfce = (WebFrameworkConfigElement) configs.get("web-framework"); defaultLessConfigPath = wfce.getDojoDefaultLessConfig(); } else { defaultLessConfigPath = this.getWebFrameworkConfigElement().getDojoDefaultLessConfig(); } try { final InputStream in = this.getDependencyHandler().getResourceInputStream(defaultLessConfigPath); if (in != null) { this.defaultLessConfig = this.getDependencyHandler().convertResourceToString(in); } else { if (logger.isErrorEnabled()) { logger.error("Could not find the default LESS configuration at: " + defaultLessConfigPath); } // Set the configuration as the empty string as it's not in the configured location this.defaultLessConfig = ""; } } catch (final IOException e) { if (logger.isErrorEnabled()) { logger.error("An exception occurred retrieving the default LESS configuration from: " + defaultLessConfigPath, e); } } } return this.defaultLessConfig; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy