in your wicket application init method must have
getApplicationSettings().setClassResolver(new GroovyClassResolver(this));
getApplicationSettings().setClassResolver(new GroovyClassResolver(this));
/*
* $Id: GroovyWarClassLoader.java 524 2006-01-08 09:30:40Z jdonnerstag $
* $Revision: 524 $ $Date: 2006-01-08 01:30:40 -0800 (Sun, 08 Jan 2006) $
*
* ==================================================================== 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 wicket.contrib.groovy;
import groovy.lang.GroovyClassLoader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.control.CompilationFailedException;
/**
* I had some issues with the GroovyClassLoader (1.0.7-beta) which failed to
* load a groovy files contained in the same war file. E.g.
*
* <pre>
* wicket-examples.war!/web-inf/classes/groovy/A.groovy
* wicket-examples.war!/web-inf/classes/groovy/B.groovy
*
* public class A {
* public A() {
* B b = new B()
* }
* }
* </pre>
*
* GroovyClassLoader did not load Groovy class B.
*
* @author Juergen Donnerstag
*/
public class GroovyWarClassLoader extends GroovyClassLoader
{
/** Logger */
final private static Log log = LogFactory.getLog(GroovyWarClassLoader.class);
/**
* Constructor
*
* @param loader
* The servlet's class loader
*/
public GroovyWarClassLoader(ClassLoader loader)
{
super(loader);
}
/**
* First try parent's implementation. If it fails, it'll throw a
* ClassCastException. Catch it and than apply additional means to load the
* class.
*
* @param name
* Class name incl. package
* @return The Class loaded, if found
* @throws ClassNotFoundException,
* if class could not be loaded
*/
protected Class findClass(final String name) throws ClassNotFoundException
{
try
{
// Try Groovies default implementation first
return super.findClass(name);
}
catch (ClassNotFoundException ex)
{
log.debug("class name: " + name);
// classname => filename
//String filename = Strings.replaceAll(name, ".", "/") + ".groovy";
String filename = name.replace('.','/') + ".groovy";
// File exists?
final URL url = getResource(filename);
if (url != null)
{
try
{
// Get Groovy to parse the file and create the Class
final InputStream in = url.openStream();
if (in != null)
{
Class clazz = parseClass(in);
if (clazz != null)
{
return clazz;
}
}
else
{
log.warn("Groovy file not found: " + filename);
}
}
catch (CompilationFailedException e)
{
throw new ClassNotFoundException("Error parsing groovy file: "
+ filename, e);
}
catch (IOException e)
{
throw new ClassNotFoundException("Error reading groovy file: "
+ filename, e);
}
catch (Throwable e)
{
throw new ClassNotFoundException("Error while reading groovy file: "
+ filename, e);
}
}
throw ex;
}
}
}
/*
* $Id: GroovyClassResolver.java 548 2006-01-19 23:59:30Z joco01 $
* $Revision: 548 $ $Date: 2006-01-19 15:59:30 -0800 (Thu, 19 Jan 2006) $
*
* ==============================================================================
* 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 wicket.contrib.groovy;
import java.io.InputStream;
import java.io.IOException;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.control.CompilationFailedException;
import org.apache.wicket.Application;
import org.apache.wicket.application.IClassResolver;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.application.DefaultClassResolver;
import org.apache.wicket.util.listener.IChangeListener;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.watch.IModificationWatcher;
import java.util.concurrent.ConcurrentHashMap;
import java.net.URL;
/**
* Extends the default Page Factory to allow for Groovy based classes.
* Modifications to groovy files are tracked and files are reloaded if modified.
*
* @author Juergen Donnerstag
*/
public class GroovyClassResolver implements IClassResolver
{
/** Logging */
private static final Log log = LogFactory.getLog(GroovyClassResolver.class);
/**
* Caching map of class name to groovy class; not sure if GroovyClassLoader
* does it as well
*/
//private final Map classCache = new ConcurrentReaderHashMap();
private final Map classCache = new ConcurrentHashMap();
/** Default class resolver */
private final IClassResolver defaultClassResolver = new DefaultClassResolver();
/** Application */
private final Application application;
/**
* Constructor
*
* @param application
* Application
*/
public GroovyClassResolver(final Application application)
{
this.application = application;
}
/**
*
* @see org.apache.wicket.application.IClassResolver#getResources(java.lang.String)
*/
public Iterator<URL> getResources(String name)
{
HashSet<URL> loadedFiles = new HashSet<URL>();
try
{
// Try the classloader for the wicket jar/bundle
Enumeration<URL> resources = Application.class.getClassLoader().getResources(name);
loadResources(resources, loadedFiles);
// Try the classloader for the user's application jar/bundle
resources = Application.get().getClass().getClassLoader().getResources(name);
loadResources(resources, loadedFiles);
// Try the context class loader
resources = Thread.currentThread().getContextClassLoader().getResources(name);
loadResources(resources, loadedFiles);
}
catch (IOException e)
{
throw new WicketRuntimeException(e);
}
return loadedFiles.iterator();
}
/**
*
* @param resources
* @param loadedFiles
*/
private void loadResources(Enumeration<URL> resources, Set<URL> loadedFiles)
{
if (resources != null)
{
while (resources.hasMoreElements())
{
final URL url = resources.nextElement();
if (!loadedFiles.contains(url))
{
loadedFiles.add(url);
}
}
}
}
/**
* Resolve the class for the given classname. First try standard java
* classes, then groovy files. Groovy file name must be
* <classname>.groovy.
*
* @param classname
* The object's class name
* @return The class
* @see IClassResolver#resolveClass(String)
*/
public Class resolveClass(final String classname) throws ClassNotFoundException
{
try
{
return defaultClassResolver.resolveClass(classname);
}
catch (WicketRuntimeException ex)
{
; // default resolver failed. Try the groovy specific loader next
}
catch (ClassNotFoundException ex)
{
; // default resolver failed. Try the groovy specific loader next
}
// If definition already loaded, ...
Class groovyPageClass = (Class) classCache.get(classname);
if (groovyPageClass != null)
{
return groovyPageClass;
}
// Else, try Groovy.
final IResourceStream resource = application.getResourceSettings()
.getResourceStreamLocator().locate(getClass(),
classname.replace('.', '/'), null, null, ".groovy");
if (resource != null)
{
try
{
// Load the groovy file, get the Class and watch for changes
groovyPageClass = loadGroovyFileAndWatchForChanges(classname, resource);
if (groovyPageClass != null)
{
return groovyPageClass;
}
}
catch (WicketRuntimeException ex)
{
throw new WicketRuntimeException("Unable to load class with name: "
+ classname, ex);
}
}
else
{
throw new WicketRuntimeException("File not found: " + resource);
}
throw new WicketRuntimeException("Unable to load class with name: " + classname);
}
/**
* Load a Groovy file, create a Class object and put it into the cache
*
* @param classname
* The class name to be created by the Groovy filename
* @param resource
* The Groovy resource
* @return the Class object created by the groovy resouce
*/
private final Class loadGroovyFile(String classname, final IResourceStream resource)
{
// Ensure that we use the correct classloader so that we can find
// classes in an application server.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null)
{
cl = GroovyClassResolver.class.getClassLoader();
}
final GroovyWarClassLoader groovyClassLoader = new GroovyWarClassLoader(cl);
Class clazz = null;
try
{
final InputStream in = resource.getInputStream();
if (in != null)
{
clazz = groovyClassLoader.parseClass(in);
if (clazz != null)
{
// this is necessary because with groovy the filename can be
// different from the class definition included.
if (false == classname.equals(clazz.getName()))
{
log.warn("Though it is possible, the Groovy file name and "
+ "the java class name defined in that file SHOULD "
+ "match and follow the java rules");
}
classname = clazz.getName();
}
}
else
{
log.warn("Groovy file not found: " + resource);
}
}
catch (CompilationFailedException e)
{
throw new WicketRuntimeException("Error parsing groovy file: " + resource, e);
}
catch (Throwable e)
{
throw new WicketRuntimeException("Error while reading groovy file: "
+ resource, e);
}
finally
{
if (clazz == null)
{
// Groovy file not found; error while compiling etc..
// Remove it from cache
classCache.remove(classname);
}
else
{
// Put the new class definition into the cache
classCache.put(classname, clazz);
}
}
return clazz;
}
/**
* Load the groovy file and watch for changes. If changes to the groovy
* happens, than reload the file.
*
* @param classname
* @param resource
* @return Loaded class
*/
private Class loadGroovyFileAndWatchForChanges(final String classname,
final IResourceStream resource)
{
// Watch file in the future
final IModificationWatcher watcher = application.getResourceSettings()
.getResourceWatcher(true);
if (watcher != null)
{
watcher.add(resource, new IChangeListener()
{
public void onChange()
{
try
{
log.info("Reloading groovy file from " + resource);
// Reload file and update cache
final Class clazz = loadGroovyFile(classname, resource);
log.debug("Groovy file contained definition for class: "
+ clazz.getName());
}
catch (Exception e)
{
log.error("Unable to load groovyy file: " + resource, e);
}
}
});
}
log.info("Loading groovy file from " + resource);
return loadGroovyFile(classname, resource);
}
}