-
Notifications
You must be signed in to change notification settings - Fork 51
/
AbstractBinaryHandler.java
274 lines (246 loc) · 11.9 KB
/
AbstractBinaryHandler.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
266
267
268
269
270
271
272
273
274
package org.jboss.arquillian.drone.webdriver.binary.handler;
import java.io.File;
import java.net.URL;
import java.util.logging.Logger;
import org.jboss.arquillian.drone.webdriver.binary.BinaryFilesUtils;
import org.jboss.arquillian.drone.webdriver.binary.downloading.Downloader;
import org.jboss.arquillian.drone.webdriver.binary.downloading.ExternalBinary;
import org.jboss.arquillian.drone.webdriver.binary.downloading.source.ExternalBinarySource;
import org.jboss.arquillian.drone.webdriver.utils.Constants;
import org.jboss.arquillian.drone.webdriver.utils.PropertySecurityAction;
import org.jboss.arquillian.drone.webdriver.utils.Validate;
import org.openqa.selenium.remote.DesiredCapabilities;
/**
* Class that handles system properties, properties stored in capabilities, downloading, extracting and setting binaries
* as executable.
*
* @author <a href="mailto:mjobanek@redhat.com">Matous Jobanek</a>
*/
public abstract class AbstractBinaryHandler implements BinaryHandler {
private Logger log = Logger.getLogger(this.getClass().toString());
/**
* Capability property that sets downloading on or off. By default it is on
*/
public static final String DOWNLOAD_BINARIES_PROPERTY = "downloadBinaries";
/**
* Checks system properties and capabilities, whether a path to binary is already set there
* (see {@link AbstractBinaryHandler#getSystemBinaryProperty()} and {@link AbstractBinaryHandler#getBinaryProperty()} ).
* If not and the downloading is not set off ({@link AbstractBinaryHandler#DOWNLOAD_BINARIES_PROPERTY}), then the
* binary is downloaded. Resulting binary is then set as system property that is returned by the method
* {@link AbstractBinaryHandler#getSystemBinaryProperty()}
*
* @param performExecutableValidations If it should be checked whether the binary points to an executable file.
* @return Path to the binary
*/
@Override
public String checkAndSetBinary(boolean performExecutableValidations) {
String binary = PropertySecurityAction.getProperty(getSystemBinaryProperty());
if (Validate.empty(binary)) {
binary = PropertySecurityAction.getProperty(getBinaryProperty());
}
if (Validate.empty(binary) && !Validate.empty(getBinaryProperty())) {
binary = (String) getCapabilities().getCapability(getBinaryProperty());
}
if (Validate.empty(binary)) {
String downloadBinaries = (String) getCapabilities().getCapability(DOWNLOAD_BINARIES_PROPERTY);
if (Validate.empty(downloadBinaries)
|| (!downloadBinaries.toLowerCase().trim().equals("false")
&& !downloadBinaries.toLowerCase().trim().equals("no"))) {
try {
binary = downloadAndPrepare().toString();
} catch (Exception e) {
throw new IllegalStateException(
"Something bad happened when Drone was trying to download and prepare a binary. "
+ "For more information see the cause.", e);
}
}
}
setBinaryAsSystemProperty(performExecutableValidations, binary);
return binary;
}
/**
* Sets binary as a system property that is returned by method
* {@link AbstractBinaryHandler#getSystemBinaryProperty()}
*
* @param performExecutableValidations If it should be checked whether the binary points to an executable file.
* @param binary Path to the binary
*/
protected void setBinaryAsSystemProperty(boolean performExecutableValidations, String binary) {
if (Validate.nonEmpty(binary) && Validate.nonEmpty(getSystemBinaryProperty())) {
if (performExecutableValidations) {
Validate.isExecutable(binary,
"The binary must point to an executable file, " + binary);
}
PropertySecurityAction.setProperty(getSystemBinaryProperty(), binary);
}
}
/**
* This method consist of four steps:
* <br/>
* <h3>1. Checking properties</h3>
* In the first step it checks capabilities if there is set either url a binary should be downloaded from or
* a desired binary version. For more information see methods {@link AbstractBinaryHandler#getUrlToDownloadProperty()} and
* {@link AbstractBinaryHandler#getDesiredVersionProperty()}.
* <p>
* <h3>2. Downloading</h3>
* If the url is set then the binary is downloaded from the given url.
* <p>
* If there is set only the desired version, then a binary with the specified version is downloaded using an external
* binary source ({@link AbstractBinaryHandler#getExternalBinarySource()})
* </p>
* <p>
* If there is set neither a url nor a desired version, then a binary with the latest version is downloaded using
* the external binary source.
* </p>
* <p>
* <p>
* Directory where the downloaded file is stored depends on set properties. If there is set the desired version,
* or if the latest version is downloaded then the file is stored in:
* <br/>
* <code>$HOME/.arquillian/drone/ + </code>{@link AbstractBinaryHandler#getArquillianCacheSubdirectory()}<code>
* + / + version
* </code>
* <br/>
* If the version is not set, then the file is stored in: <code>target/drone/downloaded</code>
* </p>
* <p>
* <h3>3. Extraction/copy</h3>
* When the file is downloaded then it is expected that in most cases it is an archive (zip, tar) file. If the file is
* an archive then it is extracted; if the file is not an archive file, then it is copied. The targeted directory
* for this operation is:
* <br/>
* <code>target/drone/md5hash(downloaded_file)/</code>
* <p>
* <h3>4. Setting as executable</h3>
* In the last step the extracted/copied file is set as an executalbe file.
*
* @return An executable binary that was extracted/copied from the downloaded file
* @throws Exception If anything bad happens
*/
public File downloadAndPrepare() throws Exception {
String url = null;
if (!Validate.empty(getUrlToDownloadProperty())) {
url = (String) getCapabilities().getCapability(getUrlToDownloadProperty());
}
String desiredVersion = null;
if (!Validate.empty(getDesiredVersionProperty())) {
desiredVersion = (String) getCapabilities().getCapability(getDesiredVersionProperty());
}
if (Validate.nonEmpty(url)) {
if (Validate.empty(desiredVersion)) {
return downloadAndPrepare(null, url);
} else {
return downloadAndPrepare(createAndGetCacheDirectory(desiredVersion), url);
}
}
if (getExternalBinarySource() == null) {
return null;
}
ExternalBinary release = null;
if (Validate.nonEmpty(desiredVersion)) {
release = getExternalBinarySource().getReleaseForVersion(desiredVersion);
} else {
release = getExternalBinarySource().getLatestRelease();
}
return downloadAndPrepare(createAndGetCacheDirectory(release.getVersion()), release.getUrl());
}
/**
* Takes care of all steps but the first one of the method {@link AbstractBinaryHandler#downloadAndPrepare()}
*
* @param targetDir A directory where a downloaded binary should be stored
* @param from A url a binary should be downloaded from
* @return An executable binary that was extracted/copied from the downloaded file
* @throws Exception If anything bad happens
*/
protected File downloadAndPrepare(File targetDir, String from) throws Exception {
return downloadAndPrepare(targetDir, new URL(from));
}
/**
* Takes care of all steps but the first one of the method {@link AbstractBinaryHandler#downloadAndPrepare()}
*
* @param targetDir A directory where a downloaded binary should be stored
* @param from A url a binary should be downloaded from
* @return An executable binary that was extracted/copied from the downloaded file
* @throws Exception If anything bad happens
*/
protected File downloadAndPrepare(File targetDir, URL from) throws Exception {
File downloaded = Downloader.download(targetDir, from);
File extraction = BinaryFilesUtils.extract(downloaded);
File[] files = extraction.listFiles(file -> file.isFile());
if (files.length == 0) {
throw new IllegalStateException(
"The number of extracted files in the directory " + extraction + " is 0. There is no file to use");
}
return markAsExecutable(files[0]);
}
/**
* Sets the given binary to be executable (if it is not already set)
*
* @param binaryFile A binary file that should be set to be executable
* @return the given binary file set to be executable
*/
protected File markAsExecutable(File binaryFile) {
if (!Validate.executable(binaryFile.getAbsolutePath())) {
log.info("marking binary file: " + binaryFile.getPath() + " as executable");
try {
binaryFile.setExecutable(true);
} catch (SecurityException se) {
log.severe("The downloaded binary: " + binaryFile
+ " could not be set as executable. This may cause additional problems.");
}
}
return binaryFile;
}
private File createAndGetCacheDirectory(String subdirectory) {
String dirPath = Constants.ARQUILLIAN_DRONE_CACHE_DIRECTORY
+ getArquillianCacheSubdirectory()
+ (subdirectory == null ? "" : File.separator + subdirectory);
File dir = new File(dirPath);
dir.mkdirs();
return dir;
}
/**
* This method should return a capability property name which a path to an executable binary could be stored under
*
* @return A capability property name which a path to an executable binary could be stored under
*/
protected abstract String getBinaryProperty();
/**
* This method should return a system property name which a path to an executable binary should be stored under
*
* @return A system property name which a path to an executable binary should be stored under
*/
public abstract String getSystemBinaryProperty();
/**
* Name of the subdirectory that should be used for this binary handler in the Drone cache directory
* <code>($HOME/.arquillian/drone)</code>
*
* @return Name of the subdirectory that should be used for this binary handler in the Drone cache directory
*/
protected abstract String getArquillianCacheSubdirectory();
/**
* This method should return a capability property name which a desired version of a binary could be stored under
*
* @return A capability property name under a desired version of a binary could be stored under
*/
protected abstract String getDesiredVersionProperty();
/**
* This method should return a capability property name which a url pointing to a desired binary could be stored under
*
* @return A capability property name which a url pointing to a desired binary could be stored under
*/
protected abstract String getUrlToDownloadProperty();
/**
* This method should return an instance of an {@link ExternalBinary} that should be used for retrieving available
* releases of a binary
*
* @return An instance of an {@link ExternalBinary} that should be used for retrieving available releases of a binary
*/
protected abstract ExternalBinarySource getExternalBinarySource();
/**
* This method should return a desired capabilities with stored properties
*
* @return A desired capabilities with stored properties
*/
protected abstract DesiredCapabilities getCapabilities();
}