Package org.sonatype.nexus.bootstrap.jetty

Source Code of org.sonatype.nexus.bootstrap.jetty.JettyServer$JettyMainThread

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.bootstrap.jetty;

import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.sonatype.nexus.bootstrap.PropertyMap;
import org.sonatype.nexus.bootstrap.ShutdownHelper;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Jetty server.
*
* @since 2.8
*/
public class JettyServer
{
  private static final Logger log = LoggerFactory.getLogger(JettyServer.class);

  private final ClassLoader classLoader;

  private final Map<String, String> properties;

  private final String[] args;

  private JettyMainThread thread;

  public JettyServer(final ClassLoader classLoader, final Map<String, String> properties, final String[] args) {
    if (classLoader == null) {
      throw new NullPointerException();
    }
    this.classLoader = classLoader;

    if (properties == null) {
      throw new NullPointerException();
    }
    this.properties = properties;

    if (args == null) {
      throw new NullPointerException();
    }
    this.args = args;
  }

  private Exception propagateThrowable(final Throwable e) throws Exception {
    if (e instanceof RuntimeException) {
      throw (RuntimeException) e;
    }
    else if (e instanceof Exception) {
      throw (Exception) e;
    }
    else if (e instanceof Error) {
      throw (Error) e;
    }
    throw new Error(e);
  }

  public synchronized void start(final boolean waitForServer) throws Exception {
    final ClassLoader cl = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(classLoader);

    try {
      final AtomicReference<Throwable> exception = new AtomicReference<>();
      AccessController.doPrivileged(new PrivilegedAction<Object>()
      {
        public Object run() {
          try {
            doStart(waitForServer);
          }
          catch (Exception e) {
            exception.set(e);
          }
          return null;
        }
      });

      Throwable e = exception.get();
      if (e != null) {
        log.error("Start failed", e);
        throw propagateThrowable(e);
      }
    }
    finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  private void doStart(boolean waitForServer) throws Exception {
    if (thread != null) {
      throw new IllegalStateException("Already started");
    }

    log.info("Starting");

    List<LifeCycle> components = new ArrayList<>();

    PropertyMap props = new PropertyMap();
    props.putAll(JettyServer.this.properties);

    // For all arguments, load properties or parse XMLs
    XmlConfiguration last = null;
    for (String arg : args) {
      URL url = Resource.newResource(arg).getURL();

      if (url.getFile().toLowerCase(Locale.ENGLISH).endsWith(".properties")) {
        log.info("Loading properties: {}", url);

        props.load(url);
      }
      else {
        log.info("Applying configuration: {}", url);

        XmlConfiguration configuration = new XmlConfiguration(url);
        if (last != null) {
          configuration.getIdMap().putAll(last.getIdMap());
        }
        if (!props.isEmpty()) {
          configuration.getProperties().putAll(props);
        }
        Object component = configuration.configure();
        if (component instanceof LifeCycle) {
          components.add((LifeCycle) component);
        }
        last = configuration;
      }
    }

    // complain if no components configured
    if (components.isEmpty()) {
      throw new Exception("Failed to configure any components");
    }

    thread = new JettyMainThread(components);
    thread.setContextClassLoader(classLoader);
    thread.startComponents(waitForServer);
  }

  public synchronized void stop() throws Exception {
    final ClassLoader cl = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(classLoader);

    try {
      final AtomicReference<Throwable> exception = new AtomicReference<>();
      AccessController.doPrivileged(new PrivilegedAction<Object>()
      {
        public Object run() {
          try {
            doStop();
          }
          catch (Exception e) {
            exception.set(e);
          }
          return null;
        }
      });

      Throwable e = exception.get();
      if (e != null) {
        log.error("Stop failed", e);
        throw propagateThrowable(e);
      }
    }
    finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  private void doStop() throws Exception {
    if (thread == null) {
      throw new IllegalStateException("Not started");
    }

    log.info("Stopping");

    thread.stopComponents();
    thread = null;

    log.info("Stopped");
  }

  /**
   * Jetty thread used to start components, wait for the server's threads to join and stop components.
   *
   * Needed so that once {@link JettyServer#stop()} returns that we know that the server has actually stopped,
   * which is required for embedding.
   */
  private static class JettyMainThread
      extends Thread
  {
    private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(1);

    private final List<LifeCycle> components;

    private final CountDownLatch started;

    private final CountDownLatch stopped;

    private volatile Exception exception;

    public JettyMainThread(final List<LifeCycle> components) {
      super("jetty-main-" + INSTANCE_COUNTER.getAndIncrement());
      this.components = components;
      this.started = new CountDownLatch(1);
      this.stopped = new CountDownLatch(1);
    }

    @Override
    public void run() {
      try {
        Server server = null;
        try {
          for (LifeCycle component : components) {
            if (!component.isRunning()) {
              log.info("Starting: {}", component);
              component.start();
            }

            // capture the server reference
            if (component instanceof Server) {
              server = (Server) component;
            }
          }
        }
        catch (Exception e) {
          exception = e;
        }
        finally {
          started.countDown();
        }

        if (server != null) {
          log.info("Started");
          server.join();
        }
        else {
          log.error("Failed to start", exception);
          ShutdownHelper.exit(-1);
        }
      }
      catch (InterruptedException e) {
        // nothing
      }
      finally {
        stopped.countDown();
      }
    }

    public void startComponents(boolean waitForServer) throws Exception {
      start();

      if (waitForServer) {
        started.await();
      }

      if (exception != null) {
        throw exception;
      }
    }

    public void stopComponents() throws Exception {
      Collections.reverse(components);

      // if Jetty thread is still waiting for a component to start, this should unblock it
      interrupt();

      for (LifeCycle component : components) {
        if (component.isRunning()) {
          log.info("Stopping: {}", component);
          component.stop();
        }
      }

      components.clear();
      stopped.await();
    }
  }
}
TOP

Related Classes of org.sonatype.nexus.bootstrap.jetty.JettyServer$JettyMainThread

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.