-
Notifications
You must be signed in to change notification settings - Fork 11
/
BuildProxy.java
265 lines (229 loc) · 9.42 KB
/
BuildProxy.java
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package hudson.plugins.helpers;
import hudson.FilePath;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.util.IOException2;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* Proxy for the key build information.
*
* @author Stephen Connolly
* @since 12-Jan-2008 12:08:32
*/
public final class BuildProxy implements Serializable {
// ------------------------------ FIELDS ------------------------------
private final FilePath artifactsDir;
private final FilePath projectRootDir;
private final FilePath buildRootDir;
private final FilePath executionRootDir;
private final Calendar timestamp;
private final List<AbstractBuildAction<AbstractBuild<?, ?>>> actions =
new ArrayList<AbstractBuildAction<AbstractBuild<?, ?>>>();
private Result result = null;
private boolean continueBuild = true;
// -------------------------- STATIC METHODS --------------------------
/**
* (Call from master) Invokes the ghostwriter on the master and slave nodes for this build.
*
* @param ghostwriter The ghostwriter that will be doing the work for the publisher.
* @param build The build.
* @param listener The build listener.
* @return {@code true} if the build can continue.
* @throws IOException on IOException.
* @throws InterruptedException on InterruptedException.
*/
public static boolean doPerform(Ghostwriter ghostwriter,
AbstractBuild<?, ?> build,
BuildListener listener)
throws IOException, InterruptedException {
// first, do we need to do anything on the slave
if (ghostwriter instanceof Ghostwriter.SlaveGhostwriter) {
// construct the BuildProxy instance that we will use
BuildProxy buildProxy = new BuildProxy(
//TODO: It is not compatible with custom artifact managers
new FilePath(build.getArtifactsDir()),
new FilePath(build.getProject().getRootDir()),
new FilePath(build.getRootDir()),
build.getModuleRoot(),
build.getTimestamp());
BuildProxyCallableHelper callableHelper = new BuildProxyCallableHelper(buildProxy, ghostwriter, listener);
try {
buildProxy = buildProxy.getExecutionRootDir().act(callableHelper);
buildProxy.updateBuild(build);
// terminate the build if necessary
if (!buildProxy.isContinueBuild()) {
return false;
}
} catch (Exception e) {
throw unwrapException(e, listener);
}
}
// finally, on to the master
final Ghostwriter.MasterGhostwriter masterGhostwriter = Ghostwriter.MasterGhostwriter.class.cast(ghostwriter);
return masterGhostwriter == null
|| masterGhostwriter.performFromMaster(build, build.getModuleRoot(), listener);
}
//TODO: this logic undermines error propagation in the code
/**
* Takes a remote exception that has been wrapped up in the remoting layer, and rethrows it as IOException,
* InterruptedException or if all else fails, a RuntimeException.
*
* @param e The wrapped exception.
* @param listener The listener for the build.
* @return never.
* @throws IOException if the wrapped exception is an IOException.
* @throws InterruptedException if the wrapped exception is an InterruptedException.
* @throws RuntimeException if the wrapped exception is neither an IOException nor an InterruptedException.
*/
private static RuntimeException unwrapException(Exception e,
BuildListener listener)
throws IOException, InterruptedException {
if (e.getCause() instanceof IOException) {
throw new IOException(e.getCause().getMessage(), e);
}
if (e.getCause() instanceof InterruptedException) {
e.getCause().printStackTrace(listener.getLogger());
throw new InterruptedException(e.getCause().getMessage());
}
if (e.getCause() instanceof RuntimeException) {
RuntimeException ex = new RuntimeException(e.getCause());
// It is required to triage JEP-200 security exceptions
ex.addSuppressed(e);
throw ex;
}
// How on earth do we get this far down the branch
e.printStackTrace(listener.getLogger());
throw new RuntimeException("Unexpected exception", e);
}
/**
* (Designed for execution from the master) Updates the build with the results that were reported to this proxy.
*
* @param build The build to update.
*/
public void updateBuild(AbstractBuild<?, ?> build) {
// update the actions
for (AbstractBuildAction<AbstractBuild<?, ?>> action : actions) {
if (!build.getActions().contains(action)) {
action.setBuild(build);
build.getActions().add(action);
}
}
// update the result
if (result != null && result.isWorseThan(build.getResult())) {
build.setResult(result);
}
}
// --------------------------- CONSTRUCTORS ---------------------------
/**
* Constructs a new build proxy that encapsulates all the information that a build step should need from the
* slave.
*
* @param artifactsDir The artifacts directory on the master.
* @param projectRootDir The project directory on the master (i.e. the .../hudson/jobs/ProjectName/). Note for
* multi-module projects it will be .../hudson/jobs/ProjectName/modules/ModuleName/.
* @param buildRootDir The build results directory on the master.
* @param executionRootDir The build base directory on the slave.
* @param timestamp The time when the build started executing.
*/
private BuildProxy(FilePath artifactsDir,
FilePath projectRootDir,
FilePath buildRootDir,
FilePath executionRootDir,
Calendar timestamp) {
this.artifactsDir = artifactsDir;
this.projectRootDir = projectRootDir;
this.buildRootDir = buildRootDir;
this.executionRootDir = executionRootDir;
this.timestamp = timestamp;
}
// --------------------- GETTER / SETTER METHODS ---------------------
/**
* Getter for property 'actions'.
*
* @return Value for property 'actions'.
*/
public List<AbstractBuildAction<AbstractBuild<?, ?>>> getActions() {
return actions;
}
/**
* Gets the directory (on the master) where the artifacts are archived.
*
* @return the directory (on the master) where the artifacts are archived.
*/
public FilePath getArtifactsDir() {
return artifactsDir;
}
/**
* Root directory of the {@link hudson.model.AbstractBuild} on the master.
* Files related to the {@link hudson.model.AbstractBuild} should be stored below this directory.
*
* @return Root directory of the {@link hudson.model.AbstractBuild} on the master.
*/
public FilePath getBuildRootDir() {
return buildRootDir;
}
/**
* Returns the root directory of the checked-out module on the machine where the build executes.
* This is usually where <tt>pom.xml</tt>, <tt>build.xml</tt>
* and so on exists.
*
* @return Returns the root directory of the checked-out module on the machine where the build executes.
*/
public FilePath getExecutionRootDir() {
return executionRootDir;
}
/**
* Root directory of the {@link hudson.model.AbstractProject} on the master.
* Files related to the {@link hudson.model.AbstractProject} should be stored below this directory.
*
* @return Root directory of the {@link hudson.model.AbstractProject} on the master.
*/
public FilePath getProjectRootDir() {
return projectRootDir;
}
/**
* Getter for property 'result'.
*
* @return Value for property 'result'.
*/
public Result getResult() {
return result;
}
/**
* Setter for property 'result'.
*
* @param result Value to set for property 'result'.
*/
public void setResult(Result result) {
this.result = result;
}
/**
* When the build is scheduled.
*
* @return The time when the build started executing.
*/
public Calendar getTimestamp() {
return timestamp;
}
/**
* Getter for property 'continueBuild'.
*
* @return Value for property 'continueBuild'.
*/
public boolean isContinueBuild() {
return continueBuild;
}
/**
* Setter for property 'continueBuild'.
*
* @param continueBuild Value to set for property 'continueBuild'.
*/
public void setContinueBuild(boolean continueBuild) {
this.continueBuild = continueBuild;
}
}