Skip to content

Commit

Permalink
Merge commit '27483b1e8cf046fa2aa4785c44429eee4bfad316' into java10-s…
Browse files Browse the repository at this point in the history
…upport-clean

# Conflicts:
#	pom.xml
  • Loading branch information
oleg-nenashev committed Nov 13, 2018
2 parents 3c1d020 + 27483b1 commit 473cfcd
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 35 deletions.
19 changes: 15 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@
</pluginRepository>
</pluginRepositories>
<properties>
<revision>2.21</revision>
<revision>2.23</revision>
<changelist>-SNAPSHOT</changelist>
<jenkins.version>2.62</jenkins.version>
<jenkins.version>2.121.1</jenkins.version>
<java.level>8</java.level>
<no-test-jar>false</no-test-jar>
<useBeta>true</useBeta>
<git-plugin.version>3.7.0</git-plugin.version>
<workflow-scm-step-plugin.version>2.6</workflow-scm-step-plugin.version>
<workflow-step-api-plugin.version>2.13</workflow-step-api-plugin.version>
Expand All @@ -81,7 +82,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>2.25</version>
<version>2.30</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down Expand Up @@ -127,7 +128,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>structs</artifactId>
<version>1.10</version>
<version>1.14</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -177,6 +178,10 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<groupId>org.jenkins-ci</groupId>
<artifactId>annotation-indexer</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand All @@ -185,6 +190,12 @@
<version>${git-plugin.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.jenkins-ci</groupId>
<artifactId>annotation-indexer</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,26 @@
import hudson.model.Run;
import hudson.model.TaskListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.GraphListener;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction;
import org.jenkinsci.plugins.workflow.support.actions.LogActionImpl;
import org.jenkinsci.plugins.workflow.support.actions.LogStorageAction;

/**
* Partial implementation of step context.
*/
public abstract class DefaultStepContext extends StepContext {

private static final Logger LOGGER = Logger.getLogger(DefaultStepContext.class.getName());

/**
* To prevent double instantiation of task listener, once we create it we keep it here.
*/
Expand All @@ -62,17 +68,14 @@ public abstract class DefaultStepContext extends StepContext {
if (key == EnvVars.class) {
Run<?,?> run = get(Run.class);
EnvironmentAction a = run == null ? null : run.getAction(EnvironmentAction.class);
EnvVars customEnvironment = a != null ? a.getEnvironment() : run.getEnvironment(get(TaskListener.class));
EnvVars customEnvironment = a != null ? a.getEnvironment() : run.getEnvironment(getExecution().getOwner().getListener());
return key.cast(EnvironmentExpander.getEffectiveEnvironment(customEnvironment, (EnvVars) value, get(EnvironmentExpander.class)));
} else if (key == Launcher.class) {
return key.cast(makeLauncher((Launcher) value));
} else if (value != null) {
return value;
} else if (key == TaskListener.class) {
if (listener == null) {
listener = LogActionImpl.stream(getNode(), get(ConsoleLogFilter.class));
}
return key.cast(listener);
return key.cast(getListener());
} else if (Node.class.isAssignableFrom(key)) {
Computer c = get(Computer.class);
Node n = null;
Expand All @@ -97,6 +100,33 @@ public abstract class DefaultStepContext extends StepContext {
}
}

private synchronized TaskListener getListener() throws IOException, InterruptedException {
if (listener == null) {
FlowNode node = getNode();
if (!node.isActive()) {
throw new IOException("cannot start writing logs to a finished node " + node + " " + node.getDisplayFunctionName() + " in " + node.getExecution());
}
listener = LogStorageAction.listenerFor(node, TaskListenerDecorator.merge(TaskListenerDecorator.fromConsoleLogFilter(get(ConsoleLogFilter.class)), get(TaskListenerDecorator.class)));
LOGGER.log(Level.FINE, "opened log for {0}", node.getDisplayFunctionName());
if (listener instanceof AutoCloseable) {
node.getExecution().addListener(new GraphListener.Synchronous() {
@Override public void onNewHead(FlowNode newNode) {
if (!node.isActive()) {
node.getExecution().removeListener(this);
LOGGER.log(Level.FINE, "closing log for {0}", node.getDisplayFunctionName());
try {
((AutoCloseable) listener).close();
} catch (Exception x) {
LOGGER.log(Level.WARNING, null, x);
}
}
}
});
}
}
return listener;
}

private <T> T castOrNull(Class<T> key, Object o) {
if (key.isInstance(o)) return key.cast(o);
else return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@

import com.google.common.base.Charsets;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.CloseProofOutputStream;
import hudson.Util;
import hudson.console.AnnotatedLargeText;
import hudson.console.ConsoleLogFilter;
import hudson.model.AbstractBuild;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.util.StreamTaskListener;
import java.io.File;
Expand All @@ -39,18 +42,18 @@
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.commons.io.output.TeeOutputStream;
import org.apache.commons.jelly.XMLOutput;
import org.jenkinsci.plugins.workflow.actions.FlowNodeAction;
import org.jenkinsci.plugins.workflow.actions.LogAction;
import org.jenkinsci.plugins.workflow.actions.PersistentAction;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.flow.GraphListener;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.stapler.framework.io.ByteBuffer;
Expand All @@ -59,52 +62,60 @@
* {@link LogAction} implementation that stores per-node log file under {@link FlowExecutionOwner#getRootDir()}.
*
* @author Kohsuke Kawaguchi
*/
* @deprecated Use {@link LogStorageAction} instead.
*/
@Deprecated
public class LogActionImpl extends LogAction implements FlowNodeAction, PersistentAction {

private static final Logger LOGGER = Logger.getLogger(LogActionImpl.class.getName());

/**
* Try to determine whether a build corresponds to a version of {@code WorkflowRun} using {@code copyLogs}.
*/
static boolean isOld(FlowExecutionOwner owner) {
try {
Queue.Executable exec = owner.getExecutable();
return exec instanceof Run && !Util.isOverridden(Run.class, exec.getClass(), "getLogFile");
} catch (IOException x) {
LOGGER.log(Level.WARNING, null, x);
return false; // err on the side of assuming plugins are updated
}
}

@Deprecated
public static @Nonnull TaskListener stream(final @Nonnull FlowNode node, @CheckForNull ConsoleLogFilter filter) throws IOException, InterruptedException {
return stream(node, TaskListenerDecorator.fromConsoleLogFilter(filter));
}

/**
* Get or create the streaming log handle for a given flow node.
* @param node the node
* @param filter
* @param decorator some filtering
* @return a listener
*/
@SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE") // stream closed later
public static @Nonnull TaskListener stream(final @Nonnull FlowNode node, @CheckForNull ConsoleLogFilter filter) throws IOException, InterruptedException {
public static @Nonnull TaskListener stream(final @Nonnull FlowNode node, @CheckForNull TaskListenerDecorator decorator) throws IOException, InterruptedException {
LogActionImpl la = node.getAction(LogActionImpl.class);
if (la == null) {
la = new LogActionImpl(node);
node.addAction(la);
}
OutputStream os = new FileOutputStream(la.getLogFile(), true);
if (filter != null) {
os = filter.decorateLogger((AbstractBuild) null, os);
FlowExecutionOwner owner = node.getExecution().getOwner();
if (!isOld(owner)) { // in case after upgrade we had a running step using LogActionImpl
os = new TeeOutputStream(os, new CloseProofOutputStream(owner.getListener().getLogger()));
}
final StreamTaskListener result = new StreamTaskListener(os, la.getCharset());
final AtomicReference<GraphListener> graphListener = new AtomicReference<>();
LOGGER.log(Level.FINE, "opened log for {0}", node.getDisplayFunctionName());
graphListener.set(new GraphListener.Synchronous() {
@Override public void onNewHead(FlowNode newNode) {
if (!node.isActive()) {
node.getExecution().removeListener(graphListener.get());
result.getLogger().close();
LOGGER.log(Level.FINE, "closed log for {0}", node.getDisplayFunctionName());
}
}
});
node.getExecution().addListener(graphListener.get());
return result;
if (decorator != null) {
os = decorator.decorate(os);
}
return new StreamTaskListener(os, la.getCharset());
}

private transient FlowNode parent;
private transient volatile File log;
private String charset;

private LogActionImpl(FlowNode parent) throws IOException {
if (!parent.isActive()) {
throw new IOException("cannot start writing logs to a finished node " + parent + " " + parent.getDisplayFunctionName() + " in " + parent.getExecution());
}
this.parent = parent;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.workflow.support.actions;

import hudson.console.AnnotatedLargeText;
import hudson.model.TaskListener;
import java.io.IOException;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.commons.jelly.XMLOutput;
import org.jenkinsci.plugins.workflow.actions.FlowNodeAction;
import org.jenkinsci.plugins.workflow.actions.LogAction;
import org.jenkinsci.plugins.workflow.actions.PersistentAction;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.log.LogStorage;
import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* A marker for a node which had some log text using {@link LogStorage#nodeListener}.
*/
@Restricted(NoExternalUse.class) // for use from DefaultStepContext only
public class LogStorageAction extends LogAction implements FlowNodeAction, PersistentAction {

public transient FlowNode node;

private LogStorageAction(FlowNode node) {
this.node = node;
}

@Override public void onLoad(FlowNode node) {
this.node = node;
}

@Override public AnnotatedLargeText<? extends FlowNode> getLogText() {
FlowExecutionOwner owner = node.getExecution().getOwner();
return LogStorage.of(owner).stepLog(node, !node.isActive());
}

/**
* Used from <tt>console.jelly</tt> to write annotated log to the given output.
*/
@Restricted(DoNotUse.class) // Jelly
public void writeLogTo(long offset, XMLOutput out) throws IOException {
getLogText().writeHtmlTo(offset, out.asWriter());
}

/**
* Creates a sink to print output from a step.
* Will use {@link LogActionImpl} if necessary.
* @param node a node which wishes to print output
* @param decorator an optional decorator to pass to {@link TaskListenerDecorator#apply}
* @return a stream
*/
@SuppressWarnings("deprecation") // LogActionImpl here for backward compatibility
public static @Nonnull TaskListener listenerFor(@Nonnull FlowNode node, @CheckForNull TaskListenerDecorator decorator) throws IOException, InterruptedException {
FlowExecutionOwner owner = node.getExecution().getOwner();
if (LogActionImpl.isOld(owner) || node.getAction(LogActionImpl.class) != null) {
return LogActionImpl.stream(node, decorator);
} else {
if (node.getAction(LogStorageAction.class) == null) {
node.addAction(new LogStorageAction(node));
}
return TaskListenerDecorator.apply(LogStorage.of(owner).nodeListener(node), owner, decorator);
}
}

}
Loading

0 comments on commit 473cfcd

Please sign in to comment.