001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.geronimo.mavenplugins.selenium;
021
022 import java.io.File;
023 import java.io.PrintWriter;
024 import java.io.BufferedWriter;
025 import java.io.FileWriter;
026
027 import java.net.URL;
028 import java.net.MalformedURLException;
029 import java.util.Map;
030
031 import org.apache.maven.project.MavenProject;
032 import org.apache.maven.artifact.Artifact;
033 import org.apache.maven.plugin.MojoFailureException;
034 import org.apache.maven.plugin.MojoExecutionException;
035
036 import org.apache.geronimo.genesis.AntMojoSupport;
037 import org.apache.geronimo.genesis.ObjectHolder;
038
039 import org.apache.commons.io.IOUtils;
040
041 import org.apache.tools.ant.taskdefs.Java;
042 import org.apache.tools.ant.types.Environment;
043 import org.apache.tools.ant.types.Path;
044
045 import org.codehaus.plexus.util.FileUtils;
046
047 /**
048 * Start the Selenium server.
049 *
050 * @goal start-server
051 *
052 * @version $Rev: 451403 $ $Date: 2006-09-29 12:48:14 -0700 (Fri, 29 Sep 2006) $
053 */
054 public class StartServerMojo
055 extends AntMojoSupport
056 {
057 /**
058 * The port number the server will use.
059 *
060 * @parameter expression="${port}" default-value="4444"
061 */
062 private int port = -1;
063
064 /**
065 * Timeout for the server in seconds.
066 *
067 * @parameter expression="${timeout}" default-value="-1"
068 */
069 private int timeout = -1;
070
071 /**
072 * Enable the server's debug mode..
073 *
074 * @parameter expression="${debug}" default-value="false"
075 */
076 private boolean debug = false;
077
078 /**
079 * The file or resource to use for default user-extentions.js.
080 *
081 * @parameter default-value="org/apache/geronimo/mavenplugins/selenium/default-user-extentions.js"
082 */
083 private String defaultUserExtensions = null;
084
085 /**
086 * Enable or disable default user-extentions.js
087 *
088 * @parameter default-value="true"
089 */
090 private boolean defaultUserExtensionsEnabled = true;
091
092 /**
093 * Location of the user-extentions.js to load into the server.
094 * If defaultUserExtensionsEnabled is true, then this file will be appended to the defaults.
095 *
096 * @parameter
097 */
098 private String userExtensions = null;
099
100 /**
101 * Map of of plugin artifacts.
102 *
103 * @parameter expression="${plugin.artifactMap}"
104 * @required
105 * @readonly
106 */
107 private Map pluginArtifactMap = null;
108
109 /**
110 * Working directory where Selenium server will be started from.
111 *
112 * @parameter expression="${project.build.directory}/selenium"
113 * @required
114 */
115 private File workingDirectory = null;
116
117 /**
118 * Enable logging mode.
119 *
120 * @parameter expression="${logOutput}" default-value="false"
121 */
122 protected boolean logOutput = false;
123
124 /**
125 * The file that Selenium server logs will be written to.
126 *
127 * @parameter expression="${logFile}" default-value="${project.build.directory}/selenium/server.log"
128 * @required
129 */
130 private File logFile = null;
131
132 /**
133 * Flag to control if we background the server or block Maven execution.
134 *
135 * @parameter default-value="false"
136 * @required
137 */
138 private boolean background = false;
139
140 //
141 // MojoSupport Hooks
142 //
143
144 /**
145 * The maven project.
146 *
147 * @parameter expression="${project}"
148 * @required
149 * @readonly
150 */
151 private MavenProject project = null;
152
153 protected MavenProject getProject() {
154 return project;
155 }
156
157 //
158 // Mojo
159 //
160
161 private File getPluginArchive() {
162 String path = getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
163 return new File(path);
164 }
165
166 private Artifact getPluginArtifact(final String name) throws MojoExecutionException {
167 Artifact artifact = (Artifact)pluginArtifactMap.get(name);
168 if (artifact == null) {
169 throw new MojoExecutionException("Unable to locate '" + name + "' in the list of plugin artifacts");
170 }
171
172 return artifact;
173 }
174
175 protected void doExecute() throws Exception {
176 log.info("Starting Selenium server...");
177
178 final Java java = (Java)createTask("java");
179
180 FileUtils.forceMkdir(workingDirectory);
181
182 java.setFork(true);
183 java.setDir(workingDirectory);
184 java.setFailonerror(true);
185
186 if (logOutput) {
187 FileUtils.forceMkdir(logFile.getParentFile());
188
189 log.info("Redirecting output to: " + logFile);
190
191 java.setLogError(true);
192 java.setOutput(logFile);
193 }
194
195 java.setClassname("org.openqa.selenium.server.SeleniumServer");
196
197 Path classpath = java.createClasspath();
198 classpath.createPathElement().setLocation(getPluginArchive());
199 classpath.createPathElement().setLocation(getPluginArtifact("log4j:log4j").getFile());
200 classpath.createPathElement().setLocation(getPluginArtifact("org.openqa.selenium.server:selenium-server").getFile());
201
202 Environment.Variable var;
203
204 var = new Environment.Variable();
205 var.setKey("selenium.log");
206 var.setFile(logFile);
207 java.addSysproperty(var);
208
209 var = new Environment.Variable();
210 var.setKey("selenium.loglevel");
211 var.setValue(debug == true ? "DEBUG" : "INFO");
212 java.addSysproperty(var);
213
214 var = new Environment.Variable();
215 var.setKey("log4j.configuration");
216 var.setValue("org/apache/geronimo/mavenplugins/selenium/log4j.properties");
217 java.addSysproperty(var);
218
219 // Server arguments
220
221 java.createArg().setValue("-port");
222 java.createArg().setValue(String.valueOf(port));
223
224 if (debug) {
225 java.createArg().setValue("-debug");
226 }
227
228 if (timeout > 0) {
229 log.info("Timeout after: " + timeout + " seconds");
230
231 java.createArg().setValue("-timeout");
232 java.createArg().setValue(String.valueOf(timeout));
233 }
234
235 File userExtentionsFile = getUserExtentionsFile();
236 if (userExtentionsFile != null) {
237 log.info("User extensions: " + userExtentionsFile);
238
239 java.createArg().setValue("-userExtensions");
240 java.createArg().setFile(userExtentionsFile);
241 }
242
243 // Holds any exception that was thrown during startup
244 final ObjectHolder errorHolder = new ObjectHolder();
245
246 // Start the server int a seperate thread
247 Thread t = new Thread("Selenium Server Runner") {
248 public void run() {
249 try {
250 java.execute();
251 }
252 catch (Exception e) {
253 errorHolder.set(e);
254
255 //
256 // NOTE: Don't log here, as when the JVM exists an exception will get thrown by Ant
257 // but that should be fine.
258 //
259 }
260 }
261 };
262 t.start();
263
264 log.debug("Waiting for Selenium server...");
265
266 // Verify server started
267 URL url = new URL("http://localhost:" + port + "/selenium-server");
268 boolean started = false;
269 while (!started) {
270 if (errorHolder.isSet()) {
271 throw new MojoExecutionException("Failed to start Selenium server", (Throwable)errorHolder.get());
272 }
273
274 log.debug("Trying connection to: " + url);
275
276 try {
277 Object input = url.openConnection().getContent();
278 started = true;
279 }
280 catch (Exception e) {
281 // ignore
282 }
283
284 Thread.sleep(1000);
285 }
286
287 log.info("Selenium server started");
288
289 if (!background) {
290 log.info("Waiting for Selenium to shutdown...");
291
292 t.join();
293 }
294 }
295
296 /**
297 * Resolve a resource to a file, URL or resource.
298 */
299 private URL resolveResource(final String name) throws MalformedURLException, MojoFailureException {
300 assert name != null;
301
302 URL url;
303
304 File file = new File(name);
305 if (file.exists()) {
306 url = file.toURL();
307 }
308 else {
309 try {
310 url = new URL(name);
311 }
312 catch (MalformedURLException e) {
313 url = Thread.currentThread().getContextClassLoader().getResource(name);
314 }
315 }
316
317 if (url == null) {
318 throw new MojoFailureException("Could not resolve resource: " + name);
319 }
320
321 log.debug("Resolved resource '" + name + "' as: " + url);
322
323 return url;
324 }
325
326 /**
327 * Retutn the user-extentions.js file to use, or null if it should not be installed.
328 */
329 private File getUserExtentionsFile() throws Exception {
330 if (!defaultUserExtensionsEnabled && userExtensions == null) {
331 return null;
332 }
333
334 // File needs to be named 'user-extensions.js' or Selenium server will puke
335 File file = new File(workingDirectory, "user-extensions.js");
336 if (file.exists()) {
337 log.debug("Reusing previously generated file: " + file);
338
339 return file;
340 }
341
342 PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));
343
344 if (defaultUserExtensionsEnabled) {
345 URL url = resolveResource(defaultUserExtensions);
346 log.debug("Using defaults: " + url);
347
348 writer.println("//");
349 writer.println("// Default user extentions; from: " + url);
350 writer.println("//");
351
352 IOUtils.copy(url.openStream(), writer);
353 }
354
355 if (userExtensions != null) {
356 URL url = resolveResource(userExtensions);
357 log.debug("Using user extentions: " + url);
358
359 writer.println("//");
360 writer.println("// User extentions; from: " + url);
361 writer.println("//");
362
363 IOUtils.copy(url.openStream(), writer);
364 }
365
366 writer.flush();
367 writer.close();
368
369 return file;
370 }
371 }