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    }