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.geronimo.server;
021
022 import java.io.File;
023
024 import java.util.Timer;
025 import java.util.TimerTask;
026 import java.util.Map;
027 import java.util.HashMap;
028 import java.util.Properties;
029 import java.util.Iterator;
030 import java.util.StringTokenizer;
031 import java.util.List;
032 import java.util.ArrayList;
033
034 import org.apache.maven.plugin.MojoExecutionException;
035
036 import org.apache.tools.ant.taskdefs.Java;
037
038 import org.codehaus.mojo.pluginsupport.util.ObjectHolder;
039
040 import org.apache.geronimo.mavenplugins.geronimo.ServerProxy;
041
042 import org.codehaus.plexus.util.FileUtils;
043
044 import org.apache.commons.lang.time.StopWatch;
045
046 /**
047 * Start the Geronimo server.
048 *
049 * @goal start-server
050 *
051 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
052 */
053 public class StartServerMojo
054 extends InstallerMojoSupport
055 {
056 /**
057 * Set the false to skip the installation of the assembly, re-using anything
058 * that is already there.
059 *
060 * @parameter expression="${install}" default-value="true"
061 */
062 private boolean install = true;
063
064 /**
065 * Flag to control if we background the server or block Maven execution.
066 *
067 * @parameter expression="${background}" default-value="false"
068 */
069 private boolean background = false;
070
071 /**
072 * Set the maximum memory for the forked JVM.
073 *
074 * @parameter expression="${maximumMemory}"
075 */
076 private String maximumMemory = null;
077
078 /**
079 * The location of the Java Virtual Machine executable to launch the server with.
080 *
081 * @parameter
082 */
083 private File javaVirtualMachine;
084
085 /**
086 * Enable quiet mode.
087 *
088 * @parameter expression="${quiet}" default-value="false"
089 */
090 private boolean quiet = false;
091
092 /**
093 * Enable verbose mode.
094 *
095 * @parameter expression="${verbose}" default-value="false"
096 */
097 private boolean verbose = false;
098
099 /**
100 * Enable veryverbose mode.
101 *
102 * @parameter expression="${veryverbose}" default-value="false"
103 */
104 private boolean veryverbose = false;
105
106 /**
107 * Time in seconds to wait before terminating the forked JVM.
108 *
109 * @parameter expression="${timeout}" default-value="-1"
110 */
111 private int timeout = -1;
112
113 /**
114 * Time in seconds to wait while verifing that the server has started.
115 *
116 * @parameter expression="${verifyTimeout}" default-value="-1"
117 */
118 private int verifyTimeout = -1;
119
120 /**
121 * Enable propagation of <tt>org.apache.geronimo.*</tt> and <tt>geronimo.*</tt>
122 * properties from Maven to the forked server process.
123 *
124 * @parameter expression="${propagateGeronimoProperties}" default-value="true"
125 */
126 private boolean propagateGeronimoProperties;
127
128 /**
129 * An array of option sets which can be enabled by setting optionSetId.
130 *
131 * @parameter
132 */
133 private OptionSet[] optionSets = null;
134
135 /**
136 * A comma seperated list of optionSets to enabled.
137 *
138 * @parameter expression="${options}"
139 */
140 private String options = null;
141
142 /**
143 * A list of module names to be started using --override.
144 *
145 * @parameter
146 */
147 private String[] startModules = null;
148
149 private Timer timer = new Timer(true);
150
151 protected void doExecute() throws Exception {
152 if (install) {
153 installAssembly();
154 }
155 else {
156 log.info("Skipping assembly installation");
157
158 if (!geronimoHome.exists()) {
159 throw new MojoExecutionException("Missing pre-installed assembly directory: " + geronimoHome);
160 }
161 }
162
163 log.info("Starting Geronimo server...");
164
165 // Setup the JVM to start the server with
166 final Java java = (Java)createTask("java");
167 java.setJar(new File(geronimoHome, "bin/server.jar"));
168 java.setDir(geronimoHome);
169 java.setFailonerror(true);
170 java.setFork(true);
171
172 if (javaVirtualMachine != null) {
173 if (!javaVirtualMachine.exists()) {
174 throw new MojoExecutionException("Java virtual machine is not valid: " + javaVirtualMachine);
175 }
176
177 log.info("Using Java virtual machine: " + javaVirtualMachine);
178 java.setJvm(javaVirtualMachine.getCanonicalPath());
179 }
180
181 if (timeout > 0) {
182 java.setTimeout(new Long(timeout * 1000));
183 }
184
185 if (maximumMemory != null) {
186 java.setMaxmemory(maximumMemory);
187 }
188
189 // Load the Java programming language agent for JPA
190 File javaAgentJar = new File(geronimoHome, "bin/jpa.jar");
191 if (javaAgentJar.exists()) {
192 java.createJvmarg().setValue("-javaagent:" + javaAgentJar.getCanonicalPath());
193 }
194
195 // Propagate some properties from Maven to the server if enabled
196 if (propagateGeronimoProperties) {
197 Properties props = System.getProperties();
198 Iterator iter = props.keySet().iterator();
199 while (iter.hasNext()) {
200 String name = (String)iter.next();
201 String value = System.getProperty(name);
202
203 if (name.equals("geronimo.bootstrap.logging.enabled")) {
204 // Skip this property, never propagate it
205 }
206 else if (name.startsWith("org.apache.geronimo") || name.startsWith("geronimo")) {
207 if (log.isDebugEnabled()) {
208 log.debug("Propagating: " + name + "=" + value);
209 }
210 setSystemProperty(java, name, value);
211 }
212 }
213 }
214
215 // Apply option sets
216 if (options != null && (optionSets == null || optionSets.length == 0)) {
217 throw new MojoExecutionException("At least one optionSet must be defined to select one using options");
218 }
219 else if (options == null) {
220 options = "default";
221 }
222
223 if (optionSets != null && optionSets.length != 0) {
224 OptionSet[] sets = selectOptionSets();
225
226 for (int i=0; i < sets.length; i++) {
227 if (log.isDebugEnabled()) {
228 log.debug("Selected option set: " + sets[i]);
229 }
230 else {
231 log.info("Selected option set: " + sets[i].getId());
232 }
233
234 String[] options = sets[i].getOptions();
235 if (options != null) {
236 for (int j=0; j < options.length; j++) {
237 java.createJvmarg().setValue(options[j]);
238 }
239 }
240
241 Properties props = sets[i].getProperties();
242 if (props != null) {
243 Iterator iter = props.keySet().iterator();
244 while (iter.hasNext()) {
245 String name = (String)iter.next();
246 String value = props.getProperty(name);
247
248 setSystemProperty(java, name, value);
249 }
250 }
251 }
252 }
253
254 // Set the properties which we pass to the JVM from the startup script
255 setSystemProperty(java, "org.apache.geronimo.base.dir", geronimoHome);
256 // Use relative path
257 setSystemProperty(java, "java.io.tmpdir", "var/temp");
258 setSystemProperty(java, "java.endorsed.dirs", prefixSystemPath("java.endorsed.dirs", new File(geronimoHome, "lib/endorsed")));
259 setSystemProperty(java, "java.ext.dirs", prefixSystemPath("java.ext.dirs", new File(geronimoHome, "lib/ext")));
260
261 if (quiet) {
262 java.createArg().setValue("--quiet");
263 }
264 else {
265 java.createArg().setValue("--long");
266 }
267
268 if (verbose) {
269 java.createArg().setValue("--verbose");
270 }
271
272 if (veryverbose) {
273 java.createArg().setValue("--veryverbose");
274 }
275
276 if (startModules != null) {
277 if (startModules.length == 0) {
278 throw new MojoExecutionException("At least one module name must be configured with startModule");
279 }
280
281 log.info("Overriding the set of modules to be started");
282
283 java.createArg().setValue("--override");
284
285 for (int i=0; i < startModules.length; i++) {
286 java.createArg().setValue(startModules[i]);
287 }
288 }
289
290 //
291 // TODO: Check if this really does capture STDERR or not!
292 //
293
294 if (logOutput) {
295 File file = getLogFile();
296 FileUtils.forceMkdir(file.getParentFile());
297
298 log.info("Redirecting output to: " + file);
299
300 java.setOutput(file);
301 }
302
303 // Holds any exception that was thrown during startup
304 final ObjectHolder errorHolder = new ObjectHolder();
305
306 StopWatch watch = new StopWatch();
307 watch.start();
308
309 // Start the server int a seperate thread
310 Thread t = new Thread("Geronimo Server Runner") {
311 public void run() {
312 try {
313 java.execute();
314 }
315 catch (Exception e) {
316 errorHolder.set(e);
317
318 //
319 // NOTE: Don't log here, as when the JVM exists an exception will get thrown by Ant
320 // but that should be fine.
321 //
322 }
323 }
324 };
325 t.start();
326
327 log.info("Waiting for Geronimo server...");
328
329 // Setup a callback to time out verification
330 final ObjectHolder verifyTimedOut = new ObjectHolder();
331
332 TimerTask timeoutTask = new TimerTask() {
333 public void run() {
334 verifyTimedOut.set(Boolean.TRUE);
335 }
336 };
337
338 if (verifyTimeout > 0) {
339 if (log.isDebugEnabled()) {
340 log.debug("Starting verify timeout task; triggers in: " + verifyTimeout + " seconds");
341 }
342 timer.schedule(timeoutTask, verifyTimeout * 1000);
343 }
344
345 // Verify server started
346 ServerProxy server = new ServerProxy(hostname, port, username, password);
347 boolean started = false;
348 while (!started) {
349 if (verifyTimedOut.isSet()) {
350 throw new MojoExecutionException("Unable to verify if the server was started in the given time (" + verifyTimeout + " seconds)");
351 }
352
353 if (errorHolder.isSet()) {
354 throw new MojoExecutionException("Failed to start Geronimo server", (Throwable)errorHolder.get());
355 }
356
357 started = server.isFullyStarted();
358
359 if (!started) {
360 Throwable error = server.getLastError();
361 if ((error != null) && (log.isDebugEnabled())) {
362 log.debug("Server query failed; ignoring", error);
363 }
364
365 Thread.sleep(1000);
366 }
367 }
368 server.closeConnection();
369
370 // Stop the timer, server should be up now
371 timeoutTask.cancel();
372
373 log.info("Geronimo server started in " + watch);
374
375 if (!background) {
376 log.info("Waiting for Geronimo server to shutdown...");
377
378 t.join();
379 }
380 }
381
382 private String prefixSystemPath(final String name, final File file) {
383 assert name != null;
384 assert file != null;
385
386 String dirs = file.getPath();
387 String prop = System.getProperty(name, "");
388 if (prop.length() > 0) {
389 dirs += File.pathSeparator;
390 dirs += prop;
391 }
392 return dirs;
393 }
394
395 private OptionSet[] selectOptionSets() throws MojoExecutionException {
396 // Make a map of the option sets and validate ids
397 Map map = new HashMap();
398 for (int i=0; i<optionSets.length; i++) {
399 if (log.isDebugEnabled()) {
400 log.debug("Checking option set: " + optionSets[i]);
401 }
402
403 String id = optionSets[i].getId();
404
405 if (id == null && optionSets.length > 1) {
406 throw new MojoExecutionException("Must specify id for optionSet when more than one optionSet is configured");
407 }
408 else if (id == null && optionSets.length == 1) {
409 id = "default";
410 optionSets[i].setId(id);
411 }
412
413 assert id != null;
414 id = id.trim();
415
416 if (map.containsKey(id)) {
417 throw new MojoExecutionException("Must specify unique id for optionSet: " + id);
418 }
419 map.put(id, optionSets[i]);
420 }
421
422 StringTokenizer stok = new StringTokenizer(options, ",");
423
424 List selected = new ArrayList();
425 while (stok.hasMoreTokens()) {
426 String id = stok.nextToken();
427 OptionSet set = (OptionSet)map.get(id);
428
429 if (set == null) {
430 if ("default".equals(id)) {
431 if (log.isDebugEnabled()) {
432 log.debug("Default optionSet selected, but no optionSet defined with that id; ignoring");
433 }
434 }
435 else {
436 log.warn("Missing optionSet for id: " + id);
437 }
438 }
439 else {
440 selected.add(set);
441 }
442 }
443
444 return (OptionSet[]) selected.toArray(new OptionSet[selected.size()]);
445 }
446
447 protected String getFullClassName() {
448 return this.getClass().getName();
449 }
450 }