package io.jitstatic.hosted;

/*-
 * #%L
 * jitstatic
 * %%
 * Copyright (C) 2017 - 2018 H.Hegardt
 * %%
 * 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.
 * #L%
 */

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.PreUploadHookChain;
import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.WantNotValidException;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JitStaticUploadPackFactory implements UploadPackFactory<HttpServletRequest> {

    private static final Logger LOG = LoggerFactory.getLogger(UploadPack.class);
    private final ExecutorService executorService;
    private final RefLockHolderManager refLockHolderManager;
    private final String defaultRef;

    public JitStaticUploadPackFactory(final ExecutorService executorService, final RefLockHolderManager refLockHolderManager, final String defaultRef) {
        this.executorService = Objects.requireNonNull(executorService);
        this.refLockHolderManager = Objects.requireNonNull(refLockHolderManager);
        this.defaultRef = Objects.requireNonNull(defaultRef);
    }

    static class ServiceConfig {
        final boolean enabled;

        ServiceConfig(final Config cfg) {
            enabled = cfg.getBoolean("http", "uploadpack", true);
        }
    }

    /** {@inheritDoc} */
    @Override
    public UploadPack create(final HttpServletRequest req, final Repository db) throws ServiceNotEnabledException, ServiceNotAuthorizedException {
        if (db.getConfig().get(ServiceConfig::new).enabled) {
            final PackConfig cfg = new PackConfig(db.getConfig());
            cfg.setExecutor(executorService);
            final UploadPack jitStaticUploadPack = new UploadPack(db) {
                @Override
                public void upload(final InputStream input, final OutputStream output, final OutputStream messages) throws IOException {
                    try {
                        refLockHolderManager.getRefHolder(defaultRef).enqueueAndReadBlock(() -> {
                            try {
                                super.upload(input, output, messages);
                            } catch (IOException e) {
                                throw new UnwrapException(e);
                            }
                            return "upload-pack OK";
                        }).join();
                    } catch (CompletionException ce) {
                        final Throwable cause = ce.getCause().getCause();
                        if (cause instanceof UploadPackInternalServerErrorException) {
                            final Throwable wnve = cause.getCause();
                            if (wnve instanceof WantNotValidException) {
                                LOG.info("{}, aborting...", wnve.getMessage());
                            } else {
                                throw (IOException) cause;
                            }
                        } else {
                            throw (IOException) cause;
                        }
                    }
                }
            };
            jitStaticUploadPack.setRequestPolicy(RequestPolicy.ADVERTISED);
            jitStaticUploadPack.setPackConfig(cfg);
            jitStaticUploadPack.setPreUploadHook(PreUploadHookChain.newChain(List.of(new LogoPoster())));
            jitStaticUploadPack.setRefFilter(new JitStaticRefFilter(req));
            return jitStaticUploadPack;
        } else
            throw new ServiceNotEnabledException();
    }

    private static class UnwrapException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public UnwrapException(IOException e) {
            super(e);
        }
    }

}

@@ -1626,16 +1626,71 @@
 ctory;%0A%0A
+import io.jitstatic.utils.ShouldNeverHappenException;%0A%0A
 public c
@@ -3745,16 +3745,145 @@
 tCause()
+;%0A                        if (cause instanceof UnwrapException) %7B%0A                            final Throwable secondCause = cause
 .getCaus
@@ -3879,32 +3879,36 @@
 use.getCause();%0A
+    
                 
@@ -3911,33 +3911,39 @@
             if (
-c
+secondC
 ause instanceof 
@@ -4004,32 +4004,36 @@
                 
+    
 final Throwable 
@@ -4035,25 +4035,31 @@
 able wnve = 
-c
+secondC
 ause.getCaus
@@ -4055,32 +4055,36 @@
 use.getCause();%0A
+    
                 
@@ -4172,16 +4172,20 @@
         
+    
 LOG.info
@@ -4248,33 +4248,81 @@
             
+  
+  
 %7D else 
-%7B%0A
+if (secondCause instanceof IOException) %7B%0A    
             
@@ -4353,33 +4353,39 @@
 w (IOException) 
-c
+secondC
 ause;%0A          
@@ -4394,34 +4394,42 @@
                 
-%7D%0A
+    %7D%0A    
                 
@@ -4465,32 +4465,36 @@
                 
+    
 throw (IOExcepti
@@ -4497,21 +4497,220 @@
 eption) 
-cause
+secondCause;%0A                            %7D%0A                        %7D else %7B%0A                            throw new ShouldNeverHappenException(%22Unknown exception happen while reading the repository%22, cause)
 ;%0A      

Fix for possible NPE if cause could be different