Browse Source

Enhance the Java task to allow single file source program execution, a feature, introduced in Java 11

master
Jaikiran Pai 7 years ago
parent
commit
c05c56df68
6 changed files with 212 additions and 18 deletions
  1. +5
    -0
      WHATSNEW
  2. +8
    -1
      manual/Tasks/java.html
  3. +64
    -0
      src/etc/testcases/taskdefs/java.xml
  4. +39
    -11
      src/main/org/apache/tools/ant/taskdefs/Java.java
  5. +18
    -2
      src/main/org/apache/tools/ant/types/CommandlineJava.java
  6. +78
    -4
      src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java

+ 5
- 0
WHATSNEW View File

@@ -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
=====================================



+ 8
- 1
manual/Tasks/java.html View File

@@ -50,7 +50,7 @@ because it tries to read from the standard input.</p>
<tr id="classname">
<td>classname</td>
<td>the Java class to execute.</td>
<td rowspan="3">Exactly one of the three</td>
<td rowspan="4">Exactly one of the four</td>
</tr>
<tr>
<td>jar</td>
@@ -65,6 +65,13 @@ because it tries to read from the standard input.</p>
entry in the manifest). <var>fork</var> must be set to <q>true</q> if this option is
selected. <em>since Ant 1.9.7</em></td>
</tr>
<tr>
<td>sourcefile</td>
<td class="left">The location of a ".java" file or a file containing shebang with Java source code.
Set this attribute to run Java single file source programs, a feature introduced in Java 11.
<var>fork</var> must be set to <q>true</q> if this option is selected.
<em>since Ant 1.10.5</em></td>
</tr>
<tr>
<td>args</td>
<td>the arguments for the class that is executed. <em><u>Deprecated</u>, use


+ 64
- 0
src/etc/testcases/taskdefs/java.xml View File

@@ -417,4 +417,68 @@ redirect.err=&quot;${redirect.err}&quot; should be empty</fail>

<target name="foo"/>

<target name="simpleSourceFile"
description="Tests that the single source file programs, introduced in Java 11, works as expected">
<mkdir dir="${output}/javasource"/>
<echo file="/tmp/foo.txt" message="${output}"/>
<echo file="${output}/javasource/A.java">
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!");
}
}
}
</echo>
<java sourcefile="${output}/javasource/A.java" fork="true" failonerror="true" logerror="true">
<arg value="${output}/javasource/simpleSourceFileOutput.txt"/>
</java>
<loadfile property="simpleSourceFile.prog.output" srcfile="${output}/javasource/simpleSourceFileOutput.txt"/>
<condition property="simpleSourceFile.execution.success">
<equals arg1="${simpleSourceFile.prog.output}" arg2="Hello world!"/>
</condition>
<fail unless="simpleSourceFile.execution.success">Java source-file execution did not yield the expected
result</fail>
</target>


<target name="generateDummyJavaSource">
<mkdir dir="${output}/javasource"/>
<echo file="/tmp/foo.txt" message="${output}"/>
<echo file="${output}/javasource/ThrowsException.java">
public class ThrowsException {
public static void main(String[] args) throws Exception {
throw new RuntimeException("Wasn't expected to be run");
}
}
</echo>
</target>

<target name="sourceFileRequiresFork" depends="generateDummyJavaSource">
<java sourcefile="${output}/javasource/ThrowsException.java" failonerror="true" logerror="true"/>
<fail>Execution of java task, for sourcefile, was expected to fail since fork wasn't set</fail>
</target>

<target name="sourceFileCantUseClassname" depends="generateDummyJavaSource">
<java classname="foo.bar" sourcefile="${output}/javasource/ThrowsException.java"
fork="true" failonerror="true" logerror="true"/>
<fail>Execution of java task, for sourcefile, was expected to fail since classname attribute was set</fail>
</target>

<target name="sourceFileCantUseJar" depends="generateDummyJavaSource">
<java jar="irrelevant.jar" sourcefile="${output}/javasource/ThrowsException.java"
fork="true" failonerror="true" logerror="true"/>
<fail>Execution of java task, for sourcefile, was expected to fail since jar attribute was set</fail>
</target>

<target name="sourceFileCantUseModule" depends="generateDummyJavaSource">
<java module="irrelevant" sourcefile="${output}/javasource/ThrowsException.java"
fork="true" failonerror="true" logerror="true"/>
<fail>Execution of java task, for sourcefile, was expected to fail since module attribute was set</fail>
</target>

</project>

+ 39
- 11
src/main/org/apache/tools/ant/taskdefs/Java.java View File

@@ -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();
}


}

+ 18
- 2
src/main/org/apache/tools/ant/types/CommandlineJava.java View File

@@ -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,
}
}

+ 78
- 4
src/tests/junit/org/apache/tools/ant/taskdefs/JavaTest.java View File

@@ -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


Loading…
Cancel
Save