You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2013/12/06 23:11:36 UTC
svn commit: r1548730 - in /commons/proper/net/trunk/src: changes/changes.xml
main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java
Author: sebb
Date: Fri Dec 6 22:11:36 2013
New Revision: 1548730
URL: http://svn.apache.org/r1548730
Log:
NET-512 Downloading files or members from the AS400 QSYS file system is not supported
Added:
commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java (with props)
Modified:
commons/proper/net/trunk/src/changes/changes.xml
commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
Modified: commons/proper/net/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/changes/changes.xml?rev=1548730&r1=1548729&r2=1548730&view=diff
==============================================================================
--- commons/proper/net/trunk/src/changes/changes.xml [utf-8] (original)
+++ commons/proper/net/trunk/src/changes/changes.xml [utf-8] Fri Dec 6 22:11:36 2013
@@ -64,6 +64,9 @@ The <action> type attribute can be add,u
<body>
<release version="3.4" date="2013-??-??" description="
">
+ <action issue="NET-512" dev="sebb" type="add" due-to="Thomas Raddatz">
+ Downloading files or members from the AS400 QSYS file system is not supported
+ </action>
<action issue="NET-518" dev="sebb" type="fix" due-to="David Kocher">
FTPClient#initFeatureMap should not initialize empty map if reply code is 530
</action>
Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java?rev=1548730&r1=1548729&r2=1548730&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java Fri Dec 6 22:11:36 2013
@@ -17,6 +17,7 @@
package org.apache.commons.net.ftp.parser;
+import java.io.File;
import java.text.ParseException;
import org.apache.commons.net.ftp.FTPClientConfig;
@@ -24,8 +25,212 @@ import org.apache.commons.net.ftp.FTPFil
/**
* @version $Id$
+ * <pre>
+ * Example *FILE/*MEM FTP entries, when the current
+ * working directory is a file of file system QSYS:
+ * ------------------------------------------------
+ *
+ * > cwd /qsys.lib/rpgunit.lib/rpgunitc1.file
+ * 250-NAMEFMT set to 1.
+ * 250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory.
+ * > dir
+ * 227 Entering Passive Mode (10,200,36,33,40,249).
+ * 125 List started.
+ * QPGMR 135168 22.06.13 13:18:19 *FILE
+ * QPGMR *MEM MKCMD.MBR
+ * QPGMR *MEM RUCALLTST.MBR
+ * QPGMR *MEM RUCMDHLP.MBR
+ * QPGMR *MEM RUCRTTST.MBR
+ * 250 List completed.
+ *
+ *
+ * Example *FILE entry of an OS/400 save file:
+ * ---------------------------------------------------
+ *
+ * > cwd /qsys.lib/rpgunit.lib
+ * 250 "/QSYS.LIB/RPGUNIT.LIB" is current library.
+ * > dir rpgunit.file
+ * 227 Entering Passive Mode (10,200,36,33,188,106).
+ * 125 List started.
+ * QPGMR 16347136 29.06.13 15:45:09 *FILE RPGUNIT.SAVF
+ * 250 List completed.
+ *
+ *
+ * Example *STMF/*DIR FTP entries, when the
+ * current working directory is in file system "root":
+ * ---------------------------------------------------
+ *
+ * > cwd /home/raddatz
+ * 250 "/home/raddatz" is current directory.
+ * > dir test*
+ * 227 Entering Passive Mode (10,200,36,33,200,189).
+ * 125 List started.
+ * RADDATZ 200 21.05.11 12:31:18 *STMF TEST_RG_02_CRLF.properties
+ * RADDATZ 187 08.05.11 12:31:40 *STMF TEST_RG_02_LF.properties
+ * RADDATZ 187 08.05.11 12:31:52 *STMF TEST_RG_02_CR.properties
+ * RADDATZ 8192 04.07.13 09:04:14 *DIR testDir1/
+ * RADDATZ 8192 04.07.13 09:04:17 *DIR testDir2/
+ * 250 List completed.
+ *
+ *
+ * Example 1, using ANT to list specific members of a file:
+ * --------------------------------------------------------
+ *
+ * <echo/>
+ * <echo>Listing members of a file:</echo>
+ *
+ * <ftp action="list"
+ * server="${ftp.server}"
+ * userid="${ftp.user}"
+ * password="${ftp.password}"
+ * binary="false"
+ * verbose="true"
+ * remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
+ * systemTypeKey="OS/400"
+ * listing="ftp-listing.txt"
+ * >
+ * <fileset dir="./i5-downloads-file" casesensitive="false">
+ * <include name="run*.mbr" />
+ * </fileset>
+ * </ftp>
+ *
+ * Output:
+ * -------
+ *
+ * [echo] Listing members of a file:
+ * [ftp] listing files
+ * [ftp] listing RUN.MBR
+ * [ftp] listing RUNNER.MBR
+ * [ftp] listing RUNNERBND.MBR
+ * [ftp] 3 files listed
+ *
+ *
+ * Example 2, using ANT to list specific members of all files of a library:
+ * ------------------------------------------------------------------------
+ *
+ * <echo/>
+ * <echo>Listing members of all files of a library:</echo>
+ *
+ * <ftp action="list"
+ * server="${ftp.server}"
+ * userid="${ftp.user}"
+ * password="${ftp.password}"
+ * binary="false"
+ * verbose="true"
+ * remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ * systemTypeKey="OS/400"
+ * listing="ftp-listing.txt"
+ * >
+ * <fileset dir="./i5-downloads-lib" casesensitive="false">
+ * <include name="**\run*.mbr" />
+ * </fileset>
+ * </ftp>
+ *
+ * Output:
+ * -------
+ *
+ * [echo] Listing members of all files of a library:
+ * [ftp] listing files
+ * [ftp] listing RPGUNIT1.FILE\RUN.MBR
+ * [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR
+ * [ftp] listing RPGUNITT1.FILE\RUNT.MBR
+ * [ftp] listing RPGUNITY1.FILE\RUN.MBR
+ * [ftp] listing RPGUNITY1.FILE\RUNNER.MBR
+ * [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR
+ * [ftp] 6 files listed
+ *
+ *
+ * Example 3, using ANT to download specific members of a file:
+ * ------------------------------------------------------------
+ *
+ * <echo/>
+ * <echo>Downloading members of a file:</echo>
+ *
+ * <ftp action="get"
+ * server="${ftp.server}"
+ * userid="${ftp.user}"
+ * password="${ftp.password}"
+ * binary="false"
+ * verbose="true"
+ * remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
+ * systemTypeKey="OS/400"
+ * >
+ * <fileset dir="./i5-downloads-file" casesensitive="false">
+ * <include name="run*.mbr" />
+ * </fileset>
+ * </ftp>
+ *
+ * Output:
+ * -------
+ *
+ * [echo] Downloading members of a file:
+ * [ftp] getting files
+ * [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUN.MBR
+ * [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNER.MBR
+ * [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNERBND.MBR
+ * [ftp] 3 files retrieved
+ *
+ *
+ * Example 4, using ANT to download specific members of all files of a library:
+ * ----------------------------------------------------------------------------
+ *
+ * <echo/>
+ * <echo>Downloading members of all files of a library:</echo>
+ *
+ * <ftp action="get"
+ * server="${ftp.server}"
+ * userid="${ftp.user}"
+ * password="${ftp.password}"
+ * binary="false"
+ * verbose="true"
+ * remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ * systemTypeKey="OS/400"
+ * >
+ * <fileset dir="./i5-downloads-lib" casesensitive="false">
+ * <include name="**\run*.mbr" />
+ * </fileset>
+ * </ftp>
+ *
+ * Output:
+ * -------
+ *
+ * [echo] Downloading members of all files of a library:
+ * [ftp] getting files
+ * [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNIT1.FILE\RUN.MBR
+ * [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNIT1.FILE\RUNRMT.MBR
+ * [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNITT1.FILE\RUNT.MBR
+ * [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNITY1.FILE\RUN.MBR
+ * [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNITY1.FILE\RUNNER.MBR
+ * [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-lib\RPGUNITY1.FILE\RUNNERBND.MBR
+ * [ftp] 6 files retrieved
+ *
+ *
+ * Example 5, using ANT to download a save file of a library:
+ * ----------------------------------------------------------
+ *
+ * <ftp action="get"
+ * server="${ftp.server}"
+ * userid="${ftp.user}"
+ * password="${ftp.password}"
+ * binary="true"
+ * verbose="true"
+ * remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ * systemTypeKey="OS/400"
+ * >
+ * <fileset dir="./i5-downloads-savf" casesensitive="false">
+ * <include name="RPGUNIT.SAVF" />
+ * </fileset>
+ * </ftp>
+ *
+ * Output:
+ * -------
+ * [echo] Downloading save file:
+ * [ftp] getting files
+ * [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF
+ * [ftp] 1 files retrieved
+ *
+ * </pre>
*/
-
public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
{
private static final String DEFAULT_DATE_FORMAT
@@ -34,11 +239,11 @@ public class OS400FTPEntryParser extends
private static final String REGEX =
- "(\\S+)\\s+" // user
- + "(\\d+)\\s+" // size
- + "(\\S+)\\s+(\\S+)\\s+" // date stuff
- + "(\\*\\S+)\\s+" // *STMF/*DIR
- + "(\\S+/?)\\s*"; // filename
+ "(\\S+)\\s+" // user
+ + "(?:(\\d+)\\s+)?" // size, empty for members
+ + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members
+ + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM
+ + "(?:(\\S+)\\s*)?"; // filename, missing, when CWD is a *FILE
/**
@@ -85,9 +290,15 @@ public class OS400FTPEntryParser extends
{
String usr = group(1);
String filesize = group(2);
- String datestr = group(3)+" "+group(4);
+ String datestr = "";
+ if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4)))
+ {
+ datestr = group(3)+" "+group(4);
+ }
String typeStr = group(5);
String name = group(6);
+
+ boolean mustScanForPathSeparator = true;
try
{
@@ -102,10 +313,58 @@ public class OS400FTPEntryParser extends
if (typeStr.equalsIgnoreCase("*STMF"))
{
type = FTPFile.FILE_TYPE;
+ if (isNullOrEmpty(filesize) || isNullOrEmpty(name))
+ {
+ return null;
+ }
}
else if (typeStr.equalsIgnoreCase("*DIR"))
{
type = FTPFile.DIRECTORY_TYPE;
+ if (isNullOrEmpty(filesize) || isNullOrEmpty(name))
+ {
+ return null;
+ }
+ }
+ else if (typeStr.equalsIgnoreCase("*FILE"))
+ {
+ // File, defines the structure of the data (columns of a row)
+ // but the data is stored in one or more members. Typically a
+ // source file contains multiple members whereas it is
+ // recommended (but not enforced) to use one member per data
+ // file.
+ // Save files are a special type of files which are used
+ // to save objects, e.g. for backups.
+ if (name != null && name.toUpperCase().endsWith(".SAVF"))
+ {
+ mustScanForPathSeparator = false;
+ type = FTPFile.FILE_TYPE;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else if (typeStr.equalsIgnoreCase("*MEM"))
+ {
+ mustScanForPathSeparator = false;
+ type = FTPFile.FILE_TYPE;
+
+ if (isNullOrEmpty(name))
+ {
+ return null;
+ }
+ if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr)))
+ {
+ return null;
+ }
+
+ // Quick and dirty bug fix to make SelectorUtils work.
+ // Class SelectorUtils uses 'File.separator' to splitt
+ // a given path into pieces. But actually it had to
+ // use the separator of the FTP server, which is a forward
+ // slash in case of an AS/400.
+ name = name.replace('/', File.separatorChar);
}
else
{
@@ -129,10 +388,13 @@ public class OS400FTPEntryParser extends
{
name = name.substring(0, name.length() - 1);
}
- int pos = name.lastIndexOf('/');
- if (pos > -1)
+ if (mustScanForPathSeparator)
{
- name = name.substring(pos + 1);
+ int pos = name.lastIndexOf('/');
+ if (pos > -1)
+ {
+ name = name.substring(pos + 1);
+ }
}
file.setName(name);
@@ -141,6 +403,20 @@ public class OS400FTPEntryParser extends
}
return null;
}
+
+ /**
+ *
+ * @param string String value that is checked for <code>null</code>
+ * or empty.
+ * @return <code>true</code> for <code>null</code> or empty values,
+ * else <code>false</code>.
+ */
+ private boolean isNullOrEmpty(String string) {
+ if (string == null || string.length() == 0) {
+ return true;
+ }
+ return false;
+ }
/**
* Defines a default configuration to be used when this class is
Added: commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java?rev=1548730&view=auto
==============================================================================
--- commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java (added)
+++ commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java Fri Dec 6 22:11:36 2013
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPFileEntryParser;
+
+import java.util.Calendar;
+
+/**
+ * @version $Id$
+ */
+
+public class OS400FTPEntryParserAdditionalTest extends CompositeFTPParseTestFramework
+{
+ private static final String[][] badsamples =
+{
+ {
+ "QPGMR 135168 04/03/18 13:18:19 *FILE",
+ "QPGMR 135168 03/24 13:18:19 *FILE",
+ "QPGMR 135168 04/03/18 30:06:29 *FILE",
+ "QPGMR 04/03/18 13:18:19 *FILE RPGUNITC1.FILE",
+ "QPGMR 135168 03/24 13:18:19 *FILE RPGUNITC1.FILE",
+ "QPGMR 135168 04/03/18 30:06:29 *FILE RPGUNITC1.FILE",
+ "QPGMR *MEM ",
+ "QPGMR 135168 04/03/18 13:18:19 *MEM RPGUNITC1.FILE/RUCALLTST.MBR",
+ "QPGMR 135168 *MEM RPGUNITC1.FILE/RUCALLTST.MBR",
+ "QPGMR 04/03/18 13:18:19 *MEM RPGUNITC1.FILE/RUCALLTST.MBR",
+ "QPGMR USR *MEM RPGUNITC1.FILE/RUCALLTST.MBR"
+ }
+ };
+
+ private static final String[][] goodsamples =
+ {
+ {
+ "QPGMR *MEM RPGUNITC1.FILE/RUCALLTST.MBR",
+ "QPGMR 16347136 29.06.13 15:45:09 *FILE RPGUNIT.SAVF"
+ }
+ };
+
+ /**
+ * @see junit.framework.TestCase#TestCase(String)
+ */
+ public OS400FTPEntryParserAdditionalTest(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * @see FTPParseTestFramework#getBadListing()
+ */
+ @Override
+ protected String[][] getBadListings()
+ {
+ return badsamples;
+ }
+
+ /**
+ * @see FTPParseTestFramework#getGoodListing()
+ */
+ @Override
+ protected String[][] getGoodListings()
+ {
+ return goodsamples;
+ }
+
+ /**
+ * @see FTPParseTestFramework#getParser()
+ */
+ @Override
+ protected FTPFileEntryParser getParser()
+ {
+ return new CompositeFileEntryParser(new FTPFileEntryParser[]
+ {
+ new OS400FTPEntryParser(),
+ new UnixFTPEntryParser()
+ });
+ }
+
+ /**
+ * @see FTPParseTestFramework#testParseFieldsOnDirectory()
+ */
+ @Override
+ public void testParseFieldsOnDirectory() throws Exception
+ {
+ FTPFile f = getParser().parseFTPEntry("PEP 36864 04/03/24 14:06:34 *DIR dir1/");
+ assertNotNull("Could not parse entry.",
+ f);
+ assertTrue("Should have been a directory.",
+ f.isDirectory());
+ assertEquals("PEP",
+ f.getUser());
+ assertEquals("dir1",
+ f.getName());
+ assertEquals(36864,
+ f.getSize());
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.MONTH, Calendar.MARCH);
+
+ cal.set(Calendar.YEAR, 2004);
+ cal.set(Calendar.DATE, 24);
+ cal.set(Calendar.HOUR_OF_DAY, 14);
+ cal.set(Calendar.MINUTE, 6);
+ cal.set(Calendar.SECOND, 34);
+
+ assertEquals(df.format(cal.getTime()),
+ df.format(f.getTimestamp().getTime()));
+ }
+
+ @Override
+ protected void doAdditionalGoodTests(String test, FTPFile f)
+ {
+ if (test.startsWith("d"))
+ {
+ assertEquals("directory.type",
+ FTPFile.DIRECTORY_TYPE, f.getType());
+ }
+ }
+
+ /**
+ * @see FTPParseTestFramework#testParseFieldsOnFile()
+ */
+ @Override
+ public void testParseFieldsOnFile() throws Exception
+ {
+ FTPFile f = getParser().parseFTPEntry("PEP 5000000000 04/03/24 14:06:29 *STMF build.xml");
+ assertNotNull("Could not parse entry.",
+ f);
+ assertTrue("Should have been a file.",
+ f.isFile());
+ assertEquals("PEP",
+ f.getUser());
+ assertEquals("build.xml",
+ f.getName());
+ assertEquals(5000000000L,
+ f.getSize());
+
+ Calendar cal = Calendar.getInstance();
+
+ cal.set(Calendar.DATE, 24);
+ cal.set(Calendar.MONTH, Calendar.MARCH);
+ cal.set(Calendar.YEAR, 2004);
+ cal.set(Calendar.HOUR_OF_DAY, 14);
+ cal.set(Calendar.MINUTE, 6);
+ cal.set(Calendar.SECOND, 29);
+ assertEquals(df.format(cal.getTime()),
+ df.format(f.getTimestamp().getTime()));
+ }
+}
Propchange: commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/net/trunk/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserAdditionalTest.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision