| /* |
| * Copyright (c) 2020 Kentyou. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Kentyou - initial API and implementation |
| */ |
| package org.eclipse.sensinact.gateway.test; |
| |
| import org.eclipse.sensinact.gateway.util.IOUtils; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| import java.util.jar.JarOutputStream; |
| import java.util.jar.Manifest; |
| import java.util.zip.ZipEntry; |
| |
| public abstract class MidOSGiTest implements BundleContextProvider { |
| /** |
| * Completes the initialization configuration Map |
| * |
| * @param configuration the initialization |
| * configuration Map to be completed |
| */ |
| protected abstract void doInit(Map configuration); |
| |
| /** |
| * Returns true if the file whose name is passed as |
| * parameter is excluded from this MidOSGiTest's classpath; |
| * returns false otherwise |
| * |
| * @param name the name of the file to exclude or not |
| * @return true if the specified file is excluded from |
| * this MidOSGiTest's classpath; false otherwise |
| */ |
| protected abstract boolean isExcluded(String name); |
| |
| static { |
| try { |
| java.security.Policy.setPolicy(new TestPolicy()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| protected static final String AUTO_PROCESSOR = "org.apache.felix.main.AutoProcessor"; |
| protected static final String FELIX_FRAMEWORK = "org.osgi.framework.launch.Framework"; |
| protected static final String FELIX_FRAMEWORK_FACTORY = "org.apache.felix.framework.FrameworkFactory"; |
| protected static final String BUNDLE = "org.osgi.framework.Bundle"; |
| protected static final String BUNDLE_GET_CONTEXT = "getBundleContext"; |
| protected static final String BUNDLE_STATE = "getState"; |
| protected static final String FRAMEWORK_INIT = "init"; |
| protected static final String FRAMEWORK_START = "start"; |
| protected static final String FRAMEWORK_STOP = "stop"; |
| protected static final String FRAMEWORK_FACTORY_INIT_FRAMEWORK = "newFramework"; |
| protected static final Class<?>[] FRAMEWORK_FACTORY_INIT_FRAMEWORK_TYPES = new Class<?>[]{Map.class}; |
| protected static final String FRAMEWORK_WAIT_FOR_STOP = "waitForStop"; |
| protected static final Class<?>[] FRAMEWORK_WAIT_FOR_STOP_TYPES = new Class<?>[]{long.class}; |
| |
| protected static final long WAIT_FOR_STOP_TIMEOUT = 60000; |
| |
| protected final FilterOSGiClassLoader classloader; |
| protected final ClassLoader current; |
| protected Object felix; |
| protected Class<?> frameworkClass; |
| protected BundleContext context; |
| protected String policy = null; |
| |
| protected File felixDir = null; |
| protected File cacheDir = null; |
| protected File bundleDir = null; |
| protected File loadDir = null; |
| |
| public MidOSGiTest() throws Exception { |
| String directoryName = "target/felix"; |
| felixDir = new File(directoryName); |
| bundleDir = new File(felixDir, "bundle"); |
| if (!bundleDir.exists()) { |
| bundleDir.mkdir(); |
| } |
| loadDir = new File(felixDir, "load"); |
| if (!loadDir.exists()) { |
| loadDir.mkdir(); |
| } |
| cacheDir = new File(felixDir, "felix-cache"); |
| if (!cacheDir.exists()) { |
| cacheDir.mkdir(); |
| } |
| File confDir = new File(felixDir, "conf"); |
| if (!confDir.exists()) { |
| confDir.mkdir(); |
| } |
| this.current = Thread.currentThread().getContextClassLoader(); |
| FilesEnumerator enumerator = new FilesEnumerator(bundleDir); |
| |
| List<URL> urls = new ArrayList<URL>(); |
| |
| while (enumerator.hasMoreElements()) { |
| File file = enumerator.nextElement(); |
| if (file.getName().endsWith(".jar") && !this.isExcluded(file.getName())) { |
| urls.add(file.toURI().toURL()); |
| } |
| } |
| this.classloader = new FilterOSGiClassLoader(current, this, urls.toArray(new URL[0])); |
| } |
| |
| /** |
| * Creates a new <code>JarInputStream</code> and reads the optional |
| * manifest. If a manifest is present and verify is true, also attempts |
| * to verify the signatures if the JarInputStream is signed. |
| * |
| * @param manifestFile |
| * @param destDirectory |
| * @param sourceDirectories |
| * @throws IOException if an I/O error has occurred |
| */ |
| public void createDynamicBundle(File manifestFile, File destDirectory, File... sourceDirectories) throws IOException { |
| if (!destDirectory.exists()) { |
| return; |
| } |
| File dynamicBundleFile = new File(destDirectory, "dynamicBundle.jar"); |
| //Assume all necessary imports and variables are declared |
| FileOutputStream fOut = new FileOutputStream(dynamicBundleFile); |
| Manifest manifest = null; |
| try { |
| manifest = new Manifest(new FileInputStream(manifestFile)); |
| } catch(FileNotFoundException e) { |
| e.printStackTrace(); |
| try { |
| fOut.close(); |
| }catch(IOException ex) {} |
| return; |
| } |
| // Create a buffer for reading the files |
| byte[] buf = new byte[1024]; |
| |
| JarOutputStream jarOut = new JarOutputStream(fOut, manifest); |
| FileInputStream reader = null; |
| int index = 0; |
| int length = sourceDirectories == null ? 0 : sourceDirectories.length; |
| |
| for (; index < length; index++) { |
| File sourceDirectory = sourceDirectories[index]; |
| FilesEnumerator enumerator = new FilesEnumerator(sourceDirectory); |
| while (enumerator.hasMoreElements()) { |
| File file = enumerator.nextElement(); |
| if (file.isFile()) { |
| String entryName = null; |
| if (sourceDirectory.isDirectory()) { |
| entryName = file.getAbsolutePath().substring(sourceDirectory.getAbsolutePath().length() + 1); |
| |
| } else { |
| entryName = file.getName(); |
| } |
| entryName = entryName.replace(File.separatorChar, '/'); |
| jarOut.putNextEntry(new ZipEntry(entryName)); |
| |
| reader = new FileInputStream(file); |
| int len; |
| while ((len = reader.read(buf)) > 0) { |
| jarOut.write(buf, 0, len); |
| } |
| // Complete the entry |
| reader.close(); |
| jarOut.closeEntry(); |
| } |
| } |
| } |
| jarOut.close(); |
| this.classloader.addFiltered(new File(destDirectory, "dynamicBundle.jar").toURI().toURL()); |
| } |
| |
| /** |
| * @param url |
| */ |
| public Bundle installDynamicBundle(final URL url) { |
| try { |
| int length = loadDir.listFiles().length; |
| String fileName = String.format("test%s.jar",length); |
| File testFile = new File(loadDir, fileName); |
| URL testFileURL = testFile.toURI().toURL(); |
| FileOutputStream output = new FileOutputStream(new File(loadDir,fileName)); |
| byte[] testJar = IOUtils.read(url.openStream(), true); |
| IOUtils.write(testJar, output); |
| this.classloader.addFiltered(testFileURL); |
| return getBundleContext().installBundle(testFileURL.toExternalForm()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see BundleContextProvider#getBundleContext() |
| */ |
| @Override |
| public BundleContext getBundleContext() { |
| return this.context; |
| } |
| |
| /** |
| * @throws Exception |
| */ |
| @After |
| public void tearDown() throws Exception { |
| long start = System.currentTimeMillis(); |
| frameworkClass.getDeclaredMethod(FRAMEWORK_STOP).invoke(felix); |
| frameworkClass.getDeclaredMethod(FRAMEWORK_WAIT_FOR_STOP, FRAMEWORK_WAIT_FOR_STOP_TYPES).invoke(felix, new Object[]{WAIT_FOR_STOP_TIMEOUT}); |
| felix = null; |
| File prefix = new File("target/felix"); |
| Stack<String> stack = new Stack<String>(); |
| for(String s:prefix.list()) { |
| if(s.equals("bundle") || s.equals("conf")) { |
| continue; |
| } |
| stack.push(s); |
| } |
| while(true) { |
| String current = stack.peek(); |
| File f = new File(prefix,current); |
| String[] list = f.list(); |
| if(list!=null && list.length>0) { |
| for(String s:list) { |
| stack.push(current + "/" + s); |
| } |
| } else { |
| f.delete(); |
| stack.pop(); |
| } |
| if(stack.isEmpty()){ |
| break; |
| } |
| } |
| new File(prefix,"load").mkdir(); |
| System.out.println("Felix stopped [" + (System.currentTimeMillis() - start) + "]"); |
| //let the environment finalize |
| Thread.sleep(5000); |
| } |
| |
| /** |
| * @throws Exception |
| */ |
| @SuppressWarnings({"unchecked", "rawtypes", "serial"}) |
| @Before |
| public void init() throws Exception { |
| final Map configuration = new HashMap(); |
| if (System.getSecurityManager() == null) { |
| configuration.put("org.osgi.framework.security", "osgi"); |
| } |
| configuration.put("felix.cache.rootdir", felixDir.getPath()); |
| configuration.put("org.osgi.framework.storage", "felix-cache"); |
| configuration.put("org.osgi.framework.bootdelegation", "*"); |
| configuration.put("org.osgi.framework.system.packages.extra", "org.eclipse.sensinact.gateway.test,org.slf4j," + "com.sun.net.httpserver," + "javax.activation," + "javax.net.ssl," + "javax.xml.parsers," + "javax.imageio," + "javax.management," + "javax.naming," + "javax.sql," + "javax.swing," + "javax.swing.border," + "javax.swing.event," + "javax.management.modelmbean," + "javax.management.remote," + "javax.security.auth," + "javax.security.cert," + "org.w3c.dom," + "org.xml.sax," + "org.xml.sax.helpers," + "sun.misc," + "javax.mail," + "javax.mail.internet," + "sun.security.action"); |
| configuration.put("org.osgi.framework.storage.clean", "onFirstInit"); |
| configuration.put("felix.auto.deploy.action", "install"); |
| configuration.put("felix.log.level", "4"); |
| configuration.put("felix.fileinstall.log.level", "4"); |
| configuration.put("felix.fileinstall.dir", loadDir.getPath()); |
| configuration.put("felix.fileinstall.noInitialDelay", "true"); |
| configuration.put("felix.fileinstall.poll", "1000"); |
| configuration.put("felix.fileinstall.bundles.new.start", "true"); |
| configuration.put("org.osgi.framework.startlevel.beginning", "5"); |
| configuration.put("felix.startlevel.bundle", "5"); |
| configuration.put("org.eclipse.sensinact.simulated.gui.enabled", "false"); |
| configuration.put("felix.bootdelegation.classloaders", new HashMap() { |
| public Object get(Object key) { |
| if (Bundle.class.isAssignableFrom(key.getClass())) { |
| if (MidOSGiTest.this.isExcluded(((Bundle) key).getSymbolicName())) { |
| return null; |
| } |
| return classloader; |
| } |
| return super.get(key); |
| } |
| }); |
| configuration.put("org.eclipse.sensinact.gateway.test.codeBase", getAllowedCodeBase()); |
| this.doInit(configuration); |
| |
| final Class<?> factoryClass = classloader.loadClass(FELIX_FRAMEWORK_FACTORY); |
| final Class<?> bundleClass = classloader.loadClass(BUNDLE); |
| final Class<?> autoProcessorClass = classloader.loadClass(AUTO_PROCESSOR); |
| |
| frameworkClass = classloader.loadClass(FELIX_FRAMEWORK); |
| |
| File manifestFile = new File("./target/generated-test-sources/META-INF/MANIFEST.MF"); |
| this.createDynamicBundle(manifestFile, bundleDir, new File[]{new File("./target/classes")}); |
| |
| Object factory = factoryClass.newInstance(); |
| |
| felix = factoryClass.getDeclaredMethod(FRAMEWORK_FACTORY_INIT_FRAMEWORK, FRAMEWORK_FACTORY_INIT_FRAMEWORK_TYPES).invoke(factory, new Object[]{configuration}); |
| frameworkClass.getDeclaredMethod(FRAMEWORK_INIT).invoke(felix); |
| |
| context = (BundleContext) bundleClass.getDeclaredMethod(BUNDLE_GET_CONTEXT).invoke(felix); |
| autoProcessorClass.getDeclaredMethod("process", new Class<?>[]{Map.class, BundleContext.class}).invoke(null, new Object[]{configuration, context}); |
| frameworkClass.getDeclaredMethod(FRAMEWORK_START).invoke(felix); |
| |
| Assert.assertTrue(bundleClass == Bundle.class); |
| Assert.assertTrue(((Integer) bundleClass.getDeclaredMethod(BUNDLE_STATE).invoke(felix)) == Bundle.ACTIVE); |
| //let the environment initialize |
| Thread.sleep(5000); |
| } |
| |
| /** |
| * @return |
| */ |
| protected String getAllowedCodeBase() { |
| String m2 = this.getMavenRepository(); |
| String path = "file:".concat(m2.concat("/*")); |
| path = path.concat(",http://felix.extensions:9/"); |
| |
| String testPath = new File("target/test-classes").getAbsolutePath(); |
| path = path.concat(String.format(",file:%s%s", testPath.startsWith("/") ? "" : "/", testPath)); |
| path = path.concat("/*"); |
| return path; |
| } |
| |
| /** |
| * @return |
| */ |
| protected String getMavenRepository() { |
| String m2 = System.getenv().get("M2_REPO"); |
| if (m2 == null) { |
| m2 = System.getProperty("user.home"); |
| m2 = m2.concat(".m2/repository"); |
| } |
| if (!m2.startsWith("/")) { |
| m2 = "/".concat(m2); |
| } |
| return m2; |
| } |
| } |