/*
* 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.aries.mocks;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;
import junit.framework.AssertionFailedError;
import org.apache.aries.unittest.mocks.MethodCall;
import org.apache.aries.unittest.mocks.MethodCallHandler;
import org.apache.aries.unittest.mocks.Skeleton;
import org.apache.aries.unittest.mocks.annotations.Singleton;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleReference;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
@Singleton
public class BundleMock
{
private final String symbolicName;
private final Dictionary<?, ?> headers;
private final BundleContext bc;
private String location;
private BundleClassLoader cl;
private class BundleClassLoader extends URLClassLoader implements BundleReference
{
List<Bundle> otherBundlesToCheck = new ArrayList<Bundle>();
public BundleClassLoader(URL[] urls)
{
super(urls);
}
public BundleClassLoader(URL[] urls, ClassLoader parent)
{
super(urls, parent);
}
public void addBundle(Bundle ... otherBundles)
{
otherBundlesToCheck.addAll(Arrays.asList(otherBundles));
}
public Bundle[] getBundles()
{
return otherBundlesToCheck.toArray(new Bundle[otherBundlesToCheck.size()]);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
Class<?> result = null;
for (Bundle b : otherBundlesToCheck) {
try {
result = b.loadClass(name);
} catch (ClassNotFoundException e) {
// do nothing here.
}
if (result != null) return result;
}
return super.findClass(name);
}
public Bundle getBundle()
{
return Skeleton.newMock(BundleMock.this, Bundle.class);
}
}
public BundleMock(String name, Dictionary<?, ?> bundleHeaders)
{
symbolicName = name;
headers = bundleHeaders;
bc = Skeleton.newMock(new BundleContextMock(Skeleton.newMock(this, Bundle.class)), BundleContext.class);
cl = AccessController.doPrivileged(new PrivilegedAction<BundleClassLoader>() {
public BundleClassLoader run()
{
return new BundleClassLoader(new URL[0], this.getClass().getClassLoader());
}
});
}
public BundleMock(String name, Dictionary<?,?> bundleHeaders, boolean dummy)
{
this(name, bundleHeaders);
cl = null;
}
public BundleMock(String name, Dictionary<?, ?> bundleHeaders, String location)
{
this(name, bundleHeaders);
this.location = location;
if (location != null) {
String cp = (String)bundleHeaders.get(Constants.BUNDLE_CLASSPATH);
if (cp == null) cp = ".";
String[] cpEntries = cp.split(",");
final List<URL> urls = new ArrayList<URL>();
try {
for (String cpEntry : cpEntries) {
if (".".equals(cpEntry.trim())) {
urls.add(new URL(location));
} else {
urls.add(new URL(location + "/" + cpEntry));
}
}
cl = AccessController.doPrivileged(new PrivilegedAction<BundleClassLoader>() {
public BundleClassLoader run()
{
return new BundleClassLoader(urls.toArray(new URL[urls.size()]));
}
});
} catch (MalformedURLException e) {
Error err = new AssertionFailedError("The location was not a valid url");
err.initCause(e);
throw err;
}
}
}
private static class PrivateDataFileHandler implements MethodCallHandler
{
private final File location;
public PrivateDataFileHandler(File f)
{
this.location = f;
}
public Object handle(MethodCall call, Skeleton parent)
{
File privateStorage = new File(location.getAbsolutePath(), "_private");
if (!!!privateStorage.exists())
privateStorage.mkdirs();
return new File(privateStorage, (String) call.getArguments()[0]);
}
}
public BundleMock(String name, Dictionary<?, ?> properties, File location) throws Exception
{
this(name,properties,location.toURL().toExternalForm());
Skeleton bcSkel = Skeleton.getSkeleton(bc);
bcSkel.registerMethodCallHandler(
new MethodCall(BundleContext.class,"getDataFile", new Object[] { String.class }),
new PrivateDataFileHandler(location)
);
}
public String getSymbolicName()
{
return symbolicName;
}
public Dictionary<?, ?> getHeaders()
{
return headers;
}
public Enumeration<URL> findEntries(String baseDir, String matchRule, boolean recurse)
{
System.err.println("findEntries: " + baseDir + ", " + matchRule + ", " + recurse);
File base;
try {
base = new File(new File(new URL(location.replaceAll(" ", "%20")).toURI()), baseDir);
System.err.println("Base dir: " + base);
} catch (Exception e) {
Error err = new AssertionFailedError("Unable to findEntries from " + location);
err.initCause(e);
throw err;
}
if (matchRule.equals("*.xml")) matchRule = ".*\\.xml";
else matchRule = matchRule.replaceAll("\\*", ".*");
System.err.println("matchrule: " + matchRule);
final Pattern p = Pattern.compile(matchRule);
File[] files = base.listFiles(new FileFilter(){
public boolean accept(File pathname)
{
return pathname.isFile() &&
p.matcher(pathname.getName()).matches();
}
});
Vector<URL> v = new Vector<URL>();
if (files != null) {
for (File f : files) {
try {
v.add(f.toURL());
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
System.err.println("no matching files");
}
if (v.isEmpty()) {
return null;
} else {
System.err.println(v);
return v.elements();
}
}
public URL getResource(String name)
{
if (cl != null) return cl.getResource(name);
try {
File f = new File(name);
if(f.exists() || "Entities.jar".equals(name)) return f.toURL();
else return null;
} catch (MalformedURLException e) {
Error err = new AssertionFailedError("The resource " + name + " could not be found.");
err.initCause(e);
throw err;
}
}
public Enumeration<URL> getResources(String name)
{
if (cl != null)
{
try {
return cl.getResources(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
final URL resource = getResource(name);
if(resource != null) {
return new Enumeration<URL>() {
boolean hasMore = true;
public boolean hasMoreElements()
{
return hasMore;
}
public URL nextElement()
{
hasMore = false;
return resource;
}
};
}
}
return new Enumeration<URL>(){
public URL nextElement()
{
return null;
}
public boolean hasMoreElements()
{
return false;
}
};
}
public Class<?> loadClass(String name) throws ClassNotFoundException
{
if (cl != null) return Class.forName(name, false, cl);
throw new ClassNotFoundException("Argh, things went horribly wrong trying to load " + name);
}
public String getLocation()
{
try {
return (location == null) ? new File(symbolicName + ".jar").toURL().toString() : location;
} catch (MalformedURLException e) {
Error err = new AssertionFailedError("We could not generate a valid url for the bundle");
err.initCause(e);
throw err;
}
}
public BundleContext getBundleContext()
{
return bc;
}
public Version getVersion()
{
String res = (String) headers.get("Bundle-Version");
if (res != null)
return new Version(res);
else
return new Version("0.0.0");
}
public int getState()
{
return Bundle.ACTIVE;
}
public void addToClassPath(URL ... urls)
{
if (cl != null) {
URL[] existingURLs = cl.getURLs();
final URL[] mergedURLs = new URL[urls.length + existingURLs.length];
int i = 0;
for (; i < existingURLs.length; i++) {
mergedURLs[i] = existingURLs[i];
}
for (int j = 0; j < urls.length; j++, i++) {
mergedURLs[i] = urls[j];
}
BundleClassLoader newCl = AccessController.doPrivileged(new PrivilegedAction<BundleClassLoader>() {
public BundleClassLoader run()
{
return new BundleClassLoader(mergedURLs, cl.getParent());
}
});
newCl.addBundle(cl.getBundles());
cl = newCl;
}
}
public void addBundleToClassPath(Bundle ... bundles) {
if (cl != null) {
cl.addBundle(bundles);
}
}
public ClassLoader getClassLoader()
{
return cl;
}
// This is good enough for Mocks' needs in unit test, but isn't how it works in the real world!
public ServiceReference[] getRegisteredServices()
{
ServiceReference[] result = null;
try {
result = bc.getServiceReferences((String) null, null);
} catch (InvalidSyntaxException isx) {
// no-op: Swallow exception
}
return result;
}
}