001// Copyright 2009, 2010, 2011, 2013 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.test; 016 017import org.apache.commons.cli.*; 018import org.apache.tapestry5.test.constants.TapestryRunnerConstants; 019import org.eclipse.jetty.server.Server; 020import org.eclipse.jetty.server.ssl.SslSocketConnector; 021import org.eclipse.jetty.util.ssl.SslContextFactory; 022import org.eclipse.jetty.webapp.WebAppContext; 023 024import java.io.File; 025 026/** 027 * Launches an instance of Jetty. 028 */ 029public class JettyRunner implements ServletContainerRunner 030{ 031 private Server jettyServer; 032 033 private String description; 034 035 private int port; 036 037 private int sslPort; 038 039 public JettyRunner() 040 { 041 // un-configured runner 042 } 043 044 public JettyRunner(String webappFolder, String contextPath, int port, int sslPort) throws Exception 045 { 046 configure(webappFolder, contextPath, port, sslPort).start(); 047 } 048 049 public JettyRunner configure(String webappFolder, String contextPath, int port, int sslPort) throws Exception 050 { 051 this.port = port; 052 053 this.sslPort = sslPort; 054 055 String expandedPath = expand(webappFolder); 056 057 description = String.format("<JettyRunner: %s:%s/%s (%s)", contextPath, port, sslPort, expandedPath); 058 059 jettyServer = new Server(port); 060 061 WebAppContext webapp = new WebAppContext(); 062 webapp.setContextPath(contextPath); 063 webapp.setWar(expandedPath); 064 065 // SSL support 066 File keystoreFile = new File(TapestryRunnerConstants.MODULE_BASE_DIR, "src/test/conf/keystore"); 067 068 if (keystoreFile.exists()) 069 { 070 SslContextFactory sslContextFactory = new SslContextFactory(); 071 072 sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); 073 074 sslContextFactory.setKeyStorePassword("tapestry"); 075 076 sslContextFactory.setKeyManagerPassword("tapestry"); 077 078 SslSocketConnector sslConnector = new SslSocketConnector(sslContextFactory); 079 080 sslConnector.setPort(sslPort); 081 082 jettyServer.addConnector(sslConnector); 083 } 084 085 jettyServer.setHandler(webapp); 086 return this; 087 } 088 089 public void start() throws Exception 090 { 091 jettyServer.start(); 092 } 093 094 /** 095 * Immediately shuts down the server instance. 096 */ 097 @Override 098 public void stop() 099 { 100 System.out.printf("Stopping Jetty instance on port %d/%d\n", port, sslPort); 101 102 try 103 { 104 // Stop immediately and not gracefully. 105 jettyServer.stop(); 106 } catch (Exception ex) 107 { 108 throw new RuntimeException("Error stopping Jetty instance: " + ex.toString(), ex); 109 } 110 111 System.out.println("Jetty instance has stopped."); 112 } 113 114 public Server getServer() 115 { 116 return jettyServer; 117 } 118 119 @Override 120 public String toString() 121 { 122 return description; 123 } 124 125 /** 126 * Needed inside Maven multi-projects to expand a path relative to the module to a complete 127 * path. If the path already is absolute and points to an existing directory, it will be used 128 * unchanged. 129 * 130 * @param moduleLocalPath 131 * @return expanded path 132 * @see TapestryRunnerConstants#MODULE_BASE_DIR 133 */ 134 protected String expand(String moduleLocalPath) 135 { 136 File path = new File(moduleLocalPath); 137 138 // Don't expand if the path provided already exists. 139 if (path.isAbsolute() && path.isDirectory()) 140 return moduleLocalPath; 141 142 return new File(TapestryRunnerConstants.MODULE_BASE_DIR, moduleLocalPath).getPath(); 143 } 144 145 /** 146 * Main entrypoint used to run the Jetty instance from the command line. 147 * 148 * @since 5.4 149 */ 150 public static void main(String[] args) throws Exception 151 { 152 String commandName = JettyRunner.class.getName(); 153 154 Options options = new Options(); 155 156 String webapp = "src/main/webapp"; 157 String context = "/"; 158 int httpPort = 8080; 159 int sslPort = 8443; 160 161 options.addOption(OptionBuilder.withLongOpt("directory") 162 .withDescription("Root context directory (defaults to 'src/main/webapp')") 163 .hasArg().withArgName("DIR") 164 .create('d')) 165 .addOption(OptionBuilder.withLongOpt("context") 166 .withDescription("Context path for application (defaults to '/')") 167 .hasArg().withArgName("CONTEXT") 168 .create('c')) 169 .addOption(OptionBuilder.withLongOpt("port") 170 .withDescription("HTTP port (defaults to 8080)") 171 .hasArg().withArgName("PORT") 172 .create('p')) 173 .addOption(OptionBuilder.withLongOpt("secure-port") 174 .withDescription("HTTPS port (defaults to 8443)") 175 .hasArg().withArgName("PORT") 176 .create('s')) 177 .addOption("h", "help", false, "Display command usage"); 178 179 180 CommandLine line = new BasicParser().parse(options, args); 181 182 boolean usage = line.hasOption('h'); 183 184 if (!usage) 185 { 186 if (line.hasOption('d')) 187 { 188 webapp = line.getOptionValue('d'); 189 } 190 191 File folder = new File(webapp); 192 193 if (!folder.exists()) 194 { 195 System.err.printf("%s: Directory `%s' does not exist.%n", commandName, webapp); 196 System.exit(-1); 197 } 198 199 if (line.hasOption('p')) 200 { 201 try 202 { 203 httpPort = Integer.parseInt(line.getOptionValue('p')); 204 } catch (NumberFormatException e) 205 { 206 usage = true; 207 } 208 } 209 210 if (line.hasOption('s')) 211 { 212 try 213 { 214 sslPort = Integer.parseInt(line.getOptionValue('s')); 215 } catch (NumberFormatException e) 216 { 217 usage = true; 218 } 219 } 220 221 if (line.hasOption('c')) 222 { 223 context = line.getOptionValue('c'); 224 } 225 226 } 227 228 if (usage) 229 { 230 new HelpFormatter().printHelp(commandName, options); 231 System.exit(-1); 232 } 233 234 new JettyRunner(webapp, context, httpPort, sslPort); 235 } 236}