【问题标题】:Java apache commons FTPClient fails allocating space for destination mainframe fileJava apache commons FTPClient 无法为目标大型机文件分配空间
【发布时间】:2012-03-19 05:54:56
【问题描述】:

这是一个我已经努力解决了大约一个星期的工作问题,但没有成功。

我有使用 apache commons net FTPClient 库将文件发送到大型机的遗留 Java 代码。该代码适用于 86Mb 以下的文件。但是,当遇到超过 86Mb 的文件时,它会失败并返回 CopyStreamException(没有其他有用信息)。我添加了超时和 CopyStreamListener 无济于事。侦听器输出一些消息,表明 ftp 已成功将数据缓冲区上传到大型机,直到达到 86Mb(示例日志输出如下)。

我最初认为问题与网络/防火墙有关(这就是为什么您会在代码中看到如此多的超时操作),但现在我明白这与我发送到大型机的空间分配命令有关。我在这里得到了一位大型机专家的帮助,他就 BLKSIZE、SPACE、LRECL 等的不同组合提出了许多建议。但没有一个奏效。有一次我确实完成了传输工作,但是大型机工作人员随后告诉我,在大型机上创建的文件的块大小和格式参数不正确,所以我不得不放弃它(我会解释它是什么在下面的这种情况下工作)。

首先,这是 Java 代码:

public static boolean copyFileThruFTP(
    String srcFileName, String remoteFileName, Properties exportProps) {
    boolean success = true;
    String serverUserName = exportProps.getProperty(WebUtil.FTP_SERVER_USER);
    String serverPwd = exportProps.getProperty(WebUtil.FTP_SERVER_PWD);
    String serverIp = exportProps.getProperty(WebUtil.FTP_SERVER_IP);
    File f = new File(srcFileName);
    FTPClient ftpClient = null;
    try {
        String errorMessage = null;
        FileInputStream input = new FileInputStream(f);
        ftpClient = new FTPClient();

        ftpClient.setDataTimeout(6000000); // 100 minutes
        ftpClient.setConnectTimeout(6000000); // 100 minutes
        ftpClient.connect(serverIp);
        int reply = ftpClient.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            errorMessage = "FTP server refused connection: " + ftpClient.getReplyString();
        } else {
            if (!ftpClient.login(serverUserName,serverPwd)) {
                errorMessage = "Failed to copy file thru FTP: login failed.";
            } else {
                ftpClient.setBufferSize(1024000);
                ftpClient.setControlKeepAliveTimeout(30); // sends a keepalive every thirty seconds

                if (ftpClient.deleteFile(remoteFileName)) {
                    log.warn("Deleted existing file from remote server: " + remoteFileName);
                }
                ftpClient.setFileTransferMode(FTP.ASCII_FILE_TYPE);
                ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
                ftpClient.sendSiteCommand("RECFM=FB");
                ftpClient.sendSiteCommand("LRECL=2000");
                ftpClient.sendSiteCommand("BLKSIZE=27000");
                //ftpClient.sendSiteCommand("DCB=(RECFM=FB,LRECL=2000,BLKSIZE=26000)");
                ftpClient.sendSiteCommand("SPACE=(TRK,(500,500),RLSE)");

                OutputStream ftpOut = ftpClient.storeFileStream(remoteFileName);
                if (ftpOut == null) {
                    errorMessage = "FTP server could not open file for write: " + ftpClient.getReplyString();
                } else {
                    OutputStream output = new BufferedOutputStream(ftpOut);

                    log.warn("copyFileThruFTP calling copyStream() for file: " + f.getAbsolutePath());
                    Util.copyStream(input, output, ftpClient.getBufferSize(), f.length(),
                        new CopyStreamAdapter() {
                            public void bytesTransferred(
                                long totalBytesTransferred, int bytesTransferred, long streamSize) {
                                    log.warn(bytesTransferred + " bytes written; total: " + totalBytesTransferred + " of " + streamSize);
                            }
                        });
                    input.close();
                    output.close();
                    if (!ftpClient.completePendingCommand()) {
                        errorMessage = "Failed to copy file thru FTP: completePendingCommand failed.";
                    }
                }
            }
            ftpClient.logout();
            ftpClient.disconnect();
            ftpClient = null;
        }
        if (!StringUtils.isEmpty(errorMessage)) {
            log.error(errorMessage);
            System.out.print(errorMessage);
            success = false;
        }
    } catch (CopyStreamException cse){
        cse.printStackTrace();
        log.error("Failed to copy file thru FTP (CopyStreamException).", cse);
        success = false;
    } catch (IOException e){
        e.printStackTrace();
        log.error("Failed to copy file thru FTP (IOException).", e);
        success = false;
    } finally {
        try {
            if (ftpClient != null) {
                ftpClient.logout();
                ftpClient.disconnect();
            }
        } catch (IOException ignore) {}
    }

    return success;    
}

现在,正如我所说的,这段代码对于 86Mb 以下的所有文件都非常有效,所以虽然从知识的角度来看它可能很有用,但我真的不需要 Java 编码风格等方面的提示。另外请注意,在发布此方法时,我删除了 cmets 和无关代码,因此可能存在一两个语法错误,尽管当我将其复制回 eclipse 时没有看到任何错误。 我要解决的是为什么这段代码适用于小文件,但不适用于大文件!

这里是一个大文件的日志输出示例(为了美观稍作修改):


2012-02-29 11:13 WARN - copyFileThruFTP calling copyStream() for file: C:\data\mergedcdi\T0090200.txt
2012-02-29 11:13 WARN - 1024000 bytes; total: 1024000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 2048000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 3072000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 4096000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 5120000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 6144000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 7168000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 8192000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 9216000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 10240000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 11264000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 12288000 of 96580484
2012-02-29 11:13 WARN - 1024000 bytes; total: 13312000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 14336000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 15360000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 16384000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 17408000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 18432000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 19456000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 20480000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 21504000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 22528000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 23552000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 24576000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 25600000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 26624000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 27648000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 28672000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 29696000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 30720000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 31744000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 32768000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 33792000 of 96580484
2012-02-29 11:14 WARN - 1024000 bytes; total: 34816000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 35840000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 36864000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 37888000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 38912000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 39936000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 40960000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 41984000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 43008000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 44032000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 45056000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 46080000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 47104000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 48128000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 49152000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 50176000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 51200000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 52224000 of 96580484
2012-02-29 11:15 WARN - 1024000 bytes; total: 53248000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 54272000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 55296000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 56320000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 57344000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 58368000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 59392000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 60416000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 61440000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 62464000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 63488000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 64512000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 65536000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 66560000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 67584000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 68608000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 69632000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 70656000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 71680000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 72704000 of 96580484
2012-02-29 11:16 WARN - 1024000 bytes; total: 73728000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 74752000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 75776000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 76800000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 77824000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 78848000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 79872000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 80896000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 81920000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 82944000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 83968000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 84992000 of 96580484
2012-02-29 11:17 WARN - 1024000 bytes; total: 86016000 of 96580484
2012-02-29 11:17 ERROR- Failed to copy file thru FTP (CopyStreamException).
org.apache.commons.net.io.CopyStreamException: IOException caught while copying.
    at org.apache.commons.net.io.Util.copyStream(Util.java:130)
    at org.apache.commons.net.io.Util.copyStream(Util.java:174)
    at com.pa.rollupedit.util.WebUtil.copyFileThruFTP(WebUtil.java:1120)
    at com.pa.rollupedit.util.CdiBuilder.process(CdiBuilder.java:361)
    at com.pa.rollupedit.controller.ExportCDI.doGet(ExportCDI.java:55)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
    at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
    at com.pa.rollupedit.controller.LoginFilter.doFilter(LoginFilter.java:90)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
    at com.pa.rollupedit.util.RequestTimeFilter.doFilter(RequestTimeFilter.java:18)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3496)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.security.service.SecurityManager.runAs(Unknown Source)
    at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2180)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2086)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1406)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)


好的,现在来看看大型机上发生的事情。以下是大型机人提供给我的关于传输不完整生成的文件的信息:

    Data Set Name . . . . : BSFP.ICEDCDI.SPC10.T0090200

General Data Current Allocation Management class . . : MCDFLT Allocated tracks . : 1,650 Storage class . . . : SCSTD Allocated extents . : 32 Volume serial . . . : SMS217 + Device type . . . . : 3390 Data class . . . . . : DCDFLT Organization . . . : PS Current Utilization Record format . . . : FB Used tracks . . . . : 1,650 Record length . . . : 2000 Used extents . . . : 32 Block size . . . . : 26000 1st extent tracks . : 100 Secondary tracks . : 50 Dates Data set name type : Creation date . . . : 2012/02/29 SMS Compressible. . : NO Referenced date . . : 2012/02/29 Expiration date . . : None

请注意,记录长度、记录格式和块大小似乎与您期望的一样,它们基于我发送的站点命令。但是,在我看来,第一个范围和次要轨道是不正确的。大型机专家给了我大约 10 种不同的 SPACE 命令供我尝试(以及对块大小的一些修改)。记录大小绝对是 2000 个字符,因此保持一致。但到目前为止,他的任何建议都没有奏效。

就我自己而言,我发现了一种不同的参数设置方法,并曾尝试过。您可以在代码中将其视为注释掉的行:

 //ftpClient.sendSiteCommand("DCB=(RECFM=FB,LRECL=2000,BLKSIZE=26000)");

有趣的是,当我使用这个命令(并注释掉RECFM/LRECL/BLKSIZE命令)时,ftp传输成功了!!但是主机大佬随后分享了如下远程文件信息,说明各种参数设置不正确。 DCB 命令似乎根本不起作用。


    Data Set Name . . . . : BSFP.ICEDCDI.SPC10.T0090200

General Data Current Allocation Management class . . : MCDFLT Allocated tracks . : 230 Storage class . . . : SCSTD Allocated extents . : 4 Volume serial . . . : SMS195 Device type . . . . : 3390 Data class . . . . . : DCDFLT Organization . . . : PS Current Utilization Record format . . . : VB Used tracks . . . . : 230 Record length . . . : 256 Used extents . . . : 4 Block size . . . . : 27000 1st extent tracks . : 100 Secondary tracks . : 50 Dates Data set name type : Creation date . . . : 2012/02/28 SMS Compressible. . : NO Referenced date . . : 2012/02/29 Expiration date . . : None

不用说,这确实对事情产生了影响。

今天早上的更新:大型机专家联系了其他大型机专家,其中一位告诉他“TRK”在 SPACE 命令中不正确,而是使用以下内容:


    ftpClient.sendSiteCommand("SPACE=(TRA,(PRI=500,SEC=500))");

我尝试了这个(以及其他变体,例如没有 PRI= 和 SEC=),但结果完全相同(即在 86Mb 时失败)。

如您所见,这不是一个无聊的问题。我什至没有深入研究我在验证这不是网络问题时所经历的许多变化。在这一点上,我有 98.6% 的把握确定这个问题是由大型机站点命令引起的。

如果您能提供任何帮助,我将不胜感激!

【问题讨论】:

    标签: apache-commons mainframe ftp-client


    【解决方案1】:

    由于您在特定大小上失败,这是您的大型机空间命令。

    一个大型机文件只能有 16 个扩展区、1 个主分配和 15 个辅助分配。如果磁盘包已满或几乎已满,您可能无法获得主分配。

    在您当前的分配中,您要求 500 个主要轨道和 15 * 500 个辅助轨道,总共 8,000 个轨道。一个磁道将容纳 2 或 3 个 26,000 字节的块,具体取决于磁盘驱动器磁道的大小。如果指定块大小为 0,IBM 操作系统将计算最有效的块大小。

    稍加计算表明您已经分配了 208,000 条记录(最坏情况)。

    您可能应该在圆柱体中指定您的空间,并有一个小的主要和较大的次要。比如:

    ftpClient.sendSiteCommand("SPACE=(CYL,(30,300),RLSE)"); 
    

    如果您的大型机商店坚持要您指定轨道,试试这个:

    ftpClient.sendSiteCommand("SPACE=(TRK,(450,4500),RLSE)");
    

    我让这两个数字都可以被 15 整除,因为如果我没记错的话,一个圆柱体包含 15 个磁道。

    编辑添加:

    我刚刚在 IBM 网站上找到了 IBM FTP commands 页面。看来您必须单独发送空格命令的每个部分,就像 DCB 命令一样。

    ftpClient.sendSiteCommand("TR");
    ftpClient.sendSiteCommand("PRI=450");
    ftpClient.sendSiteCommand("SEC=4500");
    

    【讨论】:

    • 现在尝试 CYL 解决方案。手指交叉。
    • 您上次编辑中的说明似乎有效。在我投票之前,我只是在等待大型机人员验证文件信息。
    • 很高兴我能帮上忙。在过去的 6 年里我只需要这样做一次,所以细节很模糊。
    • 非常感谢。你的回答解决了我的问题。拍拍自己的背。我一个星期的努力和这里五六个人的参与都不足以解决这个问题。我猜应该早点转向 stackoverflow。
    • 最后的评论,以防有人在这里遇到同样的问题。您为 IBM FTP 命令发布的源似乎也表明我用来设置块大小的命令不正确:BLKSIZE=27000 应该是 BL=27000 或 BLKSIZE=27000——这最终与结果一致-- 实际的大型机文件最终的块大小为 26000,这显然是大型机在没有从 ftp 获得成功命令的情况下认为的最佳大小。
    【解决方案2】:

    我想为 Gilbert Le Blanc 给出的出色答案添加一些 cmets

    指定一个小的主要空间和大的辅助空间的原因有点违反直觉。 主空间分配总是要求连续的空间(即在一个大 块)。当找不到具有该数量的卷(磁盘)时,作业将失败并返回 B37 上的连续空间。在主要空间分配代表重要的情况下 体积的百分比(3390 DASD 设备实际上相当小 - 大约 10Gb 左右), 找不到具有所需空间的卷的可能性可能很大。因此这份工作 在 B37 上爆炸。

    次要分配不必分配连续的块,并且可以跨越多个卷。经过 请求一个相当小的主要数量,您可以通过允许数据集来避免 B37 异常终止 分配辅助空间以满足总空间需求。这种方法的收获 是每个卷只能获得 15 个辅助扩展区(这是一个人为但历史性的限制,可以 由您商店的 DASD 管理部门解除)。为避免超出范围限制,导致 E37 异常终止,您 请求大范围大小。

    最后,您可以指定一个FTP VCOUNT参数,该参数可以设置一个数据集的卷数 可以跨越(每个卷最多可以有 15 个扩展区)。这可以产生绝对庞大的数据集。

    所有这些都表明,了解 IBM 大型机上的传统数据集分配涉及 有点巫术。

    依靠存储管理系统 (SMS) 为您计算,生活可以变得更简单。 大多数商店都会有短信。而不必做所有的数学 要计算主要/次要范围大小,您可能只需询问适当的 SMS DATACLAS。大多数安装 将有适合您的大型数据集的东西。每个安装都定义了自己的 DATACLS 名称,因此您的大型机人员将不得不为您挖掘它们。 SMS DATACLAS 已指定 使用 FTP LOCSITE 选项:DATAC=xxx 其中 xxx 是适当的 DATACLAS 名称。

    【讨论】:

    • 谢谢。我的大型机人员没有提供 DATACLAS 选项。
    猜你喜欢
    • 2013-08-04
    • 2012-03-31
    • 2013-06-11
    • 2021-02-08
    • 2023-04-05
    • 2013-11-21
    • 2015-01-17
    • 2012-03-21
    • 2014-06-18
    相关资源
    最近更新 更多