001/**
002 * Copyright 2004-2015 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.test.launch;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.StringTokenizer;
022
023import javax.servlet.Servlet;
024
025import org.apache.commons.collections.CollectionUtils;
026import org.apache.commons.lang.builder.ToStringBuilder;
027import org.eclipse.jetty.server.Server;
028import org.eclipse.jetty.servlet.ServletContextHandler;
029import org.eclipse.jetty.servlet.ServletHolder;
030import org.eclipse.jetty.util.resource.ResourceCollection;
031import org.eclipse.jetty.webapp.WebAppContext;
032
033public class JettyLauncher {
034
035    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(JettyLauncher.class);
036
037    /**
038     * The name of an attribute we set in the ServletContext to indicate to the webapp
039     * that it is running within unit tests, in case it needs to alter its configuration
040     * or behavior.
041     */
042    public static final String JETTYSERVER_TESTMODE_ATTRIB = "JETTYSERVER_TESTMODE";
043
044    private int port;
045    private String contextName;
046    private List<String> relativeWebappRoots = new ArrayList<String>();
047    private Class<? extends Servlet> servletClass;
048    private Server server;
049    private ServletContextHandler context;
050    private boolean failOnContextFailure;
051
052    /**
053     * Whether we are in test mode
054     */
055    private boolean testMode = false;
056
057    public JettyLauncher() {
058        this(8080);
059    }
060
061    public JettyLauncher(int port) {
062        this(port, null, null, null);
063    }
064
065    public JettyLauncher(int port, String contextName) {
066        this(port, contextName, null, null);
067    }
068
069    public JettyLauncher(int port, String contextName, String relativeWebappRoot) {
070        this(port, contextName, relativeWebappRoot, null);
071    }
072
073    public JettyLauncher(int port, String contextName, Class<? extends Servlet> servletClass) {
074        this(port, contextName, null, servletClass);
075    }
076
077    public JettyLauncher(int port, String contextName, String relativeWebappRoots, Class<? extends Servlet> servletClass) {
078        this.port = port;
079        this.contextName = contextName;
080        StringTokenizer tokenizer = new StringTokenizer(relativeWebappRoots, ",");
081        while (tokenizer.hasMoreTokens()) {
082            String relativeWebappRoot = tokenizer.nextToken();
083            this.relativeWebappRoots.add(relativeWebappRoot);
084        }
085        this.servletClass = servletClass;
086    }
087
088    public void setTestMode(boolean t) {
089        this.testMode = t;
090    }
091
092    public boolean isTestMode() {
093        return testMode;
094    }
095
096    public Server getServer() {
097        return server;
098    }
099
100    public ServletContextHandler getContext() {
101        return context;
102    }
103
104    public void start() throws Exception {
105        server = createServer();
106        server.start();
107        if (isFailOnContextFailure() && contextStartupFailed()) {
108            try {
109                server.stop();
110            } catch (Exception e) {
111                LOG.warn("Failed to stop server after web application startup failure.");
112            }
113            throw new Exception("Failed to startup web application context!  Check logs for specific error.");
114        }
115    }
116
117    public void stop() throws Exception {
118        server.stop();
119    }
120
121    public boolean isStarted() {
122        return server.isStarted();
123    }
124
125    protected Server createServer() {
126        Server server = new Server(getPort());
127        setBaseDirSystemProperty();
128        if (useWebAppContext()) {
129            File tmpDir = new File(System.getProperty("basedir") + "/target/jetty-tmp");
130            tmpDir.mkdirs();
131            WebAppContext webAppContext = new WebAppContext();
132            webAppContext.setContextPath(getContextName());
133            String[] fullRelativeWebappRoots = new String[this.relativeWebappRoots.size()];
134            for (int i = 0; i < this.relativeWebappRoots.size(); i++) {
135                String fullRelativeWebappRoot = this.relativeWebappRoots.get(i);
136                fullRelativeWebappRoots[i] = System.getProperty("basedir") + fullRelativeWebappRoot;
137                if (LOG.isInfoEnabled()) {
138                    LOG.info("WebAppRoot = " + fullRelativeWebappRoots[i]);
139                }
140            }
141            webAppContext.setBaseResource(new ResourceCollection(fullRelativeWebappRoots));
142            webAppContext.setTempDirectory(tmpDir);
143            webAppContext.setAttribute(JETTYSERVER_TESTMODE_ATTRIB, String.valueOf(isTestMode()));
144            context = webAppContext;
145            server.setHandler(context);
146        } else {
147            ServletContextHandler root = new ServletContextHandler(server,"/",ServletContextHandler.SESSIONS);
148            root.addServlet(new ServletHolder(servletClass), getContextName());
149            root.setAttribute(JETTYSERVER_TESTMODE_ATTRIB, String.valueOf(isTestMode()));
150            context = root;
151        }
152        return server;
153    }
154
155    protected void setBaseDirSystemProperty() {
156        if (System.getProperty("basedir") == null) {
157            System.setProperty("basedir", System.getProperty("user.dir"));
158        }
159    }
160
161    private boolean useWebAppContext() {
162        return CollectionUtils.isNotEmpty(this.relativeWebappRoots);
163    }
164
165    protected boolean contextStartupFailed() throws Exception {
166        return !context.isAvailable();
167    }
168
169    public String getContextName() {
170        if (contextName == null) {
171            return "/SampleRiceClient";
172        }
173        return contextName;
174    }
175
176    public void setContextName(String contextName) {
177        this.contextName = contextName;
178    }
179
180    public int getPort() {
181        return port;
182    }
183
184    public void setPort(int port) {
185        this.port = port;
186    }
187
188
189    public boolean isFailOnContextFailure() {
190        return this.failOnContextFailure;
191    }
192
193    public void setFailOnContextFailure(boolean failOnContextFailure) {
194        this.failOnContextFailure = failOnContextFailure;
195    }
196
197    public String toString() {
198        return new ToStringBuilder(this).append("port", port)
199                .append("contextName", contextName)
200                .append("relativeWebappRoots", relativeWebappRoots)
201                .append("servletClass", servletClass)
202                .toString();
203    }
204
205    public static void main(String[] args) {
206        int port = args.length > 0 ? Integer.parseInt(args[0]) : 8080;
207        String contextName = args.length > 1 ? args[1] : null;
208        String relativeWebappRoot = args.length > 2 ? args[2] : null;
209        try {
210            new JettyLauncher(port, contextName, relativeWebappRoot).start();
211        } catch (Exception e) {
212            e.printStackTrace();
213        }
214    }
215}