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.net.URL; 020import java.util.Iterator; 021import java.util.List; 022import java.util.Set; 023import java.util.TreeSet; 024import java.util.concurrent.CopyOnWriteArrayList; 025 026import org.apache.wicket.util.collections.UrlExternalFormComparator; 027import org.apache.wicket.util.lang.Args; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * A thread safe compound {@link IClassResolver}. Class resolving is done by iterating through all 033 * {@link IClassResolver}s until the class is found. Resource resolving is done by combining the 034 * results of all {@link IClassResolver}s. 035 * 036 * @author Jesse Long 037 */ 038public class CompoundClassResolver implements IClassResolver 039{ 040 private static final Logger logger = LoggerFactory.getLogger(CompoundClassResolver.class); 041 042 private final List<IClassResolver> resolvers = new CopyOnWriteArrayList<IClassResolver>(); 043 044 /** 045 * {@inheritDoc} 046 * <p> 047 * This implementation iterates through all the {@link IClassResolver} trying to load the class 048 * until the class is found. 049 * 050 * @param className 051 * The name of the class to resolve. 052 * @return The {@link Class}, if it is found. 053 * @throws ClassNotFoundException 054 * If the class was not found 055 */ 056 @Override 057 public Class<?> resolveClass(final String className) throws ClassNotFoundException 058 { 059 boolean debugEnabled = logger.isDebugEnabled(); 060 061 for (IClassResolver resolver : resolvers) 062 { 063 try 064 { 065 return resolver.resolveClass(className); 066 } 067 catch (ClassNotFoundException cnfx) 068 { 069 if (debugEnabled) 070 { 071 logger.debug("ClassResolver '{}' cannot find class: '{}'", resolver.getClass() 072 .getName(), className); 073 } 074 } 075 } 076 077 throw new ClassNotFoundException(className); 078 } 079 080 /** 081 * {@inheritDoc} 082 * <p> 083 * This implementation iterates through all {@link IClassResolver}s added, and combines the 084 * results into one {@link Set} of {@link URL}s, and returns an {@link Iterator} for the set. 085 * {@link URL}s are unique in the set. 086 * 087 * @param name 088 * The name of the resource to find. 089 * @return An {@link Iterator} of all the {@link URL}s matching the resource name. 090 */ 091 @Override 092 public Iterator<URL> getResources(final String name) 093 { 094 Args.notNull(name, "name"); 095 096 Set<URL> urls = new TreeSet<URL>(new UrlExternalFormComparator()); 097 098 for (IClassResolver resolver : resolvers) 099 { 100 Iterator<URL> it = resolver.getResources(name); 101 while (it.hasNext()) 102 { 103 URL url = it.next(); 104 urls.add(url); 105 } 106 } 107 108 return urls.iterator(); 109 } 110 111 /** 112 * @return the class loader returned by the first registered IClassResolver. If there is no 113 * registered IClassResolver then the current thread's context class loader will be returned. 114 */ 115 @Override 116 public ClassLoader getClassLoader() 117 { 118 final ClassLoader classLoader; 119 if (resolvers.isEmpty() == false) 120 { 121 classLoader = resolvers.iterator().next().getClassLoader(); 122 } 123 else 124 { 125 classLoader = Thread.currentThread().getContextClassLoader(); 126 } 127 return classLoader; 128 } 129 130 /** 131 * Adds a resolver 132 * 133 * @param resolver 134 * The resolver to add 135 * @return {@code this} for chaining 136 */ 137 public CompoundClassResolver add(final IClassResolver resolver) 138 { 139 Args.notNull(resolver, "resolver"); 140 resolvers.add(resolver); 141 return this; 142 } 143 144 /** 145 * Removes a resolver 146 * 147 * @param resolver 148 * The resolver to remove 149 * @return {@code this} for chaining 150 */ 151 public CompoundClassResolver remove(final IClassResolver resolver) 152 { 153 resolvers.remove(resolver); 154 return this; 155 } 156}