summaryrefslogtreecommitdiffstats
path: root/uitest/src/com/vaadin/launcher
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2012-08-30 17:24:36 +0300
committerArtur Signell <artur@vaadin.com>2012-08-30 17:24:36 +0300
commit7b25b3886ea95bc6495506fbe9472e45fcbde684 (patch)
tree0b93cb65dab437feb46720659a63b8f1ef48f7f4 /uitest/src/com/vaadin/launcher
parent8941056349e302e687e40e94c13709e75f256d73 (diff)
downloadvaadin-framework-7b25b3886ea95bc6495506fbe9472e45fcbde684.tar.gz
vaadin-framework-7b25b3886ea95bc6495506fbe9472e45fcbde684.zip
Renamed tests -> uitest and tests/testbench -> uitest/src (#9299)
Diffstat (limited to 'uitest/src/com/vaadin/launcher')
-rw-r--r--uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java302
-rw-r--r--uitest/src/com/vaadin/launcher/DemoLauncher.java227
-rw-r--r--uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java231
-rw-r--r--uitest/src/com/vaadin/launcher/jetty-webdefault.xml281
-rw-r--r--uitest/src/com/vaadin/launcher/keystorebin0 -> 1367 bytes
-rw-r--r--uitest/src/com/vaadin/launcher/util/BrowserLauncher.java139
6 files changed, 1180 insertions, 0 deletions
diff --git a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java
new file mode 100644
index 0000000000..e05979ede0
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.vaadin.Application;
+import com.vaadin.UIRequiresMoreInformationException;
+import com.vaadin.server.AbstractApplicationServlet;
+import com.vaadin.server.AbstractUIProvider;
+import com.vaadin.server.WrappedHttpServletRequest;
+import com.vaadin.server.WrappedRequest;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.UI;
+
+@SuppressWarnings("serial")
+public class ApplicationRunnerServlet extends AbstractApplicationServlet {
+
+ /**
+ * The name of the application class currently used. Only valid within one
+ * request.
+ */
+ private LinkedHashSet<String> defaultPackages = new LinkedHashSet<String>();
+
+ private final ThreadLocal<HttpServletRequest> request = new ThreadLocal<HttpServletRequest>();
+
+ @Override
+ public void init(ServletConfig servletConfig) throws ServletException {
+ super.init(servletConfig);
+ String initParameter = servletConfig
+ .getInitParameter("defaultPackages");
+ if (initParameter != null) {
+ Collections.addAll(defaultPackages, initParameter.split(","));
+ }
+ String str = TestBase.class.getName().replace('.', '/') + ".class";
+ URL url = getDeploymentConfiguration().getClassLoader()
+ .getResource(str);
+ if ("file".equals(url.getProtocol())) {
+ File comVaadinTests = new File(url.getPath()).getParentFile()
+ .getParentFile();
+ addDirectories(comVaadinTests, defaultPackages, "com.vaadin.tests");
+
+ }
+ }
+
+ private void addDirectories(File parent, LinkedHashSet<String> packages,
+ String parentPackage) {
+ packages.add(parentPackage);
+
+ for (File f : parent.listFiles()) {
+ if (f.isDirectory()) {
+ String newPackage = parentPackage + "." + f.getName();
+ addDirectories(f, packages, newPackage);
+ }
+ }
+ }
+
+ @Override
+ protected void service(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+ this.request.set(request);
+ try {
+ super.service(request, response);
+ } finally {
+ this.request.set(null);
+ }
+ }
+
+ @Override
+ protected URL getApplicationUrl(HttpServletRequest request)
+ throws MalformedURLException {
+ URL url = super.getApplicationUrl(request);
+
+ String path = url.toString();
+ path += getApplicationRunnerApplicationClassName(request);
+ path += "/";
+
+ return new URL(path);
+ }
+
+ @Override
+ protected Application getNewApplication(HttpServletRequest request)
+ throws ServletException {
+
+ // Creates a new application instance
+ try {
+ final Class<?> classToRun = getClassToRun();
+ if (UI.class.isAssignableFrom(classToRun)) {
+ Application application = new Application();
+ application.addUIProvider(new AbstractUIProvider() {
+
+ @Override
+ public Class<? extends UI> getUIClass(
+ Application application, WrappedRequest request)
+ throws UIRequiresMoreInformationException {
+ return (Class<? extends UI>) classToRun;
+ }
+ });
+ return application;
+ } else if (Application.class.isAssignableFrom(classToRun)) {
+ return (Application) classToRun.newInstance();
+ } else {
+ throw new ServletException(classToRun.getCanonicalName()
+ + " is neither an Application nor a UI");
+ }
+ } catch (final IllegalAccessException e) {
+ throw new ServletException(e);
+ } catch (final InstantiationException e) {
+ throw new ServletException(e);
+ } catch (final ClassNotFoundException e) {
+ throw new ServletException(
+ new InstantiationException(
+ "Failed to load application class: "
+ + getApplicationRunnerApplicationClassName(request)));
+ }
+
+ }
+
+ private String getApplicationRunnerApplicationClassName(
+ HttpServletRequest request) {
+ return getApplicationRunnerURIs(request).applicationClassname;
+ }
+
+ private static class URIS {
+ String staticFilesPath;
+ // String applicationURI;
+ // String context;
+ // String runner;
+ String applicationClassname;
+
+ }
+
+ /**
+ * Parses application runner URIs.
+ *
+ * If request URL is e.g.
+ * http://localhost:8080/vaadin/run/com.vaadin.demo.Calc then
+ * <ul>
+ * <li>context=vaadin</li>
+ * <li>Runner servlet=run</li>
+ * <li>Vaadin application=com.vaadin.demo.Calc</li>
+ * </ul>
+ *
+ * @param request
+ * @return string array containing widgetset URI, application URI and
+ * context, runner, application classname
+ */
+ private static URIS getApplicationRunnerURIs(HttpServletRequest request) {
+ final String[] urlParts = request.getRequestURI().toString()
+ .split("\\/");
+ String context = null;
+ // String runner = null;
+ URIS uris = new URIS();
+ String applicationClassname = null;
+ String contextPath = request.getContextPath();
+ if (urlParts[1].equals(contextPath.replaceAll("\\/", ""))) {
+ // class name comes after web context and runner application
+ context = urlParts[1];
+ // runner = urlParts[2];
+ if (urlParts.length == 3) {
+ throw new IllegalArgumentException("No application specified");
+ }
+ applicationClassname = urlParts[3];
+
+ uris.staticFilesPath = "/" + context;
+ // uris.applicationURI = "/" + context + "/" + runner + "/"
+ // + applicationClassname;
+ // uris.context = context;
+ // uris.runner = runner;
+ uris.applicationClassname = applicationClassname;
+ } else {
+ // no context
+ context = "";
+ // runner = urlParts[1];
+ if (urlParts.length == 2) {
+ throw new IllegalArgumentException("No application specified");
+ }
+ applicationClassname = urlParts[2];
+
+ uris.staticFilesPath = "/";
+ // uris.applicationURI = "/" + runner + "/" + applicationClassname;
+ // uris.context = context;
+ // uris.runner = runner;
+ uris.applicationClassname = applicationClassname;
+ }
+ return uris;
+ }
+
+ @Override
+ protected Class<? extends Application> getApplicationClass()
+ throws ClassNotFoundException {
+ Class<?> classToRun = getClassToRun();
+ if (UI.class.isAssignableFrom(classToRun)) {
+ return Application.class;
+ } else if (Application.class.isAssignableFrom(classToRun)) {
+ return classToRun.asSubclass(Application.class);
+ } else {
+ throw new ClassCastException(classToRun.getCanonicalName()
+ + " is not an Application nor a UI");
+ }
+ }
+
+ private Class<?> getClassToRun() throws ClassNotFoundException {
+ // TODO use getClassLoader() ?
+
+ Class<?> appClass = null;
+
+ String baseName = getApplicationRunnerApplicationClassName(request
+ .get());
+ try {
+ appClass = getClass().getClassLoader().loadClass(baseName);
+ return appClass;
+ } catch (Exception e) {
+ //
+ for (String pkg : defaultPackages) {
+ try {
+ appClass = getClass().getClassLoader().loadClass(
+ pkg + "." + baseName);
+ } catch (ClassNotFoundException ee) {
+ // Ignore as this is expected for many packages
+ } catch (Exception e2) {
+ // TODO: handle exception
+ getLogger().log(
+ Level.FINE,
+ "Failed to find application class " + pkg + "."
+ + baseName, e2);
+ }
+ if (appClass != null) {
+ return appClass;
+ }
+ }
+
+ }
+
+ throw new ClassNotFoundException();
+ }
+
+ @Override
+ protected String getRequestPathInfo(HttpServletRequest request) {
+ String path = request.getPathInfo();
+ if (path == null) {
+ return null;
+ }
+
+ path = path.substring(1 + getApplicationRunnerApplicationClassName(
+ request).length());
+ return path;
+ }
+
+ @Override
+ protected String getStaticFilesLocation(HttpServletRequest request) {
+ URIS uris = getApplicationRunnerURIs(request);
+ String staticFilesPath = uris.staticFilesPath;
+ if (staticFilesPath.equals("/")) {
+ staticFilesPath = "";
+ }
+
+ return staticFilesPath;
+ }
+
+ @Override
+ protected WrappedHttpServletRequest createWrappedRequest(
+ HttpServletRequest request) {
+ return new WrappedHttpServletRequest(request,
+ getDeploymentConfiguration()) {
+ @Override
+ public String getRequestPathInfo() {
+ return ApplicationRunnerServlet.this.getRequestPathInfo(this);
+ }
+ };
+ }
+
+ private Logger getLogger() {
+ return Logger.getLogger(ApplicationRunnerServlet.class.getName());
+ }
+
+}
diff --git a/uitest/src/com/vaadin/launcher/DemoLauncher.java b/uitest/src/com/vaadin/launcher/DemoLauncher.java
new file mode 100644
index 0000000000..d858b91483
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/DemoLauncher.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.launcher;
+
+import java.awt.FlowLayout;
+import java.awt.HeadlessException;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.WindowConstants;
+
+import com.vaadin.launcher.util.BrowserLauncher;
+
+/**
+ * This class starts servlet container and opens a simple control dialog.
+ *
+ */
+public class DemoLauncher {
+
+ public static void main(String[] args) throws Exception {
+
+ final Map<String, String> serverArgs = DevelopmentServerLauncher
+ .parseArguments(args);
+ boolean deployed = false;
+ try {
+ // Default deployment: embedded.war
+ deployed = deployEmbeddedWarfile(serverArgs);
+ } catch (final IOException e1) {
+ e1.printStackTrace();
+ deployed = false;
+ }
+
+ // Check if deployment was succesful
+ if (!deployed && !serverArgs.containsKey("webroot")) {
+ // Default deployment failed, try other means
+ if (new File("WebContent").exists()) {
+ // Using WebContent directory as webroot
+ serverArgs.put("webroot", "WebContent");
+ } else {
+ System.err.print("Failed to deploy Vaadin application. "
+ + "Please add --webroot parameter. Exiting.");
+ return;
+ }
+ }
+
+ // Start the Jetty servlet container
+ final String url = DevelopmentServerLauncher.runServer(serverArgs,
+ "Demo Server");
+
+ if (!serverArgs.containsKey("nogui") && url != null) {
+
+ // Open browser into application URL
+ BrowserLauncher.openBrowser(url);
+
+ // Open control dialog
+ /*
+ * Swing components should never be manipulated outside the event
+ * dispatch thread.
+ */
+ java.awt.EventQueue.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ openServerControlDialog(url);
+ } catch (HeadlessException e) {
+ // nop, starting from console
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Open a control dialog for embedded server.
+ *
+ * @param applicationUrl
+ * Application URL
+ */
+ private static void openServerControlDialog(final String applicationUrl) {
+
+ // Main frame
+ final String title = "Desktop Server";
+ final JFrame frame = new JFrame(title);
+ frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+
+ // Create link label and listen mouse click
+ final JLabel link = new JLabel("<html>"
+ + "<center>Desktop Server is running at: <br>" + "<a href=\""
+ + applicationUrl + "\">" + applicationUrl
+ + "</a><br>Close this window to shutdown the server.</center>"
+ + "</html>");
+ link.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ BrowserLauncher.openBrowser(applicationUrl);
+ }
+ });
+
+ // Create a panel and add components to it.
+ final JPanel contentPane = new JPanel(new FlowLayout());
+ frame.setContentPane(contentPane);
+ contentPane.add(link);
+
+ // Close confirmation
+ final JLabel question = new JLabel(
+ "This will stop the server. Are you sure?");
+ final JButton okButton = new JButton("OK");
+ final JButton cancelButton = new JButton("Cancel");
+
+ // List for close verify buttons
+ final ActionListener buttonListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == okButton) {
+ System.exit(0);
+ } else {
+ Rectangle bounds = frame.getBounds();
+ frame.setTitle(title);
+ contentPane.removeAll();
+ contentPane.add(link);
+ contentPane.setBounds(bounds);
+ frame.setBounds(bounds);
+ frame.setVisible(true);
+ frame.repaint();
+ }
+ }
+ };
+ okButton.addActionListener(buttonListener);
+ cancelButton.addActionListener(buttonListener);
+
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ final Rectangle bounds = frame.getBounds();
+ frame.setTitle("Confirm close");
+ contentPane.removeAll();
+ contentPane.add(question);
+ contentPane.add(okButton);
+ contentPane.add(cancelButton);
+ frame.setBounds(bounds);
+ frame.setVisible(true);
+ frame.repaint();
+ }
+ });
+
+ // Position the window nicely
+ final java.awt.Dimension screenSize = java.awt.Toolkit
+ .getDefaultToolkit().getScreenSize();
+ final int w = 270;
+ final int h = 95;
+ final int margin = 20;
+ frame.setBounds(new Rectangle(screenSize.width - w - margin,
+ screenSize.height - h - margin * 2, w, h));
+ frame.toFront();
+ frame.setVisible(true);
+ }
+
+ /**
+ * Deploy file named "embedded.war" from classpath (inside jar file).
+ *
+ * @param args
+ * @return
+ * @throws IOException
+ */
+ protected static boolean deployEmbeddedWarfile(Map<String, String> args)
+ throws IOException {
+ final String embeddedWarfileName = "/embedded.war";
+ final InputStream embeddedWarfile = DemoLauncher.class
+ .getResourceAsStream(embeddedWarfileName);
+ if (embeddedWarfile != null) {
+ final File tempWarfile = File.createTempFile("embedded", ".war")
+ .getAbsoluteFile();
+ tempWarfile.getParentFile().mkdirs();
+ tempWarfile.deleteOnExit();
+
+ final String embeddedWebroot = "winstoneEmbeddedWAR";
+ final File tempWebroot = new File(tempWarfile.getParentFile(),
+ embeddedWebroot);
+ tempWebroot.mkdirs();
+
+ final OutputStream out = new FileOutputStream(tempWarfile, true);
+ int read = 0;
+ final byte buffer[] = new byte[2048];
+ while ((read = embeddedWarfile.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ out.close();
+ embeddedWarfile.close();
+
+ args.put("warfile", tempWarfile.getAbsolutePath());
+ args.put("webroot", tempWebroot.getAbsolutePath());
+ args.remove("webappsDir");
+ args.remove("hostsDir");
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
new file mode 100644
index 0000000000..6162820dac
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.security.SslSocketConnector;
+import org.mortbay.jetty.webapp.WebAppContext;
+
+import com.vaadin.launcher.util.BrowserLauncher;
+
+/**
+ * Class for running Jetty servlet container within Eclipse project.
+ *
+ */
+public class DevelopmentServerLauncher {
+
+ private static final String KEYSTORE = "src/com/vaadin/launcher/keystore";
+ private final static int serverPort = 8888;
+
+ /**
+ * Main function for running Jetty.
+ *
+ * Command line Arguments are passed through to Jetty, see runServer method
+ * for options.
+ *
+ * @param args
+ * @throws Exception
+ */
+ public static void main(String[] args) {
+ System.setProperty("java.awt.headless", "true");
+
+ // Pass-through of arguments for Jetty
+ final Map<String, String> serverArgs = parseArguments(args);
+
+ if (serverArgs.containsKey("shutdownPort")) {
+ int port = Integer.parseInt(serverArgs.get("shutdownPort"));
+ try {
+ // Try to notify another instance that it's time to close
+ Socket socket = new Socket((String) null, port);
+ // Wait until the other instance says it has closed
+ socket.getInputStream().read();
+ // Then tidy up
+ socket.close();
+ } catch (IOException e) {
+ // Ignore if port is not open
+ }
+ }
+
+ // Start Jetty
+ System.out.println("Starting Jetty servlet container.");
+ String url;
+ try {
+ url = runServer(serverArgs, "Development Server Mode");
+ // Start Browser
+ if (serverArgs.containsKey("gui") && url != null) {
+ System.out.println("Starting Web Browser.");
+
+ // Open browser into application URL
+ BrowserLauncher.openBrowser(url);
+ }
+ } catch (Exception e) {
+ // NOP exception already on console by jetty
+ }
+ }
+
+ /**
+ * Run the server with specified arguments.
+ *
+ * @param serverArgs
+ * @return
+ * @throws Exception
+ * @throws Exception
+ */
+ protected static String runServer(Map<String, String> serverArgs,
+ String mode) throws Exception {
+
+ // Assign default values for some arguments
+ assignDefault(serverArgs, "webroot", "WebContent");
+ assignDefault(serverArgs, "httpPort", "" + serverPort);
+ assignDefault(serverArgs, "context", "");
+
+ int port = serverPort;
+ try {
+ port = Integer.parseInt(serverArgs.get("httpPort"));
+ } catch (NumberFormatException e) {
+ // keep default value for port
+ }
+
+ // Add help for System.out
+ System.out
+ .println("-------------------------------------------------\n"
+ + "Starting Vaadin in "
+ + mode
+ + ".\n"
+ + "Running in http://localhost:"
+ + port
+ + "\n-------------------------------------------------\n");
+
+ final Server server = new Server();
+
+ final Connector connector = new SelectChannelConnector();
+
+ connector.setPort(port);
+ if (serverArgs.containsKey("withssl")) {
+ final SslSocketConnector sslConnector = new SslSocketConnector();
+ sslConnector.setPort(8444);
+ sslConnector.setTruststore(KEYSTORE);
+ sslConnector.setTrustPassword("password");
+ sslConnector.setKeystore(KEYSTORE);
+ sslConnector.setKeyPassword("password");
+ sslConnector.setPassword("password");
+ server.setConnectors(new Connector[] { connector, sslConnector });
+ } else {
+ server.setConnectors(new Connector[] { connector });
+ }
+
+ final WebAppContext webappcontext = new WebAppContext();
+ String path = DevelopmentServerLauncher.class.getPackage().getName()
+ .replace(".", File.separator);
+ webappcontext.setDefaultsDescriptor(path + File.separator
+ + "jetty-webdefault.xml");
+ webappcontext.setContextPath(serverArgs.get("context"));
+ webappcontext.setWar(serverArgs.get("webroot"));
+ server.setHandler(webappcontext);
+
+ try {
+ server.start();
+
+ if (serverArgs.containsKey("shutdownPort")) {
+ int shutdownPort = Integer.parseInt(serverArgs
+ .get("shutdownPort"));
+ final ServerSocket serverSocket = new ServerSocket(
+ shutdownPort, 1, InetAddress.getByName("127.0.0.1"));
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ System.out
+ .println("Waiting for shutdown signal on port "
+ + serverSocket.getLocalPort());
+ // Start waiting for a close signal
+ Socket accept = serverSocket.accept();
+ // First stop listening to the port
+ serverSocket.close();
+ // Then stop the jetty server
+ server.stop();
+ // Send a byte to tell the other process that it can
+ // start jetty
+ OutputStream outputStream = accept
+ .getOutputStream();
+ outputStream.write(0);
+ outputStream.flush();
+ // Finally close the socket
+ accept.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ };
+
+ }.start();
+
+ }
+ } catch (Exception e) {
+ server.stop();
+ throw e;
+ }
+
+ return "http://localhost:" + port + serverArgs.get("context");
+ }
+
+ /**
+ * Assign default value for given key.
+ *
+ * @param map
+ * @param key
+ * @param value
+ */
+ private static void assignDefault(Map<String, String> map, String key,
+ String value) {
+ if (!map.containsKey(key)) {
+ map.put(key, value);
+ }
+ }
+
+ /**
+ * Parse all command line arguments into a map.
+ *
+ * Arguments format "key=value" are put into map.
+ *
+ * @param args
+ * @return map of arguments key value pairs.
+ */
+ protected static Map<String, String> parseArguments(String[] args) {
+ final Map<String, String> map = new HashMap<String, String>();
+ for (int i = 0; i < args.length; i++) {
+ final int d = args[i].indexOf("=");
+ if (d > 0 && d < args[i].length() && args[i].startsWith("--")) {
+ final String name = args[i].substring(2, d);
+ final String value = args[i].substring(d + 1);
+ map.put(name, value);
+ }
+ }
+ return map;
+ }
+
+}
diff --git a/uitest/src/com/vaadin/launcher/jetty-webdefault.xml b/uitest/src/com/vaadin/launcher/jetty-webdefault.xml
new file mode 100644
index 0000000000..5a2465af5a
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/jetty-webdefault.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!-- ===================================================================== -->
+<!-- This file contains the default descriptor for web applications. -->
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+<!-- The intent of this descriptor is to include jetty specific or common -->
+<!-- configuration for all webapps. If a context has a webdefault.xml -->
+<!-- descriptor, it is applied before the contexts own web.xml file -->
+<!-- -->
+<!-- A context may be assigned a default descriptor by: -->
+<!-- + Calling WebApplicationContext.setDefaultsDescriptor -->
+<!-- + Passed an arg to addWebApplications -->
+<!-- -->
+<!-- This file is used both as the resource within the jetty.jar (which is -->
+<!-- used as the default if no explicit defaults descriptor is set) and it -->
+<!-- is copied to the etc directory of the Jetty distro and explicitly -->
+<!-- by the jetty.xml file. -->
+<!-- -->
+<!-- ===================================================================== -->
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ metadata-complete="true"
+ version="2.5">
+
+ <description>
+ Default web.xml file.
+ This file is applied to a Web application before it's own WEB_INF/web.xml file
+ </description>
+
+
+ <!-- ==================================================================== -->
+ <!-- Context params to control Session Cookies -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- UNCOMMENT TO ACTIVATE
+ <context-param>
+ <param-name>org.mortbay.jetty.servlet.SessionDomain</param-name>
+ <param-value>127.0.0.1</param-value>
+ </context-param>
+
+ <context-param>
+ <param-name>org.mortbay.jetty.servlet.SessionPath</param-name>
+ <param-value>/</param-value>
+ </context-param>
+
+ <context-param>
+ <param-name>org.mortbay.jetty.servlet.MaxAge</param-name>
+ <param-value>-1</param-value>
+ </context-param>
+ -->
+
+ <context-param>
+ <param-name>org.mortbay.jetty.webapp.NoTLDJarPattern</param-name>
+ <param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value>
+ </context-param>
+
+
+
+ <!-- ==================================================================== -->
+ <!-- The default servlet. -->
+ <!-- This servlet, normally mapped to /, provides the handling for static -->
+ <!-- content, OPTIONS and TRACE methods for the context. -->
+ <!-- The following initParameters are supported: -->
+ <!-- -->
+ <!-- acceptRanges If true, range requests and responses are -->
+ <!-- supported -->
+ <!-- -->
+ <!-- dirAllowed If true, directory listings are returned if no -->
+ <!-- welcome file is found. Else 403 Forbidden. -->
+ <!-- -->
+ <!-- redirectWelcome If true, redirect welcome file requests -->
+ <!-- else use request dispatcher forwards -->
+ <!-- -->
+ <!-- gzip If set to true, then static content will be served-->
+ <!-- as gzip content encoded if a matching resource is -->
+ <!-- found ending with ".gz" -->
+ <!-- -->
+ <!-- resoureBase Can be set to replace the context resource base -->
+ <!-- -->
+ <!-- relativeResourceBase -->
+ <!-- Set with a pathname relative to the base of the -->
+ <!-- servlet context root. Useful for only serving -->
+ <!-- static content from only specific subdirectories. -->
+ <!-- -->
+ <!-- useFileMappedBuffer -->
+ <!-- If set to true (the default), a memory mapped -->
+ <!-- file buffer will be used to serve static content -->
+ <!-- when using an NIO connector. Setting this value -->
+ <!-- to false means that a direct buffer will be used -->
+ <!-- instead. If you are having trouble with Windows -->
+ <!-- file locking, set this to false. -->
+ <!-- -->
+ <!-- cacheControl If set, all static content will have this value -->
+ <!-- set as the cache-control header. -->
+ <!-- -->
+ <!-- maxCacheSize Maximum size of the static resource cache -->
+ <!-- -->
+ <!-- maxCachedFileSize Maximum size of any single file in the cache -->
+ <!-- -->
+ <!-- maxCachedFiles Maximum number of files in the cache -->
+ <!-- -->
+ <!-- cacheType "nio", "bio" or "both" to determine the type(s) -->
+ <!-- of resource cache. A bio cached buffer may be used-->
+ <!-- by nio but is not as efficient as a nio buffer. -->
+ <!-- An nio cached buffer may not be used by bio. -->
+ <!-- -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <servlet>
+ <servlet-name>default</servlet-name>
+ <servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class>
+ <init-param>
+ <param-name>acceptRanges</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>dirAllowed</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>redirectWelcome</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCacheSize</param-name>
+ <param-value>256000000</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCachedFileSize</param-name>
+ <param-value>10000000</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCachedFiles</param-name>
+ <param-value>1000</param-value>
+ </init-param>
+ <init-param>
+ <param-name>cacheType</param-name>
+ <param-value>both</param-value>
+ </init-param>
+ <init-param>
+ <param-name>gzip</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>useFileMappedBuffer</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <!--
+ <init-param>
+ <param-name>cacheControl</param-name>
+ <param-value>max-age=3600,public</param-value>
+ </init-param>
+ -->
+ <load-on-startup>0</load-on-startup>
+ </servlet>
+
+ <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
+
+
+ <!-- ==================================================================== -->
+ <!-- Dynamic Servlet Invoker. -->
+ <!-- This servlet invokes anonymous servlets that have not been defined -->
+ <!-- in the web.xml or by other means. The first element of the pathInfo -->
+ <!-- of a request passed to the envoker is treated as a servlet name for -->
+ <!-- an existing servlet, or as a class name of a new servlet. -->
+ <!-- This servlet is normally mapped to /servlet/* -->
+ <!-- This servlet support the following initParams: -->
+ <!-- -->
+ <!-- nonContextServlets If false, the invoker can only load -->
+ <!-- servlets from the contexts classloader. -->
+ <!-- This is false by default and setting this -->
+ <!-- to true may have security implications. -->
+ <!-- -->
+ <!-- verbose If true, log dynamic loads -->
+ <!-- -->
+ <!-- * All other parameters are copied to the -->
+ <!-- each dynamic servlet as init parameters -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Uncomment for dynamic invocation
+ <servlet>
+ <servlet-name>invoker</servlet-name>
+ <servlet-class>org.mortbay.jetty.servlet.Invoker</servlet-class>
+ <init-param>
+ <param-name>verbose</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>nonContextServlets</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>dynamicParam</param-name>
+ <param-value>anyValue</param-value>
+ </init-param>
+ <load-on-startup>0</load-on-startup>
+ </servlet>
+
+ <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
+ -->
+
+
+
+ <!-- ==================================================================== -->
+ <session-config>
+ <session-timeout>30</session-timeout>
+ </session-config>
+
+ <!-- ==================================================================== -->
+ <!-- Default MIME mappings -->
+ <!-- The default MIME mappings are provided by the mime.properties -->
+ <!-- resource in the org.mortbay.jetty.jar file. Additional or modified -->
+ <!-- mappings may be specified here -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- UNCOMMENT TO ACTIVATE
+ <mime-mapping>
+ <extension>mysuffix</extension>
+ <mime-type>mymime/type</mime-type>
+ </mime-mapping>
+ -->
+
+ <!-- ==================================================================== -->
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ <welcome-file>index.htm</welcome-file>
+ <welcome-file>index.jsp</welcome-file>
+ </welcome-file-list>
+
+ <!-- ==================================================================== -->
+ <locale-encoding-mapping-list>
+ <locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
+ <locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>
+ </locale-encoding-mapping-list>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Disable TRACE</web-resource-name>
+ <url-pattern>/</url-pattern>
+ <http-method>TRACE</http-method>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+
+</web-app>
+
diff --git a/uitest/src/com/vaadin/launcher/keystore b/uitest/src/com/vaadin/launcher/keystore
new file mode 100644
index 0000000000..1314185e2a
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/keystore
Binary files differ
diff --git a/uitest/src/com/vaadin/launcher/util/BrowserLauncher.java b/uitest/src/com/vaadin/launcher/util/BrowserLauncher.java
new file mode 100644
index 0000000000..c9b3622894
--- /dev/null
+++ b/uitest/src/com/vaadin/launcher/util/BrowserLauncher.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.launcher.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * This class opens default browser for DemoLauncher class. Default browser is
+ * detected by the operating system.
+ *
+ */
+public class BrowserLauncher {
+
+ /**
+ * Open browser on specified URL.
+ *
+ * @param url
+ */
+ public static void openBrowser(String url) {
+
+ final Runtime runtime = Runtime.getRuntime();
+ boolean started = false;
+
+ final String os = System.getProperty("os.name", "windows")
+ .toLowerCase();
+
+ // Linux
+ if (os.indexOf("linux") >= 0) {
+ // See if the default browser is Konqueror by resolving the symlink.
+ boolean isDefaultKonqueror = false;
+ try {
+ // Find out the location of the x-www-browser link from path.
+ Process process = runtime.exec("which x-www-browser");
+ BufferedInputStream ins = new BufferedInputStream(
+ process.getInputStream());
+ BufferedReader bufreader = new BufferedReader(
+ new InputStreamReader(ins));
+ String defaultLinkPath = bufreader.readLine();
+ ins.close();
+
+ // The path is null if the link did not exist.
+ if (defaultLinkPath != null) {
+ // See if the default browser is Konqueror.
+ File file = new File(defaultLinkPath);
+ String canonical = file.getCanonicalPath();
+ if (canonical.indexOf("konqueror") != -1) {
+ isDefaultKonqueror = true;
+ }
+ }
+ } catch (IOException e1) {
+ // The symlink was probably not found, so this is ok.
+ }
+
+ // Try x-www-browser, which is symlink to the default browser,
+ // except if we found that it is Konqueror.
+ if (!started && !isDefaultKonqueror) {
+ try {
+ runtime.exec("x-www-browser " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+
+ // Try firefox
+ if (!started) {
+ try {
+ runtime.exec("firefox " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+
+ // Try mozilla
+ if (!started) {
+ try {
+ runtime.exec("mozilla " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+
+ // Try konqueror
+ if (!started) {
+ try {
+ runtime.exec("konqueror " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+ }
+
+ // OS X
+ if (os.indexOf("mac os x") >= 0) {
+
+ // Try open
+ if (!started) {
+ try {
+ runtime.exec("open " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+ }
+
+ // Try cmd /start command on windows
+ if (os.indexOf("win") >= 0) {
+ if (!started) {
+ try {
+ runtime.exec("cmd /c start " + url);
+ started = true;
+ } catch (final IOException e) {
+ }
+ }
+ }
+
+ if (!started) {
+ System.out.println("Failed to open browser. Please go to " + url);
+ }
+ }
+
+}