1616// under the License.
1717package org .openqa .selenium .manager ;
1818
19+ import static java .nio .file .StandardCopyOption .REPLACE_EXISTING ;
20+ import static java .util .Objects .requireNonNull ;
21+ import static java .util .UUID .randomUUID ;
1922import static org .openqa .selenium .Platform .LINUX ;
2023import static org .openqa .selenium .Platform .MAC ;
2124import static org .openqa .selenium .Platform .UNIX ;
@@ -213,11 +216,10 @@ private synchronized Path getBinary() {
213216 }
214217
215218 binary = getBinaryInCache (SELENIUM_MANAGER + extension );
216- if (!binary .toFile ().exists ()) {
217- String binaryPathInJar = String .format ("%s/%s%s" , folder , SELENIUM_MANAGER , extension );
218- try (InputStream inputStream = this .getClass ().getResourceAsStream (binaryPathInJar )) {
219+ if (!Files .exists (binary )) {
220+ try (InputStream inputStream = findBinaryInClasspath (folder , extension )) {
219221 Files .createDirectories (binary .getParent ());
220- Files . copy (inputStream , binary );
222+ saveToFileSafely (inputStream , binary );
221223 }
222224 }
223225 } catch (Exception e ) {
@@ -233,6 +235,29 @@ private synchronized Path getBinary() {
233235 return binary ;
234236 }
235237
238+ /**
239+ * Protect from concurrency issue when executed by 2+ processes simultaneously. Every process sees
240+ * the file created by another process only when the file is fully completed.
241+ */
242+ private void saveToFileSafely (InputStream inputStream , Path target ) throws IOException {
243+ Path temporaryFile = target .resolveSibling (target .getFileName () + "." + randomUUID () + ".tmp" );
244+ Files .copy (inputStream , temporaryFile );
245+ try {
246+ if (!Files .exists (target )) {
247+ Files .move (temporaryFile , target , REPLACE_EXISTING );
248+ }
249+ } finally {
250+ Files .deleteIfExists (temporaryFile );
251+ }
252+ }
253+
254+ private InputStream findBinaryInClasspath (String folder , String extension ) {
255+ String binaryPathInJar = String .format ("%s/%s%s" , folder , SELENIUM_MANAGER , extension );
256+ return requireNonNull (
257+ getClass ().getResourceAsStream (binaryPathInJar ),
258+ () -> "Resource not found in classpath: " + binaryPathInJar );
259+ }
260+
236261 /**
237262 * Executes Selenium Manager to get the locations of the requested assets
238263 *
0 commit comments