Package com.google.gerrit.gwtdebug

Source Code of com.google.gerrit.gwtdebug.GerritDebugLauncher$MyWebAppContext$MyLoader

/*
* Copyright 2008 Google Inc.
*
* 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.gwtdebug;

import com.google.gwt.core.ext.ServletContainer;
import com.google.gwt.core.ext.ServletContainerLauncher;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.shell.jetty.JettyNullLogger;

import org.mortbay.component.AbstractLifeCycle;
import org.mortbay.jetty.AbstractConnector;
import org.mortbay.jetty.HttpFields.Field;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.RequestLog;
import org.mortbay.jetty.Response;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.RequestLogHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppClassLoader;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.log.Log;
import org.mortbay.log.Logger;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;

public class GerritDebugLauncher extends ServletContainerLauncher {
  /**
   * Log jetty requests/responses to TreeLogger.
   */
  public static class JettyRequestLogger extends AbstractLifeCycle implements
      RequestLog {

    private final TreeLogger logger;

    public JettyRequestLogger(TreeLogger logger) {
      this.logger = logger;
    }

    /**
     * Log an HTTP request/response to TreeLogger.
     */
    @SuppressWarnings("unchecked")
    public void log(Request request, Response response) {
      int status = response.getStatus();
      if (status < 0) {
        // Copied from NCSARequestLog
        status = 404;
      }
      TreeLogger.Type logStatus, logHeaders;
      if (status >= 500) {
        logStatus = TreeLogger.ERROR;
        logHeaders = TreeLogger.INFO;
      } else if (status >= 400) {
        logStatus = TreeLogger.WARN;
        logHeaders = TreeLogger.INFO;
      } else {
        logStatus = TreeLogger.INFO;
        logHeaders = TreeLogger.DEBUG;
      }
      String userString = request.getRemoteUser();
      if (userString == null) {
        userString = "";
      } else {
        userString += "@";
      }
      String bytesString = "";
      if (response.getContentCount() > 0) {
        bytesString = " " + response.getContentCount() + " bytes";
      }
      if (logger.isLoggable(logStatus)) {
        TreeLogger branch =
            logger.branch(logStatus, String.valueOf(status) + " - "
                + request.getMethod() + ' ' + request.getUri() + " ("
                + userString + request.getRemoteHost() + ')' + bytesString);
        if (branch.isLoggable(logHeaders)) {
          // Request headers
          TreeLogger headers = branch.branch(logHeaders, "Request headers");
          Iterator<Field> headerFields =
              request.getConnection().getRequestFields().getFields();
          while (headerFields.hasNext()) {
            Field headerField = headerFields.next();
            headers.log(logHeaders, headerField.getName() + ": "
                + headerField.getValue());
          }
          // Response headers
          headers = branch.branch(logHeaders, "Response headers");
          headerFields = response.getHttpFields().getFields();
          while (headerFields.hasNext()) {
            Field headerField = headerFields.next();
            headers.log(logHeaders, headerField.getName() + ": "
                + headerField.getValue());
          }
        }
      }
    }
  }

  /**
   * An adapter for the Jetty logging system to GWT's TreeLogger. This
   * implementation class is only public to allow {@link Log} to instantiate it.
   *
   * The weird static data / default construction setup is a game we play with
   * {@link Log}'s static initializer to prevent the initial log message from
   * going to stderr.
   */
  public static class JettyTreeLogger implements Logger {
    private final TreeLogger logger;

    public JettyTreeLogger(TreeLogger logger) {
      if (logger == null) {
        throw new NullPointerException();
      }
      this.logger = logger;
    }

    public void debug(String msg, Object arg0, Object arg1) {
      logger.log(TreeLogger.SPAM, format(msg, arg0, arg1));
    }

    public void debug(String msg, Throwable th) {
      logger.log(TreeLogger.SPAM, msg, th);
    }

    public Logger getLogger(String name) {
      return this;
    }

    public void info(String msg, Object arg0, Object arg1) {
      logger.log(TreeLogger.INFO, format(msg, arg0, arg1));
    }

    public boolean isDebugEnabled() {
      return logger.isLoggable(TreeLogger.SPAM);
    }

    public void setDebugEnabled(boolean enabled) {
      // ignored
    }

    public void warn(String msg, Object arg0, Object arg1) {
      logger.log(TreeLogger.WARN, format(msg, arg0, arg1));
    }

    public void warn(String msg, Throwable th) {
      logger.log(TreeLogger.WARN, msg, th);
    }

    /**
     * Copied from org.mortbay.log.StdErrLog.
     */
    private String format(String msg, Object arg0, Object arg1) {
      int i0 = msg.indexOf("{}");
      int i1 = i0 < 0 ? -1 : msg.indexOf("{}", i0 + 2);

      if (arg1 != null && i1 >= 0) {
        msg = msg.substring(0, i1) + arg1 + msg.substring(i1 + 2);
      }
      if (arg0 != null && i0 >= 0) {
        msg = msg.substring(0, i0) + arg0 + msg.substring(i0 + 2);
      }
      return msg;
    }
  }

  /**
   * The resulting {@link ServletContainer} this is launched.
   */
  protected static class JettyServletContainer extends ServletContainer {
    private final int actualPort;
    private final File appRootDir;
    private final TreeLogger logger;
    private final Server server;
    private final WebAppContext wac;

    public JettyServletContainer(TreeLogger logger, Server server,
        WebAppContext wac, int actualPort, File appRootDir) {
      this.logger = logger;
      this.server = server;
      this.wac = wac;
      this.actualPort = actualPort;
      this.appRootDir = appRootDir;
    }

    @Override
    public int getPort() {
      return actualPort;
    }

    @Override
    public void refresh() throws UnableToCompleteException {
      String msg =
          "Reloading web app to reflect changes in "
              + appRootDir.getAbsolutePath();
      TreeLogger branch = logger.branch(TreeLogger.INFO, msg);
      // Temporarily log Jetty on the branch.
      Log.setLog(new JettyTreeLogger(branch));
      try {
        wac.stop();
        wac.start();
        branch.log(TreeLogger.INFO, "Reload completed successfully");
      } catch (Exception e) {
        branch.log(TreeLogger.ERROR, "Unable to restart embedded Jetty server",
            e);
        throw new UnableToCompleteException();
      } finally {
        // Reset the top-level logger.
        Log.setLog(new JettyTreeLogger(logger));
      }
    }

    @Override
    public void stop() throws UnableToCompleteException {
      TreeLogger branch =
          logger.branch(TreeLogger.INFO, "Stopping Jetty server");
      // Temporarily log Jetty on the branch.
      Log.setLog(new JettyTreeLogger(branch));
      try {
        server.stop();
        server.setStopAtShutdown(false);
        branch.log(TreeLogger.INFO, "Stopped successfully");
      } catch (Exception e) {
        branch.log(TreeLogger.ERROR, "Unable to stop embedded Jetty server", e);
        throw new UnableToCompleteException();
      } finally {
        // Reset the top-level logger.
        Log.setLog(new JettyTreeLogger(logger));
      }
    }
  }

  /**
   * A {@link WebAppContext} tailored to GWT hosted mode. Features hot-reload
   * with a new {@link WebAppClassLoader} to pick up disk changes. The default
   * Jetty {@code WebAppContext} will create new instances of servlets, but it
   * will not create a brand new {@link ClassLoader}. By creating a new {@code
   * ClassLoader} each time, we re-read updated classes from disk.
   *
   * Also provides special class filtering to isolate the web app from the GWT
   * hosting environment.
   */
  protected final class MyWebAppContext extends WebAppContext {
    /**
     * Parent ClassLoader for the Jetty web app, which can only load JVM
     * classes. We would just use <code>null</code> for the parent ClassLoader
     * except this makes Jetty unhappy.
     */
    private final ClassLoader bootStrapOnlyClassLoader =
        new ClassLoader(null) {};

    private final ClassLoader systemClassLoader =
        Thread.currentThread().getContextClassLoader();

    @SuppressWarnings("unchecked")
    private MyWebAppContext(String webApp, String contextPath) {
      super(webApp, contextPath);

      // Prevent file locking on Windows; pick up file changes.
      getInitParams().put(
          "org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false");

      // Since the parent class loader is bootstrap-only, prefer it first.
      setParentLoaderPriority(true);
    }

    @Override
    protected void doStart() throws Exception {
      setClassLoader(new MyLoader());
      super.doStart();
    }

    @Override
    protected void doStop() throws Exception {
      super.doStop();
      setClassLoader(null);
    }

    private class MyLoader extends WebAppClassLoader {
      MyLoader() throws IOException {
        super(bootStrapOnlyClassLoader, MyWebAppContext.this);

        final URLClassLoader scl = (URLClassLoader) systemClassLoader;
        final URL[] urls = scl.getURLs();
        for (URL u : urls) {
          if ("file".equals(u.getProtocol())) {
            addClassPath(u.getPath());
          }
        }
      }

      @Override
      public boolean isSystemPath(String name) {
        name = name.replace('/', '.');
        return super.isSystemPath(name) //
            || name.startsWith("org.bouncycastle.");
      }

      @Override
      protected Class<?> findClass(String name) throws ClassNotFoundException {
        // For system path, always prefer the outside world.
        if (isSystemPath(name)) {
          try {
            return systemClassLoader.loadClass(name);
          } catch (ClassNotFoundException e) {
          }
        }
        return super.findClass(name);
      }
    }
  }

  static {
    // Suppress spammy Jetty log initialization.
    System
        .setProperty("org.mortbay.log.class", JettyNullLogger.class.getName());
    Log.getLog();

    /*
     * Make JDT the default Ant compiler so that JSP compilation just works
     * out-of-the-box. If we don't set this, it's very, very difficult to make
     * JSP compilation work.
     */
    String antJavaC =
        System.getProperty("build.compiler",
            "org.eclipse.jdt.core.JDTCompilerAdapter");
    System.setProperty("build.compiler", antJavaC);

    System.setProperty("Gerrit.GwtDevMode", "" + true);
  }

  private String bindAddress = null;

  @Override
  public void setBindAddress(String bindAddress) {
    this.bindAddress = bindAddress;
  }

  @Override
  public ServletContainer start(TreeLogger logger, int port, File warDir)
      throws Exception {
    TreeLogger branch =
        logger.branch(TreeLogger.INFO, "Starting Jetty on port " + port, null);

    checkStartParams(branch, port, warDir);

    // Setup our branch logger during startup.
    Log.setLog(new JettyTreeLogger(branch));

    // Turn off XML validation.
    System.setProperty("org.mortbay.xml.XmlParser.Validating", "false");

    AbstractConnector connector = getConnector();
    if (bindAddress != null) {
      connector.setHost(bindAddress.toString());
    }
    connector.setPort(port);

    // Don't share ports with an existing process.
    connector.setReuseAddress(false);

    // Linux keeps the port blocked after shutdown if we don't disable this.
    connector.setSoLingerTime(0);

    Server server = new Server();
    server.addConnector(connector);

    // warDir is "$top/gerrit-gwtui/target/gwt-hosted-mode"
    //
    File top = warDir.getParentFile().getParentFile().getParentFile();
    File app = new File(top, "gerrit-war/src/main/webapp");
    File webxml = new File(app, "WEB-INF/web.xml");

    // Jetty won't start unless this directory exists.
    if (!warDir.exists() && !warDir.mkdirs())
      logger.branch(TreeLogger.ERROR, "Cannot create "+warDir, null);

    // Create a new web app in the war directory.
    //
    WebAppContext wac =
        new MyWebAppContext(warDir.getAbsolutePath(), "/");
    wac.setDescriptor(webxml.getAbsolutePath());

    RequestLogHandler logHandler = new RequestLogHandler();
    logHandler.setRequestLog(new JettyRequestLogger(logger));
    logHandler.setHandler(wac);
    server.setHandler(logHandler);
    server.start();
    server.setStopAtShutdown(true);

    // Now that we're started, log to the top level logger.
    Log.setLog(new JettyTreeLogger(logger));

    return new JettyServletContainer(logger, server, wac, connector
        .getLocalPort(), warDir);
  }

  protected AbstractConnector getConnector() {
    return new SelectChannelConnector();
  }

  private void checkStartParams(TreeLogger logger, int port, File appRootDir) {
    if (logger == null) {
      throw new NullPointerException("logger cannot be null");
    }

    if (port < 0 || port > 65535) {
      throw new IllegalArgumentException(
          "port must be either 0 (for auto) or less than 65536");
    }

    if (appRootDir == null) {
      throw new NullPointerException("app root direcotry cannot be null");
    }
  }
}
TOP

Related Classes of com.google.gerrit.gwtdebug.GerritDebugLauncher$MyWebAppContext$MyLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.