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: 552399 $ $Date: 2007-07-02 00:31:56 -0400 (Mon, 02 Jul 2007) $
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 * @paramter
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 log.debug("Propagating: " + name + "=" + value);
208 setSystemProperty(java, name, value);
209 }
210 }
211 }
212
213 // Apply option sets
214 if (options != null && (optionSets == null || optionSets.length == 0)) {
215 throw new MojoExecutionException("At least one optionSet must be defined to select one using options");
216 }
217 else if (options == null) {
218 options = "default";
219 }
220
221 if (optionSets != null && optionSets.length != 0) {
222 OptionSet[] sets = selectOptionSets();
223
224 for (int i=0; i < sets.length; i++) {
225 if (log.isDebugEnabled()) {
226 log.debug("Selected option set: " + sets[i]);
227 }
228 else {
229 log.info("Selected option set: " + sets[i].getId());
230 }
231
232 String[] options = sets[i].getOptions();
233 if (options != null) {
234 for (int j=0; j < options.length; j++) {
235 java.createJvmarg().setValue(options[j]);
236 }
237 }
238
239 Properties props = sets[i].getProperties();
240 if (props != null) {
241 Iterator iter = props.keySet().iterator();
242 while (iter.hasNext()) {
243 String name = (String)iter.next();
244 String value = props.getProperty(name);
245
246 setSystemProperty(java, name, value);
247 }
248 }
249 }
250 }
251
252 // Set the properties which we pass to the JVM from the startup script
253 setSystemProperty(java, "org.apache.geronimo.base.dir", geronimoHome);
254 // Use relative path
255 setSystemProperty(java, "java.io.tmpdir", "var/temp");
256 setSystemProperty(java, "java.endorsed.dirs", prefixSystemPath("java.endorsed.dirs", new File(geronimoHome, "lib/endorsed")));
257 setSystemProperty(java, "java.ext.dirs", prefixSystemPath("java.ext.dirs", new File(geronimoHome, "lib/ext")));
258
259 if (quiet) {
260 java.createArg().setValue("--quiet");
261 }
262 else {
263 java.createArg().setValue("--long");
264 }
265
266 if (verbose) {
267 java.createArg().setValue("--verbose");
268 }
269
270 if (veryverbose) {
271 java.createArg().setValue("--veryverbose");
272 }
273
274 if (startModules != null) {
275 if (startModules.length == 0) {
276 throw new MojoExecutionException("At least one module name must be configured with startModule");
277 }
278
279 log.info("Overriding the set of modules to be started");
280
281 java.createArg().setValue("--override");
282
283 for (int i=0; i < startModules.length; i++) {
284 java.createArg().setValue(startModules[i]);
285 }
286 }
287
288 //
289 // TODO: Check if this really does capture STDERR or not!
290 //
291
292 if (logOutput) {
293 File file = getLogFile();
294 FileUtils.forceMkdir(file.getParentFile());
295
296 log.info("Redirecting output to: " + file);
297
298 java.setOutput(file);
299 }
300
301 // Holds any exception that was thrown during startup
302 final ObjectHolder errorHolder = new ObjectHolder();
303
304 StopWatch watch = new StopWatch();
305 watch.start();
306
307 // Start the server int a seperate thread
308 Thread t = new Thread("Geronimo Server Runner") {
309 public void run() {
310 try {
311 java.execute();
312 }
313 catch (Exception e) {
314 errorHolder.set(e);
315
316 //
317 // NOTE: Don't log here, as when the JVM exists an exception will get thrown by Ant
318 // but that should be fine.
319 //
320 }
321 }
322 };
323 t.start();
324
325 log.info("Waiting for Geronimo server...");
326
327 // Setup a callback to time out verification
328 final ObjectHolder verifyTimedOut = new ObjectHolder();
329
330 TimerTask timeoutTask = new TimerTask() {
331 public void run() {
332 verifyTimedOut.set(Boolean.TRUE);
333 }
334 };
335
336 if (verifyTimeout > 0) {
337 log.debug("Starting verify timeout task; triggers in: " + verifyTimeout + " seconds");
338 timer.schedule(timeoutTask, verifyTimeout * 1000);
339 }
340
341 // Verify server started
342 ServerProxy server = new ServerProxy(hostname, port, username, password);
343 boolean started = false;
344 while (!started) {
345 if (verifyTimedOut.isSet()) {
346 throw new MojoExecutionException("Unable to verify if the server was started in the given time (" + verifyTimeout + " seconds)");
347 }
348
349 if (errorHolder.isSet()) {
350 throw new MojoExecutionException("Failed to start Geronimo server", (Throwable)errorHolder.get());
351 }
352
353 started = server.isFullyStarted();
354
355 if (!started) {
356 Throwable error = server.getLastError();
357 if (error != null) {
358 log.debug("Server query failed; ignoring", error);
359 }
360
361 Thread.sleep(1000);
362 }
363 }
364
365 // Stop the timer, server should be up now
366 timeoutTask.cancel();
367
368 log.info("Geronimo server started in " + watch);
369
370 if (!background) {
371 log.info("Waiting for Geronimo server to shutdown...");
372
373 t.join();
374 }
375 }
376
377 private String prefixSystemPath(final String name, final File file) {
378 assert name != null;
379 assert file != null;
380
381 String dirs = file.getPath();
382 String prop = System.getProperty(name, "");
383 if (prop.length() > 0) {
384 dirs += File.pathSeparator;
385 dirs += prop;
386 }
387 return dirs;
388 }
389
390 private OptionSet[] selectOptionSets() throws MojoExecutionException {
391 // Make a map of the option sets and validate ids
392 Map map = new HashMap();
393 for (int i=0; i<optionSets.length; i++) {
394 if (log.isDebugEnabled()) {
395 log.debug("Checking option set: " + optionSets[i]);
396 }
397
398 String id = optionSets[i].getId();
399
400 if (id == null && optionSets.length > 1) {
401 throw new MojoExecutionException("Must specify id for optionSet when more than one optionSet is configured");
402 }
403 else if (id == null && optionSets.length == 1) {
404 id = "default";
405 optionSets[i].setId(id);
406 }
407
408 assert id != null;
409 id = id.trim();
410
411 if (map.containsKey(id)) {
412 throw new MojoExecutionException("Must specify unique id for optionSet: " + id);
413 }
414 map.put(id, optionSets[i]);
415 }
416
417 StringTokenizer stok = new StringTokenizer(options, ",");
418
419 List selected = new ArrayList();
420 while (stok.hasMoreTokens()) {
421 String id = stok.nextToken();
422 OptionSet set = (OptionSet)map.get(id);
423
424 if (set == null) {
425 if ("default".equals(id)) {
426 log.debug("Default optionSet selected, but no optionSet defined with that id; ignoring");
427 }
428 else {
429 log.warn("Missing optionSet for id: " + id);
430 }
431 }
432 else {
433 selected.add(set);
434 }
435 }
436
437 return (OptionSet[]) selected.toArray(new OptionSet[selected.size()]);
438 }
439
440 protected String getFullClassName() {
441 return this.getClass().getName();
442 }
443 }