001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.application; 018 019import java.lang.ref.WeakReference; 020import java.net.URL; 021import java.util.Enumeration; 022import java.util.Iterator; 023import java.util.Set; 024import java.util.TreeSet; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.ConcurrentMap; 027 028import org.apache.wicket.Application; 029import org.apache.wicket.WicketRuntimeException; 030import org.apache.wicket.util.collections.UrlExternalFormComparator; 031 032/** 033 * An abstract implementation of a {@link IClassResolver} which uses a {@link ClassLoader} for 034 * resolving classes. 035 * 036 * @see org.apache.wicket.settings.ApplicationSettings#getClassResolver() 037 * 038 * @author Juergen Donnerstag 039 * @author Jonathan Locke 040 */ 041public abstract class AbstractClassResolver implements IClassResolver 042{ 043 /** 044 * Usually class loaders implement more efficient caching strategies than we could possibly do, 045 * but we experienced synchronization issue resulting in stack traces like: 046 * java.lang.LinkageError: duplicate class definition: 047 * 048 * <pre> 049 * wicket/examples/repeater/RepeatingPage at java.lang.ClassLoader.defineClass1(Native Method) 050 * </pre> 051 * 052 * This problem has gone since we synchronize the access. 053 */ 054 private final ConcurrentMap<String, WeakReference<Class<?>>> classes = new ConcurrentHashMap<>(); 055 056 @Override 057 public final Class<?> resolveClass(final String className) throws ClassNotFoundException 058 { 059 Class<?> clazz = null; 060 WeakReference<Class<?>> ref = classes.get(className); 061 062 // Might be garbage-collected between getting the WeakRef and retrieving 063 // the Class from it. 064 if (ref != null) 065 { 066 clazz = ref.get(); 067 } 068 if (clazz == null) 069 { 070 switch (className) 071 { 072 case "byte": 073 clazz = byte.class; 074 break; 075 case "short": 076 clazz = short.class; 077 break; 078 case "int": 079 clazz = int.class; 080 break; 081 case "long": 082 clazz = long.class; 083 break; 084 case "float": 085 clazz = float.class; 086 break; 087 case "double": 088 clazz = double.class; 089 break; 090 case "boolean": 091 clazz = boolean.class; 092 break; 093 case "char": 094 clazz = char.class; 095 break; 096 default: 097 // synchronize on the only class member to load only one class at a time and 098 // prevent LinkageError. See above for more info 099 synchronized (classes) 100 { 101 clazz = Class.forName(className, false, getClassLoader()); 102 if (clazz == null) 103 { 104 throw new ClassNotFoundException(className); 105 } 106 } 107 classes.put(className, new WeakReference<Class<?>>(clazz)); 108 break; 109 } 110 } 111 return clazz; 112 } 113 114 @Override 115 public Iterator<URL> getResources(final String name) 116 { 117 Set<URL> resultSet = new TreeSet<>(new UrlExternalFormComparator()); 118 119 try 120 { 121 // Try the classloader for the wicket jar/bundle 122 Enumeration<URL> resources = Application.class.getClassLoader().getResources(name); 123 loadResources(resources, resultSet); 124 125 // Try the classloader for the user's application jar/bundle 126 resources = Application.get().getClass().getClassLoader().getResources(name); 127 loadResources(resources, resultSet); 128 129 // Try the context class loader 130 resources = getClassLoader().getResources(name); 131 loadResources(resources, resultSet); 132 } 133 catch (Exception e) 134 { 135 throw new WicketRuntimeException(e); 136 } 137 138 return resultSet.iterator(); 139 } 140 141 /** 142 * 143 * @param resources 144 * @param loadedResources 145 */ 146 private void loadResources(Enumeration<URL> resources, Set<URL> loadedResources) 147 { 148 if (resources != null) 149 { 150 while (resources.hasMoreElements()) 151 { 152 final URL url = resources.nextElement(); 153 loadedResources.add(url); 154 } 155 } 156 } 157}