// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.slim; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import fitnesse.slim.fixtureInteraction.FixtureInteraction; import fitnesse.socketservice.PlainServerSocketFactory; import fitnesse.socketservice.ServerSocketFactory; import fitnesse.socketservice.SslServerSocketFactory; import util.CommandLine; import static fitnesse.slim.JavaSlimFactory.*; public class SlimService { private static final String OPTION_DESCRIPTOR = "[-v] [-i interactionClass] [-s statementTimeout] [-d] [-ssl parameterClass] port"; public static class Options { public final boolean verbose; public final int port; public final FixtureInteraction interaction; /** * daemon mode: keep accepting new connections indefinitely. */ public final boolean daemon; public final Integer statementTimeout; final boolean useSSL; final String sslParameterClassName; public Options(boolean verbose, int port, FixtureInteraction interaction, boolean daemon, Integer statementTimeout, boolean useSSL, String sslParameterClassName) { this.verbose = verbose; this.port = port; this.interaction = interaction; this.daemon = daemon; this.statementTimeout = statementTimeout; this.useSSL = useSSL; this.sslParameterClassName = sslParameterClassName; } } private final ServerSocket serverSocket; private final SlimServer slimServer; private final boolean daemon; private final Executor executor = Executors.newFixedThreadPool(5); public static void main(String[] args) throws IOException { Options options = parseCommandLine(args); if (options != null) { try { startWithFactory(createJavaSlimFactory(options), options); System.exit(0); } catch (Exception e) { e.printStackTrace(); System.err.println("Exiting as exception occured: " + e.getMessage()); System.exit(98); } } else { parseCommandLineFailed(args); System.exit(97); } } protected static void parseCommandLineFailed(String[] args) { System.err.println("Invalid command line arguments: " + Arrays.asList(args)); System.err.println("Usage:"); System.err.println(" " + SlimService.class.getName() + " " + OPTION_DESCRIPTOR); } public static void startWithFactory(SlimFactory slimFactory, Options options) throws IOException { ServerSocket socket; if (options.port == 1) { socket = new SlimPipeSocket(); if (options.daemon) { System.err .println("Warning: in Slim Pipe mode the daemon flag is not supported."); } } else { ServerSocketFactory serverSocketFactory = options.useSSL ? new SslServerSocketFactory( true, options.sslParameterClassName) : new PlainServerSocketFactory(); socket = serverSocketFactory.createServerSocket(options.port); } try { SlimService slimservice = new SlimService(slimFactory.getSlimServer(), socket, options.daemon); slimservice.accept(); } catch (java.lang.OutOfMemoryError e) { System.err.println("Out of Memory. Aborting."); e.printStackTrace(); System.exit(99); throw e; } catch (BindException e) { System.err.println("Can not bind to port " + options.port + ". Aborting."); e.printStackTrace(); throw e; } } public static Options parseCommandLine(String[] args) { CommandLine commandLine = new CommandLine(OPTION_DESCRIPTOR); if (commandLine.parse(args)) { boolean verbose = commandLine.hasOption("v"); String interactionClassName = commandLine.getOptionArgument("i", "interactionClass"); String portString = commandLine.getArgument("port"); int port = (portString == null) ? 1 : Integer.parseInt(portString); String statementTimeoutString = commandLine.getOptionArgument("s", "statementTimeout"); Integer statementTimeout = (statementTimeoutString == null) ? null : Integer.parseInt(statementTimeoutString); boolean daemon = commandLine.hasOption("d"); String sslParameterClassName = commandLine.getOptionArgument("ssl", "parameterClass"); boolean useSSL = commandLine.hasOption("ssl"); FixtureInteraction interaction = createInteraction(interactionClassName); return new Options(verbose, port, interaction, daemon, statementTimeout, useSSL, sslParameterClassName); } return null; } public SlimService(SlimServer slimServer, ServerSocket serverSocket, boolean daemon) throws IOException { this.daemon = daemon; this.slimServer = slimServer; this.serverSocket = serverSocket; // } public int getPort() { return serverSocket.getLocalPort(); } public void accept() throws IOException { try { if (daemon) { acceptMany(); } else { acceptOne(); } } catch (java.lang.OutOfMemoryError e) { System.err.println("Out of Memory. Aborting"); e.printStackTrace(); System.exit(99); } finally { serverSocket.close(); } } private void acceptMany() throws IOException { while (true) { final Socket socket = serverSocket.accept(); executor.execute(new Runnable() { @Override public void run() { try { handle(socket); } catch (IOException e) { throw new SlimError(e); } } }); } } private void handle(Socket socket) throws IOException { try { slimServer.serve(socket); } finally { socket.close(); } } private void acceptOne() throws IOException { Socket socket = serverSocket.accept(); handle(socket); } }