com.technophobia.substeps.jmx.SubstepsServer Maven / Gradle / Ivy
/*
* 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.jmx;
import com.technophobia.substeps.execution.ExecutionNodeResult;
import com.technophobia.substeps.execution.ExecutionResult;
import com.technophobia.substeps.execution.node.FeatureNode;
import com.technophobia.substeps.execution.node.IExecutionNode;
import com.technophobia.substeps.execution.node.RootNode;
import com.technophobia.substeps.runner.ExecutionNodeRunner;
import com.technophobia.substeps.runner.IExecutionListener;
import com.technophobia.substeps.runner.SubstepExecutionFailure;
import com.technophobia.substeps.runner.SubstepsExecutionConfig;
import com.typesafe.config.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.substeps.config.SubstepsConfigLoader;
import org.substeps.runner.NewSubstepsExecutionConfig;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @author ian
*/
public class SubstepsServer extends NotificationBroadcasterSupport implements SubstepsServerMBean, IExecutionListener {
private final Logger log = LoggerFactory.getLogger(SubstepsServer.class);
private ExecutionNodeRunner nodeRunner = null;
private final CountDownLatch shutdownSignal;
private long notificationSequenceNumber = 1;
public SubstepsServer(final CountDownLatch shutdownSignal) {
this.shutdownSignal = shutdownSignal;
}
// TODO - used ?
@Override
public byte[] prepareExecutionConfigAsBytes(SubstepsExecutionConfig theConfig) {
Config fallback =
ConfigFactory.load(ConfigParseOptions.defaults(), ConfigResolveOptions.noSystem().setAllowUnresolved(true));
Config masterConfig = NewSubstepsExecutionConfig.toConfig(theConfig).withFallback(fallback);
log.debug("MASTER CONFIG:\n" + SubstepsConfigLoader.render(masterConfig));
Config cfg = SubstepsConfigLoader.splitMasterConfig(masterConfig).get(0);
NewSubstepsExecutionConfig.setThreadLocalConfig(cfg);
log.debug("SPLIT CONFIG[0]:\n" + SubstepsConfigLoader.render(cfg));
return prepareExecutionConfigAsBytes(cfg);
}
private byte[] prepareExecutionConfigAsBytes(final Config theConfig) {
RootNode rtn;
try {
rtn = prepareExecutionConfig(theConfig);
log.debug("execution config prepared");
}
catch (Exception e) {
log.error("Error preparing ExecutionConfig", e);
List empty = Collections.emptyList();
String env = System.getProperty("environment", "localhost");
rtn = new RootNode("Substeps Test", empty, env, NewSubstepsExecutionConfig.getTags(theConfig), NewSubstepsExecutionConfig.getNonFatalTags(theConfig));
ExecutionNodeResult result = rtn.getResult();
result.setThrown(e);
result.setResult(ExecutionResult.PARSE_FAILURE);
}
return getBytes(rtn);
}
@Override
public byte[] prepareExecutionConfigAsBytes(final String configString) {
Config theConfig = ConfigFactory.parseString(configString);
return prepareExecutionConfigAsBytes(theConfig);
}
@Override
public byte[] prepareRemoteExecutionConfig(String mavenFallbackConfig, String featureFile, String scenarioName) {
Config mvnConfig = ConfigFactory.parseString(mavenFallbackConfig);
Config masterConfig = SubstepsConfigLoader.loadResolvedConfig(mvnConfig);
List configs = SubstepsConfigLoader.splitMasterConfig(masterConfig);
Config theConfig = configs.get(0);
if (scenarioName != null){
theConfig = theConfig.withValue("org.substeps.executionConfig.scenarioName", ConfigValueFactory.fromAnyRef(scenarioName));
}
if (featureFile != null){
theConfig = theConfig.withValue("org.substeps.executionConfig.featureFile", ConfigValueFactory.fromAnyRef(featureFile));
}
log.debug("prepareRemoteExecutionConfig with config:\n" + SubstepsConfigLoader.render(theConfig));
return prepareExecutionConfigAsBytes(theConfig);
}
private byte[] getBytes(Object rtn) {
byte[] rtnBytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(rtn);
rtnBytes = bos.toByteArray();
} catch (IOException e) {
log.error("IOException writing bytes", e);
} finally {
try {
bos.close();
} catch (IOException e) {
log.error("IOException closing output stream", e);
}
}
return rtnBytes;
}
@Override
public byte[] runAsBytes() {
RootNode rtn = run();
return getBytes(rtn);
}
@Override
public String getProjectSubstepsVersion() {
log.info("getProjectSubstepsVersion called");
return "SUBSTEPS_1.1";
}
@Override
public void shutdown() {
this.shutdownSignal.countDown();
}
@Override
public RootNode prepareExecutionConfig(Config theConfig) {
this.nodeRunner = new ExecutionNodeRunner();
return this.nodeRunner.prepareExecutionConfig(theConfig);
}
/*
* (non-Javadoc)
*
* @see
* com.technopobia.substeps.jmx.SubstepsMBean#prepareExecutionConfig(com
* .technophobia.substeps.runner.ExecutionConfig)
*/
// @Override
public RootNode prepareExecutionConfig(final SubstepsExecutionConfig theConfig) {
// TODO - synchronise around the init call ?
this.nodeRunner = new ExecutionNodeRunner();
Config masterConfig = NewSubstepsExecutionConfig.toConfig(theConfig);
log.debug("MASTER CONFIG:\n" + SubstepsConfigLoader.render(masterConfig));
Config cfg = SubstepsConfigLoader.splitMasterConfig(masterConfig).get(0);
NewSubstepsExecutionConfig.setThreadLocalConfig(cfg);
log.debug("SPLIT CONFIG[0]:\n" + SubstepsConfigLoader.render(cfg));
return this.nodeRunner.prepareExecutionConfig(cfg);
}
/*
* (non-Javadoc)
*
* @see com.technopobia.substeps.jmx.SubstepsMBean#run()
*/
@Override
public RootNode run() {
// attach a result listener to broadcast
this.nodeRunner.addNotifier(this);
final RootNode rootNode;
try {
rootNode = this.nodeRunner.run();
} finally {
// now send the final notification
final Notification n = new Notification("ExecConfigComplete", SubstepsServerMBean.SUBSTEPS_JMX_MBEAN_NAME, this.notificationSequenceNumber);
this.log.trace("sending complete notification sequence: " + this.notificationSequenceNumber);
sendNotification(n);
}
return rootNode;
}
private void doNotification(final IExecutionNode node) {
final Notification n = new Notification("ExNode", SubstepsServerMBean.SUBSTEPS_JMX_MBEAN_NAME, this.notificationSequenceNumber);
this.notificationSequenceNumber++;
n.setUserData(getBytes(node.getResult()));
log.trace("sending notification for node id: " + node.getId() + " sequence: "
+ this.notificationSequenceNumber);
sendNotification(n);
}
/*
* (non-Javadoc)
*
* @see
* com.technophobia.substeps.runner.INotifier#notifyNodeFailed(com.technophobia
* .substeps.execution.ExecutionNode, java.lang.Throwable)
*/
@Override
public void onNodeFailed(final IExecutionNode node, final Throwable cause) {
doNotification(node);
}
/*
* (non-Javadoc)
*
* @see
* com.technophobia.substeps.runner.INotifier#notifyNodeStarted(com.technophobia
* .substeps.execution.ExecutionNode)
*/
@Override
public void onNodeStarted(final IExecutionNode node) {
doNotification(node);
}
/*
* (non-Javadoc)
*
* @see com.technophobia.substeps.runner.INotifier#notifyNodeFinished(com.
* technophobia.substeps.execution.ExecutionNode)
*/
@Override
public void onNodeFinished(final IExecutionNode node) {
doNotification(node);
}
/*
* (non-Javadoc)
*
* @see
* com.technophobia.substeps.runner.INotifier#notifyNodeIgnored(com.technophobia
* .substeps.execution.ExecutionNode)
*/
@Override
public void onNodeIgnored(final IExecutionNode node) {
doNotification(node);
}
@Override
public List getFailures() {
return this.nodeRunner.getFailures();
}
@Override
public void addNotifier(final IExecutionListener notifier) {
this.nodeRunner.addNotifier(notifier);
}
}