├── .github
├── CODEOWNERS
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── cd.yaml
│ └── jenkins-security-scan.yml
├── .gitignore
├── .mvn
├── extensions.xml
└── maven.config
├── CHANGELOG.md
├── Jenkinsfile
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── workflow
│ │ └── support
│ │ ├── DefaultStepContext.java
│ │ ├── PipelineIOUtils.java
│ │ ├── actions
│ │ ├── EnvironmentAction.java
│ │ ├── LogActionImpl.java
│ │ ├── LogStorageAction.java
│ │ ├── PauseAction.java
│ │ ├── WorkspaceActionImpl.java
│ │ └── WorkspaceRunAction.java
│ │ ├── concurrent
│ │ ├── ChainingListenableFuture.java
│ │ ├── DirectExecutor.java
│ │ ├── Futures.java
│ │ ├── ListFuture.java
│ │ ├── MoreExecutors.java
│ │ ├── Timeout.java
│ │ ├── WithThreadName.java
│ │ └── package-info.java
│ │ ├── pickles
│ │ ├── SecretPickle.java
│ │ ├── SingleTypedPickleFactory.java
│ │ ├── ThrowablePickle.java
│ │ ├── TryRepeatedly.java
│ │ ├── XStreamPickle.java
│ │ ├── package-info.java
│ │ └── serialization
│ │ │ ├── DryCapsule.java
│ │ │ ├── DryOwner.java
│ │ │ ├── PickleResolver.java
│ │ │ ├── RiverReader.java
│ │ │ └── RiverWriter.java
│ │ ├── steps
│ │ ├── build
│ │ │ └── RunWrapper.java
│ │ └── input
│ │ │ └── POSTHyperlinkNote.java
│ │ ├── storage
│ │ ├── BulkFlowNodeStorage.java
│ │ ├── FlowNodeStorage.java
│ │ └── SimpleXStreamFlowNodeStorage.java
│ │ └── visualization
│ │ └── table
│ │ ├── ArgumentsColumn.java
│ │ ├── ConsoleColumn.java
│ │ ├── FlowGraphTable.java
│ │ └── StatusColumn.java
└── resources
│ ├── META-INF
│ └── hudson.remoting.ClassFilter
│ ├── index.jelly
│ └── org
│ └── jenkinsci
│ └── plugins
│ └── workflow
│ └── support
│ ├── actions
│ ├── LogActionImpl
│ │ ├── index.jelly
│ │ └── index.properties
│ ├── LogStorageAction
│ │ ├── index.jelly
│ │ └── index.properties
│ ├── Messages.properties
│ ├── Messages_zh_CN.properties
│ ├── WorkspaceActionImpl
│ │ └── sidepanel.jelly
│ └── WorkspaceRunAction
│ │ ├── index.jelly
│ │ ├── index.properties
│ │ └── index_zh_CN.properties
│ ├── steps
│ ├── build
│ │ └── RunWrapper
│ │ │ └── help.html
│ └── input
│ │ └── POSTHyperlinkNote
│ │ └── script.js
│ └── visualization
│ └── table
│ ├── ArgumentsColumn
│ ├── column.jelly
│ └── columnHeader.jelly
│ ├── ConsoleColumn
│ └── column.jelly
│ ├── FlowGraphTable
│ └── ajax.jelly
│ └── StatusColumn
│ └── column.jelly
└── test
├── java
└── org
│ └── jenkinsci
│ └── plugins
│ └── workflow
│ ├── support
│ ├── actions
│ │ ├── LogActionImplTest.java
│ │ └── PauseActionTest.java
│ ├── concurrent
│ │ └── TimeoutTest.java
│ ├── pickles
│ │ ├── ThrowablePickleTest.java
│ │ └── serialization
│ │ │ ├── EphemeralPickleResolverTest.java
│ │ │ ├── PickleResolverTest.java
│ │ │ ├── RiverWriterTest.java
│ │ │ └── SerializationSecurityTest.java
│ ├── steps
│ │ └── build
│ │ │ └── RunWrapperTest.java
│ ├── storage
│ │ ├── AbstractStorageTest.java
│ │ ├── BulkFlowNodeStorageTest.java
│ │ ├── BulkStorageTest.java
│ │ ├── MockFlowExecution.java
│ │ ├── SimpleXStreamStorageTest.java
│ │ └── StorageTestUtils.java
│ └── visualization
│ │ └── table
│ │ └── FlowGraphTableTest.java
│ └── test
│ └── steps
│ ├── BlockSemaphoreStep.java
│ ├── SemaphoreStep.java
│ ├── SemaphoreStepTest.java
│ ├── SynchronousResumeNotSupportedExceptionTest.java
│ ├── input
│ └── POSTHyperlinkNoteTest.java
│ └── package-info.java
└── resources
└── org
└── jenkinsci
└── plugins
└── workflow
└── support
├── storage
├── BulkFlowNodeStorageTest
│ └── actionDeserializationShouldBeRobust
│ │ ├── jobs
│ │ └── test0
│ │ │ ├── builds
│ │ │ ├── 1
│ │ │ │ ├── build.xml
│ │ │ │ ├── log
│ │ │ │ ├── log-index
│ │ │ │ └── workflow
│ │ │ │ │ └── flowNodeStore.xml
│ │ │ ├── legacyIds
│ │ │ └── permalinks
│ │ │ ├── config.xml
│ │ │ └── nextBuildNumber
│ │ └── org.jenkinsci.plugins.workflow.flow.FlowExecutionList.xml
└── SimpleXStreamStorageTest
│ └── actionDeserializationShouldBeRobust
│ ├── jobs
│ └── test0
│ │ ├── builds
│ │ ├── 1
│ │ │ ├── build.xml
│ │ │ ├── log
│ │ │ ├── log-index
│ │ │ ├── program.dat
│ │ │ └── workflow
│ │ │ │ ├── 2.xml
│ │ │ │ ├── 3.xml
│ │ │ │ ├── 4.xml
│ │ │ │ └── 5.xml
│ │ ├── legacyIds
│ │ └── permalinks
│ │ ├── config.xml
│ │ └── nextBuildNumber
│ └── org.jenkinsci.plugins.workflow.flow.FlowExecutionList.xml
└── visualization
└── table
└── FlowGraphTableTest
└── corruptedFlowGraph.zip
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @jenkinsci/workflow-support-plugin-developers
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2 |
3 | version: 2
4 | updates:
5 | - package-ecosystem: "maven"
6 | directory: "/"
7 | schedule:
8 | interval: "weekly"
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | _extends: .github
2 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yaml:
--------------------------------------------------------------------------------
1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins
2 |
3 | name: cd
4 | on:
5 | workflow_dispatch:
6 | check_run:
7 | types:
8 | - completed
9 |
10 | jobs:
11 | maven-cd:
12 | uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1
13 | secrets:
14 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
15 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/jenkins-security-scan.yml:
--------------------------------------------------------------------------------
1 | name: Jenkins Security Scan
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | types: [ opened, synchronize, reopened ]
9 | workflow_dispatch:
10 |
11 | permissions:
12 | security-events: write
13 | contents: read
14 | actions: read
15 |
16 | jobs:
17 | security-scan:
18 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2
19 | with:
20 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate.
21 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | work
3 | *.iml
4 | .idea
5 | /.classpath
6 | /.project
7 | /.settings/
8 | # macOS
9 | .DS_Store
10 |
--------------------------------------------------------------------------------
/.mvn/extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | io.jenkins.tools.incrementals
4 | git-changelist-maven-extension
5 | 1.8
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.mvn/maven.config:
--------------------------------------------------------------------------------
1 | -Pconsume-incrementals
2 | -Pmight-produce-incrementals
3 | -Dchangelist.format=%d.v%s
4 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | buildPlugin(useContainerAgent: true, configurations: [
2 | [platform: 'linux', jdk: 21],
3 | [platform: 'windows', jdk: 17],
4 | ])
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pipeline: Supporting APIs Plugin
2 |
3 | ## Introduction
4 |
5 | This plugin provides APIs that are used by core Pipeline plugins for features such as persistence and step visualization.
6 |
7 | ## Version History
8 |
9 | See [the changelog](CHANGELOG.md).
10 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/PipelineIOUtils.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.workflow.support;
2 |
3 | import hudson.XmlFile;
4 | import hudson.util.XStream2;
5 |
6 | import edu.umd.cs.findbugs.annotations.NonNull;
7 | import java.io.BufferedOutputStream;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 | import java.nio.file.Files;
12 | import java.nio.file.InvalidPathException;
13 | import java.nio.file.StandardOpenOption;
14 |
15 | /**
16 | * Utilities to assist with IO and in some cases improve performance specifically for pipeline.
17 | */
18 | public class PipelineIOUtils {
19 | /**
20 | * Convenience method to transparently write data directly or atomicly using {@link hudson.util.AtomicFileWriter}.
21 | * @param toWrite Object to write to file
22 | * @param location File to write object to
23 | * @param xstream xstream to use for output
24 | * @param atomicWrite If true, do an atomic write, otherwise do a direct write to file.
25 | * @throws IOException
26 | */
27 | public static void writeByXStream(@NonNull Object toWrite, @NonNull File location, @NonNull XStream2 xstream, boolean atomicWrite) throws IOException {
28 | if (atomicWrite) {
29 | XmlFile file = new XmlFile(xstream, location);
30 | file.write(toWrite);
31 | } else {
32 | try(OutputStream os = new BufferedOutputStream(
33 | Files.newOutputStream(location.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))){
34 | xstream.toXMLUTF8(toWrite, os); // No atomic nonsense, just write and write and write!
35 | } catch (InvalidPathException ipe) {
36 | throw new IOException(ipe);
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/actions/EnvironmentAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2014 Jesse Glick.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.actions;
26 |
27 | import hudson.EnvVars;
28 | import hudson.model.Action;
29 | import hudson.model.Run;
30 | import hudson.model.TaskListener;
31 | import java.io.IOException;
32 | import java.util.Map;
33 | import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;
34 | import org.jenkinsci.plugins.workflow.support.DefaultStepContext;
35 |
36 | /**
37 | * A {@linkplain Run#addAction run action} which reports environment variables.
38 | * If present, will be used from {@link DefaultStepContext#get} on {@link EnvVars}
39 | * after amendment by {@link EnvironmentExpander#getEffectiveEnvironment}.
40 | */
41 | public interface EnvironmentAction extends Action {
42 |
43 | /**
44 | * Gets the complete global environment for a build, including both {@link Run#getEnvironment(TaskListener)} and any {@link IncludingOverrides#getOverriddenEnvironment}.
45 | */
46 | EnvVars getEnvironment() throws IOException, InterruptedException;
47 |
48 | /**
49 | * Optional extension interface that allows the overrides to be distinguished.
50 | */
51 | interface IncludingOverrides extends EnvironmentAction {
52 |
53 | /**
54 | * Gets any environment variables set during this build that were not originally present.
55 | */
56 | Map getOverriddenEnvironment();
57 |
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/actions/LogStorageAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2016, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.actions;
26 |
27 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
28 | import hudson.console.AnnotatedLargeText;
29 | import hudson.model.TaskListener;
30 | import java.io.IOException;
31 | import edu.umd.cs.findbugs.annotations.CheckForNull;
32 | import edu.umd.cs.findbugs.annotations.NonNull;
33 | import org.apache.commons.jelly.XMLOutput;
34 | import org.jenkinsci.plugins.workflow.actions.FlowNodeAction;
35 | import org.jenkinsci.plugins.workflow.actions.LogAction;
36 | import org.jenkinsci.plugins.workflow.actions.PersistentAction;
37 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
38 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
39 | import org.jenkinsci.plugins.workflow.log.LogStorage;
40 | import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
41 | import org.kohsuke.accmod.Restricted;
42 | import org.kohsuke.accmod.restrictions.DoNotUse;
43 | import org.kohsuke.accmod.restrictions.NoExternalUse;
44 |
45 | /**
46 | * A marker for a node which had some log text using {@link LogStorage#nodeListener}.
47 | */
48 | @Restricted(NoExternalUse.class) // for use from DefaultStepContext only
49 | public class LogStorageAction extends LogAction implements FlowNodeAction, PersistentAction {
50 |
51 | @SuppressFBWarnings(value="PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification="TODO clean up")
52 | public transient FlowNode node;
53 |
54 | private LogStorageAction(FlowNode node) {
55 | this.node = node;
56 | }
57 |
58 | @Override public void onLoad(FlowNode node) {
59 | this.node = node;
60 | }
61 |
62 | @Override public AnnotatedLargeText extends FlowNode> getLogText() {
63 | FlowExecutionOwner owner = node.getExecution().getOwner();
64 | return LogStorage.of(owner).stepLog(node, !node.isActive());
65 | }
66 |
67 | /**
68 | * Used from console.jelly to write annotated log to the given output.
69 | */
70 | @Restricted(DoNotUse.class) // Jelly
71 | public void writeLogTo(long offset, XMLOutput out) throws IOException {
72 | // Similar to Run#writeWholeLogTo but terminates even if node.isActive(). Adapated from WorkflowRun.writeLogTo.
73 | long pos = offset;
74 | while (true) {
75 | long pos2 = getLogText().writeHtmlTo(pos, out.asWriter());
76 | if (pos2 <= pos) {
77 | break;
78 | }
79 | pos = pos2;
80 | }
81 | }
82 |
83 | /**
84 | * Creates a sink to print output from a step.
85 | * Will use {@link LogActionImpl} if necessary.
86 | * @param node a node which wishes to print output
87 | * @param decorator an optional decorator to pass to {@link TaskListenerDecorator#apply}
88 | * @return a stream
89 | */
90 | @SuppressWarnings("deprecation") // LogActionImpl here for backward compatibility
91 | public static @NonNull TaskListener listenerFor(@NonNull FlowNode node, @CheckForNull TaskListenerDecorator decorator) throws IOException, InterruptedException {
92 | FlowExecutionOwner owner = node.getExecution().getOwner();
93 | if (LogActionImpl.isOld(owner) || node.getAction(LogActionImpl.class) != null) {
94 | return LogActionImpl.stream(node, decorator);
95 | } else {
96 | if (node.getAction(LogStorageAction.class) == null) {
97 | node.addAction(new LogStorageAction(node));
98 | }
99 | return TaskListenerDecorator.apply(LogStorage.of(owner).nodeListener(node), owner, decorator);
100 | }
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/actions/PauseAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.jenkinsci.plugins.workflow.support.actions;
25 |
26 | import hudson.model.Action;
27 | import hudson.model.InvisibleAction;
28 | import org.jenkinsci.plugins.workflow.actions.PersistentAction;
29 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
30 |
31 | import java.io.IOException;
32 | import java.util.ArrayList;
33 | import java.util.List;
34 | import java.util.logging.Level;
35 | import java.util.logging.Logger;
36 | import edu.umd.cs.findbugs.annotations.CheckForNull;
37 | import edu.umd.cs.findbugs.annotations.NonNull;
38 |
39 | /**
40 | * Pause {@link FlowNode} Action.
41 | * Simply marks the node as being a node that causes the build to pause e.g. an Input node.
42 | *
43 | * @author tom.fennelly@gmail.com
44 | */
45 | public class PauseAction extends InvisibleAction implements PersistentAction {
46 |
47 | private static final Logger LOGGER = Logger.getLogger(PauseAction.class.getName());
48 |
49 | private String cause;
50 | private long startTime = System.currentTimeMillis();
51 | private long endTime;
52 |
53 | public PauseAction(String cause) {
54 | this.cause = cause;
55 | }
56 |
57 | public String getCause() {
58 | return cause;
59 | }
60 |
61 | public void setCause(String cause) {
62 | this.cause = cause;
63 | }
64 |
65 | public long getStartTime() {
66 | return startTime;
67 | }
68 |
69 | public void setStartTime(long startTime) {
70 | this.startTime = startTime;
71 | }
72 |
73 | public long getEndTime() {
74 | return endTime;
75 | }
76 |
77 | public void setEndTime(long endTime) {
78 | this.endTime = endTime;
79 | }
80 |
81 | public boolean isPaused() {
82 | // The node is paused if the end time is not set on it.
83 | return (endTime == 0L);
84 | }
85 |
86 | /**
87 | * Get the pause duration for this flow node.
88 | * If the node is paused, the duration will be calculated against the current time.
89 | *
90 | * @return The pause duration in milliseconds.
91 | */
92 | public long getPauseDuration() {
93 | if (isPaused()) {
94 | return (System.currentTimeMillis() - startTime);
95 | } else {
96 | return (endTime - startTime);
97 | }
98 | }
99 |
100 | public static @CheckForNull PauseAction getCurrentPause(@NonNull FlowNode node) {
101 | List pauseActions = getPauseActions(node);
102 |
103 | if (!pauseActions.isEmpty()) {
104 | return pauseActions.get(pauseActions.size() - 1);
105 | }
106 |
107 | return null;
108 | }
109 |
110 | public static void endCurrentPause(@NonNull FlowNode node) throws IOException {
111 | PauseAction currentPause = getCurrentPause(node);
112 |
113 | if (currentPause != null) {
114 | currentPause.setEndTime(System.currentTimeMillis());
115 | node.save();
116 | } else {
117 | LOGGER.log(Level.FINE, "‘endCurrentPause’ was called for a FlowNode (‘{0}’) that does not have an active pause. ‘endCurrentPause’ may have already been called.", node.getDisplayName());
118 | }
119 | }
120 |
121 | /**
122 | * Simple helper method to test if the supplied node is a pause node.
123 | * @param node The node to test.
124 | * @return True if the node is pause node, otherwise false.
125 | */
126 | public static boolean isPaused(@NonNull FlowNode node) {
127 | PauseAction currentPause = getCurrentPause(node);
128 |
129 | if (currentPause != null) {
130 | return currentPause.isPaused();
131 | }
132 |
133 | return false;
134 | }
135 |
136 | /**
137 | * Get the {@link PauseAction} instances for the supplied node.
138 | * @param node The node to be searched.
139 | * @return The {@link PauseAction} instances for the supplied node. Returns an empty list if there are none.
140 | */
141 | public static @NonNull List getPauseActions(@NonNull FlowNode node) {
142 | List pauseActions = new ArrayList<>();
143 | List actions = node.getActions();
144 |
145 | for (Action action : actions) {
146 | if (action instanceof PauseAction) {
147 | pauseActions.add((PauseAction) action);
148 | }
149 | }
150 |
151 | return pauseActions;
152 | }
153 |
154 | /**
155 | * get the aggregate pause duration of the supplied flow node.
156 | * @param node The node to calculate on.
157 | * @return The pause duration in milliseconds.
158 | */
159 | public static long getPauseDuration(@NonNull FlowNode node) {
160 | List pauseActions = getPauseActions(node);
161 | long pauseDuration = 0L;
162 |
163 | for (PauseAction pauseAction : pauseActions) {
164 | pauseDuration += pauseAction.getPauseDuration();
165 | }
166 |
167 | return pauseDuration;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/actions/WorkspaceActionImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2014 Jesse Glick.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.actions;
26 |
27 | import hudson.FilePath;
28 | import hudson.model.DirectoryBrowserSupport;
29 | import hudson.model.Item;
30 | import hudson.model.Node;
31 | import hudson.model.Queue;
32 | import hudson.model.labels.LabelAtom;
33 | import hudson.security.AccessControlled;
34 | import java.io.FileNotFoundException;
35 | import java.io.IOException;
36 | import java.util.Set;
37 | import java.util.TreeSet;
38 | import jenkins.model.Jenkins;
39 | import org.jenkinsci.plugins.workflow.actions.FlowNodeAction;
40 | import org.jenkinsci.plugins.workflow.actions.WorkspaceAction;
41 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
42 | import org.jenkinsci.plugins.workflow.FilePathUtils;
43 |
44 | public final class WorkspaceActionImpl extends WorkspaceAction implements FlowNodeAction {
45 |
46 | private static final long serialVersionUID = 1;
47 |
48 | private final String node;
49 | private final String path;
50 | private final Set labels;
51 | private transient FlowNode parent;
52 |
53 | public WorkspaceActionImpl(FilePath workspace, FlowNode parent) {
54 | node = FilePathUtils.getNodeName(workspace);
55 | Jenkins j = Jenkins.getInstanceOrNull();
56 | Node n = j == null ? null : node.isEmpty() ? j : j.getNode(node);
57 | labels = new TreeSet<>();
58 | if (n != null) {
59 | labels.addAll(n.getAssignedLabels());
60 | labels.remove(n.getSelfLabel());
61 | }
62 | path = workspace.getRemote();
63 | this.parent = parent;
64 | }
65 |
66 | @Override public String getNode() {
67 | return node;
68 | }
69 |
70 | @Override public String getPath() {
71 | return path;
72 | }
73 |
74 | @Override public Set getLabels() {
75 | return labels;
76 | }
77 |
78 | public FlowNode getParent() {
79 | return parent;
80 | }
81 |
82 | @Override public void onLoad(FlowNode parent) {
83 | this.parent = parent;
84 | }
85 |
86 | @Override public String getIconFileName() {
87 | return "folder.png";
88 | }
89 |
90 | @Override public String getDisplayName() {
91 | return "Workspace";
92 | }
93 |
94 | @Override public String getUrlName() {
95 | return "ws";
96 | }
97 |
98 | // Analogous to AbstractProject.doWs.
99 | // TODO this trick fails when a file or dir is named parent/node/path/workspace/iconFileName/displayName/urlName; how to convince Stapler that this method should take precedence?
100 | public DirectoryBrowserSupport doDynamic() throws IOException {
101 | Queue.Executable executable = parent.getExecution().getOwner().getExecutable();
102 | if (executable instanceof AccessControlled) {
103 | ((AccessControlled) executable).checkPermission(Item.WORKSPACE);
104 | }
105 | FilePath ws = getWorkspace();
106 | if (ws == null) {
107 | throw new FileNotFoundException();
108 | }
109 | return new DirectoryBrowserSupport(this, ws, "Workspace", "folder.png", true);
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/actions/WorkspaceRunAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2018 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.actions;
26 |
27 | import hudson.Extension;
28 | import hudson.model.Action;
29 | import hudson.model.Item;
30 | import hudson.security.AccessControlled;
31 | import java.io.IOException;
32 | import java.util.ArrayList;
33 | import java.util.Collection;
34 | import java.util.Collections;
35 | import java.util.List;
36 | import java.util.logging.Level;
37 | import java.util.logging.Logger;
38 | import jenkins.model.TransientActionFactory;
39 | import org.jenkinsci.plugins.workflow.flow.FlowExecution;
40 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
41 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
42 | import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner;
43 | import org.kohsuke.accmod.Restricted;
44 | import org.kohsuke.accmod.restrictions.NoExternalUse;
45 |
46 | /**
47 | * Display of all {@link WorkspaceActionImpl}s for a build.
48 | */
49 | @Restricted(NoExternalUse.class)
50 | public final class WorkspaceRunAction implements Action {
51 |
52 | private static final Logger LOGGER = Logger.getLogger(WorkspaceRunAction.class.getName());
53 |
54 | private final FlowExecutionOwner.Executable build;
55 | public final FlowExecutionOwner owner;
56 |
57 | WorkspaceRunAction(FlowExecutionOwner.Executable build, FlowExecutionOwner owner) {
58 | this.build = build;
59 | this.owner = owner;
60 | }
61 |
62 | private boolean hasNoWorkspacePermission() {
63 | return (build instanceof AccessControlled && !((AccessControlled) build).hasPermission(Item.WORKSPACE));
64 | }
65 |
66 | @Override public String getIconFileName() {
67 | return hasNoWorkspacePermission() ? null : "folder.png";
68 | }
69 |
70 | @Override public String getDisplayName() {
71 | return hasNoWorkspacePermission() ? null : Messages.workspaces();
72 | }
73 |
74 | @Override public String getUrlName() {
75 | return hasNoWorkspacePermission() ? null : "ws";
76 | }
77 |
78 | public List getActions() {
79 | FlowExecution exec;
80 | try {
81 | exec = owner.get();
82 | } catch (IOException x) {
83 | LOGGER.log(Level.WARNING, null, x);
84 | // Broken flow, cannot display anything.
85 | return Collections.emptyList();
86 | }
87 | List r = new ArrayList<>();
88 | for (FlowNode node : new DepthFirstScanner().allNodes(exec)) {
89 | r.addAll(node.getActions(WorkspaceActionImpl.class));
90 | }
91 | Collections.reverse(r);
92 | return r;
93 | }
94 |
95 | @Extension public static final class Factory extends TransientActionFactory {
96 |
97 | @Override public Class type() {
98 | return FlowExecutionOwner.Executable.class;
99 | }
100 |
101 | @Override public Collection extends Action> createFor(FlowExecutionOwner.Executable target) {
102 | FlowExecutionOwner owner = target.asFlowExecutionOwner();
103 | if (owner == null) {
104 | return Collections.emptySet();
105 | }
106 | return Collections.singleton(new WorkspaceRunAction(target, owner));
107 | }
108 |
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 |
15 | package org.jenkinsci.plugins.workflow.support.concurrent;
16 |
17 | import com.google.common.annotations.GwtCompatible;
18 | import org.kohsuke.accmod.Restricted;
19 | import org.kohsuke.accmod.restrictions.NoExternalUse;
20 | import java.util.concurrent.Executor;
21 |
22 | /**
23 | * An {@link Executor} that runs each task in the thread that invokes {@link Executor#execute
24 | * execute}.
25 | */
26 | @GwtCompatible
27 | @Restricted(NoExternalUse.class)
28 | enum DirectExecutor implements Executor {
29 | INSTANCE;
30 |
31 | @Override
32 | public void execute(Runnable command) {
33 | command.run();
34 | }
35 |
36 | @Override
37 | public String toString() {
38 | return "MoreExecutors.directExecutor()";
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 |
15 | package org.jenkinsci.plugins.workflow.support.concurrent;
16 |
17 | import com.google.common.annotations.GwtCompatible;
18 | import org.kohsuke.accmod.Restricted;
19 | import org.kohsuke.accmod.restrictions.NoExternalUse;
20 | import java.util.concurrent.Executor;
21 | import java.util.concurrent.ExecutorService;
22 |
23 | /**
24 | * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService},
25 | * and {@link java.util.concurrent.ThreadFactory}.
26 | *
27 | * @author Eric Fellheimer
28 | * @author Kyle Littlefield
29 | * @author Justin Mahoney
30 | * @since 3.0
31 | */
32 | @GwtCompatible(emulated = true)
33 | @Restricted(NoExternalUse.class)
34 | public final class MoreExecutors {
35 | private MoreExecutors() {}
36 |
37 | /**
38 | * Returns an {@link Executor} that runs each task in the thread that invokes {@link
39 | * Executor#execute execute}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}.
40 | *
41 | *
This executor is appropriate for tasks that are lightweight and not deeply chained.
42 | * Inappropriate {@code directExecutor} usage can cause problems, and these problems can be
43 | * difficult to reproduce because they depend on timing. For example:
44 | *
45 | *
46 | *
A call like {@code future.transform(function, directExecutor())} may execute the function
47 | * immediately in the thread that is calling {@code transform}. (This specific case happens
48 | * if the future is already completed.) If {@code transform} call was made from a UI thread
49 | * or other latency-sensitive thread, a heavyweight function can harm responsiveness.
50 | *
If the task will be executed later, consider which thread will trigger the execution --
51 | * since that thread will execute the task inline. If the thread is a shared system thread
52 | * like an RPC network thread, a heavyweight task can stall progress of the whole system or
53 | * even deadlock it.
54 | *
If many tasks will be triggered by the same event, one heavyweight task may delay other
55 | * tasks -- even tasks that are not themselves {@code directExecutor} tasks.
56 | *
If many such tasks are chained together (such as with {@code
57 | * future.transform(...).transform(...).transform(...)....}), they may overflow the stack.
58 | * (In simple cases, callers can avoid this by registering all tasks with the same {@code
59 | * MoreExecutors#newSequentialExecutor} wrapper around {@code directExecutor()}. More
60 | * complex cases may require using thread pools or making deeper changes.)
61 | *
62 | *
63 | * Additionally, beware of executing tasks with {@code directExecutor} while holding a lock. Since
64 | * the task you submit to the executor (or any other arbitrary work the executor does) may do slow
65 | * work or acquire other locks, you risk deadlocks.
66 | *
67 | *
This should be preferred to {@code #newDirectExecutorService()} because implementing the
78 | * {@link ExecutorService} subinterface necessitates significant performance overhead.
79 | *
80 | *
81 | * @since 18.0
82 | */
83 | public static Executor directExecutor() {
84 | return DirectExecutor.INSTANCE;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/Timeout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2017 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.concurrent;
26 |
27 | import hudson.FilePath;
28 | import hudson.Util;
29 | import hudson.remoting.VirtualChannel;
30 | import hudson.util.ClassLoaderSanityThreadFactory;
31 | import hudson.util.DaemonThreadFactory;
32 | import hudson.util.NamingThreadFactory;
33 | import java.util.concurrent.Executors;
34 | import java.util.concurrent.ScheduledExecutorService;
35 | import java.util.concurrent.ScheduledFuture;
36 | import java.util.concurrent.TimeUnit;
37 | import java.util.logging.Level;
38 | import java.util.logging.Logger;
39 |
40 | /**
41 | * Allows operations to be limited in execution time.
42 | * For example, {@link VirtualChannel#call} or {@link FilePath#isDirectory} could otherwise hang forever.
43 | * Use in a {@code try}-with-resources block.
44 | */
45 | public class Timeout implements AutoCloseable {
46 |
47 | private static final Logger LOGGER = Logger.getLogger(Timeout.class.getName());
48 |
49 | private static final ScheduledExecutorService interruptions = Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory(new ClassLoaderSanityThreadFactory(new DaemonThreadFactory()), "Timeout.interruptions"));
50 |
51 | private final Thread thread;
52 | private volatile boolean completed;
53 | private volatile ScheduledFuture> future;
54 | private long endTime;
55 | /*
56 | private final String originalName;
57 | */
58 |
59 | private Timeout(long time, TimeUnit unit) {
60 | thread = Thread.currentThread();
61 | LOGGER.log(Level.FINER, "Might interrupt {0} after {1} {2}", new Object[] {thread.getName(), time, unit});
62 | /* see below:
63 | originalName = thread.getName();
64 | thread.setName(String.format("%s (Timeout@%h: %s)", originalName, this, Util.getTimeSpanString(unit.toMillis(time))));
65 | */
66 | ping(time, unit);
67 | }
68 |
69 | @Override public void close() {
70 | completed = true;
71 | if (future != null) {
72 | future.cancel(true);
73 | }
74 | /*
75 | thread.setName(originalName);
76 | */
77 | LOGGER.log(Level.FINER, "completed {0}", thread.getName());
78 | }
79 |
80 | private void ping(final long time, final TimeUnit unit) {
81 | future = interruptions.schedule(() -> {
82 | if (completed) {
83 | LOGGER.log(Level.FINER, "{0} already finished, no need to interrupt", thread.getName());
84 | return;
85 | }
86 | if (LOGGER.isLoggable(Level.FINE)) {
87 | Throwable t = new Throwable();
88 | t.setStackTrace(thread.getStackTrace());
89 | LOGGER.log(Level.FINE, "Interrupting " + thread.getName() + " after " + time + " " + unit, t);
90 | }
91 | thread.interrupt();
92 | if (endTime == 0) {
93 | // First interruption.
94 | endTime = System.nanoTime();
95 | } else {
96 | // Not dead yet?
97 | String unresponsiveness = Util.getTimeSpanString((System.nanoTime() - endTime) / 1_000_000);
98 | LOGGER.log(Level.INFO, "{0} unresponsive for {1}", new Object[] {thread.getName(), unresponsiveness});
99 | /* TODO does not work; thread.getName() does not seem to return the current value when called from another thread, even w/ synchronized access, and running with -Xint
100 | thread.setName(thread.getName().replaceFirst(String.format("(Timeout@%h: )[^)]+", this), "$1unresponsive for " + unresponsiveness));
101 | */
102 | }
103 | ping(5, TimeUnit.SECONDS);
104 | }, time, unit);
105 | }
106 |
107 | public static Timeout limit(final long time, final TimeUnit unit) {
108 | return new Timeout(time, unit);
109 | }
110 |
111 | // TODO JENKINS-32986 offer a variant that will escalate to Thread.stop
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/WithThreadName.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2017 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.concurrent;
26 |
27 | /**
28 | * Utility to temporarily append some information to the name of the current thread.
29 | * This is helpful for making thread dumps more readable and informative:
30 | * stack trace elements do not contain any information about object identity.
31 | */
32 | public final class WithThreadName implements AutoCloseable {
33 |
34 | private final String original;
35 |
36 | /**
37 | * Sets the current thread’s name.
38 | * @param suffix text to append to the original name
39 | */
40 | public WithThreadName(String suffix) {
41 | Thread t = Thread.currentThread();
42 | original = t.getName();
43 | t.setName(original + suffix);
44 | }
45 |
46 | /**
47 | * Restores the original name.
48 | */
49 | @Override public void close() {
50 | Thread.currentThread().setName(original);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copies of several beta APIs in Guava to protect ourselves from potential API changes in the future.
3 | *
4 | * TODO: write a tool that checks the use of Guava beta APIs.
5 | */
6 | package org.jenkinsci.plugins.workflow.support.concurrent;
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/SecretPickle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 Jesse Glick.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles;
26 |
27 | import com.google.common.util.concurrent.ListenableFuture;
28 | import hudson.Extension;
29 | import hudson.util.Secret;
30 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
31 | import org.jenkinsci.plugins.workflow.support.concurrent.Futures;
32 |
33 | /**
34 | * {@link Pickle} of a {@link Secret} which stores the encrypted value.
35 | * Needed because {@link Secret} itself only provides an XStream converter, nothing for Java serialization, much less JBoss Marshalling.
36 | */
37 | public class SecretPickle extends Pickle {
38 |
39 | private final String encryptedValue;
40 |
41 | private SecretPickle(Secret secret) {
42 | encryptedValue = secret.getEncryptedValue();
43 | }
44 |
45 | @Override public ListenableFuture> rehydrate() {
46 | return Futures.immediateFuture(Secret.fromString(encryptedValue));
47 | }
48 |
49 | @Extension public static final class Factory extends SingleTypedPickleFactory {
50 |
51 | @Override protected Pickle pickle(Secret secret) {
52 | return new SecretPickle(secret);
53 | }
54 |
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/SingleTypedPickleFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles;
26 |
27 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
28 | import org.jenkinsci.plugins.workflow.pickles.PickleFactory;
29 | import hudson.Functions;
30 |
31 | import edu.umd.cs.findbugs.annotations.NonNull;
32 |
33 | /**
34 | * {@link PickleFactory} implementation for a common situation where only kind of ephemeral object is being pickled.
35 | * @param the ephemeral object type
36 | */
37 | public abstract class SingleTypedPickleFactory extends PickleFactory {
38 | private final Class type;
39 |
40 | @SuppressWarnings("unchecked")
41 | protected SingleTypedPickleFactory() {
42 | type = Functions.getTypeParameter(getClass(), SingleTypedPickleFactory.class, 0);
43 | }
44 |
45 | protected abstract @NonNull Pickle pickle(@NonNull T object);
46 |
47 | @Override public final Pickle writeReplace(Object object) {
48 | if (type.isInstance(object)) {
49 | return pickle(type.cast(object));
50 | }
51 | return null;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/ThrowablePickle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2018 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles;
26 |
27 | import com.google.common.util.concurrent.ListenableFuture;
28 | import hudson.Extension;
29 | import hudson.Functions;
30 | import hudson.remoting.ProxyException;
31 | import java.io.IOException;
32 | import java.io.NotSerializableException;
33 | import java.io.OutputStream;
34 | import java.util.logging.Level;
35 | import java.util.logging.Logger;
36 | import org.apache.commons.io.output.NullOutputStream;
37 | import org.jboss.marshalling.Marshaller;
38 | import org.jboss.marshalling.Marshalling;
39 | import org.jboss.marshalling.MarshallingConfiguration;
40 | import org.jboss.marshalling.river.RiverMarshallerFactory;
41 | import org.jenkinsci.plugins.scriptsecurity.sandbox.Whitelist;
42 | import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox;
43 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
44 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
45 | import org.jenkinsci.plugins.workflow.pickles.PickleFactory;
46 | import org.jenkinsci.plugins.workflow.support.concurrent.Futures;
47 | import org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter;
48 | import org.kohsuke.accmod.Restricted;
49 | import org.kohsuke.accmod.restrictions.NoExternalUse;
50 |
51 | /**
52 | * Ensures that exceptions are safely serializable.
53 | * Replaces anything problematic with {@link ProxyException}.
54 | * Mainly defends against {@link NotSerializableException}.
55 | */
56 | @Restricted(NoExternalUse.class)
57 | public final class ThrowablePickle extends Pickle {
58 |
59 | private static final Logger LOGGER = Logger.getLogger(ThrowablePickle.class.getName());
60 | private static final long serialVersionUID = 1;
61 |
62 | /** Stack trace of the original exception. */
63 | private final ProxyException t;
64 | /** Class name of the original exception. */
65 | private final String clazz;
66 | /** Stack trace of the problem serializing the original exception. */
67 | private final String error;
68 |
69 | private ThrowablePickle(Throwable t, Exception x) {
70 | LOGGER.log(Level.FINE, "Sanitizing {0} due to {1}", new Object[] {t, x});
71 | this.t = new ProxyException(t);
72 | clazz = t.getClass().getName();
73 | error = Functions.printThrowable(x);
74 | }
75 |
76 | @Override public ListenableFuture> rehydrate(FlowExecutionOwner owner) {
77 | try {
78 | owner.getListener().getLogger().println(error.trim());
79 | owner.getListener().getLogger().println("Loading unserializable exception; result will no longer be assignable to class " + clazz);
80 | } catch (IOException x) {
81 | LOGGER.log(Level.WARNING, null, x);
82 | }
83 | return Futures.immediateFuture(t);
84 | }
85 |
86 | @Extension public static final class Factory extends PickleFactory {
87 |
88 | /** @see RiverWriter */
89 | @Override public Pickle writeReplace(Object o) {
90 | if (o instanceof Throwable) {
91 | Throwable t = (Throwable) o;
92 | try (OutputStream ignore = new NullOutputStream();
93 | // Could set an ObjectResolver to ignore _other_ pickles, but we do really expect an Exception to have fields of, say, FilePath.
94 | Marshaller marshaller = new RiverMarshallerFactory().createMarshaller(new MarshallingConfiguration())) {
95 | GroovySandbox.runInSandbox(() -> {
96 | marshaller.start(Marshalling.createByteOutput(ignore));
97 | marshaller.writeObject(t);
98 | return null;
99 | }, Whitelist.all());
100 | } catch (Exception x) {
101 | return new ThrowablePickle(t, x);
102 | }
103 | }
104 | return null;
105 | }
106 |
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/TryRepeatedly.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles;
26 |
27 | import com.google.common.util.concurrent.AbstractFuture;
28 | import com.google.common.util.concurrent.ListenableFuture;
29 | import hudson.Functions;
30 | import hudson.console.ModelHyperlinkNote;
31 | import hudson.model.TaskListener;
32 | import java.io.IOException;
33 | import jenkins.util.Timer;
34 |
35 | import java.util.concurrent.ScheduledFuture;
36 | import java.util.concurrent.TimeUnit;
37 | import java.util.logging.Level;
38 | import java.util.logging.Logger;
39 | import edu.umd.cs.findbugs.annotations.CheckForNull;
40 | import edu.umd.cs.findbugs.annotations.NonNull;
41 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
42 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
43 |
44 | /**
45 | * {@link ListenableFuture} that promises a value that needs to be periodically tried.
46 | * Specialized for use from {@link Pickle#rehydrate(FlowExecutionOwner)}.
47 | */
48 | public abstract class TryRepeatedly extends AbstractFuture {
49 |
50 | private static final Logger LOGGER = Logger.getLogger(TryRepeatedly.class.getName());
51 |
52 | private final int delay;
53 | private ScheduledFuture> next;
54 | /** Number of {@link #tryLater} calls to run between logging attempts. */
55 | private float backoff = 1;
56 | /** Amount by which {@link #backoff} gets multiplied, so we do not flood the log with endless messages. */
57 | private static final float BACKOFF_EXPONENT = 1.42f; // >√2
58 | /** Reset to {@link #backoff} after each message, then decremented by on each call to {@link #tryLater}. */
59 | private int retriesRemaining;
60 |
61 | protected TryRepeatedly(int delay) {
62 | this(delay, delay);
63 | }
64 |
65 | protected TryRepeatedly(int delay, int initialDelay) {
66 | this.delay = delay;
67 | tryLater(initialDelay);
68 | }
69 |
70 | /**
71 | * Override to supply the owner passed to {@link Pickle#rehydrate(FlowExecutionOwner)}.
72 | */
73 | protected @NonNull FlowExecutionOwner getOwner() {
74 | return FlowExecutionOwner.dummyOwner();
75 | }
76 |
77 | /**
78 | * Assuming {@link #getOwner} has been overridden, override to print a message to the build log explaining why the pickle is still unloadable.
79 | * Could use {@link ModelHyperlinkNote} etc.
80 | */
81 | protected void printWaitingMessage(@NonNull TaskListener listener) {
82 | listener.getLogger().println("Still trying to load " + this);
83 | }
84 |
85 | private void tryLater(int currentDelay) {
86 | if (isCancelled()) {
87 | return;
88 | }
89 |
90 | next = Timer.get().schedule(new Runnable() {
91 | @Override
92 | public void run() {
93 | try {
94 | V v = tryResolve();
95 | if (v == null) {
96 | if (retriesRemaining == 0) {
97 | try {
98 | TaskListener listener = getOwner().getListener();
99 | try {
100 | printWaitingMessage(listener);
101 | } catch (Exception x) {
102 | Functions.printStackTrace(x, listener.getLogger());
103 | }
104 | } catch (IOException x) {
105 | LOGGER.log(Level.WARNING, null, x);
106 | }
107 | backoff *= BACKOFF_EXPONENT;
108 | retriesRemaining = (int) backoff;
109 | } else {
110 | retriesRemaining--;
111 | }
112 | tryLater(delay);
113 | } else {
114 | set(v);
115 | }
116 | } catch (Throwable t) {
117 | setException(t);
118 | }
119 | }
120 | }, currentDelay, TimeUnit.SECONDS);
121 | }
122 |
123 | @Override
124 | public boolean cancel(boolean mayInterruptIfRunning) {
125 | if (next != null) {
126 | next.cancel(mayInterruptIfRunning);
127 | }
128 | LOGGER.log(Level.FINE, "Cancelling {0} in {1}", new Object[] {this, getOwner()});
129 | return super.cancel(mayInterruptIfRunning);
130 | }
131 |
132 | /**
133 | * This method is called periodically to attempt to resolve the value that this future promises.
134 | *
135 | * @return
136 | * null to retry this at a later moment.
137 | * @throws Exception
138 | * Any exception thrown will cause the future to fail.
139 | */
140 | protected abstract @CheckForNull V tryResolve() throws Exception;
141 | }
142 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/XStreamPickle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles;
26 |
27 | import com.google.common.util.concurrent.ListenableFuture;
28 | import hudson.model.Items;
29 | import java.io.Serializable;
30 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
31 | import org.jenkinsci.plugins.workflow.support.concurrent.Futures;
32 |
33 | /**
34 | * A way of pickling Jenkins objects which have a well-defined XStream representation but are not {@link Serializable}.
35 | * Can also be used for objects which are {@link Serializable} but mistakenly so.
36 | * For any such type you wish to save, create a {@link SingleTypedPickleFactory} returning this pickle.
37 | *
Uses {@link Items#XSTREAM2} so suitable for things normally kept in job configuration.
38 | *
Note that the object ought to be self-contained and require no initialization,
39 | * so do not use this for anything with an {@code onLoad} or {@code setOwner} method, etc.
40 | */
41 | public final class XStreamPickle extends Pickle {
42 |
43 | private final String xml;
44 |
45 | public XStreamPickle(Object o) {
46 | xml = Items.XSTREAM2.toXML(o);
47 | }
48 |
49 | @Override public ListenableFuture> rehydrate() {
50 | return Futures.immediateFuture(Items.XSTREAM2.fromXML(xml));
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | /**
26 | * Helper classes to assist engines to serialize various Jenkins domain objects.
27 | */
28 | package org.jenkinsci.plugins.workflow.support.pickles;
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/serialization/DryCapsule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles.serialization;
26 |
27 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
28 |
29 | import java.io.Serializable;
30 |
31 | /**
32 | * Written into stream in place of an ephemeral value.
33 | *
34 | * The {@link Pickle} that represents how to restore them is written as a list into a separate stream,
35 | * and a capsule refers to them by index.
36 | *
37 | * {@link PickleResolver} replaces occurrences of this object by the actual value.
38 | *
39 | * @author Kohsuke Kawaguchi
40 | */
41 | class DryCapsule implements Serializable {
42 | final int id;
43 |
44 | public DryCapsule(int id) {
45 | this.id = id;
46 | }
47 |
48 | private static final long serialVersionUID = 1L;
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/serialization/DryOwner.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.workflow.support.pickles.serialization;
2 |
3 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
4 |
5 | import java.io.Serializable;
6 |
7 | /**
8 | * Replaces {@link FlowExecutionOwner} in the persisted object graph.
9 | *
10 | *
11 | * This allows the program state to be restored with the "correct" {@link FlowExecutionOwner}
12 | * that's implicit by the context in which the read-back happens.
13 | *
14 | * @author Kohsuke Kawaguchi
15 | * @see RiverReader#owner
16 | * @see RiverWriter#owner
17 | */
18 | public class DryOwner implements Serializable {
19 |
20 | private static final long serialVersionUID = 1L;
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/workflow/support/pickles/serialization/PickleResolver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013-2014, CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.support.pickles.serialization;
26 |
27 | import com.google.common.base.Function;
28 | import com.google.common.util.concurrent.Futures;
29 | import com.google.common.util.concurrent.ListenableFuture;
30 | import com.google.common.util.concurrent.MoreExecutors;
31 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
32 | import java.util.ArrayList;
33 | import java.util.Collection;
34 | import java.util.List;
35 | import java.util.concurrent.TimeUnit;
36 | import jenkins.util.SystemProperties;
37 | import jenkins.util.Timer;
38 | import org.jboss.marshalling.ObjectResolver;
39 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
40 | import org.jenkinsci.plugins.workflow.pickles.Pickle;
41 | import org.kohsuke.accmod.Restricted;
42 | import org.kohsuke.accmod.restrictions.NoExternalUse;
43 |
44 | /**
45 | * {@link ObjectResolver} that resolves {@link DryCapsule} to unpickled objects.
46 | *
47 | * @author Kohsuke Kawaguchi
48 | */
49 | public class PickleResolver implements ObjectResolver {
50 |
51 | /**
52 | * Pickle resolution will fail automatically after this many seconds.
53 | *
This is intended to prevent Pipeline builds from hanging forever in unusual cases.
54 | */
55 | @Restricted(NoExternalUse.class)
56 | @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Non-final for modification from script console")
57 | public static long RESOLUTION_TIMEOUT_SECONDS = SystemProperties.getLong(PickleResolver.class + ".RESOLUTION_TIMEOUT_SECONDS", TimeUnit.HOURS.toSeconds(1));
58 |
59 | /**
60 | * Persisted forms of the stateful objects.
61 | */
62 | private final List extends Pickle> pickles;
63 |
64 | /**
65 | * Unpicked objects from {@link #pickles}, when they are all ready.
66 | */
67 | private List