package org.nocket.page;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.wicket.Application;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.application.DefaultClassResolver;
import org.apache.wicket.application.IClassResolver;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.collections.UrlExternalFormComparator;
import org.nocket.gen.domain.WebDomainProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class resolver is a copy-paste-construct from Wicket's
* AbstractClassResolver and DefaultClassResolver concerning the default class
* resolvement. As usual, Wicket defines a lot of stuff final and private, so
* that we can't simply derive from DefaultClassResolver.<br>
* The important extension is the possibility to trigger the in-memory page and
* panel class creation and register these classes here so that they can always
* be found from within Wicket's core.
*
* @author less02
*/
public class InMemoryClassResolver implements IClassResolver {
private static final Logger log = LoggerFactory.getLogger(InMemoryClassResolver.class);
protected final ConcurrentMap<String, WeakReference<Class<?>>> classes = new ConcurrentHashMap<String, WeakReference<Class<?>>>();
/**
* The in-memory classes are hard-referenced because they can't be recreated
* from here on the fly However, this map is only used for canonic page
* classes which are very small and only a few
*/
protected final ConcurrentMap<String, Class<?>> inMemoryClasses = new ConcurrentHashMap<String, Class<?>>();
public ClassLoader getClassLoader() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = DefaultClassResolver.class.getClassLoader();
}
return loader;
}
public final Class<?> resolveClass(final String className) throws ClassNotFoundException {
try {
return resolvePersistentClass(className);
} catch (ClassNotFoundException cnfx) {
return resolveInMemoryClass(className);
}
}
protected Class<?> resolveInMemoryClass(String className) throws ClassNotFoundException {
Class<?> inMemoryClass = inMemoryClasses.get(className);
if (inMemoryClass == null)
throw new ClassNotFoundException();
return inMemoryClass;
}
protected final Class<?> resolvePersistentClass(final String className) throws ClassNotFoundException {
Class<?> clazz = null;
WeakReference<Class<?>> ref = classes.get(className);
// Might be garbage-collected between getting the WeakRef and retrieving
// the Class from it.
if (ref != null) {
clazz = ref.get();
}
if (clazz == null) {
if (className.equals("byte")) {
clazz = byte.class;
} else if (className.equals("short")) {
clazz = short.class;
} else if (className.equals("int")) {
clazz = int.class;
} else if (className.equals("long")) {
clazz = long.class;
} else if (className.equals("float")) {
clazz = float.class;
} else if (className.equals("double")) {
clazz = double.class;
} else if (className.equals("boolean")) {
clazz = boolean.class;
} else if (className.equals("char")) {
clazz = char.class;
} else {
// synchronize on the only class member to load only one class at a time and
// prevent LinkageError. See above for more info
synchronized (classes) {
clazz = Class.forName(className, false, getClassLoader());
if (clazz == null) {
throw new ClassNotFoundException(className);
}
}
classes.put(className, new WeakReference<Class<?>>(clazz));
}
}
return clazz;
}
public Iterator<URL> getResources(final String name) {
Set<URL> resultSet = new TreeSet<URL>(new UrlExternalFormComparator());
try {
// Try the classloader for the wicket jar/bundle
Enumeration<URL> resources = Application.class.getClassLoader().getResources(name);
loadResources(resources, resultSet);
// Try the classloader for the user's application jar/bundle
resources = Application.get().getClass().getClassLoader().getResources(name);
loadResources(resources, resultSet);
// Try the context class loader
resources = getClassLoader().getResources(name);
loadResources(resources, resultSet);
} catch (Exception e) {
throw new WicketRuntimeException(e);
}
return resultSet.iterator();
}
/**
*
* @param resources
* @param loadedResources
*/
private void loadResources(Enumeration<URL> resources, Set<URL> loadedResources) {
if (resources != null) {
while (resources.hasMoreElements()) {
final URL url = resources.nextElement();
loadedResources.add(url);
}
}
}
synchronized public Class<?> buildPageClassInMemory(Class<?> domainClass) throws ClassNotFoundException {
String fullyQualifiedPageClassName = domainClass.getName() + "Page";
log.debug("Looking for in-memory page class " + fullyQualifiedPageClassName);
Class<?> pageClass = inMemoryClasses.get(fullyQualifiedPageClassName);
if (pageClass == null) {
Class<? extends WebPage> pageBaseClass = new WebDomainProperties(domainClass).getHTMLPageBaseClass();
log.debug("In-memory page class " + fullyQualifiedPageClassName
+ " not yet registered, building on-the-fly... ");
pageClass = new InMemoryPageClassBuilder(domainClass, pageBaseClass).buildClassInMemory();
registerInMemoryClass(pageClass);
}
return pageClass;
}
synchronized public Class<?> buildPanelClassInMemory(Class<?> domainClass) throws ClassNotFoundException {
String fullyQualifiedPanelClassName = domainClass.getName() + "Panel";
log.debug("Looking for in-memory panel class " + fullyQualifiedPanelClassName);
Class<?> panelClass = inMemoryClasses.get(fullyQualifiedPanelClassName);
if (panelClass == null) {
Class<? extends Panel> panelBaseClass = new WebDomainProperties(domainClass).getHTMLPanelBaseClass();
log.debug("In-memory panel class " + fullyQualifiedPanelClassName
+ " not yet registered, building on-the-fly... ");
//TODO JL: machen ;-)
panelClass = new InMemoryPanelClassBuilder(domainClass, panelBaseClass).buildClassInMemory();
registerInMemoryClass(panelClass);
}
return panelClass;
}
synchronized protected void registerInMemoryClass(Class<?> inMemoryClass) {
inMemoryClasses.put(inMemoryClass.getName(), inMemoryClass);
}
}