Author: mgrigorov
Date: Thu Jul 14 11:01:25 2011
New Revision: 1146646
URL: http://svn.apache.org/viewvc?rev=1146646&view=rev
Log:
WICKET-3898 Add CompoundClassResolver
Added:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/AbstractClassResolver.java
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/CompoundClassResolver.java
Modified:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/DefaultClassResolver.java
Added:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/AbstractClassResolver.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/AbstractClassResolver.java?rev=1146646&view=auto
==============================================================================
---
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/AbstractClassResolver.java
(added)
+++
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/AbstractClassResolver.java
Thu Jul 14 11:01:25 2011
@@ -0,0 +1,185 @@
+/*
+ * 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.wicket.application;
+
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketRuntimeException;
+
+/**
+ * An abstract implementation of a {@link IClassResolver} which uses a {@link
ClassLoader} for
+ * resolving classes.
+ *
+ * @see org.apache.wicket.settings.IApplicationSettings#getClassResolver()
+ *
+ * @author Juergen Donnerstag
+ * @author Jonathan Locke
+ */
+public abstract class AbstractClassResolver implements IClassResolver
+{
+ /**
+ * Usually class loaders implement more efficient caching strategies
than we could possibly do,
+ * but we experienced synchronization issue resulting in stack traces
like:
+ * java.lang.LinkageError: duplicate class definition:
+ *
+ * <pre>
+ * wicket/examples/repeater/RepeatingPage at
java.lang.ClassLoader.defineClass1(Native Method)
+ * </pre>
+ *
+ * This problem has gone since we synchronize the access.
+ */
+ private final ConcurrentMap<String, WeakReference<Class<?>>> classes =
new ConcurrentHashMap<String, WeakReference<Class<?>>>();
+
+ public final Class<?> resolveClass(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;
+ }
+
+ /**
+ * Returns the {@link ClassLoader} to be used for resolving classes
+ *
+ * @return the {@link ClassLoader} to be used for resolving classes
+ */
+ protected abstract ClassLoader getClassLoader();
+
+ public Iterator<URL> getResources(final String name)
+ {
+ List<URL> resultList = new ArrayList<URL>();
+
+ // URIs should be used instead of URLs as Set keys. See
WICKET-3867.
+ HashSet<URI> loadedFiles = new HashSet<URI>();
+ 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 = getClassLoader().getResources(name);
+ loadResources(resources, loadedFiles);
+
+ for (URI uri : loadedFiles)
+ {
+ resultList.add(uri.toURL());
+ }
+ }
+ catch (Exception e)
+ {
+ throw new WicketRuntimeException(e);
+ }
+
+ return resultList.iterator();
+ }
+
+ /**
+ *
+ * @param resources
+ * @param loadedFiles
+ * @throws URISyntaxException
+ * if URL.toURI() throws
+ */
+ private void loadResources(Enumeration<URL> resources, Set<URI>
loadedFiles)
+ throws URISyntaxException
+ {
+ if (resources != null)
+ {
+ while (resources.hasMoreElements())
+ {
+ final URL url = resources.nextElement();
+ URI uri = url.toURI();
+ if (!loadedFiles.contains(uri))
+ {
+ loadedFiles.add(uri);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
Added:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/CompoundClassResolver.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/CompoundClassResolver.java?rev=1146646&view=auto
==============================================================================
---
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/CompoundClassResolver.java
(added)
+++
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/CompoundClassResolver.java
Thu Jul 14 11:01:25 2011
@@ -0,0 +1,136 @@
+/*
+ * 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.wicket.application;
+
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.wicket.util.lang.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A thread safe compound {@link IClassResolver}. Class resolving is done by
iterating through all
+ * {@link IClassResolver}s until the class is found. Resource resolving is
done by combining the
+ * results of all {@link IClassResolver}s.
+ *
+ * @author Jesse Long
+ */
+public class CompoundClassResolver implements IClassResolver
+{
+ private static final Logger logger =
LoggerFactory.getLogger(CompoundClassResolver.class);
+
+ private final List<IClassResolver> resolvers = new
CopyOnWriteArrayList<IClassResolver>();
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation iterates through all the {@link IClassResolver}
trying to load the class
+ * until the class is found.
+ *
+ * @param className
+ * The name of the class to resolve.
+ * @return The {@link Class}, if it is found.
+ * @throws ClassNotFoundException
+ * If the class was not found
+ */
+ public Class<?> resolveClass(final String className) throws
ClassNotFoundException
+ {
+ boolean debugEnabled = logger.isDebugEnabled();
+
+ for (IClassResolver resolver : resolvers)
+ {
+ try
+ {
+ return resolver.resolveClass(className);
+ }
+ catch (ClassNotFoundException cnfx)
+ {
+ if (debugEnabled)
+ {
+ logger.debug("ClassResolver '{}' cannot
find class: '{}'", resolver.getClass()
+ .getName(), className);
+ }
+ }
+ }
+
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation iterates through all {@link IClassResolver}s
added, and combines the
+ * results into one {@link Set} of {@link URL}s, and returns an {@link
Iterator} for the set.
+ * {@link URL}s are unique in the set.
+ *
+ * @param name
+ * The name of the resource to find.
+ * @return An {@link Iterator} of all the {@link URL}s matching the
resource name.
+ */
+ public Iterator<URL> getResources(final String name)
+ {
+ Args.notNull(name, "name");
+
+ Set<URL> urls = new TreeSet<URL>();
+ for (IClassResolver resolver : resolvers)
+ {
+ Iterator<URL> it = resolver.getResources(name);
+ while (it.hasNext())
+ {
+ URL url = it.next();
+ if (urls.contains(url) == false)
+ {
+ urls.add(url);
+ }
+ }
+ }
+
+ return urls.iterator();
+ }
+
+ /**
+ * Adds a resolver
+ *
+ * @param resolver
+ * The resolver to add
+ * @return {@code this} for chaining
+ */
+ public CompoundClassResolver add(final IClassResolver resolver)
+ {
+ Args.notNull(resolver, "resolver");
+ resolvers.add(resolver);
+ return this;
+ }
+
+ /**
+ * Removes a resolver
+ *
+ * @param resolver
+ * The resolver to remove
+ * @return {@code this} for chaining
+ */
+ public CompoundClassResolver remove(final IClassResolver resolver)
+ {
+ resolvers.remove(resolver);
+ return this;
+ }
+}
\ No newline at end of file
Modified:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/DefaultClassResolver.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/DefaultClassResolver.java?rev=1146646&r1=1146645&r2=1146646&view=diff
==============================================================================
---
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/DefaultClassResolver.java
(original)
+++
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/application/DefaultClassResolver.java
Thu Jul 14 11:01:25 2011
@@ -16,21 +16,6 @@
*/
package org.apache.wicket.application;
-import java.lang.ref.WeakReference;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.WicketRuntimeException;
-
/**
* Resolves a class by using the classloader that loaded this class.
*
@@ -39,152 +24,23 @@ import org.apache.wicket.WicketRuntimeEx
* @author Juergen Donnerstag
* @author Jonathan Locke
*/
-public final class DefaultClassResolver implements IClassResolver
+public final class DefaultClassResolver extends AbstractClassResolver
{
- /**
- * Usually class loaders implement more efficient caching strategies
than we could possibly do,
- * but we experienced synchronization issue resulting in stack traces
like:
- * java.lang.LinkageError: duplicate class definition:
- *
- * <pre>
- * wicket/examples/repeater/RepeatingPage at
java.lang.ClassLoader.defineClass1(Native Method)
- * </pre>
- *
- * This problem has gone since we synchronize the access.
- */
- private final ConcurrentHashMap<String, WeakReference<Class<?>>>
classes = new ConcurrentHashMap<String, WeakReference<Class<?>>>();
-
- /**
- * @see
org.apache.wicket.application.IClassResolver#resolveClass(java.lang.String)
- */
- public final Class<?> resolveClass(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)
- {
- ClassLoader loader =
Thread.currentThread().getContextClassLoader();
- if (loader == null)
- {
- loader =
DefaultClassResolver.class.getClassLoader();
- }
- // see
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6500212
- // clazz = loader.loadClass(classname);
- clazz = Class.forName(classname, false,
loader);
- if (clazz == null)
- {
- throw new
ClassNotFoundException(classname);
- }
- }
- classes.put(classname, new
WeakReference<Class<?>>(clazz));
- }
- }
- return clazz;
- }
-
- /**
- *
- * @see
org.apache.wicket.application.IClassResolver#getResources(java.lang.String)
- */
- public Iterator<URL> getResources(String name)
- {
- List<URL> resultList = new ArrayList<URL>();
-
- // URIs should be used instead of URLs as Set keys. See
WICKET-3867.
- HashSet<URI> loadedFiles = new HashSet<URI>();
- 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);
-
- for (URI uri : loadedFiles)
- {
- resultList.add(uri.toURL());
- }
- }
- catch (Exception e)
- {
- throw new WicketRuntimeException(e);
- }
-
- return resultList.iterator();
- }
/**
+ * {@inheritDoc}
*
- * @param resources
- * @param loadedFiles
- * @throws URISyntaxException
- * if URL.toURI() throws
+ * @return the current {@link Thread}s context {@link ClassLoader} if
it is set, or the
+ * {@link ClassLoader} that was used to load this class.
*/
- private void loadResources(Enumeration<URL> resources, Set<URI>
loadedFiles)
- throws URISyntaxException
+ @Override
+ protected ClassLoader getClassLoader()
{
- if (resources != null)
+ ClassLoader loader =
Thread.currentThread().getContextClassLoader();
+ if (loader == null)
{
- while (resources.hasMoreElements())
- {
- final URL url = resources.nextElement();
- URI uri = url.toURI();
- if (!loadedFiles.contains(uri))
- {
- loadedFiles.add(uri);
- }
- }
+ loader = DefaultClassResolver.class.getClassLoader();
}
+ return loader;
}
}