forked from jenkinsci/scriptler-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGroovyScript.java
More file actions
173 lines (151 loc) · 5.83 KB
/
GroovyScript.java
File metadata and controls
173 lines (151 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package org.jenkinsci.plugins.scriptler.util;
import edu.umd.cs.findbugs.annotations.NonNull;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.TaskListener;
import java.io.PrintStream;
import java.io.Serial;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import jenkins.security.Roles;
import org.apache.commons.collections.map.LRUMap;
import org.jenkinsci.plugins.scriptler.Messages;
import org.jenkinsci.plugins.scriptler.config.Parameter;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
/**
* Inspired by hudson.util.RemotingDiagnostics.Script, but adding parameters.
*/
public class GroovyScript extends MasterToSlaveCallable<Object, RuntimeException> {
@Serial
private static final long serialVersionUID = 1L;
private final String script;
@NonNull
private final Collection<Parameter> parameters;
private final boolean failWithException;
private final TaskListener listener;
private final transient AbstractBuild<?, ?> build;
private final transient Launcher launcher;
private transient ClassLoader cl;
@SuppressWarnings("unchecked")
private static final Map<String, ConcurrentLinkedQueue<Script>> cache = Collections.synchronizedMap(new LRUMap(10));
private static final Set<String> DEFAULT_VARIABLES = new HashSet<>();
static {
DEFAULT_VARIABLES.add("out");
DEFAULT_VARIABLES.add("build");
DEFAULT_VARIABLES.add("listener");
DEFAULT_VARIABLES.add("launcher");
}
/**
* This constructor can only be used when the script is executed on the built-in node, because launcher and build
* can not be transferred to an agent and therefore the execution will fail
* @param script the script to be executed
* @param parameters the parameters to be passed to the script
* @param failWithException should the job fail with an exception
* @param listener access to logging via listener
* @param launcher the launcher
* @param build the current build
*/
public GroovyScript(
String script,
@NonNull Collection<Parameter> parameters,
boolean failWithException,
TaskListener listener,
Launcher launcher,
AbstractBuild<?, ?> build) {
this.script = script;
this.parameters = new ArrayList<>(parameters);
this.failWithException = failWithException;
this.listener = listener;
this.cl = getClassLoader();
this.build = build;
this.launcher = launcher;
}
/**
* Constructor
* @param script the script to be executed
* @param parameters the parameters to be passed to the script
* @param failWithException should the job fail with an exception
* @param listener access to logging via listener
*/
public GroovyScript(
String script,
@NonNull Collection<Parameter> parameters,
boolean failWithException,
TaskListener listener) {
this(script, parameters, failWithException, listener, null, null);
}
public ClassLoader getClassLoader() {
return Jenkins.get().getPluginManager().uberClassLoader;
}
public Object call() {
// if we run locally, cl!=null. Otherwise the delegating classloader will be available as context classloader.
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
}
PrintStream logger = listener.getLogger();
GroovyShell shell = new GroovyShell(cl);
for (Parameter param : parameters) {
final String paramName = param.getName();
if (DEFAULT_VARIABLES.contains(paramName)) {
logger.println(Messages.skipParameter(paramName));
} else {
shell.setVariable(paramName, param.getValue());
}
}
// set default variables
shell.setVariable("out", logger);
shell.setVariable("listener", listener);
if (build != null) shell.setVariable("build", build);
if (launcher != null) shell.setVariable("launcher", launcher);
ConcurrentLinkedQueue<Script> scriptPool = cache.get(script);
if (scriptPool == null) {
scriptPool = new ConcurrentLinkedQueue<>();
cache.put(script, scriptPool);
scriptPool = cache.get(script);
}
Script parsedScript = scriptPool.poll();
try {
if (parsedScript == null) {
parsedScript = shell.parse(script);
}
parsedScript.setBinding(shell.getContext());
Object output = parsedScript.run();
if (output != null) {
logger.println(Messages.resultPrefix() + " " + output);
return output;
} else {
return "";
}
} catch (Throwable t) {
if (failWithException) {
throw new ScriptlerExecutionException(t);
}
t.printStackTrace(logger);
return Boolean.FALSE;
} finally {
if (parsedScript != null) {
scriptPool.add(parsedScript);
}
}
}
@Override
public void checkRoles(RoleChecker roleChecker) throws SecurityException {
if (launcher != null && build != null) {
roleChecker.check(this, Roles.MASTER);
} else {
roleChecker.check(this, Role.UNKNOWN);
}
}
private static final class ScriptlerExecutionException extends RuntimeException {
@Serial
private static final long serialVersionUID = 1L;
public ScriptlerExecutionException(Throwable cause) {
super(cause);
}
}
}