Ant Network and Remote Tasks
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page covers the Ant tasks that open a network connection: get (HTTP, HTTPS and other URL schemes), setproxy, the three
SSH tasks sshexec, scp and sshsession, ftp, mail and sql. It is written against Apache Ant 1.10.18 and was checked
on 2026-10-16 with JDK 26 on macOS. Network work is where a build file most easily leaks a secret or trusts the wrong
server, so the page also covers the 1.10.x security changes, including CVE-2026-78254, which is fixed in 1.10.18.
How the examples were run. There is no remote server in this environment and none was contacted. Every example that
produced output below ran against a throwaway server on localhost that was started for this page: a small Python HTTP
server (with a redirect and a Basic-authentication route) and a fake HTTP proxy for get and setproxy, a minimal
passive-mode FTP server, an SMTP sink that prints what it receives, and an embedded H2 database for sql. These servers are
test aids and are not shown. The password used in the examples is the placeholder local-test-only, passed on the command
line (-Dftp.password=…); no real host, user or secret appears. The SSH tasks need a real SSH daemon, and starting one would
mean opening a login path to this machine, so they were not run here beyond the failure cases noted in their section.
FTPS and TLS mail were also not run here. Absolute paths are shortened to ., the clean and prepare target lines are
left out of the listings, and nothing else is edited. All tasks named here ship in Ant’s optional JARs; the libraries they
depend on do not, which is the first thing to get right.
Libraries that these tasks need
Ant’s lib directory contains ant-commons-net.jar, ant-jsch.jar, ant-jakartamail.jar and ant-javamail.jar, which hold
the task classes. The third-party library each task calls is not bundled. Without it Ant reports a task that "cannot be
created", as the second row of the next table shows.
| Task | Needs | Notes |
|---|---|---|
|
nothing extra |
Uses the JDK’s |
|
Apache Commons Net ( |
Minimum 1.4.0, but the manual says not to use 3.2 because of performance problems. In this run |
|
JSch ( |
Ant’s manual says 0.1.55 or later. Without it: |
|
Jakarta Mail and Jakarta Activation (or the older |
Since Ant 1.10.13 both are supported and |
|
A JDBC driver |
Given with |
The two ways to supply the JARs are -lib path on the command line (used for the runs below) and placing them in
ANT_HOME/lib or ~/.ant/lib. The choice and the trade-offs are discussed on
Installation; resolving the JARs with Ivy or the Maven
Resolver tasks instead of vendoring them is covered on
Ivy Ant Tasks and
Maven Resolver Ant Tasks.
Source: Ant manual, Library Dependencies
get
get downloads from any URL scheme the JVM supports: http:, https:, ftp: and jar:. It shows one . per 100 KB when
verbose is on. The attributes you will meet:
| Attribute or element | Effect |
|---|---|
|
One URL, or a set of them. With more than one, |
|
File or directory to write to. Required. |
|
Download only if the remote file is newer than the local copy. HTTP only. The downloaded file gets the remote timestamp. |
|
Skip files that already exist, whatever their age. |
|
Attempts to open the URL, default 3. The name is misleading: 1 means "do not retry" and 0 means "do not even try". |
|
Seconds a single download may take before it is interrupted and treated as an error. 0 (the default) means unlimited. |
|
Log errors without failing; log errors only; show progress dots. |
|
HTTP Basic authentication. Only safe over HTTPS. |
|
Send the credentials to the redirect target too. Default |
|
The |
|
Ask for gzip and decompress transparently. It also means |
|
A hint to the connection about caching; implementations may ignore it. |
A plain download
The target downloads the station list from the local server and prints its first two lines:
<target name="get-basic" depends="prepare">
<get src="${server}/stations.csv" dest="${build.dir}/dl/stations.csv"/>
<loadfile srcfile="${build.dir}/dl/stations.csv" property="csv">
<filterchain><headfilter lines="2"/></filterchain>
</loadfile>
<echo>${csv}</echo>
</target>
get-basic:
[get] Getting: http://localhost:8099/stations.csv
[get] To: ./build/dl/stations.csv
[echo] id,name,latitude,longitude,constituents
[echo] SF,San Francisco,37.8063,-122.4659,M2:0.55:120;S2:0.13:150;K1:0.38:210
BUILD SUCCESSFUL
Total time: 0 seconds
Source: Ant manual, get
The destination directory must exist
When dest is a file, get does not create its parent directory. The same task, without the mkdir that the prepare
target performs, fails with a Java exception and no hint about the cause:
BUILD FAILED
./build.xml:15: java.nio.file.NoSuchFileException: ./build/dl/stations.csv
Add <mkdir dir="…"/> before get. (When several URLs are given and dest is a directory, Ant creates it.)
Only download what changed
usetimestamp="true" sends the local file’s time to the server and keeps the existing copy when the server says it is not
newer. Running the task twice shows both outcomes:
<target name="get-timestamp" depends="prepare">
<get src="${server}/stations.csv" dest="${build.dir}/dl/stations.csv" usetimestamp="true" verbose="true"/>
<get src="${server}/stations.csv" dest="${build.dir}/dl/stations.csv" usetimestamp="true"/>
</target>
get-timestamp:
[get] Getting: http://localhost:8099/stations.csv
[get] To: ./build/dl/stations.csv
[get] .
[get] last modified = Fri Oct 16 11:38:34 CEST 2026
[get] Getting: http://localhost:8099/stations.csv
[get] To: ./build/dl/stations.csv
[get] Not modified - so not downloaded
BUILD SUCCESSFUL
The facility only works over HTTP. For other schemes use skipexisting, or compare with uptodate
(Conditions & Control Flow).
Source: Ant manual, get
Failure, retries and ignoreerrors
When the server cannot be reached the build fails with the connection error; retries bounds the attempts (the refused
connection is not retried forever):
get-down:
[get] Getting: http://localhost:8098/stations.csv
[get] To: ./build/dl/stations.csv
[get] Error getting http://localhost:8098/stations.csv to ./build/dl/stations.csv
BUILD FAILED
./build.xml:32: java.net.ConnectException: Connection refused
With ignoreerrors="true" the same download logs the error and the build continues, which is right for an optional
download (a cache warm-up) and wrong for anything the build needs:
get-ignore:
[get] Getting: http://localhost:8098/stations.csv
[get] To: ./build/dl/stations.csv
[get] Error getting http://localhost:8098/stations.csv to ./build/dl/stations.csv
[echo] build continues
BUILD SUCCESSFUL
Credentials and redirects
Ant 1.10.13 added authenticateOnRedirect, because credentials sent along a redirect can reach a different host than the one
the user trusted. The default is false, so builds that relied on credentials being re-sent after a redirect may break, as
the release notes warn. The test server answers /moved-private with a redirect to /private.csv, which needs Basic
authentication. The server’s own log is the evidence of what was sent:
<target name="get-auth-redirect" depends="prepare">
<get src="${server}/moved-private" dest="${build.dir}/dl/private.csv"
username="tidepool" password="${demo.password}" retries="1"/>
</target>
<target name="get-auth-redirect-on" depends="prepare">
<get src="${server}/moved-private" dest="${build.dir}/dl/private.csv"
username="tidepool" password="${demo.password}" authenticateOnRedirect="true"/>
<echo>downloaded ${build.dir}/dl/private.csv</echo>
</target>
Run as ant get-auth-redirect -Ddemo.password=local-test-only. The default fails after the redirect:
get-auth-redirect:
[get] Getting: http://localhost:8099/moved-private
[get] To: ./build/dl/private.csv
[get] http://localhost:8099/moved-private moved to /private.csv
BUILD FAILED
./build.xml:42: HTTP Authorization failure
What the server saw, request by request (auth=yes means an Authorization header arrived):
GET /moved-private auth=yes
GET /private.csv auth=no
With authenticateOnRedirect="true" the second request carries the header and the download succeeds:
GET /moved-private auth=yes
GET /private.csv auth=yes
Keep the password out of the build file: pass it with -D, read it from an environment variable with
<property environment="env"/> and ${env.NAME}, or ask for it with input (see
Utility Tasks). The manual states plainly that Basic
authentication sends the password as plain text and is only secure over HTTPS. Use authenticateOnRedirect="true" only when you
control every host the redirect can lead to.
Source: Ant manual, get; Ant 1.10.18 WHATSNEW (the 1.10.13 entry)
Several files, headers and a mapper
Nested url elements select several downloads, header adds request headers, and a mapper renames the results. The
firstmatchmapper below renames only the redirecting URL and flattens the rest to their last path segment:
<target name="get-multi" depends="clean">
<get dest="${build.dir}/dl">
<url url="${server}/stations.csv"/>
<url url="${server}/moved"/>
<header name="X-Build" value="tidepool-docs"/>
<firstmatchmapper>
<globmapper from="${server}/moved" to="moved.csv"/>
<flattenmapper/>
</firstmatchmapper>
</get>
<fileset id="dl" dir="${build.dir}/dl"/>
<pathconvert property="dl.list" refid="dl" pathsep=", " dirsep="/">
<map from="${build.dir}/dl/" to=""/>
</pathconvert>
<echo>downloaded: ${dl.list}</echo>
</target>
[echo] downloaded: moved.csv, stations.csv
BUILD SUCCESSFUL
The manual’s example get-and-checksum macro downloads an artifact and its .sha1 file together and verifies the checksum
with checksum; the same idea is recommended for any file whose integrity matters
(Supply Chain Security).
Source: Apache Ant manual: Get
setproxy and proxies
setproxy sets the JVM’s HTTP, FTP and SOCKS proxy system properties, so tasks that run inside the same JVM (such as get)
go through the proxy. Its attributes are proxyhost, proxyport (default 80), proxyuser, proxypassword,
nonproxyhosts (separated by |), socksproxyhost and socksproxyport (default 1080). An empty proxyhost or
socksproxyhost turns that proxy off. Ant 1.7 and later can also use the operating system’s proxy settings with the
-autoproxy command-line option (sql uses them by default, and -noproxy switches that off). The manual warns that
with proxies on, a get against localhost may be relayed to the proxy.
To show the effect without a real proxy, the target points setproxy at a fake proxy on 127.0.0.1:8097 that records what it
receives, with nonproxyhosts emptied so that no host bypasses it, and requests a URL on the unresolvable name
tidepool.invalid:
<target name="proxy" depends="prepare">
<setproxy proxyhost="127.0.0.1" proxyport="8097" nonproxyhosts=""/>
<get src="http://tidepool.invalid/stations.csv" dest="${build.dir}/dl/proxied.csv"/>
<setproxy proxyhost=""/>
<echo>done</echo>
</target>
proxy:
[setproxy] Setting proxy to 127.0.0.1:8097
[get] Getting: http://tidepool.invalid/stations.csv
[get] To: ./build/dl/proxied.csv
[setproxy] resetting http proxy
[echo] done
BUILD SUCCESSFUL
The fake proxy’s own log shows that the request reached it in absolute form, so the JVM did not try to resolve
tidepool.invalid itself:
PROXY saw: GET http://tidepool.invalid/stations.csv Host: tidepool.invalid
Two limits from the manual: the ftp task does not use the proxy set by setproxy and cannot go through a SOCKS firewall, and
setproxy changes JVM-wide state, so reset it when you are done, as the target does.
Source: Ant manual, setproxy
ftp
ftp is a basic FTP client with the action values put (the default, also spelled send), get (recv), del
(delete), list, chmod, mkdir, rmdir and site. It needs server, userid and password; port defaults to 21.
Files are chosen with nested filesets: for put the fileset is evaluated locally, for get, del, list and chmod it is
evaluated against the remote files under remotedir. The most useful attributes:
| Attribute | Effect |
|---|---|
|
The remote base directory. It must already exist: Ant does not create it for |
|
Use passive mode. Try it first if you can connect but not transfer; it is what works through most firewalls. |
|
Binary (default) or text transfers. |
|
Transfer only new or changed files. Time-zone and granularity problems are handled with |
|
How hard to try before giving up. |
|
Keep the remote timestamp on downloaded files. |
|
Unix permissions for uploads. |
|
Describe a server whose directory listing format cannot be auto-detected. |
|
Use FTPS, and the |
|
Check that the data connection goes to the same host as the control connection. On by default; leave it on. |
|
Milliseconds to wait for data. |
|
Allow received files to land outside the destination directory. Default |
Make the directory, then put, list and get
Run against the throwaway FTP server on port 2121 with -lib pointing at commons-net and commons-io. The target
ftp-mkdir creates the remote directory (a put into a missing directory fails, the first listing below), ftp-put
uploads, ftp-list writes a listing to a file, and ftp-get downloads into a local directory:
<target name="ftp-mkdir" depends="prepare">
<ftp action="mkdir" server="localhost" port="2121" userid="tidepool" password="${ftp.password}"
remotedir="incoming" passive="yes"/>
</target>
<target name="ftp-put" depends="ftp-mkdir">
<ftp server="localhost" port="2121" userid="tidepool" password="${ftp.password}"
remotedir="incoming" passive="yes" verbose="yes" depends="yes">
<fileset dir="data" includes="*.csv"/>
</ftp>
</target>
<target name="ftp-list" depends="ftp-put">
<ftp action="list" server="localhost" port="2121" userid="tidepool" password="${ftp.password}"
remotedir="incoming" passive="yes" listing="${build.dir}/remote-listing.txt">
<fileset dir="."><include name="*.csv"/></fileset>
</ftp>
<loadfile srcfile="${build.dir}/remote-listing.txt" property="listing"/>
<echo>${listing}</echo>
</target>
<target name="ftp-get" depends="ftp-put">
<ftp action="get" server="localhost" port="2121" userid="tidepool" password="${ftp.password}"
remotedir="incoming" passive="yes" verbose="yes" preservelastmodified="yes">
<fileset dir="${build.dir}/dl"><include name="*.csv"/></fileset>
</ftp>
<echo>fetched into ${build.dir}/dl</echo>
</target>
A put into a directory that does not exist fails at once; the remote server answered 550 to CWD incoming:
BUILD FAILED
./build.xml:128: could not change remote directory: 550 no such directory
With the directory made, ant -Dftp.password=local-test-only ftp-list ran the upload and the listing, and ftp-get the
download (path prefixes shortened):
ftp-put:
[ftp] sending files
[ftp] transferring ./data/stations.csv
[ftp] 1 files sent
ftp-list:
[ftp] listing files
[ftp] 1 files listed
[echo] -rw-r--r-- 1 ftp ftp 239 Oct 16 09:40 stations.csv
ftp-get:
[ftp] getting files
[ftp] transferring stations.csv to ./build/dl/stations.csv
[ftp] 1 files retrieved
[echo] fetched into ./build/dl
A wrong password stops the build with a short message (the server’s 530 is only in its own log):
BUILD FAILED
./build.xml:157: Could not login to FTP server
FTPS (useFtps="true") needs a server with TLS and was not run here. Plain FTP sends the password and the data in clear
text and, as the security page notes, a machine in the middle can substitute files; prefer FTPS or SFTP, or do not use it at
all.
Sources: Ant manual, ftp, Ant manual, get
Source: Apache Ant manual: FTP
sshexec, scp and sshsession
The three SSH tasks share a set of connection attributes: host, username, port (default 22), password or keyfile
(with passphrase), trust, knownhosts and failonerror. Each task also accepts sshConfig (Ant 1.10.8) to read the user
name and key from an OpenSSH config file, and a nested additionalConfig (Ant 1.10.10) for JSch session options Ant has no
attribute for.
-
sshexecrunscommand(or the commands incommandResource) on the remote host. Output can go to a file (output,errorOutput,append), to properties (outputproperty,errorproperty,resultproperty), or be suppressed.timeoutis in milliseconds (unliketelnet, which uses seconds).useptyallocates a pseudo-terminal,serverAliveIntervalSecondskeeps idle connections alive, and thehideSensitive,bindSensitive,sensitiveDelimiterandplaceholderBracketsattributes of Ant 1.10.16 mask secrets in the log without hiding the whole output. -
scpcopies a file or a fileset to or from a remote machine. A remote path has the formuser[:password]@host:/dir, and the task decides local versus remote by looking for an@; uselocalFile,remoteFile,localTodir,remoteTodirand related attributes when a local path contains one.sftp="true"uses the SFTP protocol (recommended for servers without SCP1 support);preserveLastModified,filemodeanddirmode(Ant 1.9.5) andcompressed(Ant 1.9.8) are also available. A fileset can only be uploaded, not downloaded. -
sshsessionopens one connection, sets up any number of local tunnels (localtunnels, or nestedlocaltunnelwithlport,rhost,rport) and remote tunnels (remotetunnels, orremotetunnel), and runs nested tasks inside asequentialelement while the connection is up.
Host keys: trust and knownhosts
With the default trust="false" the host must already be listed in the knownhosts file (default
${user.home}/.ssh/known_hosts, SSH2 format only), and the file must exist. trust="true" accepts any unknown host, which
removes the protection against a machine in the middle and should not appear in a build that handles secrets. Keep a pinned
known_hosts file in the repository or on the CI agent and point knownhosts at it.
Example (not run here)
The following pair of tasks deploys the tidepool-cli distribution and unpacks it. The user name, key and host are
placeholders taken from properties; none is a real value. The tasks were not run here because they need an SSH daemon:
<scp todir="${deploy.user}@${deploy.host}:/srv/tidepool" keyfile="${deploy.key}" passphrase="${deploy.key.passphrase}"
sftp="true" knownhosts="${basedir}/ssh/known_hosts">
<fileset dir="${dist.dir}" includes="tidepool-cli-*.zip"/>
</scp>
<sshexec host="${deploy.host}" username="${deploy.user}" keyfile="${deploy.key}" passphrase="${deploy.key.passphrase}"
knownhosts="${basedir}/ssh/known_hosts" command="cd /srv/tidepool && unzip -o tidepool-cli-*.zip"
timeout="60000" resultproperty="deploy.exit" failonerror="false"/>
What was run is the failure behaviour. With JSch 0.1.55 on the class path and a throwaway RSA key generated for the test,
both tasks aimed at a closed port on localhost fail while connecting:
ssh-refused:
[sshexec] Connecting to localhost:2299
BUILD FAILED
./build.xml:167: com.jcraft.jsch.JSchException: java.net.ConnectException: Connection refused
scp-refused:
[scp] Connecting to localhost:2299
BUILD FAILED
./build.xml:172: com.jcraft.jsch.JSchException: java.net.ConnectException: Connection refused
Without the JSch JAR the same sshexec cannot even be created:
ssh-nojsch:
BUILD FAILED
./build.xml:162: Problem: failed to create task or type sshexec
Cause: Could not load a dependent class com/jcraft/jsch/Logger
It is not enough to have Ant's optional JARs
you need the JAR files that the optional tasks depend upon.
The Ant manual requires JSch 0.1.55 or later, so check which JSch artifact your organisation allows before adopting these tasks.
Source: Apache Ant manual: SCP and SSHEXEC
mail sends an SMTP message. mailhost defaults to localhost and mailport to 25. Recipients go in tolist, cclist and
bcclist, or in nested to, cc, bcc elements; addresses may be of the form name <addr> (the angle brackets must be XML
entities). The body comes from message, a nested message element or messagefile. Attachments are files or a nested
attachments path. Other attributes: subject, from, replyto, encoding (mime, uu, plain or auto), charset,
failonerror, ignoreInvalidRecipients.
SMTP authentication (user, password) and TLS/SSL (ssl, enableStartTLS, requireStartTLS, checkServerIdentity) need
Jakarta Mail and MIME encoding. Two security changes matter:
-
Ant 1.10.16 made the
mailtask andMailLoggerverify the server identity (RFC 2595) on TLS connections; setcheckServerIdentity="false"only for a test server. -
Ant 1.10.16 also added
requireStartTLS. Without it a server that does not offer STARTTLS can silently push the exchange back to unencrypted authentication.
The fallback when Jakarta Mail is missing
With only ant.jar and no mail libraries on the class path, Ant tries MIME mail (Jakarta, then javax), then UU mail, and
finally sends plain text with the attachment pasted into the body. The target sends a message with data/stations.csv
attached to the SMTP sink on port 2525:
<target name="mail">
<tstamp/>
<mail mailhost="localhost" mailport="2525" subject="tidepool build ${DSTAMP}"
from="build@tidepool.invalid" tolist="team@tidepool.invalid" files="data/stations.csv">
<message>The nightly build finished.</message>
</mail>
</target>
mail:
[mail] Could not find Jakarta MIME mail: jakarta.activation.DataHandler
[mail] Could not find MIME mail: javax.activation.DataHandler
[mail] Failed to initialise MIME mail: Abstract class org.apache.tools.ant.taskdefs.email.Mailer
[mail] Failed to initialise UU mail: class org.apache.tools.ant.util.ClasspathUtils cannot access a member of class org.apache.tools.ant.taskdefs.email.UUMailer with package access
[mail] Sending email: tidepool build 20261016
[mail] Sent email with 1 attachment
The message is delivered, but the sink shows that the "attachment" is the file’s text appended to the body (plain
encoding, Content-Type: text/plain). The UU line is noise on a modern JDK. With Jakarta Mail (jakarta.mail-api 2.1.5 and
the Angus Mail implementation 2.0.5) and Jakarta Activation (jakarta.activation-api 2.1.4 and Angus Activation 2.0.3) added
with -lib, the output is quiet and the sink receives a real multipart/mixed message:
mail:
[mail] Sending email: tidepool build 20261016
[mail] Sent email with 1 attachment
The part headers the sink printed for the attachment: Content-Type: application/octet-stream; name=stations.csv and
Content-Disposition: attachment; filename=stations.csv.
Source: Apache Ant manual: Mail
Requiring STARTTLS
The SMTP sink does not offer STARTTLS. With requireStartTLS="true" the build refuses to talk to it:
mail-starttls:
[mail] Sending email: tls
[mail] Failed to send email: STARTTLS is required but host does not support STARTTLS
BUILD FAILED
./build.xml:116: Problem while sending mime mail:
And with nothing listening on the port:
mail-down:
[mail] Sending email: x
[mail] Failed to send email: Couldn't connect to host, port: localhost, 2526; timeout -1
BUILD FAILED
./build.xml:121: Problem while sending mime mail:
TLS and SMTP authentication against a real mail server were not run here. Never put a mail password in the build file; use
the same property or environment techniques as for get.
Source: Ant manual, mail; Ant 1.10.18 WHATSNEW (1.10.16 entries)
sql
sql runs SQL through JDBC. The required attributes are driver, url, userid and password; the statements come from
src, from a nested resource collection (each resource runs in its own transaction), from nested transaction elements, or
from the task body. Statements are separated by ; by default (delimiter and delimitertype change that: row requires
the delimiter on a line by itself, which stored-procedure scripts need, usually with keepformat). Lines starting with --,
// or REM are comments.
The behaviour switches to know: autocommit (default false, so the statements run as one transaction), onerror
(abort, the default, continue or stop), print, showheaders, showtrailers, output and append for result sets,
csvColumnSeparator and csvQuoteCharacter, errorproperty, warningproperty and rowcountproperty, and
failOnConnectionError="false" to continue when the database is unreachable. expandproperties is on by default, so a
property reference such as ${name} inside the SQL is replaced by Ant before the database sees it; that is a convenience and an injection risk if the
property is user-supplied. connectionProperty passes extra JDBC properties.
Load the station list and query it
The example uses H2 2.4.240 from the local Maven repository as a file database (org.h2.Driver). The first sql runs
sql/init.sql, which loads data/stations.csv with H2’s CSVREAD; the second prints a result set to a file:
-- load the station list that the other examples download
DROP TABLE IF EXISTS station;
CREATE TABLE station AS SELECT id, name, latitude, longitude FROM CSVREAD('data/stations.csv');
<path id="jdbc.path">
<pathelement location="${user.home}/.m2/repository/com/h2database/h2/2.4.240/h2-2.4.240.jar"/>
</path>
<target name="sql" depends="prepare">
<sql driver="org.h2.Driver" url="jdbc:h2:file:${build.dir}/tidepool-db"
userid="sa" password="" classpathref="jdbc.path" src="sql/init.sql"/>
<sql driver="org.h2.Driver" url="jdbc:h2:file:${build.dir}/tidepool-db"
userid="sa" password="" classpathref="jdbc.path"
print="true" output="${build.dir}/stations-report.txt">
SELECT id, name FROM station ORDER BY id;
</sql>
<loadfile srcfile="${build.dir}/stations-report.txt" property="report"/>
<echo>${report}</echo>
</target>
sql:
[sql] Executing resource: ./sql/init.sql
[sql] 2 of 2 SQL statements executed successfully
[sql] Executing commands
[sql] 1 of 1 SQL statements executed successfully
[echo] ID,NAME
[echo] BOS,Boston
[echo] HNL,Honolulu
[echo] SF,San Francisco
[echo]
[echo] 0 rows affected
The empty password is H2’s default for a file database that was just created; use a property for a real database.
Source: Apache Ant manual: SQL
Errors: continue, stop and abort
With onerror="continue" a failing statement is reported and the following ones still run, and errorproperty records that
something failed. The run below (with -v for the per-statement lines) executed a good query, a query on a missing column and a
count:
sql-error:
[sql] connecting to jdbc:h2:file:./build/tidepool-db
[sql] Loading org.h2.Driver using a cached AntClassLoader.
[sql] Executing commands
[sql] SQL: SELECT id FROM station WHERE id = 'SF'
[sql] ID
[sql] SF
[sql] SQL: SELECT nope FROM station
[sql] Failed to execute: SELECT nope FROM station
[sql] org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "NOPE" not found; SQL statement:
[sql] SELECT nope FROM station [42122-240]
[sql] SQL: SELECT COUNT(*) AS n FROM station
[sql] N
[sql] 3
[sql] 2 of 3 SQL statements executed successfully
[echo] sql.failed=true
(Blank lines and the "0 rows affected" trailers are left out of this listing.) Without the driver on the class path, the task reports the cause at once:
./build.xml:103: Class Not Found: JDBC driver org.h2.Driver could not be loaded
Keep JDBC drivers and credentials out of ANT_HOME/lib; give them to the task with classpath or classpathref, where Ant
caches the loader (caching="true", the default).
Sources: Ant manual, sql
CVE-2026-78254: path traversal in ftp and scp
The Apache Ant security page lists Moderate: Path traversal in ftp and scp tasks allows arbitrary file write, CVE-2026-78254,
fixed in Ant 1.10.18. The ftp and scp tasks can download files from a remote server, and a malicious server could return
relative paths that make the task write outside the target directory, overwriting files with the permissions of the user
running Ant. Exploiting it needs a malicious server or a machine in the middle. For scp and for ftp over FTPS the server
must also pass the identity checks; for plain ftp a machine in the middle can supply malicious files. The report was
received on 20 August 2026 and made public on 6 September 2026. The advisory sits under "Fixed in Apache Ant 1.10.18", while
its "Affects" line reads "all version with ftp or scp tasks up to 1.10.18" (sic); the manual documents
allowFilesToEscapeDest as new in 1.10.18, so use 1.10.18 or later and re-read the page when a newer release appears.
The fix is the allowFilesToEscapeDest attribute, default false, on ftp (for get) and scp (for downloads). The test
FTP server was started with a listing that also returned the name ../escape.csv. On Ant 1.10.18 the task skips that entry:
<target name="ftp-get-evil" depends="ftp-put">
<ftp action="get" server="localhost" port="2122" userid="tidepool" password="${ftp.password}"
remotedir="incoming" passive="yes" verbose="yes">
<fileset dir="${build.dir}/dl"><include name="**"/></fileset>
</ftp>
<available file="${build.dir}/escape.csv" property="escaped"/>
<echo>file written outside build/dl: ${escaped}</echo>
</target>
ftp-get-evil:
[ftp] getting files
[ftp] skipping ../escape.csv as its target ./build/escape.csv is outside of ./build/dl.
[ftp] transferring stations.csv to ./build/dl/stations.csv
[ftp] 1 files retrieved
[ftp] 1 files were not successfully retrieved
[echo] file written outside build/dl: ${escaped}
${escaped} stayed unset, so nothing was written to build/escape.csv. Note that the build still ended with BUILD SUCCESSFUL:
the skipped file produced a log line and a count, not a failure, so a build that must treat it as an error has to check for
it. What to do: upgrade to 1.10.18 or later; leave allowFilesToEscapeDest at false; use scp or ftp only with servers
you control and with host-key or TLS identity checks on.
Source: Apache Ant security reports
Equivalents in Maven and Gradle
-
Download a file: Maven has no download goal in the core; the
maven-dependency-plugincan fetch artifacts, and a build usually declares what it needs as a dependency instead. Gradle declares dependencies too. See Dependency Management Fundamentals. -
Deploy over SSH or FTP: Maven reaches non-HTTP repositories through Wagon providers (see Install, Deploy & Distribution Management); Gradle has no built-in SSH task, and a build that needs one usually calls Ant’s tasks through Ant integration.
-
Proxy settings: Maven uses
settings.xmlproxy blocks (settings.xml & Password Encryption); Gradle usesgradle.propertiesproxy properties. Ant usessetproxyor-autoproxy. -
Run SQL: Maven has the sql-maven-plugin from Mojo; Gradle has no SQL task in the core and uses plugins or the Ant
sqltask.
Where the books and older build files are outdated
-
Examples that pass
usernameandpasswordstraight intogetorftppredate any discussion of leaking secrets in a build file or in the log; use properties and keep logs free of them. -
Pre-1.10.13
getexamples that rely on credentials being re-sent after a redirect stop working unless you setauthenticateOnRedirect="true". -
javax.mailsetups still work, but Jakarta Mail is preferred from Ant 1.10.13 and the jar and package names changed. -
The JSch requirement moved: older text says 0.1.28 or 0.1.29; the manual now says 0.1.55 or later.
-
The
timediffmillisattribute offtpis deprecated; usetimestampGranularityandserverTimeZoneConfig.
References
-
Apache Ant manual: get
-
Apache Ant manual: setproxy
-
Apache Ant manual: sshexec
-
Apache Ant manual: scp
-
Apache Ant manual: sshsession
-
Apache Ant manual: ftp
-
Apache Ant manual: mail
-
Apache Ant manual: sql
-
Apache Ant manual: Library Dependencies
-
Apache Ant: Security reports
-
Apache Ant source, 1.10.18 tag: WHATSNEW