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