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}