001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.types;
020:
021: import org.apache.tools.ant.BuildException;
022: import org.apache.tools.ant.Project;
023: import org.apache.tools.ant.util.JavaEnvUtils;
024:
025: import java.util.Enumeration;
026: import java.util.LinkedList;
027: import java.util.List;
028: import java.util.ListIterator;
029: import java.util.Properties;
030: import java.util.Vector;
031:
032: /**
033: * A representation of a Java command line that is
034: * a composite of 2 <tt>Commandline</tt>s. One is used for the
035: * vm/options and one for the classname/arguments. It provides
036: * specific methods for a Java command line.
037: *
038: */
039: public class CommandlineJava implements Cloneable {
040:
041: /**
042: * commands to the JVM
043: */
044: private Commandline vmCommand = new Commandline();
045: /**
046: * actual java commands
047: */
048: private Commandline javaCommand = new Commandline();
049: /**
050: * properties to add using -D
051: */
052: private SysProperties sysProperties = new SysProperties();
053: private Path classpath = null;
054: private Path bootclasspath = null;
055: private String vmVersion;
056: private String maxMemory = null;
057: /**
058: * any assertions to make? Currently only supported in forked JVMs
059: */
060: private Assertions assertions = null;
061:
062: /**
063: * Indicate whether it will execute a jar file or not, in this case
064: * the first vm option must be a -jar and the 'executable' is a jar file.
065: */
066: private boolean executeJar = false;
067:
068: /**
069: * Whether system properties and bootclasspath shall be cloned.
070: * @since Ant 1.7
071: */
072: private boolean cloneVm = false;
073:
074: /**
075: * Specialized Environment class for System properties.
076: */
077: public static class SysProperties extends Environment implements
078: Cloneable {
079: // CheckStyle:VisibilityModifier OFF - bc
080: /** the system properties. */
081: Properties sys = null;
082: // CheckStyle:VisibilityModifier ON
083: private Vector propertySets = new Vector();
084:
085: /**
086: * Get the properties as an array; this is an override of the
087: * superclass, as it evaluates all the properties.
088: * @return the array of definitions; may be null.
089: * @throws BuildException on error.
090: */
091: public String[] getVariables() throws BuildException {
092:
093: List definitions = new LinkedList();
094: ListIterator list = definitions.listIterator();
095: addDefinitionsToList(list);
096: if (definitions.size() == 0) {
097: return null;
098: } else {
099: return (String[]) definitions
100: .toArray(new String[definitions.size()]);
101: }
102: }
103:
104: /**
105: * Add all definitions (including property sets) to a list.
106: * @param listIt list iterator supporting add method.
107: */
108: public void addDefinitionsToList(ListIterator listIt) {
109: String[] props = super .getVariables();
110: if (props != null) {
111: for (int i = 0; i < props.length; i++) {
112: listIt.add("-D" + props[i]);
113: }
114: }
115: Properties propertySetProperties = mergePropertySets();
116: for (Enumeration e = propertySetProperties.keys(); e
117: .hasMoreElements();) {
118: String key = (String) e.nextElement();
119: String value = propertySetProperties.getProperty(key);
120: listIt.add("-D" + key + "=" + value);
121: }
122: }
123:
124: /**
125: * Get the size of the sysproperties instance. This merges all
126: * property sets, so is not an O(1) operation.
127: * @return the size of the sysproperties instance.
128: */
129: public int size() {
130: Properties p = mergePropertySets();
131: return variables.size() + p.size();
132: }
133:
134: /**
135: * Cache the system properties and set the system properties to the
136: * new values.
137: * @throws BuildException if Security prevented this operation.
138: */
139: public void setSystem() throws BuildException {
140: try {
141: sys = System.getProperties();
142: Properties p = new Properties();
143: for (Enumeration e = sys.propertyNames(); e
144: .hasMoreElements();) {
145: String name = (String) e.nextElement();
146: p.put(name, sys.getProperty(name));
147: }
148: p.putAll(mergePropertySets());
149: for (Enumeration e = variables.elements(); e
150: .hasMoreElements();) {
151: Environment.Variable v = (Environment.Variable) e
152: .nextElement();
153: v.validate();
154: p.put(v.getKey(), v.getValue());
155: }
156: System.setProperties(p);
157: } catch (SecurityException e) {
158: throw new BuildException(
159: "Cannot modify system properties", e);
160: }
161: }
162:
163: /**
164: * Restore the system properties to the cached value.
165: * @throws BuildException if Security prevented this operation, or
166: * there were no system properties to restore.
167: */
168: public void restoreSystem() throws BuildException {
169: if (sys == null) {
170: throw new BuildException(
171: "Unbalanced nesting of SysProperties");
172: }
173:
174: try {
175: System.setProperties(sys);
176: sys = null;
177: } catch (SecurityException e) {
178: throw new BuildException(
179: "Cannot modify system properties", e);
180: }
181: }
182:
183: /**
184: * Create a deep clone.
185: * @return a cloned instance of SysProperties.
186: * @exception CloneNotSupportedException for signature.
187: */
188: public Object clone() throws CloneNotSupportedException {
189: try {
190: SysProperties c = (SysProperties) super .clone();
191: c.variables = (Vector) variables.clone();
192: c.propertySets = (Vector) propertySets.clone();
193: return c;
194: } catch (CloneNotSupportedException e) {
195: return null;
196: }
197: }
198:
199: /**
200: * Add a propertyset to the total set.
201: * @param ps the new property set.
202: */
203: public void addSyspropertyset(PropertySet ps) {
204: propertySets.addElement(ps);
205: }
206:
207: /**
208: * Add a propertyset to the total set.
209: * @param ps the new property set.
210: * @since Ant 1.6.3
211: */
212: public void addSysproperties(SysProperties ps) {
213: variables.addAll(ps.variables);
214: propertySets.addAll(ps.propertySets);
215: }
216:
217: /**
218: * Merge all property sets into a single Properties object.
219: * @return the merged object.
220: */
221: private Properties mergePropertySets() {
222: Properties p = new Properties();
223: for (Enumeration e = propertySets.elements(); e
224: .hasMoreElements();) {
225: PropertySet ps = (PropertySet) e.nextElement();
226: p.putAll(ps.getProperties());
227: }
228: return p;
229: }
230:
231: }
232:
233: /**
234: * Constructor uses the VM we are running on now.
235: */
236: public CommandlineJava() {
237: setVm(JavaEnvUtils.getJreExecutable("java"));
238: setVmversion(JavaEnvUtils.getJavaVersion());
239: }
240:
241: /**
242: * Create a new argument to the java program.
243: * @return an argument to be configured.
244: */
245: public Commandline.Argument createArgument() {
246: return javaCommand.createArgument();
247: }
248:
249: /**
250: * Create a new JVM argument.
251: * @return an argument to be configured.
252: */
253: public Commandline.Argument createVmArgument() {
254: return vmCommand.createArgument();
255: }
256:
257: /**
258: * Add a system property.
259: * @param sysp a property to be set in the JVM.
260: */
261: public void addSysproperty(Environment.Variable sysp) {
262: sysProperties.addVariable(sysp);
263: }
264:
265: /**
266: * Add a set of system properties.
267: * @param sysp a set of properties.
268: */
269: public void addSyspropertyset(PropertySet sysp) {
270: sysProperties.addSyspropertyset(sysp);
271: }
272:
273: /**
274: * Add a set of system properties.
275: * @param sysp a set of properties.
276: * @since Ant 1.6.3
277: */
278: public void addSysproperties(SysProperties sysp) {
279: sysProperties.addSysproperties(sysp);
280: }
281:
282: /**
283: * Set the executable used to start the new JVM.
284: * @param vm the executable to use.
285: */
286: public void setVm(String vm) {
287: vmCommand.setExecutable(vm);
288: }
289:
290: /**
291: * Set the JVM version required.
292: * @param value the version required.
293: */
294: public void setVmversion(String value) {
295: vmVersion = value;
296: }
297:
298: /**
299: * Set whether system properties will be copied to the cloned VM--as
300: * well as the bootclasspath unless you have explicitly specified
301: * a bootclasspath.
302: * @param cloneVm if true copy the system properties.
303: * @since Ant 1.7
304: */
305: public void setCloneVm(boolean cloneVm) {
306: this .cloneVm = cloneVm;
307: }
308:
309: /**
310: * Get the current assertions.
311: * @return assertions or null.
312: */
313: public Assertions getAssertions() {
314: return assertions;
315: }
316:
317: /**
318: * Add an assertion set to the command.
319: * @param assertions assertions to make.
320: */
321: public void setAssertions(Assertions assertions) {
322: this .assertions = assertions;
323: }
324:
325: /**
326: * Set a jar file to execute via the -jar option.
327: * @param jarpathname the pathname of the jar to execute.
328: */
329: public void setJar(String jarpathname) {
330: javaCommand.setExecutable(jarpathname);
331: executeJar = true;
332: }
333:
334: /**
335: * Get the name of the jar to be run.
336: * @return the pathname of the jar file to run via -jar option
337: * or <tt>null</tt> if there is no jar to run.
338: * @see #getClassname()
339: */
340: public String getJar() {
341: if (executeJar) {
342: return javaCommand.getExecutable();
343: }
344: return null;
345: }
346:
347: /**
348: * Set the classname to execute.
349: * @param classname the fully qualified classname.
350: */
351: public void setClassname(String classname) {
352: javaCommand.setExecutable(classname);
353: executeJar = false;
354: }
355:
356: /**
357: * Get the name of the class to be run.
358: * @return the name of the class to run or <tt>null</tt> if there is no class.
359: * @see #getJar()
360: */
361: public String getClassname() {
362: if (!executeJar) {
363: return javaCommand.getExecutable();
364: }
365: return null;
366: }
367:
368: /**
369: * Create a classpath.
370: * @param p the project to use to create the path.
371: * @return a path to be configured.
372: */
373: public Path createClasspath(Project p) {
374: if (classpath == null) {
375: classpath = new Path(p);
376: }
377: return classpath;
378: }
379:
380: /**
381: * Create a boot classpath.
382: * @param p the project to use to create the path.
383: * @return a path to be configured.
384: * @since Ant 1.6
385: */
386: public Path createBootclasspath(Project p) {
387: if (bootclasspath == null) {
388: bootclasspath = new Path(p);
389: }
390: return bootclasspath;
391: }
392:
393: /**
394: * Get the vm version.
395: * @return the vm version.
396: */
397: public String getVmversion() {
398: return vmVersion;
399: }
400:
401: /**
402: * Get the command line to run a Java vm.
403: * @return the list of all arguments necessary to run the vm.
404: */
405: public String[] getCommandline() {
406: //create the list
407: List commands = new LinkedList();
408: final ListIterator listIterator = commands.listIterator();
409: //fill it
410: addCommandsToList(listIterator);
411: //convert to an array
412: return (String[]) commands.toArray(new String[commands.size()]);
413: }
414:
415: /**
416: * Add all the commands to a list identified by the iterator passed in.
417: * @param listIterator an iterator that supports the add method.
418: * @since Ant 1.6
419: */
420: private void addCommandsToList(final ListIterator listIterator) {
421: //create the command to run Java, including user specified options
422: getActualVMCommand().addCommandToList(listIterator);
423: // properties are part of the vm options...
424: sysProperties.addDefinitionsToList(listIterator);
425:
426: if (isCloneVm()) {
427: SysProperties clonedSysProperties = new SysProperties();
428: PropertySet ps = new PropertySet();
429: PropertySet.BuiltinPropertySetName sys = new PropertySet.BuiltinPropertySetName();
430: sys.setValue("system");
431: ps.appendBuiltin(sys);
432: clonedSysProperties.addSyspropertyset(ps);
433: clonedSysProperties.addDefinitionsToList(listIterator);
434: }
435: //boot classpath
436: Path bcp = calculateBootclasspath(true);
437: if (bcp.size() > 0) {
438: listIterator.add("-Xbootclasspath:" + bcp.toString());
439: }
440: //main classpath
441: if (haveClasspath()) {
442: listIterator.add("-classpath");
443: listIterator.add(classpath.concatSystemClasspath("ignore")
444: .toString());
445: }
446: //now any assertions are added
447: if (getAssertions() != null) {
448: getAssertions().applyAssertions(listIterator);
449: }
450: // JDK usage command line says that -jar must be the first option, as there is
451: // a bug in JDK < 1.4 that forces the jvm type to be specified as the first
452: // option, it is appended here as specified in the docs even though there is
453: // in fact no order.
454: if (executeJar) {
455: listIterator.add("-jar");
456: }
457: // this is the classname to run as well as its arguments.
458: // in case of 'executeJar', the executable is a jar file.
459: javaCommand.addCommandToList(listIterator);
460: }
461:
462: /**
463: * Specify max memory of the JVM.
464: * -mx or -Xmx depending on VM version.
465: * @param max the string to pass to the jvm to specifiy the max memory.
466: */
467: public void setMaxmemory(String max) {
468: this .maxMemory = max;
469: }
470:
471: /**
472: * Get a string description.
473: * @return the command line as a string.
474: */
475: public String toString() {
476: return Commandline.toString(getCommandline());
477: }
478:
479: /**
480: * Return a String that describes the command and arguments suitable for
481: * verbose output before a call to <code>Runtime.exec(String[])<code>.
482: * @return the description string.
483: * @since Ant 1.5
484: */
485: public String describeCommand() {
486: return Commandline.describeCommand(getCommandline());
487: }
488:
489: /**
490: * Return a String that describes the java command and arguments
491: * for in-VM executions.
492: *
493: * <p>The class name is the executable in this context.</p>
494: * @return the description string.
495: * @since Ant 1.5
496: */
497: public String describeJavaCommand() {
498: return Commandline.describeCommand(getJavaCommand());
499: }
500:
501: /**
502: * Get the VM command parameters, including memory settings.
503: * @return the VM command parameters.
504: */
505: protected Commandline getActualVMCommand() {
506: Commandline actualVMCommand = (Commandline) vmCommand.clone();
507: if (maxMemory != null) {
508: if (vmVersion.startsWith("1.1")) {
509: actualVMCommand.createArgument().setValue(
510: "-mx" + maxMemory);
511: } else {
512: actualVMCommand.createArgument().setValue(
513: "-Xmx" + maxMemory);
514: }
515: }
516: return actualVMCommand;
517: }
518:
519: /**
520: * Get the size of the java command line. This is a fairly intensive
521: * operation, as it has to evaluate the size of many components.
522: * @return the total number of arguments in the java command line.
523: * @see #getCommandline()
524: * @deprecated since 1.7.
525: * Please dont use this, it effectively creates the
526: * entire command.
527: */
528: public int size() {
529: int size = getActualVMCommand().size() + javaCommand.size()
530: + sysProperties.size();
531: // cloned system properties
532: if (isCloneVm()) {
533: size += System.getProperties().size();
534: }
535: // classpath is "-classpath <classpath>" -> 2 args
536: if (haveClasspath()) {
537: size += 2;
538: }
539: // bootclasspath is "-Xbootclasspath:<classpath>" -> 1 arg
540: if (calculateBootclasspath(true).size() > 0) {
541: size++;
542: }
543: // jar execution requires an additional -jar option
544: if (executeJar) {
545: size++;
546: }
547: //assertions take up space too
548: if (getAssertions() != null) {
549: size += getAssertions().size();
550: }
551: return size;
552: }
553:
554: /**
555: * Get the Java command to be used.
556: * @return the java command--not a clone.
557: */
558: public Commandline getJavaCommand() {
559: return javaCommand;
560: }
561:
562: /**
563: * Get the VM command, including memory.
564: * @return A deep clone of the instance's VM command, with memory settings added.
565: */
566: public Commandline getVmCommand() {
567: return getActualVMCommand();
568: }
569:
570: /**
571: * Get the classpath for the command.
572: * @return the classpath or null.
573: */
574: public Path getClasspath() {
575: return classpath;
576: }
577:
578: /**
579: * Get the boot classpath.
580: * @return boot classpath or null.
581: */
582: public Path getBootclasspath() {
583: return bootclasspath;
584: }
585:
586: /**
587: * Cache current system properties and set them to those in this
588: * Java command.
589: * @throws BuildException if Security prevented this operation.
590: */
591: public void setSystemProperties() throws BuildException {
592: sysProperties.setSystem();
593: }
594:
595: /**
596: * Restore the cached system properties.
597: * @throws BuildException if Security prevented this operation, or
598: * there was no system properties to restore
599: */
600: public void restoreSystemProperties() throws BuildException {
601: sysProperties.restoreSystem();
602: }
603:
604: /**
605: * Get the system properties object.
606: * @return The system properties object.
607: */
608: public SysProperties getSystemProperties() {
609: return sysProperties;
610: }
611:
612: /**
613: * Deep clone the object.
614: * @return a CommandlineJava object.
615: * @throws BuildException if anything went wrong.
616: * @throws CloneNotSupportedException never.
617: */
618: public Object clone() throws CloneNotSupportedException {
619: try {
620: CommandlineJava c = (CommandlineJava) super .clone();
621: c.vmCommand = (Commandline) vmCommand.clone();
622: c.javaCommand = (Commandline) javaCommand.clone();
623: c.sysProperties = (SysProperties) sysProperties.clone();
624: if (classpath != null) {
625: c.classpath = (Path) classpath.clone();
626: }
627: if (bootclasspath != null) {
628: c.bootclasspath = (Path) bootclasspath.clone();
629: }
630: if (assertions != null) {
631: c.assertions = (Assertions) assertions.clone();
632: }
633: return c;
634: } catch (CloneNotSupportedException e) {
635: throw new BuildException(e);
636: }
637: }
638:
639: /**
640: * Clear out the java arguments.
641: */
642: public void clearJavaArgs() {
643: javaCommand.clearArgs();
644: }
645:
646: /**
647: * Determine whether the classpath has been specified, and whether it shall
648: * really be used or be nulled by build.sysclasspath.
649: * @return true if the classpath is to be used.
650: * @since Ant 1.6
651: */
652: protected boolean haveClasspath() {
653: Path fullClasspath = classpath != null ? classpath
654: .concatSystemClasspath("ignore") : null;
655: return fullClasspath != null
656: && fullClasspath.toString().trim().length() > 0;
657: }
658:
659: /**
660: * Determine whether the bootclasspath has been specified, and whether it
661: * shall really be used (build.sysclasspath could be set or the VM may not
662: * support it).
663: *
664: * @param log whether to log a warning if a bootclasspath has been
665: * specified but will be ignored.
666: * @return true if the bootclasspath is to be used.
667: * @since Ant 1.6
668: */
669: protected boolean haveBootclasspath(boolean log) {
670: return calculateBootclasspath(log).size() > 0;
671: }
672:
673: /**
674: * Calculate the bootclasspath based on the bootclasspath
675: * specified, the build.sysclasspath and ant.build.clonevm magic
676: * properties as well as the cloneVm attribute.
677: * @param log whether to write messages to the log.
678: * @since Ant 1.7
679: */
680: private Path calculateBootclasspath(boolean log) {
681: if (vmVersion.startsWith("1.1")) {
682: if (bootclasspath != null && log) {
683: bootclasspath.log("Ignoring bootclasspath as "
684: + "the target VM doesn't support it.");
685: }
686: } else {
687: if (bootclasspath != null) {
688: return bootclasspath
689: .concatSystemBootClasspath(isCloneVm() ? "last"
690: : "ignore");
691: } else if (isCloneVm()) {
692: return Path.systemBootClasspath;
693: }
694: }
695: return new Path(null);
696: }
697:
698: /**
699: * Find out whether either of the cloneVm attribute or the magic property
700: * ant.build.clonevm has been set.
701: * @return <code>boolean</code>.
702: * @since 1.7
703: */
704: private boolean isCloneVm() {
705: return cloneVm
706: || "true".equals(System
707: .getProperty("ant.build.clonevm"));
708: }
709: }
|