Package runjettyrun

Source Code of runjettyrun.Bootstrap

package runjettyrun;
/*
* $Id: Bootstrap.java 279 2011-12-18 07:50:51Z tonylovejava@gmail.com $
* $HeadURL: https://run-jetty-run.googlecode.com/svn/trunk/bootstrap/src/runjettyrun/Bootstrap.java $
*
* ==============================================================================
* 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.
*/

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.resource.FileResource;
import org.mortbay.resource.Resource;
import org.mortbay.resource.ResourceCollection;
import org.mortbay.util.Scanner;
import org.mortbay.xml.XmlConfiguration;

import runjettyrun.scanner.RJRFileChangeListener;

/**
* Started up by the plugin's runner. Starts Jetty.
*
* @author hillenius, jsynge, jumperchen
*/
public class Bootstrap {

  private static Server server;

  private static WebAppContext web;

  /**
   * Main function, starts the jetty server.
   *
   * @param args
   */
  public static void main(String[] args) throws Exception {
    System.err.println("Running Jetty 6.1.26");

    final Configs configs = new Configs();

    configs.validation();

    server = new Server();

    initConnnector(server, configs);

    initWebappContext(server,configs);


    if(configs.getJettyXML() != null && !"".equals(configs.getJettyXML().trim())){
      System.err.println("Loading Jetty.xml:"+configs.getJettyXML());
      try{
        XmlConfiguration configuration = new XmlConfiguration(
            new File(configs.getJettyXML()).toURI().toURL());
        configuration.configure(server);
      }catch(Exception ex){
        System.err.println("Exception happened when loading Jetty.xml:");
        ex.printStackTrace();
      }
    }

    // configureScanner
    if (configs.getEnablescanner())
      initScanner(web, configs);

    initEclipseListener(configs);
    initCommandListener(configs);

    try {
      server.start();
      server.join();
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(100);
    }
    return;
  }

  /*
   * It's too hard to build a built-in supprot with the red button,
   * actually we get very close with this feature,
   * I could event handle this well when user is not in debug mode.
   *
   * But when we are running under debug mode ,
   * the DebugTarget will terminate JVM directly even before it terminate process.
   *
   *  I got no chance to prevent the default behavior ,
   *  (Maybe it did , but I am too tired to look into the details right now ,
   *   it's not fun to work with JDT core. lol)
   *
   *  So I think the simply workaround is ,
   *  when user want to test with graceful shutdown ,
   *  please type the shutdown command in console by himself .
   *
   *  Isn't it really a simple solution ? ;)
   *
   *  Also we create a way to let user restart the server by command ,
   *  so we are going to supprot following command after 1.3
   *
   *  "restart" or "r"  : restart the server like what scanner do , it's case-insensitive.
   *  "quit" or "q" or "exit" : shutdown the server.
   *
   *  Actually I don't think this is a common issue , it should be some "debugging" case,
   *  and will not be a common case, if you have good use case on this , we could check this later.
   *
   *  If you think it's not good enough ,we are also looking for
   *  good suggestion to implement this as possible as we could.
   *
   *  Maybe we will provide a Jetty view to make it simpler in future,
   *  that depends how many user need this feature.
   *
   */
  private static void initCommandListener(final Configs configs){
    //init eclipse hook
    Thread commandListener = new Thread(){
      public void run() {
        try {
          BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
          while(true){
            String inputStr = input.readLine();
            if(inputStr != null){
              inputStr = inputStr.trim();

              if("exit".equalsIgnoreCase(inputStr) ||
                  "quit".equalsIgnoreCase(inputStr) ||
                  "q".equalsIgnoreCase(inputStr)
              ){
                System.err.println("shutting down");
                shutdownServer();
              }else if("r".equalsIgnoreCase(inputStr)
                  || "restart".equalsIgnoreCase(inputStr)
              ){
                try{
                  System.err.println("Stopping webapp ...");
                  web.stop();

                  if (configs.getWebAppClassPath() != null) {
                    ProjectClassLoader loader = new ProjectClassLoader(web,
                        configs.getWebAppClassPath(),configs.getExcludedclasspath(), false);
                    web.setClassLoader(loader);
                  }
                  System.err.println("Restarting webapp ...");
                  web.start();
                  System.err.println("Restart completed.");
                } catch (Exception e) {
                  System.err
                  .println("Error reconfiguring/restarting webapp after change in watched files");
                  e.printStackTrace();
                }
              }

            }
          }

        } catch (UnknownHostException e) {
          System.err.println("lost connection with Eclipse , shutting down.");
          shutdownServer();
        } catch (IOException e) {
          System.err.println("lost connection with Eclipse , shutting down.");
          shutdownServer();
        }
      };
    };
    commandListener.start();

  }
  private static void initEclipseListener(final Configs configs){
    //init eclipse hook
    if(configs.getEclipseListenerPort() != -1 ){
      Thread eclipseListener = new Thread(){
        public void run() {
          try {
            while(true){
              Thread.sleep(5000L);
              Socket sock = new Socket("127.0.0.1", configs.getEclipseListenerPort());
              byte[] response = new byte[4];
              sock.getInputStream().read(response);

              //@see runjettyrun.Plugin#enableListenter
              //TODO applied on Jetty7 and Jetty8
              if(response[0] ==1 && response[1] ==2){
                //it's ok!
              }else{
                //Eclipse crashs
                shutdownServer();
              }

            }

          } catch (UnknownHostException e) {
            System.err.println("lost connection with Eclipse , shutting down.");
            shutdownServer();
          } catch (IOException e) {
            System.err.println("lost connection with Eclipse , shutting down.");
            shutdownServer();
          } catch (InterruptedException e) {
            System.err.println("lost connection with Eclipse , shutting down.");
            shutdownServer();
          }
        };
      };
      eclipseListener.start();
    }

  }

  private static void shutdownServer(){
    try {
      server.stop();
      System.exit(-1);
    } catch (Exception e1) {
      e1.printStackTrace();
    }

  }

  private static void initWebappContext(Server server,Configs configs)
    throws IOException, URISyntaxException {
    web = new WebAppContext();

    if (configs.getParentLoaderPriority()) {
      System.err.println("ParentLoaderPriority enabled");
      web.setParentLoaderPriority(true);
    }

    web.setContextPath(configs.getContext());
    System.err.println("Context path:"+configs.getContext());
    web.setWar(configs.getWebAppDir());

    /**
     * open a way to set the configuration classes
     */
    List<String> configurationClasses = configs.getConfigurationClassesList();
    if (configurationClasses.size() != 0) {
      web.setConfigurationClasses(configurationClasses.toArray(new String[0]));

      for (String conf : configurationClasses)
        System.err.println("Enable config class:" + conf);

    }


    // Fix issue 7, File locking on windows/Disable Jetty's locking of
    // static files
    // http://code.google.com/p/run-jetty-run/issues/detail?id=7
    // by disabling the use of the file mapped buffers. The default Jetty
    // behavior is
    // intended to provide a performance boost, but run-jetty-run is focused
    // on
    // development (especially debugging) of web apps, not high-performance
    // production
    // serving of static content. Therefore, I'm not worried about the
    // performance
    // degradation of this change. My only concern is that there might be a
    // need to
    // test this feature that I'm disabling.
    web.setInitParams(Collections.singletonMap(
        "org.mortbay.jetty.servlet.Default.useFileMappedBuffer",
        "false"));

    if (configs.getWebAppClassPath() != null) {
      ProjectClassLoader loader = new ProjectClassLoader(web,
          configs.getWebAppClassPath(),configs.getExcludedclasspath());
      web.setClassLoader(loader);
    }


    List<Resource> resources = new ArrayList<Resource>();

    URL urlWebapp = new File(configs.getWebAppDir()).toURI().toURL();
    Resource webapp = new FileResource(urlWebapp);
    resources.add(webapp);

    Map<String,String> map = configs.getResourceMap();
    for(String key : map.keySet()){
/*
*       URL url = new File(map.get(key)).toURI().toURL();
      Resource resource;
      try {
        resource = new FileResource(url);
        final ResourceHandler handler = new ResourceHandler();
        handler.setBaseResource(resource);
        handler.setServer(server);
        handler.setContextPath(key);
        web.addHandler(handler);
      } catch (URISyntaxException e) {
        e.printStackTrace();
      }

*/
      resources.add(new VirtualResource(webapp,"/"+key,map.get(key)));
//      final WebAppContext js = new WebAppContext();
//      js.setContextPath(key);
//      js.setResourceBase(map.get(key)); // or whatever the correct path is in your case
//      js.setParentLoaderPriority(true);
//      server.addHandler(js);
    }

    ResourceCollection webAppDirResources = new ResourceCollection();
    webAppDirResources.setResources(resources.toArray(new Resource[0]));
        web.setBaseResource(webAppDirResources);

    server.addHandler(web);
  }

  private static void initConnnector(Server server, Configs configObj) {
    SelectChannelConnector connector = new SelectChannelConnector();

    //Don't set any host , or the port detection will failed. -_-#
    //connector.setHost("127.0.0.1");
    connector.setPort(configObj.getPort());

    if (configObj.getEnablessl() && configObj.getSslport() != null){
      if (!available(configObj.getSslport())) {
        throw new IllegalStateException("SSL port :" + configObj.getSslport()
            + " already in use!");
      }
      connector.setConfidentialPort(configObj.getSslport());
    }

    server.addConnector(connector);

    if (configObj.getEnablessl() && configObj.getSslport() != null)
      initSSL(server, configObj.getSslport(), configObj.getKeystore(),
          configObj.getPassword(), configObj.getKeyPassword(),
          configObj.getNeedClientAuth());

  }


  private static boolean available(int port) {
    if (port <= 0) {
      throw new IllegalArgumentException("Invalid start port: " + port);
    }

    ServerSocket ss = null;
    DatagramSocket ds = null;
    try {
      ss = new ServerSocket(port);
      ss.setReuseAddress(true);
      ds = new DatagramSocket(port);
      ds.setReuseAddress(true);
      return true;
    } catch (IOException e) {
    } finally {
      if (ds != null) {
        ds.close();
      }

      if (ss != null) {
        try {
          ss.close();
        } catch (IOException e) {
          /* should not be thrown */
        }
      }
    }

    return false;
  }

  private static void initSSL(Server server, int sslport, String keystore,
      String password, String keyPassword, boolean needClientAuth) {

    if (keystore == null) {
      throw new IllegalStateException(
          "you need to provide argument -Drjrkeystore with -Drjrsslport");
    }
    if (password == null) {
      throw new IllegalStateException(
          "you need to provide argument -Drjrpassword with -Drjrsslport");
    }
    if (keyPassword == null) {
      throw new IllegalStateException(
          "you need to provide argument -Drjrkeypassword with -Drjrsslport");
    }

    SslSocketConnector sslConnector = new SslSocketConnector();
    sslConnector.setKeystore(keystore);
    sslConnector.setPassword(password);
    sslConnector.setKeyPassword(keyPassword);

    if (needClientAuth) {
      System.err.println("Enable NeedClientAuth.");
      sslConnector.setNeedClientAuth(needClientAuth);
    }
    sslConnector.setMaxIdleTime(30000);
    sslConnector.setPort(sslport);

    server.addConnector(sslConnector);
  }


  /**
   * add source scanner to restart server when source change
   * @param web
   * @param webAppClassPath
   * @param scanIntervalSeconds
   */
  private static void initScanner(final WebAppContext web,
      final Configs config ) {

    int scanIntervalSeconds = config.getScanIntervalSeconds();

    final ArrayList<File> scanList = new ArrayList<File>();

    System.err.println("init scanning folders...");
    if (config.getScanlist() != null) {
      String[] items = config.getScanlist().split(File.pathSeparator);
      for (String item:items) {
        File f = new File(item);
        scanList.add(f);
        System.err.println("add to scan list:"+item);
      }
    }

    // startScanner
    Scanner scanner = new Scanner();
    scanner.setScanInterval(scanIntervalSeconds);
    scanner.setScanDirs(scanList);
    scanner.setRecursive(true);
    scanner.setReportExistingFilesOnStartup(true);
    scanner.addListener(new RJRFileChangeListener(web,config));
    System.err.println("Starting scanner at interval of "
        + scanIntervalSeconds + " seconds.");
    scanner.start();
  }


}
TOP

Related Classes of runjettyrun.Bootstrap

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.