diff --git a/src/main/java/org/jenkinsci/plugins/durabletask/BourneShellScript.java b/src/main/java/org/jenkinsci/plugins/durabletask/BourneShellScript.java index bed07841..162c4e23 100644 --- a/src/main/java/org/jenkinsci/plugins/durabletask/BourneShellScript.java +++ b/src/main/java/org/jenkinsci/plugins/durabletask/BourneShellScript.java @@ -43,6 +43,7 @@ import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import org.kohsuke.stapler.DataBoundConstructor; +import org.apache.commons.io.IOUtils; /** * Runs a Bourne shell script on a Unix node using {@code nohup}. @@ -85,7 +86,9 @@ public String getScript() { listener.getLogger().println("Warning: was asked to run an empty script"); } - ShellController c = new ShellController(ws); + String encoding = ws.act(new ZosCheck()) ? "IBM1047" : null; + + ShellController c = new ShellController(ws, encoding); FilePath shf = c.getScriptFile(ws); @@ -95,7 +98,7 @@ public String getScript() { String defaultShell = jenkins.getInjector().getInstance(Shell.DescriptorImpl.class).getShellOrDefault(ws.getChannel()); s = "#!"+defaultShell+" -xe\n" + s; } - shf.write(s, "UTF-8"); + shf.write(s, encoding==null ? "UTF-8" : encoding); shf.chmod(0755); envVars.put(cookieVariable, "please-do-not-kill-me"); @@ -153,8 +156,8 @@ public String getScript() { private int pid; private final long startTime = System.currentTimeMillis(); - private ShellController(FilePath ws) throws IOException, InterruptedException { - super(ws); + private ShellController(FilePath ws, String encoding) throws IOException, InterruptedException { + super(ws, encoding); } public FilePath getScriptFile(FilePath ws) throws IOException, InterruptedException { @@ -170,7 +173,10 @@ private synchronized int pid(FilePath ws) throws IOException, InterruptedExcepti FilePath pidFile = pidFile(ws); if (pidFile.exists()) { try { - pid = Integer.parseInt(pidFile.readToString().trim()); + final String pidStr = this.getEncoding() == null ? + pidFile.readToString().trim() : // original behavior + IOUtils.toString(pidFile.read(),this.getEncoding()); + pid = Integer.parseInt(pidStr.trim()); } catch (NumberFormatException x) { throw new IOException("corrupted content in " + pidFile + ": " + x, x); } @@ -221,4 +227,10 @@ private static final class DarwinCheck extends MasterToSlaveCallable { + @Override public Boolean call() throws RuntimeException { + return Platform.current() == Platform.UNIX && System.getProperty("os.name").equals("z/OS"); + } + } + } diff --git a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java index 163ca833..84261208 100644 --- a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java +++ b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java @@ -35,7 +35,9 @@ import hudson.slaves.WorkspaceList; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.RandomAccessFile; import java.util.Collections; import java.util.UUID; @@ -43,6 +45,7 @@ import java.util.logging.Logger; import jenkins.MasterToSlaveFileCallable; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.WriterOutputStream; /** * A task which forks some external command and then waits for log and status files to be updated/created. @@ -94,18 +97,41 @@ protected static class FileMonitoringController extends Controller { */ private long lastLocation; + /** + * Encoding of input/output of the sh. + * If null no character conversion to be done. + */ + private String encoding; + + /** + * @return Encoding of the files to be monitored. + * If null handled without conversion just as byte stream. + */ + public String getEncoding() { + return encoding; + } + protected FileMonitoringController(FilePath ws) throws IOException, InterruptedException { + this(ws,null); + } + + protected FileMonitoringController(FilePath ws, String encoding) throws IOException, InterruptedException { // can't keep ws reference because Controller is expected to be serializable ws.mkdirs(); FilePath cd = tempDir(ws).child("durable-" + Util.getDigestOf(UUID.randomUUID().toString()).substring(0,8)); cd.mkdirs(); controlDir = cd.getRemote(); + this.encoding = encoding; } @Override public final boolean writeLog(FilePath workspace, OutputStream sink) throws IOException, InterruptedException { FilePath log = getLogFile(workspace); + if (this.getEncoding() != null) { + sink = new WriterOutputStream(new OutputStreamWriter(sink, "UTF-8"), this.getEncoding()); + } Long newLocation = log.act(new WriteLog(lastLocation, new RemoteOutputStream(sink))); if (newLocation != null) { + if (this.getEncoding() != null) sink.flush(); LOGGER.log(Level.FINE, "copied {0} bytes from {1}", new Object[] {newLocation - lastLocation, log}); lastLocation = newLocation; return true; @@ -149,7 +175,10 @@ private static class WriteLog extends MasterToSlaveFileCallable { FilePath status = getResultFile(workspace); if (status.exists()) { try { - return Integer.parseInt(status.readToString().trim()); + final String statusStr = this.getEncoding() == null ? + status.readToString().trim() : // original behavior + IOUtils.toString(status.read(),this.getEncoding()); + return Integer.parseInt(statusStr.trim()); } catch (NumberFormatException x) { throw new IOException("corrupted content in " + status + ": " + x, x); } @@ -160,7 +189,9 @@ private static class WriteLog extends MasterToSlaveFileCallable { @Override public byte[] getOutput(FilePath workspace, Launcher launcher) throws IOException, InterruptedException { // TODO could perhaps be more efficient for large files to send a MasterToSlaveFileCallable - return IOUtils.toByteArray(getOutputFile(workspace).read()); + return this.getEncoding() == null ? + IOUtils.toByteArray(getOutputFile(workspace).read()) : + IOUtils.toByteArray(new InputStreamReader(getOutputFile(workspace).read(),this.getEncoding()), "UTF-8"); } @Override public final void stop(FilePath workspace, Launcher launcher) throws IOException, InterruptedException {