Index: src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java (working copy) @@ -21,6 +21,7 @@ import java.util.List; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.Version; /** * @@ -33,24 +34,102 @@ public static final Bundle getHostBundle(BundleContext context,Bundle bundle) { String fragment = bundle.getHeaders().get(org.osgi.framework.Constants.FRAGMENT_HOST) + ""; + + // In the fragment host string we might have the bundle version parameter + Version hostMinimum = Version.emptyVersion; + boolean minInclusive = false; + Version hostMaximum = null; + boolean maxInclusive = false; + String versionDelimiter = ";bundle-version="; + if (fragment.contains(versionDelimiter)) { + // the version string is delimited by \" so we use that to parse it easily + String versionString = fragment.substring(fragment.indexOf("\"")+1,fragment.lastIndexOf("\"")); + fragment = fragment.substring(0, fragment.indexOf(versionDelimiter)); + + // we check if it includes a minimum host version as well as a maximum (in a format like "[0.0.1,0.2.0)") + int minMaxDelimiterIndex = versionString.indexOf(","); + if (minMaxDelimiterIndex>=0) { + + String hostMinimumString = versionString.substring(1,minMaxDelimiterIndex); + minInclusive = versionString.substring(0,1).equals("["); + hostMinimum = Version.parseVersion(hostMinimumString); + + int versionStringLength = versionString.length(); + hostMaximum = Version.parseVersion(versionString.substring(minMaxDelimiterIndex+1, versionStringLength-1)); + maxInclusive = versionString.substring(versionStringLength-1,versionStringLength).equals("]"); + + } else { + hostMinimum = Version.parseVersion(versionString); + } + + } + Bundle[] bundles = context.getBundles(); for (Bundle ibundle : bundles) { - if (ibundle.getSymbolicName().equals(fragment)) { - return ibundle; - } + + if (isBundleAttachedToFragment(ibundle,hostMinimum,minInclusive,hostMaximum,maxInclusive,fragment)) { + return ibundle; + } + + } - throw new RuntimeException(); + throw new RuntimeException("Fragment host not found or fragment is not attached to any host"); } - public static final Class loadClass(String clazz, Bundle bundleHost) { - try { - Class loadClass = bundleHost.loadClass(clazz); - return loadClass; + private static boolean isBundleAttachedToFragment(Bundle bundle, Version hostMinimum, + boolean minInclusive, Version hostMaximum, boolean maxInclusive, + String fragment) { + + String symbolicName = bundle.getSymbolicName(); + if (symbolicName.equals(fragment)) { + + // we check if its the correct version + Version version = bundle.getVersion(); + + int minCompare = version.compareTo(hostMinimum); + boolean minOk = (minInclusive) ? minCompare>=0 : minCompare>0; + + boolean maxOk = true; + if (hostMaximum!=null) { + int maxCompare = version.compareTo(hostMaximum); + maxOk = (maxInclusive) ? maxCompare<=0 : maxCompare<0; + } + + return (minOk && maxOk); + + } + + return false; + } + + public static final Class loadClass(String clazz, Bundle bundleHost) { + Class loadClass = null; + try { + loadClass = bundleHost.loadClass(clazz); + + } catch (NoClassDefFoundError e2) { + + // Workaround for weird Eclipse bug: if a bundle is created using the wizard, it compiles + // classes onto the '/bin' folder (which doesn't happen when exported), this means that + // when running inside Eclipse the class will have an incorrect prefix (eg: bin.com.foo.bar.Test) + // Therefore we make an extra attempt. This is recursive so it works with nested compilation folders. + // To prevent infinite recursion we don't do it for classnames that do not have a "package" prefix + // as this is covered by the base case that has just thrown the exception. + // TODO: add some refactoring so thrown exception is more informative + int packageDelimiterIndex = clazz.indexOf("."); + if (packageDelimiterIndex>=0) { + loadClass = loadClass(clazz.substring(packageDelimiterIndex+1, clazz.length()),bundleHost); + } else { + throw new RuntimeException("Couldn't find test class"); + } } catch (Exception e) { + e.printStackTrace(); - throw new RuntimeException(e); + throw new RuntimeException(e); + } + return loadClass; } public static final List> getTestClass(BundleContext context,Bundle bundle) { @@ -65,6 +144,7 @@ String file = e.getFile(); String className = file.replaceAll("/", ".").replaceAll(".class", "").replaceFirst(".", ""); + Class clazz = loadClass(className, hostBundle); clazzs.add(clazz); } Index: src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java (working copy) @@ -78,7 +78,12 @@ public void test(long bundleId) { - Bundle bundle = bundles.get(bundleId); + + Bundle bundle = bundles.get(bundleId); + // requested bundle may not be on the list of testable fragments + if (bundle==null) { + System.err.println("Bundle: [ Id="+bundleId+"] is not testable (should be fragment and have the 'Unit-Test:' MANIFEST header"); + } List> testClazzs = getTestClass(context,bundle); System.out.println("Bundle : ["+bundleId+"] : "+ bundle.getSymbolicName()); System.out.println("_");