/*
 * Copyright 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.jvm.toolchain.install.internal;

import org.gradle.api.GradleException;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.cache.FileLock;
import org.gradle.internal.exceptions.Contextual;
import org.gradle.jvm.toolchain.JavaToolchainSpec;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.io.File;
import java.util.Optional;

public class DefaultJavaToolchainProvisioningService implements JavaToolchainProvisioningService {

    public static final String AUTO_DOWNLOAD = "org.gradle.java.installations.auto-download";

    @Contextual
    private static class MissingToolchainException extends GradleException {

        public MissingToolchainException(JavaToolchainSpec spec, @Nullable Throwable cause) {
            super("Unable to download toolchain matching these requirements: " + spec.getDisplayName(), cause);
        }

    }

    private final AdoptOpenJdkRemoteBinary openJdkBinary;
    private final JdkCacheDirectory cacheDirProvider;
    private final Provider<Boolean> downloadEnabled;

    @Inject
    public DefaultJavaToolchainProvisioningService(AdoptOpenJdkRemoteBinary openJdkBinary, JdkCacheDirectory cacheDirProvider, ProviderFactory factory) {
        this.openJdkBinary = openJdkBinary;
        this.cacheDirProvider = cacheDirProvider;
        this.downloadEnabled = factory.gradleProperty(AUTO_DOWNLOAD).forUseAtConfigurationTime().map(Boolean::parseBoolean);
    }

    public Optional<File> tryInstall(JavaToolchainSpec spec) {
        if (!isAutoDownloadEnabled()) {
            return Optional.empty();
        }
        String destinationFilename = openJdkBinary.toFilename(spec);
        File destinationFile = cacheDirProvider.getDownloadLocation(destinationFilename);
        final FileLock fileLock = cacheDirProvider.acquireWriteLock(destinationFile, "Downloading toolchain");
        try {
            final Optional<File> jdkArchive;
            if (destinationFile.exists()) {
                jdkArchive = Optional.of(destinationFile);
            } else {
                jdkArchive = openJdkBinary.download(spec, destinationFile);
            }
            return jdkArchive.map(cacheDirProvider::provisionFromArchive);
        } catch (Exception e) {
            throw new MissingToolchainException(spec, e);
        } finally {
            fileLock.close();
        }

    }

    private boolean isAutoDownloadEnabled() {
        return downloadEnabled.getOrElse(true);
    }

}

@@ -1714,16 +1714,82 @@
 Enabled;
+%0A    private static Object provisioningProcessLock = new Object();
 %0A%0A    @I
@@ -2233,24 +2233,77 @@
 pec spec) %7B%0A
+        synchronized (provisioningProcessLock) %7B%0A    
         if (
@@ -2338,24 +2338,28 @@
             
+  
+  
 return Optio
@@ -2379,26 +2379,33 @@
         
-%7D%0A
+    %7D%0A   
         
 String d
@@ -2396,16 +2396,17 @@
         
+ 
 String d
@@ -2458,16 +2458,20 @@
 (spec);%0A
+    
         
@@ -2548,24 +2548,28 @@
 nFilename);%0A
+    
         fina
@@ -2675,16 +2675,20 @@
         
+    
 try %7B%0A  
@@ -2681,32 +2681,363 @@
       try %7B%0A    
+            return provisionJdk(spec, destinationFile);%0A            %7D catch (Exception e) %7B%0A                throw new MissingToolchainException(spec, e);%0A            %7D finally %7B%0A                fileLock.close();%0A            %7D%0A        %7D%0A    %7D%0A%0A    private Optional%3CFile%3E provisionJdk(JavaToolchainSpec spec, File destinationFile) %7B%0A
         final Op
@@ -3065,28 +3065,24 @@
 ve;%0A        
-   
- 
 if (destinat
@@ -3097,28 +3097,24 @@
 exists()) %7B%0A
-    
             
@@ -3148,36 +3148,32 @@
 stinationFile);%0A
-    
         %7D else %7B
@@ -3177,36 +3177,32 @@
 e %7B%0A            
-    
 jdkArchive = ope
@@ -3245,36 +3245,32 @@
 nFile);%0A        
-    
 %7D%0A            re
@@ -3259,28 +3259,24 @@
   %7D%0A        
- 
-   
 return jdkAr
@@ -3330,159 +3330,8 @@
 e);%0A
-        %7D catch (Exception e) %7B%0A            throw new MissingToolchainException(spec, e);%0A        %7D finally %7B%0A            fileLock.close();%0A        %7D%0A%0A
     

Synchronize access to provisioning within daemon