/*
* Copyright 2004-2005 the original author or authors.
*
* Licensed 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.codehaus.groovy.grails.plugins;
import grails.util.Environment;
import grails.util.GrailsUtil;
import grails.util.Metadata;
import groovy.lang.*;
import groovy.util.ConfigObject;
import groovy.util.ConfigSlurper;
import groovy.util.XmlSlurper;
import groovy.util.slurpersupport.GPathResult;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.grails.commons.ConfigurationHolder;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.commons.spring.WebRuntimeSpringConfiguration;
import org.codehaus.groovy.grails.plugins.exceptions.PluginException;
import org.codehaus.groovy.grails.support.ParentApplicationContextAware;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.servlet.ServletContext;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
/**
*
A class that handles the loading and management of plug-ins in the Grails system.
* A plugin a just like a normal Grails application except that it contains a file ending
* in *Plugin.groovy in the root of the directory.
*
* A Plugin class is a Groovy class that has a version and optionally closures
* called doWithSpring, doWithContext and doWithWebDescriptor
*
* The doWithSpring closure uses the BeanBuilder syntax (@see grails.spring.BeanBuilder) to
* provide runtime configuration of Grails via Spring
*
* The doWithContext closure is called after the Spring ApplicationContext is built and accepts
* a single argument (the ApplicationContext)
*
* The doWithWebDescriptor uses mark-up building to provide additional functionality to the web.xml
* file
*
* Example:
*
* class ClassEditorGrailsPlugin {
* def version = 1.1
* def doWithSpring = { application ->
* classEditor(org.springframework.beans.propertyeditors.ClassEditor, application.classLoader)
* }
* }
*
*
* A plugin can also define "dependsOn" and "evict" properties that specify what plugins the plugin
* depends on and which ones it is incompatable with and should evict
*
* @author Graeme Rocher
* @since 0.4
*/
public class DefaultGrailsPluginManager extends AbstractGrailsPluginManager implements GrailsPluginManager {
private static final Log LOG = LogFactory.getLog(DefaultGrailsPluginManager.class);
private static final Class[] COMMON_CLASSES =
new Class[]{Boolean.class, Byte.class, Character.class, Class.class, Double.class, Float.class, Integer.class, Long.class,
Number.class, Short.class, String.class, BigInteger.class, BigDecimal.class, URL.class, URI.class};
private GrailsPluginChangeChecker pluginChangeScanner = new GrailsPluginChangeChecker(this);
private static final int SCAN_INTERVAL = Integer.getInteger("grails.scan.interval", 5000).intValue(); //in ms
private List delayedLoadPlugins = new LinkedList();
private ApplicationContext parentCtx;
private PathMatchingResourcePatternResolver resolver;
private Map delayedEvictions = new HashMap();
private ServletContext servletContext;
private Map pluginToObserverMap = new HashMap();
private long configLastModified;
private PluginFilter pluginFilter;
private static final String GRAILS_PLUGIN_SUFFIX = "GrailsPlugin";
public DefaultGrailsPluginManager(String resourcePath, GrailsApplication application) throws IOException {
super(application);
if (application == null)
throw new IllegalArgumentException("Argument [application] cannot be null!");
resolver = new PathMatchingResourcePatternResolver();
try {
this.pluginResources = resolver.getResources(resourcePath);
}
catch(IOException ioe) {
LOG.debug("Unable to load plugins for resource path " + resourcePath, ioe);
}
//this.corePlugins = new PathMatchingResourcePatternResolver().getResources("classpath:org/codehaus/groovy/grails/**/plugins/**GrailsPlugin.groovy");
this.application = application;
setPluginFilter();
}
public DefaultGrailsPluginManager(String[] pluginResources, GrailsApplication application) {
super(application);
resolver = new PathMatchingResourcePatternResolver();
List resourceList = new ArrayList();
for (int i = 0; i < pluginResources.length; i++) {
String resourcePath = pluginResources[i];
try {
Resource[] resources = resolver.getResources(resourcePath);
for (int j = 0; j < resources.length; j++) {
Resource resource = resources[j];
resourceList.add(resource);
}
}
catch (IOException ioe) {
LOG.debug("Unable to load plugins for resource path " + resourcePath, ioe);
}
}
this.pluginResources = (Resource[]) resourceList.toArray(new Resource[resourceList.size()]);
this.application = application;
setPluginFilter();
}
public DefaultGrailsPluginManager(Class[] plugins, GrailsApplication application) throws IOException {
super(application);
this.pluginClasses = plugins;
resolver = new PathMatchingResourcePatternResolver();
//this.corePlugins = new PathMatchingResourcePatternResolver().getResources("classpath:org/codehaus/groovy/grails/**/plugins/**GrailsPlugin.groovy");
this.application = application;
setPluginFilter();
}
public DefaultGrailsPluginManager(Resource[] pluginFiles, GrailsApplication application) {
super(application);
resolver = new PathMatchingResourcePatternResolver();
this.pluginResources = pluginFiles;
this.application = application;
setPluginFilter();
}
private void setPluginFilter() {
this.pluginFilter = new PluginFilterRetriever().getPluginFilter(this.application.getConfig().toProperties());
}
public void startPluginChangeScanner() {
if (this.pluginChangeScanner.isAlive()) {
throw new IllegalStateException("Plugin change scanner is already running!");
}
this.pluginChangeScanner.start();
LOG.info("Started to scan for plugin changes in every " + SCAN_INTERVAL + "ms.");
}
public void stopPluginChangeScanner() {
pluginChangeScanner.enabled = false;
try {
try {
// wait for thread to die
pluginChangeScanner.join(5000);
}
catch (InterruptedException e) {
// ignore
}
}
finally {
pluginChangeScanner = new GrailsPluginChangeChecker(this);
}
}
public void refreshPlugin(String name) {
if (hasGrailsPlugin(name)) {
GrailsPlugin plugin = getGrailsPlugin(name);
plugin.refresh();
}
}
public Collection getPluginObservers(GrailsPlugin plugin) {
if (plugin == null) throw new IllegalArgumentException("Argument [plugin] cannot be null");
Collection c = (Collection) this.pluginToObserverMap.get(plugin.getName());
// Add any wildcard observers.
Collection wildcardObservers = (Collection) this.pluginToObserverMap.get("*");
if (wildcardObservers != null) {
if (c != null) {
c.addAll(wildcardObservers);
}
else {
c = wildcardObservers;
}
}
if (c != null) {
// Make sure this plugin is not observing itself!
c.remove(plugin);
return c;
}
return Collections.EMPTY_SET;
}
public void informObservers(String pluginName, Map event) {
GrailsPlugin plugin = getGrailsPlugin(pluginName);
if (plugin != null) {
Collection observers = getPluginObservers(plugin);
for (Iterator i = observers.iterator(); i.hasNext();) {
GrailsPlugin observingPlugin = (GrailsPlugin) i.next();
observingPlugin.notifyOfEvent(event);
}
}
}
/* (non-Javadoc)
* @see org.codehaus.groovy.grails.plugins.GrailsPluginManager#loadPlugins()
*/
public void loadPlugins() throws PluginException {
if (!this.initialised) {
GroovyClassLoader gcl = application.getClassLoader();
attemptLoadPlugins(gcl);
if (!delayedLoadPlugins.isEmpty()) {
loadDelayedPlugins();
}
if (!delayedEvictions.isEmpty()) {
processDelayedEvictions();
}
pluginList = sortPlugins(pluginList);
initializePlugins();
initialised = true;
}
}
private List sortPlugins(List pluginList) {
List newList = new ArrayList(pluginList);
for (GrailsPlugin plugin : pluginList) {
int i = newList.indexOf(plugin);
for (String loadBefore : plugin.getLoadBeforeNames()) {
final GrailsPlugin loadBeforePlugin = getGrailsPlugin(loadBefore);
int j = newList.indexOf(loadBeforePlugin);
if(i > j) {
newList.remove(plugin);
newList.add(j,plugin);
}
}
i = newList.indexOf(plugin);
for(String loadAfter : plugin.getLoadAfterNames()) {
final GrailsPlugin loadAfterPlugin = getGrailsPlugin(loadAfter);
int j = newList.indexOf(loadAfterPlugin);
if(i < j) {
newList.remove(plugin);
newList.add(j,plugin);
}
}
}
return newList;
}
private void attemptLoadPlugins(GroovyClassLoader gcl) {
// retrieve load core plugins first
List grailsCorePlugins = loadCorePlugins ? findCorePlugins() : new ArrayList();
List grailsUserPlugins = findUserPlugins(gcl);
List allPlugins = new ArrayList (grailsCorePlugins);
allPlugins.addAll(grailsUserPlugins);
//filtering applies to user as well as core plugins
List filteredPlugins = getPluginFilter().filterPluginList(allPlugins);
//make sure core plugins are loaded first
List orderedCorePlugins = new ArrayList ();
List orderedUserPlugins = new ArrayList ();
for (Iterator iter = filteredPlugins.iterator(); iter.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) iter.next();
if (grailsCorePlugins.contains(plugin)) {
orderedCorePlugins.add(plugin);
}
else {
orderedUserPlugins.add(plugin);
}
}
List orderedPlugins = new ArrayList ();
orderedPlugins.addAll(orderedCorePlugins);
orderedPlugins.addAll(orderedUserPlugins);
for (Iterator iter = orderedPlugins.iterator(); iter.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) iter.next();
attemptPluginLoad(plugin);
}
}
private List findCorePlugins() {
CorePluginFinder finder = new CorePluginFinder(application);
Set classes = finder.getPluginClasses();
Iterator classesIterator = classes.iterator();
List grailsCorePlugins = new ArrayList();
while (classesIterator.hasNext()) {
Class pluginClass = (Class) classesIterator.next();
if (pluginClass != null && !Modifier.isAbstract(pluginClass.getModifiers()) && pluginClass != DefaultGrailsPlugin.class) {
GrailsPlugin plugin = new DefaultGrailsPlugin(pluginClass, application);
plugin.setApplicationContext(applicationContext);
grailsCorePlugins.add(plugin);
}
}
return grailsCorePlugins;
}
private List findUserPlugins(GroovyClassLoader gcl) {
List grailsUserPlugins = new ArrayList ();
LOG.info("Attempting to load [" + pluginResources.length + "] user defined plugins");
for (int i = 0; i < pluginResources.length; i++) {
Resource r = pluginResources[i];
Class pluginClass = loadPluginClass(gcl, r);
if (isGrailsPlugin(pluginClass)) {
GrailsPlugin plugin = new DefaultGrailsPlugin(pluginClass, r, application);
//attemptPluginLoad(plugin);
grailsUserPlugins.add(plugin);
}
else {
LOG.warn("Class [" + pluginClass + "] not loaded as plug-in. Grails plug-ins must end with the convention 'GrailsPlugin'!");
}
}
for (int i = 0; i < pluginClasses.length; i++) {
Class pluginClass = pluginClasses[i];
if (isGrailsPlugin(pluginClass)) {
GrailsPlugin plugin = new DefaultGrailsPlugin(pluginClass, application);
//attemptPluginLoad(plugin);
grailsUserPlugins.add(plugin);
}
else {
LOG.warn("Class [" + pluginClass + "] not loaded as plug-in. Grails plug-ins must end with the convention 'GrailsPlugin'!");
}
}
return grailsUserPlugins;
}
private boolean isGrailsPlugin(Class pluginClass) {
return pluginClass != null && pluginClass.getName().endsWith(GRAILS_PLUGIN_SUFFIX);
}
private void processDelayedEvictions() {
for (Iterator i = delayedEvictions.keySet().iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
String[] pluginToEvict = (String[]) delayedEvictions.get(plugin);
for (int j = 0; j < pluginToEvict.length; j++) {
String pluginName = pluginToEvict[j];
evictPlugin(plugin, pluginName);
}
}
}
private void initializePlugins() {
for (Iterator i = plugins.values().iterator(); i.hasNext();) {
Object plugin = i.next();
if (plugin instanceof ApplicationContextAware) {
((ApplicationContextAware) plugin).setApplicationContext(applicationContext);
}
}
}
/**
* This method will attempt to load that plug-ins not loaded in the first pass
*/
private void loadDelayedPlugins() {
while (!delayedLoadPlugins.isEmpty()) {
GrailsPlugin plugin = (GrailsPlugin) delayedLoadPlugins.remove(0);
if (areDependenciesResolved(plugin)) {
if (!hasValidPluginsToLoadBefore(plugin)) {
registerPlugin(plugin);
}
else {
delayedLoadPlugins.add(plugin);
}
}
else {
// ok, it still hasn't resolved the dependency after the initial
// load of all plugins. All hope is not lost, however, so lets first
// look inside the remaining delayed loads before giving up
boolean foundInDelayed = false;
for (Iterator i = delayedLoadPlugins.iterator(); i.hasNext();) {
GrailsPlugin remainingPlugin = (GrailsPlugin) i.next();
if (isDependentOn(plugin, remainingPlugin)) {
foundInDelayed = true;
break;
}
}
if (foundInDelayed)
delayedLoadPlugins.add(plugin);
else {
failedPlugins.put(plugin.getName(), plugin);
LOG.warn("WARNING: Plugin [" + plugin.getName() + "] cannot be loaded because its dependencies [" + ArrayUtils.toString(plugin.getDependencyNames()) + "] cannot be resolved");
}
}
}
}
private boolean hasValidPluginsToLoadBefore(GrailsPlugin plugin) {
String[] loadAfterNames = plugin.getLoadAfterNames();
for (Object delayedLoadPlugin : this.delayedLoadPlugins) {
GrailsPlugin other = (GrailsPlugin) delayedLoadPlugin;
for (String name : loadAfterNames) {
if (other.getName().equals(name)) {
return hasDelayedDependencies(other) || areDependenciesResolved(other);
}
}
}
return false;
}
private boolean hasDelayedDependencies(GrailsPlugin other) {
String[] dependencyNames = other.getDependencyNames();
for (int i = 0; i < dependencyNames.length; i++) {
String dependencyName = dependencyNames[i];
for (Iterator j = delayedLoadPlugins.iterator(); j.hasNext();) {
GrailsPlugin grailsPlugin = (GrailsPlugin) j.next();
if (grailsPlugin.getName().equals(dependencyName)) return true;
}
}
return false;
}
/**
* Checks whether the first plugin is dependant on the second plugin
*
* @param plugin The plugin to check
* @param dependancy The plugin which the first argument may be dependant on
* @return True if it is
*/
private boolean isDependentOn(GrailsPlugin plugin, GrailsPlugin dependancy) {
String[] dependencies = plugin.getDependencyNames();
for (int i = 0; i < dependencies.length; i++) {
String name = dependencies[i];
String requiredVersion = plugin.getDependentVersion(name);
if (name.equals(dependancy.getName()) &&
GrailsPluginUtils.isValidVersion(dependancy.getVersion(), requiredVersion))
return true;
}
return false;
}
private boolean areDependenciesResolved(GrailsPlugin plugin) {
String[] dependencies = plugin.getDependencyNames();
if (dependencies.length > 0) {
for (int i = 0; i < dependencies.length; i++) {
String name = dependencies[i];
String version = plugin.getDependentVersion(name);
if (!hasGrailsPlugin(name, version)) {
return false;
}
}
}
return true;
}
/**
* Returns true if there are no plugins left that should, if possible, be loaded before this plugin
*
* @param plugin The plugin
* @return True if there are
*/
private boolean areNoneToLoadBefore(GrailsPlugin plugin) {
String[] loadAfterNames = plugin.getLoadAfterNames();
if (loadAfterNames.length > 0) {
for (String name : loadAfterNames) {
if (getGrailsPlugin(name) == null)
return false;
}
}
return true;
}
private Class loadPluginClass(GroovyClassLoader gcl, Resource r) {
Class pluginClass;
try {
pluginClass = gcl.parseClass(r.getInputStream());
}
catch (CompilationFailedException e) {
throw new PluginException("Error compiling plugin [" + r.getFilename() + "] " + e.getMessage(), e);
}
catch (IOException e) {
throw new PluginException("Error reading plugin [" + r.getFilename() + "] " + e.getMessage(), e);
}
return pluginClass;
}
/**
* This method attempts to load a plugin based on its dependencies. If a plugin's
* dependencies cannot be resolved it will add it to the list of dependencies to
* be resolved later
*
* @param plugin The plugin
*/
private void attemptPluginLoad(GrailsPlugin plugin) {
if (areDependenciesResolved(plugin) && areNoneToLoadBefore(plugin)) {
registerPlugin(plugin);
}
else {
delayedLoadPlugins.add(plugin);
}
}
private void registerPlugin(GrailsPlugin plugin) {
if (canRegisterPlugin(plugin)) {
if (LOG.isInfoEnabled()) {
LOG.info("Grails plug-in [" + plugin.getName() + "] with version [" + plugin.getVersion() + "] loaded successfully");
}
if (plugin instanceof ParentApplicationContextAware) {
((ParentApplicationContextAware) plugin).setParentApplicationContext(parentCtx);
}
plugin.setManager(this);
String[] evictionNames = plugin.getEvictionNames();
if (evictionNames.length > 0)
delayedEvictions.put(plugin, evictionNames);
String[] observedPlugins = plugin.getObservedPluginNames();
for (int i = 0; i < observedPlugins.length; i++) {
String observedPlugin = observedPlugins[i];
Set observers = (Set) pluginToObserverMap.get(observedPlugin);
if (observers == null) {
observers = new HashSet();
pluginToObserverMap.put(observedPlugin, observers);
}
observers.add(plugin);
}
pluginList.add(plugin);
plugins.put(plugin.getName(), plugin);
classNameToPluginMap.put(plugin.getPluginClass().getName(), plugin);
}
else {
if (LOG.isInfoEnabled()) {
LOG.info("Grails plugin " + plugin + " is disabled and was not loaded");
}
}
}
protected boolean canRegisterPlugin(GrailsPlugin plugin) {
Environment environment = Environment.getCurrent();
return plugin.isEnabled() && plugin.supportsEnvironment(environment);
}
protected void evictPlugin(GrailsPlugin evictor, String evicteeName) {
GrailsPlugin pluginToEvict = plugins.get(evicteeName);
if (pluginToEvict != null) {
pluginList.remove(pluginToEvict);
plugins.remove(pluginToEvict.getName());
if (LOG.isInfoEnabled()) {
LOG.info("Grails plug-in " + pluginToEvict + " was evicted by " + evictor);
}
}
}
private boolean hasGrailsPlugin(String name, String version) {
return getGrailsPlugin(name, version) != null;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
plugin.setApplicationContext(applicationContext);
}
}
public void setParentApplicationContext(ApplicationContext parent) {
this.parentCtx = parent;
}
public void checkForChanges() {
checkForConfigChanges();
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
if (plugin.checkForChanges()) {
LOG.info("Plugin " + plugin + " changed, re-registering beans...");
reloadPlugin(plugin);
}
}
}
private void checkForConfigChanges() {
ConfigObject config = application.getConfig();
URL configURL = config.getConfigFile();
if (configURL != null) {
URLConnection connection;
try {
connection = configURL.openConnection();
}
catch (IOException e) {
LOG.error("I/O error obtaining URL connection for configuration [" + configURL + "]: " + e.getMessage(), e);
return;
}
try {
long lastModified = connection.getLastModified();
if (configLastModified == 0) {
configLastModified = lastModified;
}
else {
if (configLastModified < lastModified) {
LOG.info("Configuration [" + configURL + "] changed, reloading changes..");
ConfigSlurper slurper = new ConfigSlurper(GrailsUtil.getEnvironment());
try {
config = slurper.parse(configURL);
ConfigurationHolder.setConfig(config);
configLastModified = lastModified;
informPluginsOfConfigChange();
}
catch (GroovyRuntimeException gre) {
LOG.error("Unable to reload configuration. Please correct problem and try again: " + gre.getMessage(), gre);
}
}
}
}
finally {
if(connection!=null) {
try {
InputStream is = connection.getInputStream();
if(is!=null) is.close();
}
catch (IOException e) {
// ignore
}
}
}
}
}
private void informPluginsOfConfigChange() {
LOG.info("Informing plug-ins of configuration change..");
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
plugin.notifyOfEvent(GrailsPlugin.EVENT_ON_CONFIG_CHANGE, application.getConfig());
}
}
private void reloadPlugin(GrailsPlugin plugin) {
plugin.doArtefactConfiguration();
WebRuntimeSpringConfiguration springConfig = new WebRuntimeSpringConfiguration(this.parentCtx);
springConfig.setServletContext(getServletContext());
this.doRuntimeConfiguration(plugin.getName(), springConfig);
springConfig.registerBeansWithContext((StaticApplicationContext) this.applicationContext);
plugin.doWithApplicationContext(this.applicationContext);
plugin.doWithDynamicMethods(this.applicationContext);
}
public void doWebDescriptor(Resource descriptor, Writer target) {
try {
doWebDescriptor(descriptor.getInputStream(), target);
}
catch (IOException e) {
throw new PluginException("Unable to read web.xml [" + descriptor + "]: " + e.getMessage(), e);
}
}
private void doWebDescriptor(InputStream inputStream, Writer target) {
checkInitialised();
try {
XmlSlurper slurper = new XmlSlurper();
slurper.setEntityResolver(new EntityResolver() {
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null && systemId.equals("http://java.sun.com/dtd/web-app_2_3.dtd")) {
return new InputSource(new StringReader(getWeb23DTD()));
}
return null;
}
});
GPathResult result = slurper.parse(inputStream);
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
if(plugin.supportsCurrentScopeAndEnvironment()) {
plugin.doWithWebDescriptor(result);
}
}
writeWebDescriptorResult(result, target);
}
catch (ParserConfigurationException e) {
throw new PluginException("Unable to configure web.xml due to parser configuration problem: " + e.getMessage(), e);
}
catch (SAXException e) {
throw new PluginException("XML parsing error configuring web.xml: " + e.getMessage(), e);
}
catch (IOException e) {
throw new PluginException("Unable to read web.xml" + e.getMessage(), e);
}
}
private void writeWebDescriptorResult(GPathResult result, Writer output) throws IOException {
Binding b = new Binding();
b.setVariable("node", result);
String namespace;
Metadata metadata = Metadata.getCurrent();
String servletVersion = metadata.getServletVersion();
if(servletVersion == null || "2.4".equals(servletVersion)) {
namespace = "http://java.sun.com/xml/ns/j2ee";
}
else {
namespace = "http://java.sun.com/xml/ns/javaee";
}
Writable w = (Writable)new GroovyShell(b).evaluate(
"new groovy.xml.StreamingMarkupBuilder().bind {" +
" mkp.declareNamespace(\"\": \""+namespace+"\");" +
" mkp.yield node}");
w.writeTo(output);
}
public void doWebDescriptor(File descriptor, Writer target) {
try {
doWebDescriptor(new FileInputStream(descriptor), target);
}
catch (FileNotFoundException e) {
throw new PluginException("Unable to read web.xml [" + descriptor + "]: " + e.getMessage(), e);
}
}
public void setApplication(GrailsApplication application) {
if (application == null) throw new IllegalArgumentException("Argument [application] cannot be null");
this.application = application;
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
plugin.setApplication(application);
}
}
public void doDynamicMethods() {
checkInitialised();
// remove common meta classes just to be sure
MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
for (int i = 0; i < COMMON_CLASSES.length; i++) {
Class commonClass = COMMON_CLASSES[i];
registry.removeMetaClass(commonClass);
}
for (Iterator i = pluginList.iterator(); i.hasNext();) {
GrailsPlugin plugin = (GrailsPlugin) i.next();
if (plugin.supportsCurrentScopeAndEnvironment()) {
try {
plugin.doWithDynamicMethods(applicationContext);
}
catch (Throwable t) {
GrailsUtil.deepSanitize(t);
LOG.error("Error configuring dynamic methods for plugin "+plugin+": " + t.getMessage(), t);
}
}
}
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return servletContext;
}
void setPluginFilter(PluginFilter pluginFilter) {
this.pluginFilter = pluginFilter;
}
private PluginFilter getPluginFilter() {
if (pluginFilter == null) {
pluginFilter = new IdentityPluginFilter();
}
return pluginFilter;
}
List getPluginList() {
return Collections.unmodifiableList(pluginList);
}
public static String getWeb23DTD() {
return "\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"";
}
private class GrailsPluginChangeChecker extends Thread {
final DefaultGrailsPluginManager pluginManager;
Thread scanner;
boolean enabled = true;
GrailsPluginChangeChecker(DefaultGrailsPluginManager pluginManager) {
this.pluginManager = pluginManager;
setDaemon(true);
}
public void run() {
while (enabled) {
int sleepTime = DefaultGrailsPluginManager.SCAN_INTERVAL;
if (scanner != null && scanner.isAlive()) {
LOG.warn("plugin change scanner is still running after the scanning interval. You should set " +
"the grails.scan.interval system property to a higher value. The current value is " +
DefaultGrailsPluginManager.SCAN_INTERVAL + " ms");
scanner.interrupt();
}
else {
try {
scanner = new Thread() {
public void run() {
pluginManager.checkForChanges();
}
};
scanner.start();
}
catch(Throwable e) {
GrailsUtil.deepSanitize(e);
LOG.error("Error occured scanning for changes: " + e.getMessage(), e);
// sleep for a bit longer if an error occurs
sleepTime = DefaultGrailsPluginManager.SCAN_INTERVAL*5;
}
}
try {
sleep(sleepTime);
}
catch (InterruptedException ie) {
// We just want to continue looping, so ignore this
// exception.
}
}
}
}
}