All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.technophobia.substeps.runner.SubstepsJMXClient Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright Technophobia Ltd 2012
 *
 *   This file is part of Substeps.
 *
 *    Substeps is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    Substeps is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with Substeps.  If not, see .
 */

package com.technophobia.substeps.runner;

import com.technophobia.substeps.execution.ExecutionNodeResult;
import com.technophobia.substeps.execution.node.RootNode;
import com.technophobia.substeps.jmx.SubstepsServerMBean;
import com.technophobia.substeps.model.exception.SubstepsConfigurationException;
import com.typesafe.config.Config;
import org.apache.maven.plugin.MojoExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.substeps.execution.ExecutionNodeResultNotificationHandler;

import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.ServiceUnavailableException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
import java.util.Map;

/**
 * @author ian
 */
public class SubstepsJMXClient implements SubstepsRunner, NotificationListener {

    private static Logger log = LoggerFactory.getLogger(SubstepsJMXClient.class);
    private static final int JMX_CLIENT_TIMEOUT_SECS = 10;
    private SubstepsServerMBean mbean;

    private JMXConnector cntor = null;
    private MBeanServerConnection mbsc = null;

    private ExecutionNodeResultNotificationHandler notificiationHandler = null;

    public void setNotificiationHandler(ExecutionNodeResultNotificationHandler notificiationHandler) {
        this.notificiationHandler = notificiationHandler;
    }

    public byte[] runAsBytes() {

        return this.mbean.runAsBytes();
    }


    public void init(final int portNumber) throws MojoExecutionException {

        final String url = "service:jmx:rmi:///jndi/rmi://:" + portNumber + "/jmxrmi";

        // The address of the connector server

        try {
            final JMXServiceURL serviceURL = new JMXServiceURL(url);

            final Map environment = null;

            // Create the JMXCconnectorServer
            this.cntor = getConnector(serviceURL, environment);

            if (this.cntor != null){

                // Obtain a "stub" for the remote MBeanServer
                mbsc = this.cntor.getMBeanServerConnection();

            final ObjectName objectName = new ObjectName(SubstepsServerMBean.SUBSTEPS_JMX_MBEAN_NAME);
            this.mbean = MBeanServerInvocationHandler.newProxyInstance(mbsc, objectName, SubstepsServerMBean.class,
                    false);

            addNotificationListener(objectName);

      }

        } catch (final IOException e) {

            throw new MojoExecutionException("Failed to connect to substeps server", e);

        } catch (final MalformedObjectNameException e) {

            throw new MojoExecutionException("Failed to connect to substeps server", e);
        }
    }

    protected void addNotificationListener(ObjectName objectName) throws IOException {

        boolean added = false;
        int tries = 0;

        while (!added || tries < 3) {

            try {
                tries++;
                mbsc.addNotificationListener(objectName, this, null, null);
                added = true;
            } catch (InstanceNotFoundException e) {
                log.debug("adding notification InstanceNotFoundException", e);
            }
        }
    }


    protected JMXConnector getConnector(JMXServiceURL serviceURL, Map environment) throws IOException {
        // Create the JMXCconnectorServer

        JMXConnector connector = null;

        long timeout = System.currentTimeMillis() + (JMX_CLIENT_TIMEOUT_SECS * 1000);

        while (connector == null && System.currentTimeMillis() < timeout) {

            try {
                log.debug("trying to connect to: " + serviceURL);
                connector = JMXConnectorFactory.connect(serviceURL, environment);

                log.debug("connected");
            } catch (IOException e) {

                log.debug("e.getCause(): " + e.getCause().getClass());

                if (!(e.getCause() instanceof ServiceUnavailableException)) {
                    log.error("not a ServiceUnavailableException", e);
                    break;
                }

                log.debug("ConnectException sleeping..");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e1) {
                    log.debug("InterruptedException:", e1);
                }
            }
        }
        if (connector == null) {

            log.error("failed to get the JMXConnector in time");
        }

        return connector;
    }

    public byte[] prepareExecutionConfigAsBytes(final String theConfig) {

        try {
            return this.mbean.prepareExecutionConfigAsBytes(theConfig);
        }
        catch (SubstepsConfigurationException ex){
            log.error("Failed to init tests", ex);
            return null;
        }
    }

    @Override
    public RootNode prepareExecutionConfig(Config theConfig) {

        try {
            final ObjectName objectName = new ObjectName(SubstepsServerMBean.SUBSTEPS_JMX_MBEAN_NAME);
            Object  rootNode = mbsc.invoke(objectName,
                    "prepareExecutionConfig",
                    new Object[]{theConfig},
                    new String[]{SubstepsExecutionConfig.class.getName()});
            return (RootNode)rootNode;

        } catch (Exception e) {
            log.error("Exception thrown preparing exectionConfig", e);
        }
        return null;
    }

  @Override
    public List getFailures() {

        return this.mbean.getFailures();
    }

    @Override
    public RootNode run() {

        return this.mbean.run();
    }

    @Override
    public void addNotifier(final IExecutionListener listener) {

        //
        this.mbean.addNotifier(listener);
    }


    public boolean shutdown() {

        boolean successfulShutdown = false;
        try {
            if (this.mbean != null) {
                this.mbean.shutdown();
            }
                successfulShutdown = true;

        } catch (final RuntimeException re) {

            log.debug("Unable to connect to server to shutdown, it may have already closed", re);

        }
        return successfulShutdown;
    }

    @Override
    public void handleNotification(Notification notification, Object handback) {


        if (notification.getType().compareTo("ExNode") == 0) {
            byte[] rawBytes = (byte[]) notification.getUserData();

            ExecutionNodeResult result = getFromBytes(rawBytes);

            log.trace("received a JMX event msg: " + notification.getMessage() +
                    " seq: " + notification.getSequenceNumber() + " exec result node id: " + (result != null ? result.getExecutionNodeId() : "no result"));

                    notificiationHandler.handleNotification(result);
        } else if (notification.getType().compareTo("ExecConfigComplete") == 0) {
                    notificiationHandler.handleCompleteMessage();
        } else {
            log.error("unknown notificaion type");
        }
    }

    protected static  T getFromBytes(byte[] bytes) {
        T rn = null;
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois;
        try {
            ois = new ObjectInputStream(bis);
            rn = (T) ois.readObject();

        } catch (IOException e) {
            log.error("IOException reading object input stream", e);
        } catch (ClassNotFoundException e) {

            log.error("ClassNotFoundException", e);

        } finally {
            try {
                bis.close();
            } catch (IOException e) {
                log.error("IOException closing object input stream", e);
            }
        }
        return rn;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy