diff --git a/WHATSNEW b/WHATSNEW
index 5233a0a09..1523a8c40 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -15,6 +15,11 @@ Fixed bugs:
root.
Bugzilla Report 62502
+Other changes:
+--------------
+ * Java task now accepts a "sourcefile" attribute to allow single file
+ source program execution, a feature that is introduced in Java 11.
+
Changes from Ant 1.10.3 TO Ant 1.10.4
=====================================
diff --git a/manual/Tasks/java.html b/manual/Tasks/java.html
index f6449886b..849f594ec 100644
--- a/manual/Tasks/java.html
+++ b/manual/Tasks/java.html
@@ -50,7 +50,7 @@ because it tries to read from the standard input.
args |
the arguments for the class that is executed. Deprecated, use
diff --git a/src/etc/testcases/taskdefs/java.xml b/src/etc/testcases/taskdefs/java.xml
index 022a10f33..720959a38 100644
--- a/src/etc/testcases/taskdefs/java.xml
+++ b/src/etc/testcases/taskdefs/java.xml
@@ -417,4 +417,68 @@ redirect.err="${redirect.err}" should be empty
+
+
+
+
+ import java.nio.file.Files;
+ import java.nio.file.Paths;
+ import java.io.BufferedWriter;
+ public class A {
+ public static void main(String[] args) throws Exception {
+ final String outFile = args[0];
+ try(BufferedWriter bw = Files.newBufferedWriter(Paths.get(outFile));) {
+ bw.write("Hello world!");
+ }
+ }
+ }
+
+
+
+
+
+
+
+
+ Java source-file execution did not yield the expected
+ result
+
+
+
+
+
+
+
+ public class ThrowsException {
+ public static void main(String[] args) throws Exception {
+ throw new RuntimeException("Wasn't expected to be run");
+ }
+ }
+
+
+
+
+
+ Execution of java task, for sourcefile, was expected to fail since fork wasn't set
+
+
+
+
+ Execution of java task, for sourcefile, was expected to fail since classname attribute was set
+
+
+
+
+ Execution of java task, for sourcefile, was expected to fail since jar attribute was set
+
+
+
+
+ Execution of java task, for sourcefile, was expected to fail since module attribute was set
+
+
diff --git a/src/main/org/apache/tools/ant/taskdefs/Java.java b/src/main/org/apache/tools/ant/taskdefs/Java.java
index 5d5a99d87..6e3e54d74 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Java.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Java.java
@@ -142,7 +142,8 @@ public class Java extends Task {
protected void checkConfiguration() throws BuildException {
String classname = getCommandLine().getClassname();
String module = getCommandLine().getModule();
- if (classname == null && getCommandLine().getJar() == null && module == null) {
+ final String sourceFile = getCommandLine().getSourceFile();
+ if (classname == null && getCommandLine().getJar() == null && module == null && sourceFile == null) {
throw new BuildException("Classname must not be null.");
}
if (!fork && getCommandLine().getJar() != null) {
@@ -153,6 +154,9 @@ public class Java extends Task {
throw new BuildException(
"Cannot execute a module in non-forked mode. Please set fork='true'. ");
}
+ if (!fork && sourceFile != null) {
+ throw new BuildException("Cannot execute sourcefile in non-forked mode. Please set fork='true'");
+ }
if (spawn && !fork) {
throw new BuildException(
"Cannot spawn a java process in non-forked mode. Please set fork='true'. ");
@@ -355,12 +359,14 @@ public class Java extends Task {
*
* @param jarfile the jarfile to execute.
*
- * @throws BuildException if there is also a main class specified.
+ * @throws BuildException if there is also a {@code classname}, {@code module}
+ * or {@code sourcefile} attribute specified
*/
public void setJar(File jarfile) throws BuildException {
- if (getCommandLine().getClassname() != null || getCommandLine().getModule() != null) {
+ if (getCommandLine().getClassname() != null || getCommandLine().getModule() != null
+ || getCommandLine().getSourceFile() != null) {
throw new BuildException(
- "Cannot use 'jar' with 'classname' or 'module' attributes in same command.");
+ "Cannot use combination of 'jar', 'sourcefile', 'classname', 'module' attributes in same command");
}
getCommandLine().setJar(jarfile.getAbsolutePath());
}
@@ -370,12 +376,12 @@ public class Java extends Task {
*
* @param s the name of the main class.
*
- * @throws BuildException if the jar attribute has been set.
+ * @throws BuildException if there is also a {@code jar} or {@code sourcefile} attribute specified
*/
public void setClassname(String s) throws BuildException {
- if (getCommandLine().getJar() != null) {
+ if (getCommandLine().getJar() != null || getCommandLine().getSourceFile() != null) {
throw new BuildException(
- "Cannot use 'jar' and 'classname' attributes in same command");
+ "Cannot use combination of 'jar', 'classname', sourcefile attributes in same command");
}
getCommandLine().setClassname(s);
}
@@ -385,17 +391,37 @@ public class Java extends Task {
*
* @param module the name of the module.
*
- * @throws BuildException if the jar attribute has been set.
+ * @throws BuildException if there is also a {@code jar} or {@code sourcefile} attribute specified
* @since 1.9.7
*/
public void setModule(String module) throws BuildException {
- if (getCommandLine().getJar() != null) {
+ if (getCommandLine().getJar() != null || getCommandLine().getSourceFile() != null) {
throw new BuildException(
- "Cannot use 'jar' and 'module' attributes in same command");
+ "Cannot use combination of 'jar', 'module', sourcefile attributes in same command");
}
getCommandLine().setModule(module);
}
+ /**
+ * Set the Java source-file to execute. Support for single file source program
+ * execution, in Java, is only available since Java 11.
+ *
+ * @param sourceFile The path to the source file
+ * @throws BuildException if there is also a {@code jar}, {@code classname}
+ * or {@code module} attribute specified
+ * @since Ant 1.10.5
+ */
+ public void setSourceFile(final String sourceFile) throws BuildException {
+ final String jar = getCommandLine().getJar();
+ final String className = getCommandLine().getClassname();
+ final String module = getCommandLine().getModule();
+ if (jar != null || className != null || module != null) {
+ throw new BuildException("Cannot use 'sourcefile' in combination with 'jar' or " +
+ "'module' or 'classname'");
+ }
+ getCommandLine().setSourceFile(sourceFile);
+ }
+
/**
* Deprecated: use nested arg instead.
* Set the command line arguments for the class.
@@ -843,7 +869,7 @@ public class Java extends Task {
}
/**
- * Executes the given classname with the given arguments in a separate VM.
+ * Executes the given source-file or classname with the given arguments in a separate VM.
* @param command String[] of command-line arguments.
*/
private int fork(String[] command) throws BuildException {
@@ -1018,4 +1044,6 @@ public class Java extends Task {
public CommandlineJava.SysProperties getSysProperties() {
return getCommandLine().getSystemProperties();
}
+
+
}
diff --git a/src/main/org/apache/tools/ant/types/CommandlineJava.java b/src/main/org/apache/tools/ant/types/CommandlineJava.java
index fd6588000..1ee821aca 100644
--- a/src/main/org/apache/tools/ant/types/CommandlineJava.java
+++ b/src/main/org/apache/tools/ant/types/CommandlineJava.java
@@ -368,6 +368,15 @@ public class CommandlineJava implements Cloneable {
return null;
}
+ public void setSourceFile(final String sourceFile) {
+ this.executableType = ExecutableType.SOURCE_FILE;
+ javaCommand.setExecutable(sourceFile);
+ }
+
+ public String getSourceFile() {
+ return this.executableType == ExecutableType.SOURCE_FILE ? this.javaCommand.getExecutable() : null;
+ }
+
/**
* Set the module to execute.
* @param module the module name.
@@ -534,9 +543,11 @@ public class CommandlineJava implements Cloneable {
} else if (executableType == ExecutableType.MODULE) {
listIterator.add("-m");
}
- // this is the classname to run as well as its arguments.
+ // this is the classname/source-file to run as well as its arguments.
// in case of ExecutableType.JAR, the executable is a jar file,
// in case of ExecutableType.MODULE, the executable is a module name, potentially including a class name.
+ // in case of ExecutableType.SOURCE_FILE, the executable is a Java source file (ending in .java) or a shebang
+ // file containing Java source
javaCommand.addCommandToList(listIterator);
}
@@ -887,6 +898,11 @@ public class CommandlineJava implements Cloneable {
/**
* Module execution.
*/
- MODULE
+ MODULE,
+
+ /**
+ * Source file (introduced in Java 11)
+ */
+ SOURCE_FILE,
}
}
diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java
index 06d6b904c..2630a81a2 100644
--- a/src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java
+++ b/src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java
@@ -32,8 +32,10 @@ import java.io.PipedOutputStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildFileRule;
import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.taskdefs.condition.JavaVersion;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.TeeOutputStream;
+import org.junit.Assume;
import org.junit.AssumptionViolatedException;
import org.junit.Before;
import org.junit.Rule;
@@ -106,28 +108,28 @@ public class JavaTest {
@Test
public void testJarAndClassName() {
thrown.expect(BuildException.class);
- thrown.expectMessage("Cannot use 'jar' and 'classname' attributes in same command");
+ thrown.expectMessage("Cannot use combination of ");
buildRule.executeTarget("testJarAndClassName");
}
@Test
public void testClassnameAndJar() {
thrown.expect(BuildException.class);
- thrown.expectMessage("Cannot use 'jar' with 'classname' or 'module' attributes in same command.");
+ thrown.expectMessage("Cannot use combination of ");
buildRule.executeTarget("testClassnameAndJar");
}
@Test
public void testJarAndModule() {
thrown.expect(BuildException.class);
- thrown.expectMessage("Cannot use 'jar' and 'module' attributes in same command");
+ thrown.expectMessage("Cannot use combination of ");
buildRule.executeTarget("testJarAndModule");
}
@Test
public void testModuleAndJar() {
thrown.expect(BuildException.class);
- thrown.expectMessage("Cannot use 'jar' with 'classname' or 'module' attributes in same command.");
+ thrown.expectMessage("Cannot use combination of ");
buildRule.executeTarget("testModuleAndJar");
}
@@ -410,6 +412,78 @@ public class JavaTest {
buildRule.executeTarget("flushedInput");
}
+ /**
+ * Test that the Java single file source program feature introduced in Java 11 works fine
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSimpleSourceFile() throws Exception {
+ requireJava11();
+ buildRule.executeTarget("simpleSourceFile");
+ }
+
+ /**
+ * Test that the sourcefile option of the Java task can only be run when fork attribute is set
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSourceFileRequiresFork() throws Exception {
+ requireJava11();
+ thrown.expect(BuildException.class);
+ thrown.expectMessage("Cannot execute sourcefile in non-forked mode. Please set fork='true'");
+ buildRule.executeTarget("sourceFileRequiresFork");
+ }
+
+ /**
+ * Tests that the sourcefile attribute and the classname attribute of the Java task cannot be used
+ * together
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSourceFileCantUseClassname() throws Exception {
+ requireJava11();
+ thrown.expect(BuildException.class);
+ thrown.expectMessage("Cannot use 'sourcefile' in combination with");
+ buildRule.executeTarget("sourceFileCantUseClassname");
+ }
+
+ /**
+ * Tests that the sourcefile attribute and the jar attribute of the Java task cannot be used
+ * together
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSourceFileCantUseJar() throws Exception {
+ requireJava11();
+ thrown.expect(BuildException.class);
+ thrown.expectMessage("Cannot use 'sourcefile' in combination with");
+ buildRule.executeTarget("sourceFileCantUseJar");
+ }
+
+ /**
+ * Tests that the sourcefile attribute and the module attribute of the Java task cannot be used
+ * together
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSourceFileCantUseModule() throws Exception {
+ requireJava11();
+ thrown.expect(BuildException.class);
+ thrown.expectMessage("Cannot use 'sourcefile' in combination with");
+ buildRule.executeTarget("sourceFileCantUseModule");
+ }
+
+ private static void requireJava11() {
+ final JavaVersion javaVersion = new JavaVersion();
+ javaVersion.setAtLeast("11");
+ Assume.assumeTrue("Skipping test which requires a minimum of Java 11 runtime", javaVersion.eval());
+ }
+
/**
* entry point class with no dependencies other
* than normal JRE runtime
|