/* * 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 calculator.dosgi.test; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URL; import java.util.HashSet; import java.util.Set; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.Constants; import calculator.dosgi.CalculatorService; import calculator.dosgi.impl.CalculatorActivator; import calculator.dosgi.impl.CalculatorServiceDSImpl; import calculator.dosgi.impl.CalculatorServiceImpl; import calculator.dosgi.operations.AddService; import calculator.dosgi.operations.DivideService; import calculator.dosgi.operations.MultiplyService; import calculator.dosgi.operations.SubtractService; import calculator.dosgi.operations.impl.AddServiceImpl; import calculator.dosgi.operations.impl.DivideServiceImpl; import calculator.dosgi.operations.impl.MultiplyServiceImpl; import calculator.dosgi.operations.impl.OperationsActivator; import calculator.dosgi.operations.impl.SubtractServiceImpl; /** * * Utility class to create OSGi bundles * * @version $Rev$ $Date$ */ public class OSGiTestBundles { private static class InvocationHandlerImpl implements InvocationHandler { private Object instance; public InvocationHandlerImpl(Object instance) { super(); this.instance = instance; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method m = instance.getClass().getMethod(method.getName(), method.getParameterTypes()); return m.invoke(instance, args); } } private static String getPackageName(Class<?> cls) { String name = cls.getName(); int index = name.lastIndexOf('.'); return index == -1 ? "" : name.substring(0, index); } public static URL createBundle(String jarName, String mfFile, String[][] resources, Class<?>... classes) throws IOException { InputStream is = OSGiTestBundles.class.getClassLoader().getResourceAsStream(mfFile); Manifest manifest = new Manifest(is); is.close(); ByteArrayOutputStream out = new ByteArrayOutputStream(); JarOutputStream jarOut = new JarOutputStream(out, manifest); for (Class<?> cls : classes) { addClass(jarOut, cls); } if (resources != null) { for (String resource[] : resources) { if (resource.length >= 1) { String r1 = resource[0]; String r2 = resource.length > 1 ? resource[1] : r1; addResource(jarOut, OSGiTestBundles.class.getClassLoader(), r1, r2); } } } jarOut.close(); out.close(); File jar = new File(jarName); FileOutputStream fileOut = new FileOutputStream(jar); fileOut.write(out.toByteArray()); fileOut.close(); return jar.toURI().toURL(); } public static URL createBundle(String jarName, String bundleName, String exports, String imports, String[] resources, Class<?>... classes) throws IOException { Class<?> activator = null; Set<String> packages = new HashSet<String>(); StringBuffer exportPackages = new StringBuffer(); if (exports != null) { exportPackages.append(exports); } for (Class<?> cls : classes) { if (BundleActivator.class.isAssignableFrom(cls)) { activator = cls; } if (exports == null && cls.isInterface()) { String pkg = getPackageName(cls); if (packages.add(pkg)) { exportPackages.append(pkg).append(","); } } } int len = exportPackages.length(); if (len > 0 && exportPackages.charAt(len - 1) == ',') { exportPackages.deleteCharAt(len - 1); } Manifest manifest = new Manifest(); // This attribute Manifest-Version is required so that the MF will be added to the jar manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, bundleName); manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, "1.0.0"); manifest.getMainAttributes().putValue(Constants.BUNDLE_NAME, bundleName); manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE, exportPackages.toString()); StringBuffer importPackages = new StringBuffer(); if (imports != null) { importPackages.append(imports).append(",org.osgi.framework"); } else { importPackages.append("org.osgi.framework"); } manifest.getMainAttributes().putValue(Constants.IMPORT_PACKAGE, importPackages.toString()); if (activator != null) { manifest.getMainAttributes().putValue(Constants.BUNDLE_ACTIVATOR, activator.getName()); } ByteArrayOutputStream out = new ByteArrayOutputStream(); JarOutputStream jarOut = new JarOutputStream(out, manifest); for (Class<?> cls : classes) { addClass(jarOut, cls); } if (resources != null) { for (String resource : resources) { addResource(jarOut, OSGiTestBundles.class.getClassLoader(), resource, null); } } jarOut.close(); out.close(); File jar = new File(jarName); FileOutputStream fileOut = new FileOutputStream(jar); fileOut.write(out.toByteArray()); fileOut.close(); return jar.toURI().toURL(); } private static void addClass(JarOutputStream jarOut, Class<?> javaClass) throws IOException, FileNotFoundException { String classFile = javaClass.getName().replace('.', '/') + ".class"; URL url = javaClass.getResource(javaClass.getSimpleName() + ".class"); addEntry(jarOut, url, classFile); } private static void addResource(JarOutputStream jarOut, ClassLoader cl, String resourceName, String entryName) throws IOException, FileNotFoundException { URL url = cl.getResource(resourceName); if (entryName == null) { entryName = resourceName; } addEntry(jarOut, url, entryName); } private static void addEntry(JarOutputStream jarOut, URL url, String resourceName) throws IOException, FileNotFoundException { String path = url.getPath(); ZipEntry ze = new ZipEntry(resourceName); jarOut.putNextEntry(ze); FileInputStream file = new FileInputStream(path); byte[] fileContents = new byte[file.available()]; file.read(fileContents); jarOut.write(fileContents); jarOut.closeEntry(); file.close(); } static URL generateCalculatorBundle() throws IOException { return createBundle("target/test-classes/calculator-bundle.jar", "calculator/dosgi/META-INF/MANIFEST.MF", new String[][] { { "calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml", "OSGI-INF/remote-service/calculator-service-descriptions.xml"}, {"calculator/dosgi/OSGI-INF/calculator-component.xml", "OSGI-INF/calculator-component.xml"}, /* {"calculator/dosgi/bundle.componentType", "OSGI-INF/sca/bundle.componentType"}, {"calculator/dosgi/calculator.composite", "OSGI-INF/sca/bundle.composite"}, {"calculator/dosgi/META-INF/sca-contribution.xml", "META-INF/sca-contribution.xml"} */ }, CalculatorService.class, // Package the interfaces so that the operations bundle can be remote AddService.class, SubtractService.class, MultiplyService.class, DivideService.class, CalculatorServiceImpl.class, CalculatorServiceDSImpl.class, CalculatorActivator.class); } /** * Create the OSGi bundle for calculator SCA * @return * @throws IOException */ static URL generateCalculatorSCABundle() throws IOException { return createBundle("target/test-classes/calculator-sca-bundle.jar", "calculator/dosgi/sca/META-INF/MANIFEST.MF", new String[][] { {"calculator/dosgi/sca/OSGI-INF/sca/calculator.composite", "OSGI-INF/sca/bundle.composite"}, {"calculator/dosgi/sca/META-INF/sca-contribution.xml", "META-INF/sca-contribution.xml"}, {"calculator/dosgi/bundle.componentType", "OSGI-INF/sca/calculator.dosgi/bundle.componentType"}, {"calculator/dosgi/operations/bundle.componentType", "OSGI-INF/sca/calculator.dosgi.operations/bundle.componentType"}, }); } /** * Create the OSGi bundle for calculator operations * @return * @throws IOException */ static URL generateOperationsBundle() throws IOException { return createBundle("target/test-classes/operations-bundle.jar", "calculator/dosgi/operations/META-INF/MANIFEST.MF", new String[][] { {"calculator/dosgi/operations/OSGI-INF/add-component.xml", "OSGI-INF/add-component.xml"}, {"calculator/dosgi/operations/OSGI-INF/subtract-component.xml", "OSGI-INF/subtract-component.xml"}, {"calculator/dosgi/operations/OSGI-INF/multiply-component.xml", "OSGI-INF/multiply-component.xml"}, {"calculator/dosgi/operations/OSGI-INF/divide-component.xml", "OSGI-INF/divide-component.xml"}, /* {"calculator/dosgi/operations/bundle.componentType", "OSGI-INF/sca/bundle.componentType"}, {"calculator/dosgi/operations/operations.composite", "OSGI-INF/sca/bundle.composite"}, {"calculator/dosgi/operations/META-INF/sca-contribution.xml", "META-INF/sca-contribution.xml"} */ }, OperationsActivator.class, AddService.class, AddServiceImpl.class, SubtractService.class, SubtractServiceImpl.class, MultiplyService.class, MultiplyServiceImpl.class, DivideService.class, DivideServiceImpl.class); } /** * Returns a string representation of the given bundle. * * @param b * @param verbose * @return */ public static String bundleStatus(Bundle bundle, boolean verbose) { StringBuffer sb = new StringBuffer(); sb.append(bundle.getBundleId()).append(" ").append(bundle.getSymbolicName()); int s = bundle.getState(); if ((s & Bundle.UNINSTALLED) != 0) { sb.append(" UNINSTALLED"); } if ((s & Bundle.INSTALLED) != 0) { sb.append(" INSTALLED"); } if ((s & Bundle.RESOLVED) != 0) { sb.append(" RESOLVED"); } if ((s & Bundle.STARTING) != 0) { sb.append(" STARTING"); } if ((s & Bundle.STOPPING) != 0) { sb.append(" STOPPING"); } if ((s & Bundle.ACTIVE) != 0) { sb.append(" ACTIVE"); } if (verbose) { sb.append(" ").append(bundle.getLocation()); sb.append(" ").append(bundle.getHeaders()); } return sb.toString(); } /** * A utility to cast the object to the given interface. If the class for the object * is loaded by a different classloader, a proxy will be created. * * @param <T> * @param obj * @param cls * @return */ public static <T> T cast(Object obj, Class<T> cls) { if (cls.isInstance(obj)) { return cls.cast(obj); } else { return cls.cast(Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[] {cls}, new InvocationHandlerImpl(obj))); } } }