/* * 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. } } } } }