Java apache commons FTPClient не удается выделить место для файла конечного мэйнфрейма - PullRequest
1 голос
/ 29 февраля 2012

Вот рабочая проблема, которую я усердно атаковал около недели безуспешно.

У меня есть устаревший код Java, который использует библиотеку apache commons net FTPClient для отправки файла на мэйнфрейм.Код отлично работает для файлов до 86 Мб.Однако, когда он встречает файл размером более 86 МБ, он завершается с ошибкой CopyStreamException (без другой полезной информации).Я добавил тайм-ауты и CopyStreamListener безрезультатно.Слушатель выводит некоторые сообщения, указывающие, что ftp успешно загружает буферы данных в мэйнфрейм, пока не достигнет 86 МБ (пример выходных данных журнала включен ниже).

Сначала я думал, что проблема связана с сетью / межсетевым экраном (чтоВот почему вы видите в коде так много манипуляций с тайм-аутом), но теперь я понимаю, что это связано с командами выделения пространства, которые я посылаю мэйнфрейму.Я получил помощь от эксперта по мэйнфреймам, и он сделал МНОГИЕ предложения относительно различных комбинаций 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;    
}

Теперь, как я уже сказал, этот код работает исключительно хорошо для всех файлов до 86 МБ, поэтомухотя это может быть полезно с точки зрения знаний, мне действительно не нужны советы по стилю кодирования Java и т. д. Также обратите внимание, что при публикации этого метода я удалил комментарии и посторонний код, поэтому возможна синтаксическая ошибка или дваХотя я не видел ни одного, когда я скопировал это обратно в затмение. Я пытаюсь выяснить, почему этот код работает для небольших файлов, но не для больших файлов!

Вот пример вывода журнала длябольшой файл (слегка измененный для эстетики):

<code>
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)
Хорошо, теперь к тому, что происходит на мэйнфрейме.Вот информация, которую парень из мэйнфреймов предоставил мне в отношении файла, сгенерированного в результате неполной передачи:
<code>
    Data Set Name . . . . : BSFP.ICEDCDI.SPC10.T0090200</p>

<p>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 . . : <strong><em>None</em></strong>

Обратите внимание, что длина записи, формат записи и размер блока выглядят как выЯ ожидаю, что они будут основаны на командах сайта, которые я отправил.Тем не менее, 1-й экстент и вторичные треки кажутся мне неверными.Парень из мэйнфрейма дал мне попробовать 10 различных команд SPACE (вместе с некоторыми изменениями в размере блока).Размер записи определенно составляет 2000 символов, поэтому она остается неизменной.Но до сих пор ни одно из его предложений не сработало.

Сам по себе я обнаружил другой способ настройки параметров и попробовал это в одной точке.Вы можете видеть это в коде как закомментированную строку:

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

Что интересно, это то, что когда я использовал эту команду (и закомментировал команды RECFM / LRECL / BLKSIZE), передача ftp былауспешный !!НО мэйнфрейм затем поделился следующей информацией об удаленном файле, указывая, что различные параметры были установлены неправильно.Казалось бы, команда DCB вообще не работала.

<code>
    Data Set Name . . . . : BSFP.ICEDCDI.SPC10.T0090200</p>

<p>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 . . : <strong><em>None</em></strong>
Излишне говорить, что это действительно мешает вещам.

Обновление с сегодняшнего утра: парень из мэйнфреймов обратился к другим экспертам по мэйнфреймам, один из которых сказал ему, что "TRK" былневерно в команде SPACE, и вместо этого использовать следующее:

<code>
    ftpClient.sendSiteCommand("SPACE=(TRA,(PRI=500,SEC=500))");
Я попробовал это (наряду с другими вариантами, такими как без PRI = и SEC =), но результаты точно такие же (то есть с ошибкой на 86 Мб).

Как видите, это не легкомысленный вопрос,Я даже не углубляюсь в многочисленные движения, которые я прошел, чтобы убедиться, что это не проблема сети.На данный момент я на 98,6% уверен, что эта проблема вызвана командами сайта мэйнфрейма.

Если вы можете оказать какую-либо помощь, я был бы очень признателен за это !!

Ответы [ 2 ]

4 голосов
/ 01 марта 2012

Я хотел бы добавить несколько комментариев к прекрасному ответу Гилберта Ле Блан

Причина указания небольшого основного и большого дополнительного пространства немного противоречит интуиции. Распределение первичного пространства всегда запрашивает непрерывное пространство (т. Е. В одном большом Кусок). Задание не будет выполнено с кодом возврата B37, если том (диск) не может быть найден с таким количеством смежного пространства на нем. В ситуациях, когда выделение основного пространства представляет значительную процент объема (3390 устройств DASD на самом деле довольно малы - порядка 10 Гб или около того), вероятность не найти объем с требуемым пространством может быть значительным. Отсюда и работа взрывается на B37.

Вторичные распределения не должны быть распределены смежными блоками и могут занимать несколько томов. От запрашивая довольно маленькое первичное количество, вы избегаете выхода B37, позволяя набору данных выделить вторичное пространство для выполнения требований общего пространства. Улов с этим подходом является то, что вы можете получить только 15 вторичных экстентов на том (это искусственный, но исторический предел, который может быть подняты руководством DASD в вашем магазине). Чтобы избежать превышения предела экстента, вызывающего аварийное завершение E37, вы запрос большого размера.

Наконец, вы можете указать параметр FTP VCOUNT, который может указывать количество томов в наборе данных. может охватывать (каждый том может иметь до 15 экстентов). Это может привести к абсолютно огромным наборам данных.

Все это указывает на то, что понимание традиционного распределения набора данных на мэйнфрейме IBM требует немного колдовства.

Жизнь можно сделать намного проще, полагаясь на систему управления хранением данных (SMS), которая поможет вам в разработке. Большинство магазинов будут иметь смс на месте. Вместо того, чтобы делать всю математику чтобы рассчитать размеры первичного / вторичного экстента, вы можете просто запросить соответствующий SMS DATACLAS. Большинство установок будет иметь что-то подходящее для больших наборов данных, таких как ваш. Каждая установка определяет свою собственную DATACLS называет так, что ваши парни из мэйнфреймов должны будут выкопать их для вас. SMS DATACLAS указан используя опцию FTP LOCSITE: DATAC = xxx, где xxx - соответствующее имя DATACLAS.

4 голосов
/ 29 февраля 2012

Поскольку вы терпите неудачу при определенном размере, это ваша команда пространства мэйнфрейма.

Файл мэйнфрейма может иметь только 16 экстентов, 1 первичное и 15 вторичных распределений.Если диск заполнен или почти заполнен, вы можете не получить свое основное распределение.

В вашем текущем распределении вы запрашиваете 500 первичных дорожек и 15 * 500 вторичных дорожек, в общей сложности 8000 дорожек,Дорожка будет содержать 2 или 3 блока по 26000 байт, в зависимости от размера дорожки диска.Если вы укажете размер блока, равный 0, операционная система IBM рассчитает наиболее эффективный размер блока.

Немного математики означает, что вы выделили 208 000 записей (наихудший случай).

Вы, вероятно, должны указать свое пространство в цилиндрах, и иметь маленький основной и больший вторичный.Примерно так:

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

Если ваш магазин мэйнфреймов настаивает на указании треков, попробуйте следующее:

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

Я сделал оба числа, делимые на 15, потому что, если я правильно помню, цилиндрсодержит 15 треков.

Отредактировано для добавления:

Я только что нашел страницу Команды IBM FTP на веб-сайте IBM.Похоже, вы должны отправлять каждую часть команды пробела отдельно, как команда DCB.

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