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.genesis.ant; 021 022 import java.io.File; 023 import java.util.ArrayList; 024 import java.util.HashMap; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.Map; 028 import java.util.Properties; 029 import java.util.StringTokenizer; 030 import java.util.Timer; 031 import java.util.TimerTask; 032 033 import org.apache.maven.artifact.Artifact; 034 import org.apache.maven.artifact.repository.ArtifactRepository; 035 import org.apache.maven.plugin.MojoExecutionException; 036 import org.apache.maven.project.MavenProject; 037 import org.apache.tools.ant.taskdefs.Java; 038 import org.apache.tools.ant.types.Path; 039 import org.codehaus.plexus.util.FileUtils; 040 041 import org.apache.geronimo.genesis.util.ObjectHolder; 042 043 // 044 // FIXME: Need to find a better way to allow plugins to re-use the parameter configuration! 045 // 046 047 /** 048 * Support for mojos that launch Java processes. 049 * 050 * @version $Rev: 463461 $ $Date: 2006-10-12 15:11:08 -0700 (Thu, 12 Oct 2006) $ 051 */ 052 public abstract class JavaLauncherMojoSupport 053 extends AntMojoSupport 054 { 055 // 056 // TODO: Use AntHelper component and extend from MojoSupport 057 // 058 059 private Timer timer = new Timer(true); 060 061 /** 062 * Set the maximum memory for the forked JVM. 063 * 064 * @parameter expression="${maximumMemory}" 065 */ 066 private String maximumMemory = null; 067 068 /** 069 * The base working directory where process will be started from, a sub-directory 070 * the process name will be used for the effective working directory. 071 * 072 * @parameter expression="${project.build.directory}" 073 * @required 074 */ 075 protected File baseWorkingDirectory = null; 076 077 /** 078 * Enable logging mode. 079 * 080 * @parameter expression="${logOutput}" default-value="false" 081 */ 082 protected boolean logOutput = false; 083 084 /** 085 * Flag to control if we background the process or block Maven execution. 086 * 087 * @parameter default-value="false" 088 * @required 089 */ 090 protected boolean background = false; 091 092 /** 093 * Timeout for the process in seconds. 094 * 095 * @parameter expression="${timeout}" default-value="-1" 096 */ 097 protected int timeout = -1; 098 099 /** 100 * Time in seconds to wait while verifing that the process has started (if there is custom validation). 101 * 102 * @parameter expression="${verifyTimeout}" default-value="-1" 103 */ 104 private int verifyTimeout = -1; 105 106 /** 107 * An array of option sets which can be enabled by setting <tt>options</tt>. 108 * 109 * @parameter 110 */ 111 protected OptionSet[] optionSets = null; 112 113 /** 114 * A comma seperated list of <tt>optionSets</tt> to enabled. 115 * 116 * @parameter expression="${options}" 117 */ 118 protected String options = null; 119 120 /** 121 * Map of of plugin artifacts. 122 * 123 * @parameter expression="${plugin.artifactMap}" 124 * @required 125 * @readonly 126 */ 127 protected Map pluginArtifactMap = null; 128 129 protected void doExecute() throws Exception { 130 log.info("Starting " + getProcessTitle() + "..."); 131 132 final Java java = (Java)createTask("java"); 133 134 File workingDirectory = getWorkingDirectory(); 135 FileUtils.forceMkdir(workingDirectory); 136 java.setDir(workingDirectory); 137 138 java.setFailonerror(true); 139 java.setFork(true); 140 141 if (maximumMemory != null) { 142 java.setMaxmemory(maximumMemory); 143 } 144 145 if (timeout > 0) { 146 log.info("Timeout after: " + timeout + " seconds"); 147 148 java.setTimeout(new Long(timeout * 1000)); 149 } 150 151 if (logOutput) { 152 File file = getLogFile(); 153 log.info("Redirecting output to: " + file); 154 FileUtils.forceMkdir(file.getParentFile()); 155 156 java.setLogError(true); 157 java.setOutput(file); 158 } 159 160 java.setClassname(getClassName()); 161 setClassPath(java.createClasspath()); 162 163 applyOptionSets(java); 164 165 customizeJava(java); 166 167 // Holds any exception that was thrown during startup 168 final ObjectHolder errorHolder = new ObjectHolder(); 169 170 // Start the process in a seperate thread 171 Thread t = new Thread(getProcessTitle() + " Runner") { 172 public void run() { 173 try { 174 java.execute(); 175 } 176 catch (Exception e) { 177 errorHolder.set(e); 178 179 // 180 // NOTE: Don't log here, as when the JVM exists an exception will get thrown by Ant 181 // but that should be fine. 182 // 183 } 184 } 185 }; 186 t.start(); 187 188 log.debug("Waiting for " + getProcessTitle() + "..."); 189 190 // Setup a callback to time out verification 191 final ObjectHolder verifyTimedOut = new ObjectHolder(); 192 193 TimerTask timeoutTask = new TimerTask() { 194 public void run() { 195 verifyTimedOut.set(Boolean.TRUE); 196 } 197 }; 198 199 if (verifyTimeout > 0) { 200 log.debug("Starting verify timeout task; triggers in: " + verifyTimeout + "s"); 201 timer.schedule(timeoutTask, verifyTimeout * 1000); 202 } 203 204 // Verify the process has started 205 boolean started = false; 206 while (!started) { 207 if (verifyTimedOut.isSet()) { 208 throw new MojoExecutionException("Unable to verify if the " + getProcessTitle() + " process was started in the given time"); 209 } 210 211 if (errorHolder.isSet()) { 212 throw new MojoExecutionException("Failed to launch " + getProcessTitle(), (Throwable)errorHolder.get()); 213 } 214 215 try { 216 started = verifyProcessStarted(); 217 } 218 catch (Exception e) { 219 // ignore 220 } 221 222 Thread.sleep(1000); 223 } 224 225 log.info(getProcessTitle() + " started"); 226 227 if (!background) { 228 log.info("Waiting for " + getProcessTitle() + " to shutdown..."); 229 230 t.join(); 231 } 232 } 233 234 protected Artifact getPluginArtifact(final String name) throws MojoExecutionException { 235 assert name != null; 236 237 Artifact artifact = (Artifact)pluginArtifactMap.get(name); 238 if (artifact == null) { 239 throw new MojoExecutionException("Unable to locate '" + name + "' in the list of plugin artifacts"); 240 } 241 242 return artifact; 243 } 244 245 protected void appendArtifactFile(final Path classpath, final String name) throws MojoExecutionException { 246 assert classpath != null; 247 assert name != null; 248 249 appendArtifact(classpath, getPluginArtifact(name)); 250 } 251 252 protected void appendArtifact(final Path classpath, final Artifact artifact) throws MojoExecutionException { 253 assert classpath != null; 254 assert artifact != null; 255 256 File file = artifact.getFile(); 257 if (file == null) { 258 throw new MojoExecutionException("Artifact does not have an attached file: " + artifact); 259 } 260 261 classpath.createPathElement().setLocation(file); 262 } 263 264 private void applyOptionSets(final Java java) throws MojoExecutionException { 265 assert java != null; 266 267 // 268 // TODO: Add optionSet activation 269 // 270 271 // Apply option sets 272 if (options != null && (optionSets == null || optionSets.length == 0)) { 273 throw new MojoExecutionException("At least one optionSet must be defined to select one using options"); 274 } 275 else if (options == null) { 276 options = "default"; 277 } 278 279 if (optionSets != null && optionSets.length != 0) { 280 OptionSet[] sets = selectOptionSets(); 281 282 for (int i=0; i < sets.length; i++) { 283 if (log.isDebugEnabled()) { 284 log.debug("Selected option set: " + sets[i]); 285 } 286 else { 287 log.info("Selected option set: " + sets[i].getId()); 288 } 289 290 String[] options = sets[i].getOptions(); 291 if (options != null) { 292 for (int j=0; j < options.length; j++) { 293 java.createJvmarg().setValue(options[j]); 294 } 295 } 296 297 Properties props = sets[i].getProperties(); 298 if (props != null) { 299 Iterator iter = props.keySet().iterator(); 300 while (iter.hasNext()) { 301 String name = (String)iter.next(); 302 String value = props.getProperty(name); 303 304 setSystemProperty(java, name, value); 305 } 306 } 307 } 308 } 309 } 310 311 private OptionSet[] selectOptionSets() throws MojoExecutionException { 312 // Make a map of the option sets and validate ids 313 Map map = new HashMap(); 314 for (int i=0; i<optionSets.length; i++) { 315 if (log.isDebugEnabled()) { 316 log.debug("Checking option set: " + optionSets[i]); 317 } 318 319 String id = optionSets[i].getId(); 320 321 if (id == null && optionSets.length > 1) { 322 throw new MojoExecutionException("Must specify id for optionSet when more than one optionSet is configured"); 323 } 324 else if (id == null && optionSets.length == 1) { 325 id = "default"; 326 optionSets[i].setId(id); 327 } 328 329 assert id != null; 330 id = id.trim(); 331 332 if (map.containsKey(id)) { 333 throw new MojoExecutionException("Must specify unique id for optionSet: " + optionSets[i]); 334 } 335 map.put(id, optionSets[i]); 336 } 337 338 StringTokenizer stok = new StringTokenizer(options, ","); 339 340 List selected = new ArrayList(); 341 while (stok.hasMoreTokens()) { 342 String id = stok.nextToken(); 343 OptionSet set = (OptionSet)map.get(options); 344 345 if (set == null) { 346 if ("default".equals(options)) { 347 log.debug("Default optionSet selected, but no optionSet defined with that id; ignoring"); 348 } 349 else { 350 throw new MojoExecutionException("Missing optionSet for id: " + id); 351 } 352 } 353 else { 354 selected.add(set); 355 } 356 } 357 358 return (OptionSet[]) selected.toArray(new OptionSet[selected.size()]); 359 } 360 361 // 362 // MojoSupport Hooks 363 // 364 365 /** 366 * The maven project. 367 * 368 * @parameter expression="${project}" 369 * @required 370 * @readonly 371 */ 372 protected MavenProject project = null; 373 374 protected MavenProject getProject() { 375 return project; 376 } 377 378 /** 379 * @parameter expression="${localRepository}" 380 * @readonly 381 * @required 382 */ 383 protected ArtifactRepository artifactRepository = null; 384 385 protected ArtifactRepository getArtifactRepository() { 386 return artifactRepository; 387 } 388 389 // 390 // Sub-class API 391 // 392 393 protected abstract String getProcessName(); 394 395 protected String getProcessTitle() { 396 return getProcessName(); 397 } 398 399 protected File getWorkingDirectory() { 400 return new File(baseWorkingDirectory, getProcessName()); 401 } 402 403 protected File getLogFile() { 404 return new File(getWorkingDirectory(), getProcessName() + ".log"); 405 } 406 407 protected abstract String getClassName(); 408 409 protected abstract void setClassPath(Path classpath) throws Exception; 410 411 protected void customizeJava(final Java java) throws MojoExecutionException { 412 assert java != null; 413 414 // nothing by default 415 } 416 417 protected boolean verifyProcessStarted() throws Exception { 418 return true; 419 } 420 }