-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. 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
- */
-package org.opendaylight.controller.distribution.test;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.Socket;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.JarFile;
-
-import javax.management.JMX;
-import javax.management.MBeanServerConnection;
-import javax.management.Notification;
-import javax.management.NotificationListener;
-import javax.management.ObjectName;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.ow2.chameleon.management.beans.BundleMXBean;
-import org.ow2.chameleon.management.beans.OSGiPlatformMXBean;
-
-/**
- * This integration test assumes a running local controller. The test does the
- * following:
- * 1) Wait for HTTP, JMX and OF ports to open
- * 2) Establishes a JMX connection and registers a bundle notification listener
- * 3) Waits till all bundles reach expected states or the timeout period elapses
- */
-public class SanityIT {
- private static final int OF_PORT = Integer.getInteger("ctrl.of.port", 6633);
- private static final int HTTP_PORT = Integer.getInteger("ctrl.http.port", 8080);
- private static final int JMX_PORT = Integer.getInteger("ctrl.jmx.port", 1088);
- private static final int RMI_PORT = Integer.getInteger("ctrl.rmi.port", 1099);
- private static final String CTRL_HOST = System.getProperty("ctrl.host", "127.0.0.1");
- private static final String CTRL_HOME = System.getProperty("ctrl.home");
- private static final long TIMEOUT_MILLIS =
- Integer.getInteger("ctrl.start.timeout", 3*60) * 1000L;
- private static final String JMX_URL =
- "service:jmx:rmi:///jndi/rmi://" + CTRL_HOST + ":" + JMX_PORT + "/jmxrmi";
-
- private static final Set<String> bundles =
- Collections.synchronizedSet(new HashSet<String>());
- private static final Set<String> fragments =
- Collections.synchronizedSet(new HashSet<String>());
-
- @BeforeClass
- public static void loadBundles() throws IOException {
- log(" ctrl.home: " + CTRL_HOME);
- log(" ctrl.host: " + CTRL_HOST);
- log("ctrl.start.timeout: " + TIMEOUT_MILLIS);
- log(" jmx.url: " + JMX_URL);
-
- Assert.assertNotNull(CTRL_HOME);
- File ctrlHome = new File(CTRL_HOME);
- Assert.assertTrue(ctrlHome.exists() && ctrlHome.isDirectory());
- File configDir = new File(ctrlHome, "configuration");
- Assert.assertTrue(configDir.exists() && configDir.isDirectory());
- File configIni = new File(configDir, "config.ini");
- Assert.assertTrue(configIni.exists());
- Properties config = new Properties();
- config.load(new FileInputStream(configIni));
- processBundles(configDir, config.getProperty("osgi.bundles"));
- processBundles(new File(ctrlHome, "plugins"));
- log("Bundles found in installation: " + bundles.size());
- log("Fragments found in installation: " + fragments.size());
- }
-
-@Test
- public void sanityTest() throws Exception {
- // wait for http, jmx & of ports to open
- long startTime = System.currentTimeMillis();
- waitForListening(OF_PORT, false, startTime);
- waitForListening(JMX_PORT, false, startTime);
- waitForListening(HTTP_PORT, false, startTime);
-
- // open jmx connection
- JMXServiceURL serviceUrl = new JMXServiceURL(JMX_URL);
- JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceUrl, null);
- final MBeanServerConnection conn = jmxConnector.getMBeanServerConnection();
-
- ObjectName fmkName = new ObjectName("org.ow2.chameleon:type=framework");
- OSGiPlatformMXBean fmkBean= JMX.newMBeanProxy(conn, fmkName,
- OSGiPlatformMXBean.class, true);
- conn.addNotificationListener(fmkName, new NotificationListener() {
-
- @Override
- public void handleNotification(Notification n, Object handback) {
- try {
- //log("Notification: source: " + n.getSource());
- ObjectName bundleName = new ObjectName(
- "org.ow2.chameleon:type=bundle,id=" + n.getUserData());
- BundleMXBean bundleBean = JMX.newMXBeanProxy(conn,
- bundleName, BundleMXBean.class, true);
- log("Bundle state change: " + bundleBean.getSymbolicName() +
- " : " + stateToString(bundleBean.getState()));
- handleBundleEvent(bundleBean);
- // if its a system bundle, notify the main thread
- if (bundleBean.getBundleId() == 0 &&
- bundleBean.getState() == Bundle.ACTIVE)
- {
- synchronized(SanityIT.this) {
- SanityIT.this.notify();
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }, null, null);
-
- if (checkAllBundles(conn) > 0) {
- log("Waiting for system bundle to start... (times out in: " + TIMEOUT_MILLIS + "ms)");
- long timeElapsed = System.currentTimeMillis() - startTime;
- synchronized(this) {
- this.wait(TIMEOUT_MILLIS - timeElapsed);
- }
- log("System bundle started. Revalidating bundle states for all bundles...");
-
- // Sometimes, the system bundle starts earlier than other bundles(?). The
- // following loop will cover that case.
- do {
- Thread.sleep(2000); // 2s seems appropriate given the default timeout
- if (checkAllBundles(conn) == 0) {
- break;
- }
- } while(System.currentTimeMillis() - startTime < TIMEOUT_MILLIS);
- }
- try {
- jmxConnector.close();
- } catch (Exception ignore) {
- // dont want the tests to fail in case we can't close jmx connection
- ignore.printStackTrace();
- }
- if (bundles.size() + fragments.size() == 0) {
- log("All bundles have reached expected state");
- } else {
- log("The following bundles did not reach expected state: ");
- for (String s : bundles) log("Bundle: " + s);
- for (String s : fragments) log("Fragment: " + s);
-
- }
- Assert.assertTrue(bundles.size() == 0 && fragments.size() == 0);
- }
-
- private static int checkAllBundles(MBeanServerConnection conn) throws Exception {
- ObjectName allBundlesName = new ObjectName("org.ow2.chameleon:*");
- Set<ObjectName> bundleNames = conn.queryNames(allBundlesName, null);
- for (ObjectName bundleName : bundleNames) {
- if ("bundle".equals(bundleName.getKeyProperty("type"))) {
- BundleMXBean bundleBean = JMX.newMBeanProxy(conn, bundleName,
- BundleMXBean.class, true);
- handleBundleEvent(bundleBean);
- }
- }
- int remaining = bundles.size() + fragments.size();
- if (remaining > 0) {
- log("Bundles not in expected states: " + remaining + " Waiting...");
- }
- return remaining;
- }
-
- private synchronized static void handleBundleEvent(BundleMXBean bundleBean) {
- String name = bundleBean.getSymbolicName();
- int state = bundleBean.getState();
- // BUG in BundleMXBeanImpl - can't get bundle headers :(
- // String fragHost = bundleBean.getBundleHeaders().get(Constants.FRAGMENT_HOST);
- if (bundles.contains(name) && (state == Bundle.RESOLVED || state == Bundle.ACTIVE)) {
- bundles.remove(name);
- } else if (fragments.contains(name) && state == Bundle.RESOLVED) {
- fragments.remove(name);
- }
- //log("Bundles to be started: " + bundles.size());
- }
-
-
- // reference\:file\:../lib/org.apache.felix.fileinstall-3.1.6.jar@1:start,\
- private static void processBundles(File dir, String property) {
- Assert.assertTrue(property == null || property.length() > 0);
- for(String s : property.split(",")) {
- int idx = s.indexOf("@");
- s = s.substring(15, (idx == -1 ? s.length() : idx));
- if (!s.endsWith(".jar")) s = s + ".jar";
- processJar(new File(dir, s));
- }
- }
-
- public static void processBundles(File dir) throws IOException {
- if (!dir.exists()) throw new FileNotFoundException("Cannot find dir:" + dir);
- dir.listFiles(new FileFilter() {
-
- @Override
- public boolean accept(File file) {
- //p("---- " + file);
- if (file.getName().endsWith(".jar")) {
- return processJar(file);
- }
- return false;
- }
- });
- }
-
- public static boolean processJar(File file) {
- try {
- //log("----" + file);
- JarFile jar = new JarFile(file, false);
- Attributes jarAttributes = jar.getManifest().getMainAttributes();
- String bundleName = jarAttributes.getValue(Constants.BUNDLE_SYMBOLICNAME);
- String fragHost = jarAttributes.getValue(Constants.FRAGMENT_HOST);
- if (bundleName == null) {
- log("Found a non bundle file:" + file);
- return false;
- } else {
- int idx = bundleName.indexOf(';');
- if (idx > -1) {
- bundleName = bundleName.substring(0, idx);
- }
- }
-
- if (fragHost == null) {
- if (!bundles.add(bundleName)) {
- throw new IllegalStateException(
- "Found duplicate bundles with same symbolic name: "
- + bundleName);
- }
- } else {
- // fragments attaching to framework can't be detected
- if (fragHost.contains("extension:=\"framework\"")) return false;
- if (!fragments.add(bundleName)) {
- throw new IllegalStateException(
- "Found duplicate fragments with same symbolic name: "
- + bundleName);
- }
- }
- } catch (IOException e) {
- throw new IllegalStateException("Error processing jar: " + file , e);
- }
- return true;
- }
-
- public static long waitForListening(int port, boolean isHTTP, long beginTime)
- throws InterruptedException {
- long timeElapsedMillis = System.currentTimeMillis() - beginTime;
- long sleepTimeMillis = 500L; // 0.5 secs
-
- while (timeElapsedMillis < TIMEOUT_MILLIS) {
- long timeRemaining = TIMEOUT_MILLIS - timeElapsedMillis;
- sleepTimeMillis *= 2; // exponential backoff
- long toSleep = (sleepTimeMillis > timeRemaining)
- ? timeRemaining : sleepTimeMillis;
- Thread.sleep(toSleep);
- timeElapsedMillis = System.currentTimeMillis() - beginTime;
- if (isHTTP ? connectHTTP(port) : connectTCP(port)) {
- log("Port is open: " + port);
- return timeElapsedMillis;
- }
- }
- throw new IllegalStateException("Timeout waiting for port: " + port);
- }
-
- private static void log(String msg) {
- System.out.format("[SanityIT] [%s] %s %s", new Date().toString(), msg,
- System.lineSeparator());
- }
-
- public static boolean connectTCP(int port) {
- String host = CTRL_HOST.length() == 0 ? null : CTRL_HOST;
- try {
- Socket sock = new Socket(host, port);
- sock.getPort();
- try {
- sock.close();
- } catch (IOException ignore) {
- // Can't close socket. Ingore and let downstream validate health
- }
- return true;
- } catch (IOException ioe) {
- return false;
- }
- }
-
- public static boolean connectHTTP(int port) {
- String host = CTRL_HOST.length() == 0 ? "localhost" : CTRL_HOST;
- try {
- URL url = new URL("http", host, port, "/");
- HttpURLConnection con;
- con = (HttpURLConnection) url.openConnection();
- return (con.getResponseCode() > 0);
- } catch (IOException e) {
- return false;
- }
- }
-
- private String stateToString(int state) {
- switch (state) {
- case Bundle.ACTIVE: return "ACTIVE";
- case Bundle.INSTALLED: return "INSTALLED";
- case Bundle.RESOLVED: return "RESOLVED";
- case Bundle.UNINSTALLED: return "UNINSTALLED";
- case Bundle.STARTING: return "STARTING";
- case Bundle.STOPPING: return "STOPPING";
- default: return "UNKNOWN: " + state;
- }
- }
-
-
-
-
-}