/** * Copyright OPS4J * * 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. * * @author nmw * @version $Id: $Id */ package org.ops4j.pax.wicket.internal; import static org.ops4j.lang.NullArgumentException.validateNotEmpty; import static org.ops4j.lang.NullArgumentException.validateNotNull; import static org.ops4j.pax.wicket.api.Constants.APPLICATION_NAME; import static org.osgi.framework.Constants.OBJECTCLASS; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.wicket.application.IClassResolver; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class DelegatingClassResolver implements IClassResolver { private static final Logger LOGGER = LoggerFactory.getLogger(DelegatingClassResolver.class); private final BundleContext context; private final String applicationName; private final List<IClassResolver> resolvers = new CopyOnWriteArrayList<IClassResolver>(); private ClassResolverTracker tracker; /** * <p>Constructor for DelegatingClassResolver.</p> * * @param context a {@link org.osgi.framework.BundleContext} object. * @param applicationName a {@link java.lang.String} object. * @throws java.lang.IllegalArgumentException if any. */ public DelegatingClassResolver(BundleContext context, String applicationName) throws IllegalArgumentException { validateNotNull(context, "context"); validateNotEmpty(applicationName, "applicationName"); this.context = context; this.applicationName = applicationName; } /** * <p>intialize.</p> * * @throws java.lang.IllegalStateException if any. */ public final void intialize() throws IllegalStateException { synchronized (this) { if (tracker != null) { throw new IllegalStateException( "DelegatingClassResolver [" + this + "] had been initialized."); } tracker = new ClassResolverTracker(context, applicationName); tracker.open(); } } /** * <p>dispose.</p> * * @throws java.lang.IllegalStateException if any. */ public void dispose() throws IllegalStateException { synchronized (this) { if (tracker == null) { throw new IllegalStateException( "DelegatingClassResolver [" + this + "] had not been initialized."); } tracker.close(); tracker = null; } } /** * This method is uses only for some internal wicket stuff if the IClassResolver is NOT replaced and in some IOC * stuff also not used by pax wicket. Therefore this method should never ever be called. If it is though we want to * be informed about the problem as soon as possible. * * @return a {@link java.lang.ClassLoader} object. */ public ClassLoader getClassLoader() { throw new UnsupportedOperationException("This method should NOT BE CALLED!"); } /** {@inheritDoc} */ public Class<?> resolveClass(final String classname) throws ClassNotFoundException { LOGGER.trace("Try to resolve {} from {} resolvers", classname, resolvers.size()); for (IClassResolver resolver : resolvers) { try { Class<?> candidate = resolver.resolveClass(classname); if (candidate != null) { return candidate; } } catch (ClassNotFoundException e) { LOGGER.trace("ClassResolver {} could not find class: {}", resolver, classname); } catch (RuntimeException e) { LOGGER.warn("ClassResolver {} threw an unexpected exception.", resolver, e); } } throw new ClassNotFoundException(String.format("Class [%s] can't be resolved.", classname)); } /** {@inheritDoc} */ public Iterator<URL> getResources(String name) { ArrayList<URL> collectedResources = new ArrayList<URL>(); for (IClassResolver resolver : resolvers) { try { Iterator<URL> iterator = resolver.getResources(name); if (iterator == null) { continue; } while (iterator.hasNext()) { collectedResources.add(iterator.next()); } } catch (RuntimeException e) { LOGGER.warn("ClassResolver {} threw an unexpected exception.", resolver, e); return collectedResources.iterator(); } } return collectedResources.iterator(); } private final class ClassResolverTracker extends ServiceTracker<IClassResolver, IClassResolver> { private final String m_applicationName; ClassResolverTracker(BundleContext context, String applicationName) { super(context, createFilter(context, applicationName), null); m_applicationName = applicationName; } @Override public final IClassResolver addingService(ServiceReference<IClassResolver> reference) { IClassResolver resolver = super.addingService(reference); resolvers.add(resolver); return resolver; } @Override public final void modifiedService(ServiceReference<IClassResolver> reference, IClassResolver service) { Object objAppName = reference.getProperty(APPLICATION_NAME); if (objAppName != null) { Class<?> nameClass = objAppName.getClass(); if (String.class.isAssignableFrom(nameClass)) { if (!nameClass.isArray()) { String appName = (String) objAppName; if (m_applicationName.equals(appName)) { return; } } else { String[] appNames = (String[]) objAppName; for (String appName : appNames) { if (m_applicationName.equals(appName)) { return; } } } } } removedService(reference, service); } @Override public final void removedService(ServiceReference<IClassResolver> reference, IClassResolver service) { IClassResolver resolver = service; resolvers.remove(resolver); super.removedService(reference, service); } } private static Filter createFilter(BundleContext context, String applicationName) { String filterStr = "(&(" + OBJECTCLASS + "=" + IClassResolver.class.getName() + ")(" + APPLICATION_NAME + "=" + applicationName + "))"; try { return context.createFilter(filterStr); } catch (InvalidSyntaxException e) { String message = APPLICATION_NAME + "[" + applicationName + "] has an invalid format. "; throw new IllegalArgumentException(message); } } }