// Copyright (C) 2015 The Android Open Source Project
//
// 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 com.google.gerrit.httpd.plugins;

import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;

import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.httpd.resources.Resource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.plugins.Plugin;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
import com.google.gwtexpui.server.CacheHeaders;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.servlet.GuiceFilter;

import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Singleton
public class LfsPluginServlet extends HttpServlet
    implements StartPluginListener, ReloadPluginListener {
  private static final long serialVersionUID = 1L;
  private static final Logger log
      = LoggerFactory.getLogger(LfsPluginServlet.class);

  public static final String URL_REGEX =
      "^(?:/a)?(?:/p/|/)(.+)(?:/info/lfs/objects/batch)$";

  private List<Plugin> pending = new ArrayList<>();
  private final String pluginName;
  private final FilterChain chain;
  private GuiceFilter filter;

  @Inject
  LfsPluginServlet(@GerritServerConfig Config cfg) {
    this.pluginName = cfg.getString("lfs", null, "plugin");
    this.chain = new FilterChain() {
      @Override
      public void doFilter(ServletRequest req, ServletResponse res)
          throws IOException {
        Resource.NOT_FOUND.send(
            (HttpServletRequest) req, (HttpServletResponse) res);
      }
    };
  }

  @Override
  protected void service(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    if (filter == null) {
      CacheHeaders.setNotCacheable(res);
      res.sendError(SC_NOT_IMPLEMENTED);
      return;
    }
    filter.doFilter(req, res, chain);
  }

  @Override
  public synchronized void init(ServletConfig config) throws ServletException {
    super.init(config);

    for (Plugin plugin : pending) {
      install(plugin);
    }
    pending = null;
  }

  @Override
  public synchronized void onStartPlugin(Plugin plugin) {
    if (pending != null) {
      pending.add(plugin);
    } else {
      install(plugin);
    }
  }

  @Override
  public void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
    install(newPlugin);
  }

  private void install(Plugin plugin) {
    if (!plugin.getName().equals(pluginName)) {
      return;
    }
    filter = load(plugin);
    plugin.add(new RegistrationHandle() {
      @Override
      public void remove() {
        filter = null;
      }
    });
  }

  private GuiceFilter load(Plugin plugin) {
    if (plugin.getHttpInjector() != null) {
      final String name = plugin.getName();
      final GuiceFilter guiceFilter;
      try {
        guiceFilter = plugin.getHttpInjector().getInstance(GuiceFilter.class);
      } catch (RuntimeException e) {
        log.warn(String.format("Plugin %s cannot load GuiceFilter", name), e);
        return null;
      }

      try {
        ServletContext ctx =
            PluginServletContext.create(plugin, "/");
        guiceFilter.init(new WrappedFilterConfig(ctx));
      } catch (ServletException e) {
        log.warn(String.format("Plugin %s failed to initialize HTTP", name), e);
        return null;
      }

      plugin.add(new RegistrationHandle() {
        @Override
        public void remove() {
          guiceFilter.destroy();
        }
      });
      return guiceFilter;
    }
    return null;
  }
}

@@ -1404,16 +1404,68 @@
 il.List;
+%0Aimport java.util.concurrent.atomic.AtomicReference;
 %0A%0Aimport
@@ -2300,32 +2300,48 @@
 hain;%0A  private 
+AtomicReference%3C
 GuiceFilter filt
@@ -2335,16 +2335,17 @@
 ceFilter
+%3E
  filter;
@@ -2735,16 +2735,59 @@
 %0A    %7D;%0A
+    this.filter = new AtomicReference%3C%3E();%0A
   %7D%0A%0A  @
@@ -2928,16 +2928,22 @@
  (filter
+.get()
  == null
@@ -3059,16 +3059,22 @@
  filter.
+get().
 doFilter
@@ -3693,24 +3693,47 @@
     %7D%0A    fi
+nal GuiceFilter guiceFi
 lter = load(
@@ -3842,23 +3842,49 @@
   filter
- =
+.compareAndSet(guiceFilter,
  null
+)
 ;%0A      
@@ -3893,16 +3893,45 @@
     %7D);%0A
+    filter.set(guiceFilter);%0A
   %7D%0A%0A  p

Fix plugin reload