blob: b7bcdc97ec912d39f8e13b0784c7448221029dc5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.apache.myfaces.extensions.scripting.core.util;
import org.apache.myfaces.extensions.scripting.api.Configuration;
import org.apache.myfaces.extensions.scripting.api.ScriptingConst;
import org.apache.myfaces.extensions.scripting.api.ScriptingWeaver;
import org.apache.myfaces.extensions.scripting.core.CoreWeaver;
import org.apache.myfaces.extensions.scripting.core.util.stax.FilterClassDigester;
import org.apache.myfaces.extensions.scripting.loaders.groovy.GroovyScriptingWeaver;
import org.apache.myfaces.extensions.scripting.loaders.java.JavaScriptingWeaver;
import org.apache.myfaces.extensions.scripting.loaders.java.RecompiledClassLoader;
import org.apache.myfaces.extensions.scripting.monitor.ResourceMonitor;
import org.apache.myfaces.extensions.scripting.monitor.RefreshContext;
import org.apache.myfaces.extensions.scripting.api.extensionevents.ExtensionEventRegistry;
import org.apache.myfaces.extensions.scripting.servlet.ScriptingServletFilter;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Central initializer class for our
* WeavingContext which does some semantic checking of the web.xml
* and initializes everything in proper order
*
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class WeavingContextInitializer {
static class StartupException extends Exception {
StartupException(String message) {
super(message);
}
}
static final Logger _logger = Logger.getLogger(WeavingContextInitializer.class.getName());
static final PrivilegedExceptionAction<RecompiledClassLoader> LOADER_ACTION = new PrivilegedExceptionAction<RecompiledClassLoader>() {
public RecompiledClassLoader run() {
return new RecompiledClassLoader(ClassUtils.getContextClassLoader(), ScriptingConst.ENGINE_TYPE_JSF_JAVA, ".java");
}
};
private static final String GROOVY_NOT_FOUND = "[EXT-SCRIPTING] Groovy not found disabling Ext-Scripting Groovy support";
private static final String GROOVY_OBJECT = "groovy.lang.GroovyObject";
public static void initWeavingContext(ServletContext servletContext) {
try {
WeavingContext.setScriptingEnabled(true);
validateWebXml(servletContext);
initConfiguration(servletContext);
initExtensionEventSystem(servletContext);
validateSecurityConstraints();
initWeavers(servletContext);
validateSourcePaths();
initRefreshContext(servletContext);
initFileChangeDaemon(servletContext);
initExternalContext(servletContext);
} catch (StartupException ex) {
_logger.severe("[EXT-SCRIPTING] " + ex.getMessage());
WeavingContext.setScriptingEnabled(false);
}
}
/**
* validates the source paths which were determined by the
* startup for failures
*/
private static void validateSourcePaths() throws StartupException {
Collection<String> dirs = WeavingContext.getConfiguration().getAllSourceDirs();
for (String currentDir : dirs) {
File probe = new File(currentDir);
if (!probe.exists()) {
throw new StartupException("The directory " + probe + " does not exist, disabling scripting support");
}
}
}
/**
* asserts the security constraints
* the only security which has to be allowed
* is the creation of classloaders
*/
private static void validateSecurityConstraints() throws StartupException {
try {
AccessController.doPrivileged(LOADER_ACTION);
} catch (PrivilegedActionException e) {
throw new StartupException("Class loader creation is prohibited by your security settings, I am going to disable Ext-Scripting");
}
}
private static void initExternalContext(ServletContext servletContext) {
WeavingContext.setExternalContext(servletContext);
}
private static void initFileChangeDaemon(ServletContext servletContext) {
ResourceMonitor.startup(servletContext);
WeavingContext.getRefreshContext().setDaemon(ResourceMonitor.getInstance());
}
private static void initExtensionEventSystem(ServletContext servletContext) {
ExtensionEventRegistry registry = new ExtensionEventRegistry();
servletContext.setAttribute(ScriptingConst.CTX_ATTR_EXTENSION_EVENT_SYSTEM, registry);
WeavingContext.setExtensionEventRegistry(registry);
}
private static void initConfiguration(ServletContext servletContext) {
final Configuration configuration = new Configuration();
servletContext.setAttribute(ScriptingConst.CTX_ATTR_CONFIGURATION, configuration);
WeavingContext.setConfiguration(configuration);
//we now add the resource loader path here
/*
* we define a set of closures (inner classes) which make
* our code more reusable we define a strategy
* for each comma delimited set of values
*/
Strategy addResourceDirStrategy = new Strategy() {
public void apply(Object element) {
configuration.addResourceDir((String) element);
}
};
Strategy addAdditionalClassPathStrategy = new Strategy() {
public void apply(Object element) {
configuration.addAdditionalClassPath((String) element);
}
};
Strategy addWhiteListPackageStrategy = new Strategy() {
public void apply(Object element) {
configuration.addWhitelistPackage((String) element);
}
};
/**
* We now apply the values into our own lists
*/
applyEntries(servletContext.getInitParameter(ScriptingConst.INIT_PARAM_RESOURCE_PATH), addResourceDirStrategy);
applyEntries(servletContext.getInitParameter(ScriptingConst.INIT_PARAM_SCRIPTING_ADDITIONAL_CLASSPATH), addAdditionalClassPathStrategy);
applyEntries(servletContext.getInitParameter(ScriptingConst.INIT_PARAM_SCRIPTING_PACKAGE_WHITELIST), addWhiteListPackageStrategy);
String initialCompile = servletContext.getInitParameter(ScriptingConst.INIT_PARAM_INITIAL_COMPILE);
configuration.setInitialCompile((initialCompile == null) || initialCompile.trim().toLowerCase().equals("true"));
}
private static void applyEntries(String val, Strategy strategy) {
if (!StringUtils.isBlank(val)) {
String[] splitVal = val.split(ScriptingConst.CONTEXT_VALUE_DIVIDER);
for (String singleVal : splitVal) {
strategy.apply(singleVal);
}
}
}
private static void validateWebXml(ServletContext context) throws StartupException {
try {
URL webXml = context.getResource("/WEB-INF/web.xml");
if (webXml != null) {
if (!FilterClassDigester.findFilter(webXml, ScriptingServletFilter.class)) {
throw new StartupException(ScriptingConst.ERR_SERVLET_FILTER);
}
}
} catch (IOException e) {
throw new StartupException("Web.xml could not be parsed disabling scripting");
}
}
/**
* inits the weaver chain which depends on the scripting
* language supported by the internal jars
*
* @param servletContext the standard servlet context
*/
private static void initWeavers(ServletContext servletContext) throws StartupException {
_logger.fine("[EXT-SCRIPTING] initializing the weaving contexts");
List<ScriptingWeaver> weavers = new ArrayList<ScriptingWeaver>(2);
initGroovyWeaver(servletContext, weavers);
initJavaWeaver(servletContext, weavers);
if (WeavingContext.isFilterEnabled() && weavers.size() == 0) {
throw new StartupException("No scripting languages initialized disabling EXT-SCRIPTING");
}
WeavingContext.setWeaver(new CoreWeaver(weavers));
servletContext.setAttribute(ScriptingConst.CTX_ATTR_SCRIPTING_WEAVER, WeavingContext.getWeaver());
}
/**
* inits the standard java weaver
* for weaving and recompiling java classes on the fly
*
* @param servletContext the standard servlet context
* @param weavers our list of weavers which should receive the resulting weaver
*/
private static void initJavaWeaver(ServletContext servletContext, List<ScriptingWeaver> weavers) throws StartupException {
ScriptingWeaver javaWeaver = new JavaScriptingWeaver(servletContext);
setupScriptingPaths(servletContext, javaWeaver, ScriptingConst.JAVA_SOURCE_ROOT, ScriptingConst.INIT_PARAM_CUSTOM_JAVA_LOADER_PATHS);
if (WeavingContext.getConfiguration().getSourceDirs(ScriptingConst.ENGINE_TYPE_JSF_JAVA).size() > 0) {
weavers.add(javaWeaver);
} else {
_logger.log(Level.WARNING, "[EXT-SCRIPTING] No valid source path for Java found either add WEB-INF/java to your filesystem, or add a custom Java source path, disabling EXT-SCRIPTING Java support");
}
}
/**
* initializes our groovy weaver
*
* @param servletContext the servlet context
* @param weavers the list of weavers receiving the resulting weaver if an initialization is possoble
*/
private static void initGroovyWeaver(ServletContext servletContext, List<ScriptingWeaver> weavers) {
//check if groovy can be enabled:
try {
Class groovyObject = ClassUtils.forName(GROOVY_OBJECT);
if (groovyObject != null) {
//groovy found ewe now enabled our groovy weaving support
ScriptingWeaver groovyWeaver = new GroovyScriptingWeaver(servletContext);
setupScriptingPaths(servletContext, groovyWeaver, ScriptingConst.GROOVY_SOURCE_ROOT, ScriptingConst.INIT_PARAM_CUSTOM_GROOVY_LOADER_PATHS);
if (WeavingContext.getConfiguration().getSourceDirs(ScriptingConst.ENGINE_TYPE_JSF_GROOVY).size() > 0) {
weavers.add(groovyWeaver);
} else {
_logger.log(Level.WARNING, "[EXT-SCRIPTING] No valid source path for Groovy found either add WEB-INF/groovy to your filesystem, or add a custom Groovy source path, disabling EXT-SCRIPTING Groovy support");
}
}
} catch (Exception e) {
_logger.info(GROOVY_NOT_FOUND);
}
}
/**
* initialisation of the refresh context object
* the refresh context, is a context object which keeps
* the refresh information (refresh time, needs refresh) etc...
*
* @param servletContext the servlet context singleton which keeps
* the context for distribution
*/
private static void initRefreshContext(ServletContext servletContext) {
_logger.fine("[EXT-SCRIPTING] initializing the refresh context");
RefreshContext rContext = new RefreshContext();
servletContext.setAttribute(ScriptingConst.CTX_ATTR_REFRESH_CONTEXT, rContext);
WeavingContext.setRefreshContext(rContext);
}
private static void setupScriptingPaths(ServletContext servletContext, ScriptingWeaver weaver, String contextRootKey, String initParams) throws StartupException {
String classRoot = "";
String scriptingRoot;
String additionalLoaderPaths;
String contextRoot = servletContext.getRealPath(contextRootKey);
if (contextRoot == null) {
_logger.warning("[EXT-SCRIPTING] one of the standard paths could not be resolved: " + contextRootKey + " this is either due to the path is missing or due to a configuration error! You can bypass the problem by setting additional loader paths if they are not set already!");
contextRoot = "";
}
contextRoot = contextRoot.trim();
scriptingRoot = contextRoot;
additionalLoaderPaths = servletContext.getInitParameter(initParams);
appendAdditionalPaths(additionalLoaderPaths, weaver);
if (additionalLoaderPaths == null || additionalLoaderPaths.trim().equals("")) {
if (contextRoot.equals("")) {
throw new StartupException("Standard paths (WEB-INF/groovy and WEB-INF/java could not be determined, also no additional loader paths are set, I cannot start properly, please set additional loader paths for Ext-Scripting to work correctly! I am disabling Ext-Scripting!");
}
if (!StringUtils.isBlank(scriptingRoot)) {
File probe = new File(scriptingRoot);
if (probe.exists()) {
weaver.appendCustomScriptPath(scriptingRoot);
} else {
_logger.log(Level.WARNING, "[EXT-SCRIPING] path {0} could not be found this might cause compile problems ", scriptingRoot);
}
}
if (!StringUtils.isBlank(classRoot)) {
weaver.appendCustomScriptPath(classRoot);
}
}
}
private static void appendAdditionalPaths(String additionalLoaderPaths, ScriptingWeaver workWeaver) {
if (!StringUtils.isBlank(additionalLoaderPaths)) {
String[] additionalPaths = additionalLoaderPaths.split(",");
for (String path : additionalPaths) {
workWeaver.appendCustomScriptPath(path);
}
}
}
}