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