/**
* 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.
*/
package org.ops4j.pax.wicket.internal;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import org.apache.wicket.application.IClassResolver;
import org.ops4j.pax.wicket.api.Constants;
import org.ops4j.pax.wicket.internal.extender.ExtendedBundle;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class represents an extended class loader automatically trying to load
* from all bundles added to it.
*
* @author nmw
* @version $Id: $Id
*/
public class BundleDelegatingClassResolver implements IClassResolver, InternalBundleDelegationProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(BundleDelegatingClassResolver.class);
private final String applicationName;
private final BundleContext paxWicketBundleContext;
private final Map<String, Bundle> bundles = new HashMap<String, Bundle>();
private ServiceRegistration<IClassResolver> classResolverRegistration;
/**
* <p>Constructor for BundleDelegatingClassResolver.</p>
*
* @param paxWicketBundleContext a {@link org.osgi.framework.BundleContext} object.
* @param applicationName a {@link java.lang.String} object.
*/
public BundleDelegatingClassResolver(BundleContext paxWicketBundleContext, String applicationName) {
this.paxWicketBundleContext = paxWicketBundleContext;
this.applicationName = applicationName;
}
/**
* <p>Getter for the field <code>applicationName</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getApplicationName() {
return applicationName;
}
/**
* <p>start.</p>
*/
public void start() {
if (classResolverRegistration != null) {
throw new IllegalStateException("Service is already registered");
}
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(Constants.APPLICATION_NAME, applicationName);
try {
classResolverRegistration
= paxWicketBundleContext.registerService(IClassResolver.class, this, properties);
} catch (NullPointerException e) {
LOGGER.error("nullpointer while trying to register >{}< using {} with >{}< bundlecontext are >{}<", IClassResolver.class, this, properties, paxWicketBundleContext, e);
throw e;
}
{
}
}
/**
* <p>stop.</p>
*/
public void stop() {
if (classResolverRegistration == null) {
LOGGER.warn("Trying to unregister service although not registered");
return;
}
classResolverRegistration.unregister();
}
/** {@inheritDoc} */
public void addBundle(ExtendedBundle bundle) {
if (classResolverRegistration == null) {
throw new IllegalStateException("The service is stoped and no more bundles could be added");
}
synchronized (bundles) {
bundles.put(bundle.getBundle().getSymbolicName(), bundle.getBundle());
}
}
/** {@inheritDoc} */
public void removeBundle(ExtendedBundle bundle) {
if (classResolverRegistration == null) {
throw new IllegalStateException("The service is stoped and no more bundles could be removed");
}
synchronized (bundles) {
bundles.remove(bundle.getBundle().getSymbolicName());
}
}
/** {@inheritDoc} */
public Class<?> resolveClass(String classname) throws ClassNotFoundException {
LOGGER.trace("Trying to resolve class {} from BundleDelegatingClassResolver", classname);
synchronized (bundles) {
Collection<Bundle> values = bundles.values();
for (Bundle bundle : values) {
try {
LOGGER.trace("Trying to load class {} from bundle {}", classname, bundle.getSymbolicName());
Class<?> loadedClass = bundle.loadClass(classname);
LOGGER.debug("Loaded class {} from bundle {}", classname, bundle.getSymbolicName());
return loadedClass;
} catch (ClassNotFoundException e) {
LOGGER.trace("Could not load class {} from bundle {} because bundle does not contain the class",
classname, bundle.getSymbolicName());
} catch (IllegalStateException e) {
LOGGER.trace("Could not load class {} from bundle {} because bundle had been uninstalled",
classname,
bundle.getSymbolicName());
}
}
}
throw new ClassNotFoundException("Class [" + classname + "] can't be resolved.");
}
/** {@inheritDoc} */
public Iterator<URL> getResources(String name) {
ArrayList<URL> collectedResources = new ArrayList<URL>();
try {
synchronized (bundles) {
Collection<Bundle> values = bundles.values();
for (Bundle bundle : values) {
final Enumeration<URL> enumeration = bundle.getResources(name);
if (enumeration == null) {
continue;
}
while (enumeration.hasMoreElements()) {
collectedResources.add(enumeration.nextElement());
}
}
}
} catch (IOException e) {
LOGGER.warn("IO exception during reading resources from bundle; returning current state.");
collectedResources.iterator();
}
return collectedResources.iterator();
}
/**
* 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!");
}
}