Skip to content

Commit

Permalink
JENKINS-34334: java.io.NotSerializableException when plugin is executed
Browse files Browse the repository at this point in the history
  • Loading branch information
stolp committed May 29, 2016
1 parent 432ac9c commit 3175b5e
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 128 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -4,7 +4,7 @@
<groupId>hudson.plugins.klaros</groupId>
<artifactId>klaros-testmanagement</artifactId>
<packaging>hpi</packaging>
<version>1.8-SNAPSHOT</version>
<version>1.7.1-SNAPSHOT</version>
<name>Klaros-Testmanagement plugin</name>
<url>http://wiki.jenkins-ci.org/display/JENKINS/Klaros-Testmanagement+Plugin</url>
<description>Integrates Jenkins with Klaros-Testmanagement by publishing the test results of a Jenkins build to the Klaros-Testmanagement application.</description>
Expand Down
4 changes: 4 additions & 0 deletions src/changes/changes.xml
Expand Up @@ -6,6 +6,10 @@
</properties>
<body>

<release version="1.7.1" date="2016-04-19" description="Fix slave execution issues.">
<action dev="stolp" type="fix">JENKINS-34334: java.io.NotSerializableException when plugin is executed on a slave node</action>
</release>

<release version="1.7" date="2016-02-15" description="Fix variable expansion issues, extend list of supported formats.">
<action dev="stolp" type="add">JENKINS-32973: Extend the list of supported result file formats to the supported list of Klaros-Testmanagement 4.4</action>
<action dev="stolp" type="fix">JENKINS-32972: NullpointerException in KlarosTestResultPublisher:752</action>
Expand Down
184 changes: 145 additions & 39 deletions src/main/java/hudson/plugins/klaros/KlarosTestResultPublisher.java
Expand Up @@ -23,6 +23,7 @@
*/
package hudson.plugins.klaros;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
Expand All @@ -46,6 +47,7 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletException;
Expand Down Expand Up @@ -112,7 +114,7 @@ public class KlarosTestResultPublisher extends Recorder implements Serializable
DEFAULT_FORMATS.add(new ResultFormat("valgrind", "Valgrind"));
DEFAULT_FORMATS.add(new ResultFormat("xunitdotnet", "xUnit.net"));
}

/** The Klaros project id. */
private String config;

Expand Down Expand Up @@ -191,7 +193,7 @@ private ResultFormat[] loadFormats() {
final String strURL = buildServletURL(url) + "/supportedFormats";

GetMethod get = new GetMethod(strURL);
StringBuffer query = new StringBuffer();
StringBuilder query = new StringBuilder();
if (StringUtils.isNotEmpty(username)) {
query.append("username=").append(username).append("&password=").append(password);
}
Expand All @@ -206,7 +208,7 @@ private ResultFormat[] loadFormats() {
String[] ids = response.split("=.*\\R");
String[] names = response.split(".*=\\R");
ResultFormat[] formats = new ResultFormat[ids.length];
for (int i=0; i< ids.length; i++ ) {
for (int i = 0; i < ids.length; i++) {
formats[i] = new ResultFormat(ids[i], names[i]);
}
}
Expand Down Expand Up @@ -485,7 +487,7 @@ public String getKlarosUrl(final String sourceURL) {
if (sourceURL == null) {
// if only one URL is configured, "default URL" should mean that URL.
List<String> urls = descriptor().getUrls();
if (urls.size() >= 1) {
if (!urls.isEmpty()) {
result = urls.get(0);
}
return result;
Expand Down Expand Up @@ -537,9 +539,18 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,

try {
FileCallableImplementation exporter =
new FileCallableImplementation(build, listener);
new FileCallableImplementation(Hudson.getInstance().getRootUrl(), build
.getProject().getName(), build.getNumber(), build
.getEnvironment(listener), build.getBuildVariables(), listener);
exporter.setKlarosUrl(getKlarosUrl(url));
exporter.setResultSet(resultSet);
exporter.setConfig(config);
exporter.setIteration(iteration);
exporter.setSut(sut);
exporter.setEnv(env);
exporter.setUsername(username);
exporter.setPassword(password);
exporter.setCreateTestSuite(createTestSuite);
ws.act(exporter);

} catch (IOException e) {
Expand All @@ -548,6 +559,9 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
} catch (InterruptedException e) {
listener.getLogger().println("Failure to export test result(s).");
e.printStackTrace(listener.getLogger());
} catch (RuntimeException e) {
listener.getLogger().println("Failure to export test result(s).");
e.printStackTrace(listener.getLogger());
}

listener.getLogger().println("Test result(s) successfully exported.");
Expand Down Expand Up @@ -578,7 +592,7 @@ public String getUrl(final String sourceURL) {
// if only one URL is configured, "default URL" should mean that
// URL.
List<String> urls = descriptor().getUrls();
if (urls.size() >= 1) {
if (!urls.isEmpty()) {
result = urls.get(0);
}
return result;
Expand Down Expand Up @@ -625,23 +639,42 @@ private static String buildServletURL(final String applicationURL) {
/**
* The Class FileCallableImplementation.
*/
private final class FileCallableImplementation implements FileCallable<List<Integer>> {
private static class FileCallableImplementation implements FileCallable<List<Integer>>, Serializable {

private static final long serialVersionUID = 1560913900801548965L;

private final AbstractBuild<?, ?> build;
private final BuildListener listener;
final EnvVars environment;
final Map<String, String> buildVariables;

private String buildServerUrl;
private String buildJobId;
private String buildId;

private String klarosUrl;
private ResultSet resultSet;
private String config;
private String iteration;
private String env;
private String sut;
private String username;
private String password;
private boolean createTestSuite;

/**
* Instantiates a new file callable implementation.
*
* @param listener the build listener
*/
private FileCallableImplementation(final AbstractBuild<?, ?> build, final BuildListener listener) {

this.build = build;
private FileCallableImplementation(final String buildServerUrl, final String buildJobId,
final int buildNumber, final EnvVars environment, final Map<String, String> buildVariables,
final BuildListener listener) {

this.buildServerUrl = buildServerUrl;
this.buildJobId = buildJobId;
this.buildId = Integer.toString(buildNumber);
this.environment = environment;
this.buildVariables = buildVariables;
this.listener = listener;
}

Expand Down Expand Up @@ -681,25 +714,28 @@ public List<Integer> invoke(final File baseDir, final VirtualChannel channel) th
// Prepare HTTP PUT
for (String f : ds.getIncludedFiles()) {
final PutMethod put = new PutMethod(strURL);
final StringBuffer query =
new StringBuffer("config=").append(expandVariables(config, build));
final StringBuilder query =
new StringBuilder("config=").append(expandVariables(config, environment,
buildVariables));
if (StringUtils.isNotBlank(iteration)) {
query.append("&iteration=").append(expandVariables(iteration, build));
query.append("&iteration=").append(
expandVariables(iteration, environment, buildVariables));
}
query.append("&env=").append(expandVariables(env, build)).append("&sut=").append(
expandVariables(sut, build)).append("&type=").append(
expandVariables(resultSet.getFormat(), build));
query.append("&env=").append(expandVariables(env, environment, buildVariables)).append(
"&sut=").append(expandVariables(sut, environment, buildVariables)).append("&type=")
.append(expandVariables(resultSet.getFormat(), environment, buildVariables));
if (createTestSuite) {
query.append("&createTestSuiteResults=true");
}

query.append("&buildServerUrl=").append(Hudson.getInstance().getRootUrl());
query.append("&buildJobId=").append(build.getProject().getName());
query.append("&buildId=").append(build.getNumber());
query.append("&buildServerUrl=").append(buildServerUrl);
query.append("&buildJobId=").append(buildJobId);
query.append("&buildId=").append(buildId);

if (StringUtils.isNotBlank(username)) {
query.append("&username=").append(expandVariables(username, build)).append(
"&password=").append(expandVariables(password, build));
query.append("&username=").append(
expandVariables(username, environment, buildVariables)).append("&password=")
.append(expandVariables(password, environment, buildVariables));
}
put.setQueryString(query.toString());

Expand All @@ -714,8 +750,8 @@ public List<Integer> invoke(final File baseDir, final VirtualChannel channel) th
result = httpclient.executeMethod(put);

if (result != HttpServletResponse.SC_OK) {
StringBuffer msg =
new StringBuffer().append("Export of ").append(file.getName()).append(
StringBuilder msg =
new StringBuilder().append("Export of ").append(file.getName()).append(
" failed - Response status code: ").append(result).append(
" for request URL: ").append(strURL).append("?").append(query);
String response = new String(put.getResponseBody());
Expand All @@ -736,7 +772,7 @@ public List<Integer> invoke(final File baseDir, final VirtualChannel channel) th
}
}
} else {
listener.getLogger().println(url + ": unable to locate this Klaros URL");
listener.getLogger().println(klarosUrl + ": unable to locate this Klaros URL");
}
return results;
}
Expand All @@ -746,19 +782,21 @@ public List<Integer> invoke(final File baseDir, final VirtualChannel channel) th
*
* @param value the value
* @param environment the environment variables
* @param buildVariables the build variables
* @return the expanded string, if applicable
* @throws IOException Signals that an I/O exception has occurred.
* @throws InterruptedException Thrown when a thread is waiting, sleeping, or otherwise paused for a
* long time and another thread interrupts it using the interrupt method in class Thread.
*/
private String expandVariables(final String value, final AbstractBuild<?, ?> build)
throws IOException, InterruptedException {
private String expandVariables(final String value, final EnvVars environment,
final Map<String, String> buildVariables) throws IOException, InterruptedException {

String result = value;
if (result != null) {
for (Entry<String, String> entry : build.getEnvironment(listener).entrySet()) {
for (Entry<String, String> entry : environment.entrySet()) {
result = result.replaceAll("\\$\\{" + entry.getKey() + "\\}", entry.getValue());
}
for (Entry<String, String> entry : build.getBuildVariables().entrySet()) {
for (Entry<String, String> entry : buildVariables.entrySet()) {
result = result.replaceAll("\\$\\{" + entry.getKey() + "\\}", entry.getValue());
}
}
Expand All @@ -785,20 +823,88 @@ private void setKlarosUrl(final String value) {
klarosUrl = value;
}

/**
* Sets the Klaros project id.
*
* @param value the new project id
*/
private void setConfig(final String value) {

config = StringUtils.trim(value);
}

/**
* Sets the Klaros iteration id.
*
* @param iteration the new iteration id
*/
private void setIteration(final String iteration) {

this.iteration = iteration;
}

/**
* Sets the Klaros test environment id.
*
* @param value the new test environment id
*/
private void setEnv(final String value) {

env = StringUtils.trim(value);
}

/**
* Sets the username.
*
* @param value the new username
*/
private void setUsername(final String value) {

username = StringUtils.trim(value);
}

/**
* Sets the password.
*
* @param value the new password
*/
private void setPassword(final String value) {

password = StringUtils.trim(value);
}

/**
* Sets the Klaros system under test id.
*
* @param value the new system under test id
*/
private void setSut(final String value) {

sut = StringUtils.trim(value);
}

/**
* Sets the create test suite flag.
*
* @param createTestSuite the new create test suite flag
*/
private void setCreateTestSuite(boolean createTestSuite) {

this.createTestSuite = createTestSuite;
}
}

/**
* Descriptor for KlarosImportPublisher class. Used as a singleton. The class is
* marked as public so that it can be accessed from views.
*/
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {
public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> implements Serializable {

/** The Constant PROJECT_CONFIG_HTML. */
private static final String PROJECT_CONFIG_HTML = //
"/plugin/klaros-testmanagement/help-projectConfig.html";
private static final long serialVersionUID = 1L;

/** The Constant URL_NAME. */
private static final String PROJECT_CONFIG_HTML =
"/plugin/klaros-testmanagement/help-projectConfig.html";
private static final String URL_NAME = "url.name";

/** Global configuration information. */
Expand Down Expand Up @@ -894,7 +1000,7 @@ protected FormValidation check() throws IOException, ServletException {
}

if (!value.endsWith("/")) {
cooked += '/';
cooked += Character.toString('/');
}

FormValidation result = FormValidation.ok();
Expand Down Expand Up @@ -965,7 +1071,7 @@ public FormValidation doTestConnection(@QueryParameter final String url,
final String strURL = buildServletURL(url);

PutMethod put = new PutMethod(strURL);
StringBuffer query = new StringBuffer();
StringBuilder query = new StringBuilder();
if (username != null) {
query.append("username=").append(username).append("&password=").append(password).append(
"&type=").append("check");
Expand All @@ -975,7 +1081,7 @@ public FormValidation doTestConnection(@QueryParameter final String url,
RequestEntity entity = new StringRequestEntity("", "text/xml; charset=UTF-8", "UTF-8");
put.setRequestEntity(entity);
return putResultFile(put);
} catch (Exception e) {
} catch (RuntimeException e) {
return FormValidation.error(e.getMessage());
}
}
Expand All @@ -995,9 +1101,9 @@ private FormValidation putResultFile(PutMethod put) throws IOException, HttpExce
int result = client.executeMethod(put);
String response = "";
if (result != HttpServletResponse.SC_OK) {
StringBuffer msg = new StringBuffer();
StringBuilder msg = new StringBuilder();
response = new String(put.getResponseBody());
if (response != null && response.length() > 0) {
if (response.length() > 0) {
msg.append("Connection failed: ").append(response);
System.out.println(msg.toString());
}
Expand Down

0 comments on commit 3175b5e

Please sign in to comment.