COBOL-til-Java-konvertere producerer "Jobol"

Hvorfor COBOL-til-Java-konvertere producerer "Jobol", og hvordan man undgår det

Konverteringen blev gennemført på tre måneder. Java-applikationen blev kompileret, bestod grundlæggende røgtests og blev erklæret klar til implementering. Seks måneder inde i produktionen bemærkede platformteamet noget foruroligende: ingen i Java-teamet kunne vedligeholde koden. Gennemgangene tog dobbelt så lang tid som normale Java-gennemgange. Fejlrapporterne refererede til koncepter som WORKING-STORAGE, PROCEDURE DIVISION-logik og tætpakkede decimalfeltkonventioner, som Java-udviklere uden mainframe-erfaring ikke kunne følge. Applikationen var blevet konverteret fra COBOL til Java, men den resulterende Java tænkte, strukturerede og opførte sig som COBOL. Det var Jobol.

Jobol (undertiden kaldet JOBOL) er portmanteauet for Java-kode, der bevarer COBOLs proceduremæssige semantik uden at omfavne Javas objektorienterede muligheder. Jason Bloomberg, grundlægger af analysefirmaet Intellyx, definerede det direkte: Jobol er teknisk gyldig Java-kode, der lader den originale softwarearkitektur være på plads og bevarer COBOL-semantik, hvilket kræver, at udviklere behandler Java, som om det var COBOL. Den kritiske konsekvens følger straks: en virksomhed, der konverterer COBOL til Jobol, har ikke frigjort sig fra behovet for COBOL-ekspertise. Den har simpelthen flyttet dette ekspertisekrav til et sprog, der ikke native understøtter de COBOL-mønstre, som koden indeholder. Det har skabt et system, der er sværere at vedligeholde end det originale COBOL og ikke giver nogen af ​​de forventede Java-fordele.

Afhængighedsgraf før domænedesign

SMART TS XL kortlægger alle PERFORM- og CALL-afhængigheder, så Java-servicelaget afspejler den faktiske koblingsstruktur, ikke COBOL-klassestrukturen.

FÅ MERE AT VIDE…

Sådan ser Jobol egentlig ud

Før man kan diagnosticere årsagerne, skal mønsteret være synligt. Her er den samme forretningslogik, en kundekredittjek, i tre former: den originale COBOL, den Jobol, som automatiserede konvertere typisk producerer, og den idiomatiske Java, som en dygtig modernisering producerer.

Den originale COBOL:

COBOL

       PERFORM-CREDIT-CHECK.
           MOVE WS-CUST-ID TO DB-CUST-KEY
           READ CUSTOMER-FILE KEY IS DB-CUST-KEY
               INVALID KEY
                   MOVE 'NOT FOUND' TO WS-STATUS
                   PERFORM HANDLE-MISSING-CUSTOMER
           END-READ

           IF CUST-CREDIT-SCORE < 650
               MOVE 'DECLINED' TO WS-CREDIT-RESULT
               MOVE 'Score below threshold' TO WS-DECLINE-REASON
           ELSE IF CUST-OUTSTANDING-BALANCE > CUST-CREDIT-LIMIT
               MOVE 'DECLINED' TO WS-CREDIT-RESULT
               MOVE 'Balance exceeds limit' TO WS-DECLINE-REASON
           ELSE
               MOVE 'APPROVED' TO WS-CREDIT-RESULT
           END-IF.

Jobol produceret af en linje-for-linje-konverter:

Java

// Jobol: procedural COBOL logic mapped directly to Java
// No OOP, no encapsulation, static global state, COBOL naming conventions
public class CreditCheckProgram {
    static String WS_CUST_ID;
    static String DB_CUST_KEY;
    static String WS_STATUS;
    static String WS_CREDIT_RESULT;
    static String WS_DECLINE_REASON;
    static int CUST_CREDIT_SCORE;
    static double CUST_OUTSTANDING_BALANCE;
    static double CUST_CREDIT_LIMIT;

    public static void PERFORM_CREDIT_CHECK() {
        DB_CUST_KEY = WS_CUST_ID;
        Customer customer = CustomerFile.read(DB_CUST_KEY);
        if (customer == null) {
            WS_STATUS = "NOT FOUND";
            HANDLE_MISSING_CUSTOMER();
            return;
        }
        CUST_CREDIT_SCORE = customer.getCreditScore();
        CUST_OUTSTANDING_BALANCE = customer.getOutstandingBalance();
        CUST_CREDIT_LIMIT = customer.getCreditLimit();

        if (CUST_CREDIT_SCORE < 650) {
            WS_CREDIT_RESULT = "DECLINED";
            WS_DECLINE_REASON = "Score below threshold";
        } else if (CUST_OUTSTANDING_BALANCE > CUST_CREDIT_LIMIT) {
            WS_CREDIT_RESULT = "DECLINED";
            WS_DECLINE_REASON = "Balance exceeds limit";
        } else {
            WS_CREDIT_RESULT = "APPROVED";
        }
    }
}

Det idiomatiske Java, som en dygtig modernisering producerer:

Java

// Idiomatic Java: domain model, encapsulation, value objects, Spring service
@Service
public class CreditCheckService {

    private final CustomerRepository customerRepository;

    public CreditCheckService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    public CreditDecision check(CustomerId customerId) {
        Customer customer = customerRepository.findById(customerId)
            .orElseThrow(() -> new CustomerNotFoundException(customerId));

        if (customer.creditScore() < CreditThresholds.MINIMUM_SCORE) {
            return CreditDecision.declined("Score below threshold");
        }
        if (customer.outstandingBalance().compareTo(customer.creditLimit()) > 0) {
            return CreditDecision.declined("Balance exceeds limit");
        }
        return CreditDecision.approved();
    }
}

Jobol-versionen har 32 linjer statisk global tilstand, er proceduremæssigt struktureret, har COBOL-navngivet og er umulig at enhedsteste uden først at initialisere den globale klassetilstand. Den idiomatiske version har 20 linjer, er injicerbar, testbar og kan læses med det samme af enhver Java-udvikler uden COBOL-baggrund. Begge implementerer de samme forretningsregler. Kun én kan vedligeholdes af det team, som moderniseringen skulle muliggøre.

De seks Jobol-mønstre og hvorfor konvertere producerer dem

Mønster 1: Global statisk tilstand erstatter WORKING-STORAGE

COBOLs WORKING-STORAGE SECTION indeholder programniveauvariabler, der bevares på tværs af PERFORM-kald. Det er programmets delte, muterbare tilstand, hvor hvert afsnit læser og skriver de samme WS-variabler. Automatiserede konvertere mapper dette direkte til statiske Java-felter på en enkelt klasse, hvilket bevarer semantikken for delt tilstand, samtidig med at alle Java-indkapslingsprincipper overtrædes.

Hvorfor konvertere gør dette: WORKING-STORAGE til statisk feltkortlægning er en mekanisk en-til-en-transformation. Konverteren forstår ikke, at Javas indkapslingsmodel erstatter delt tilstand på programniveau med instansvariabler, metodeparametre og returværdier. Den konverterer det, den ser, uden at forstå, hvorfor den oprindelige struktur eksisterede.

Konsekvensen: Statisk tilstand kan ikke testes isoleret, er trådusikker uden eksplicit synkronisering og strukturelt identisk med den COBOL, den erstattede. Enhver udvikler, der har brug for at tilføje samtidighed til systemet, skal først udrede den statiske tilstand, et arbejde, der blev udskudt fra konverteringen til enhver efterfølgende vedligeholder.

Løsningen: Knyt WORKING-STORAGE-variabler til instansfelter, metodeparametre eller returværdier baseret på deres anvendelsesområde. Variabler, der kun bruges i ét afsnit, bliver lokale variabler. Variabler, der sendes mellem afsnit, bliver metodeparametre eller returværdier. Variabler, der reelt repræsenterer den delte objekttilstand, bliver instansfelter på en korrekt indkapslet domæneklasse.

Mønster 2: UDFØR Kæder, der bliver lange statiske metoder

COBOL-programmer er organiseret som PERFORM-kæder, hvor hovedproceduren PERFORM udfører en sekvens af navngivne afsnit, som hver især kan PERFORM andre. Automatiserede konvertere oversætter hvert afsnit til en statisk metode, og hver PERFORM til et statisk metodekald. Resultatet er en enkelt klasse med snesevis af statiske metoder, der kalder hinanden via en kaldsgraf, der afspejler COBOL PERFORM-strukturen.

Java

// Jobol: PERFORM chain mapped to static method calls
public class ProcessOrderProgram {
    public static void main() {
        validateOrder();
        checkInventory();
        calculateTotal();
        applyDiscounts();
        processPayment();
        generateConfirmation();
        updateInventory();
    }

    public static void validateOrder() { /* ... */ }
    public static void checkInventory() { /* ... */ }
    // ... 40 more static methods
}

Hvorfor konvertere gør dette: Den strukturelle kortlægning fra COBOL-afsnit til Java-metoden er direkte. Konverteren identificerer korrekt, at PERFORM betyder "kald denne kodeenhed". Hvad den overser er, at Javas objektmodel organiserer kode omkring domæneenheder, ikke omkring behandlingstrin.

Løsningen: Identificer de domænekoncepter, der er indlejret i PERFORM-kæden. validateOrder, calculateTotalog applyDiscounts er operationer på en ordre. processPayment er en operation i en betalingskontekst. checkInventory og updateInventory er operationer på Inventory. Refaktoreringen distribuerer de statiske metoder til de domæneklasser, de tilhører, og erstatter en lang proceduremæssig kaldkæde med en kort orkestreringsmetode, der delegerer til passende domæneobjekter.

Mønster 3: Numeriske typer uden præcisionshåndtering

COBOLs pakkede decimalfelter (COMP-3) har nøjagtig decimalaritmetik. Konverteringsprogrammer, der knytter COMP-3 til Java double or float introducere systematiske afrundingsfejl med flydende komma, der akkumuleres på tværs af millioner af finansielle transaktioner.

Java

// Jobol: COMP-3 mapped to double (wrong)
static double WS_TRANSACTION_AMOUNT;  // Was PIC S9(9)V99 COMP-3
static double WS_RUNNING_TOTAL;

// After 10M additions of 0.1:
// Expected: 1,000,000.00
// Actual:   1,000,000.0000000002 (floating point accumulation)

Java

// Idiomatic Java: COMP-3 mapped to BigDecimal (correct)
private BigDecimal transactionAmount;  // Preserves decimal precision
private BigDecimal runningTotal;

// After 10M additions of 0.1:
// Result: exactly 1,000,000.00

Hvorfor konvertere gør dette: double er Javas standard numeriske type. En konverter, der ikke specifikt forstår COMP-3 semantik, vil som standard bruge den mest almindelige Java numeriske type, hvilket er det forkerte valg til præcis decimalaritmetik.

Løsningen: Hvert COMP-3-felt knyttes til BigDecimalEnhver numerisk beregning, der involverer COMP-3-værdier, bruger BigDecimal aritmetik. Den scale() af BigDecimal matcher de implicitte decimaler i PIC-klausulen: PIC S9(9)V99 har 2 implicitte decimaler, der er knyttet til BigDecimal med skala 2.

Mønster 4: EBCDIC-strenge og byte-array-relikvier

COBOL behandler tegndata som fastlængde, polstrede strenge i EBCDIC-kodning. Konverteringsprogrammer producerer ofte Java-kode, der bevarer strengsemantikken med fast længde og mellemrumspolstring, selv efter konvertering til UTF-8, hvilket resulterer i Java, der håndterer strenge som en mainframe-programmør fra 1970'erne.

Java

// Jobol: preserves COBOL fixed-length string handling
static char[] WS_CUSTOMER_NAME = new char[40]; // Was PIC X(40)
static char[] WS_CUSTOMER_ADDR = new char[80]; // Was PIC X(80)

// COBOL-style space-padding comparison
if (new String(WS_CUSTOMER_NAME).trim().equals("SMITH")) { ... }

// COBOL-style string concatenation with padding
System.arraycopy(name.toCharArray(), 0, WS_CUSTOMER_NAME, 0,
                 Math.min(name.length(), 40));

Java

// Idiomatic Java: proper String types and domain objects
private String customerName;  // Trim on read, no padding
private String customerAddress;

// Clean comparison
if ("Smith".equalsIgnoreCase(customer.name())) { ... }

Hvorfor konvertere gør dette: COBOL PIC X(40)-definitionen har en længde, som konverteren bevarer som en Java-arraystørrelse. EBCDIC-til-UTF-8-konverteringen kan muligvis håndteres, men semantikken med fast længde overføres unødvendigt.

Løsningen: Kort PIC X(n) felter til String objekter, beskæring ved læsning og fjernelse af begrænsninger med fast længde. Den eneste kontekst, hvor længdebegrænsninger har betydning, er ved persistensgrænser (kolonnelængder i databasen, filformatpositioner), ikke i domænemodellen.

Mønster 5: Fejlhåndtering via statuskoder

COBOL kommunikerer fejltilstande via statuskoder: FILE STATUS, SQLCODE, RETURN-CODE. Konverteringsprogrammer bevarer dette mønster i Java og producerer kode, der kontrollerer heltalsstatuskoder efter hver operation i stedet for at bruge Javas undtagelsesmodel.

Java

// Jobol: COBOL status code pattern in Java
static int WS_FILE_STATUS;
static int WS_SQL_CODE;

CustomerFile.read(DB_CUST_KEY);
WS_FILE_STATUS = CustomerFile.getLastStatus();
if (WS_FILE_STATUS == 10) {
    // Not found
} else if (WS_FILE_STATUS == 35) {
    // File not available
} else if (WS_FILE_STATUS != 0) {
    // Unknown error
}

Java

// Idiomatic Java: exceptions for error conditions
try {
    Customer customer = customerRepository.findById(customerId)
        .orElseThrow(() -> new CustomerNotFoundException(customerId));
    // process customer
} catch (DatabaseUnavailableException e) {
    log.error("Database unavailable during credit check", e);
    throw new CreditCheckServiceException("Service temporarily unavailable", e);
}

Hvorfor konvertere gør dette: Statuskodekontrol er COBOL-mønsteret; konvertering af det til en Java-metode, der returnerer eller angiver et statusfelt, er en mekanisk transformation. At erkende, at Java-undtagelser er den idiomatiske ækvivalent, kræver arkitektonisk vurdering, ikke mekanisk oversættelse.

Løsningen: Knyt COBOL-fejltilstande til Java-undtagelser. FILSTATUS 10 (post ikke fundet) bliver Optional.empty() eller en domænespecifik undtagelse. SQLCODE-fejl bliver typebestemte databaseundtagelser. Java-kodestrukturen afspejler intention, try/catch for forventede fejltilstande, ukontrollerede undtagelser for uventede.

Mønster 6: Monolitisk klassestruktur

COBOL-programmer er monolitiske i design, hvor et enkelt program håndterer en hel forretningsfunktion. Konverteringsprogrammer opretter en enkelt Java-klasse pr. COBOL-program, hvilket producerer Java-klasser, der kan være tusindvis af linjer lange og håndtere flere opgaver, som objektorienteret design ville fordele på tværs af flere fokuserede klasser.

Hvorfor konvertere gør dette: Ét COBOL-program knyttes til én Java-klasse, og konverterens oversættelsesenhed er programmet. Programmets interne struktur (dets afsnit, sektioner, PERFORM-kæder) bliver klassens interne struktur uden opdeling i mindre klasser.

Løsningen: Anvend princippet om enkeltansvar på hver konverterede klasse. En Jobol-klasse på 3,000 linjer, der validerer input, læser fra en database, anvender forretningsregler, formaterer output og skriver til en fil, bør blive til: en inputvalidator, et repository, en domænetjeneste, en formaterer og en writer, der hver især kan testes isoleret, og som hver især fokuserer på ét ansvar.

Forudsætningen for strukturel analyse

Hvert Jobol-mønster har en tilsvarende refaktorering. Men refaktoreringen kan ikke anvendes af konverteren, det kræver forståelse af, hvad programmet gør, før man beslutter, hvordan det skal omstruktureres. Denne forståelse er præcis, hvad strukturel analyse af kildekoden COBOL giver, før et konverteringsværktøj kører.

Specifikt producerer strukturel analyse de input, som konverteringsbeslutninger kræver:

Forretningslogisk opgørelse. Den komplette opregning af EVALUATE-betingelser, IF-grene og COMPUTE-formler i hvert program, hvad programmet bestemmer, ikke kun hvordan det er struktureret. Denne opgørelse er den specifikation, som den refaktorerede Java skal matche. Et program med 847 EVALUATE-betingelser kræver en målklassestruktur, der kan rumme 847 beslutningspunkter; konverterens output er startudkastet, ikke det færdige produkt.

Afhængighedsgraf. Hvilke programmer kalder hvilke andre programmer. PERFORM-kæden, der spænder over flere COBOL-programmer, bliver en servicekaldsgraf i Java. Afhængighedsgrafen bestemmer, hvilke programmer der kan refaktoriseres uafhængigt (få indgående afhængigheder), og hvilke der skal refaktoriseres som koordinerede grupper (mange delte afhængigheder via kopibøger eller delte data).

Identifikation af død kode. Afsnit og sektioner, der aldrig UDFØRES fra nogen produktionsudførelsessti, er død kode, og de bør slet ikke vises i den konverterede Java. Konverteringsprogrammer, der inkluderer død kode, oppuster den konverterede kodebase og den efterfølgende refaktoreringsindsats.

Gendannelse af datamodeller. VSAM FD-posterne, DB2-tabeldefinitionerne og kopibogsstrukturerne, der definerer programmets data. Hver COBOL-datastruktur er knyttet til et eller flere Java-domæneobjekter; tilknytningen bestemmes af datamodellen, ikke af feltnavnene i WORKING-STORAGE.

AnalyseinputKonverteringsbeslutningen det driver
Antal EVALUER/HVIS-greneDomæneklassedekomponering, hvor mange beslutningssager hver tjeneste håndterer
Antal indgående afhængighederRefaktoreringssekvens, færrest afhængighedsprogrammer først
Procentdel af død kodeReduktion af omfang, ekskluder utilgængelig kode fra konvertering
COMP-3 felterBigDecimal kortlægning for hvert numerisk felt
PIC X feltlængderStrengtypekortlægning og begrænsninger på persistenslag
PERFORM grafstrukturServicelagsnedbrydning, PERFORM-kæder bliver domæneservicekald
Brug af FILSTATUSDesign af undtagelsesmodel, hvilke statuskoder der knyttes til hvilke undtagelsestyper

Hvad konverteringen i stedet bør producere

Alternativet til Jobol er en konverteringsproces, der bruger COBOL som en specifikation snarere end som en skabelon. COBOL-programmet ved, hvad forretningslogikken er, og Java-programmet bør udtrykke denne logik i Javas idiom.

Konverteringsprincippet: Konverter intentionen, ikke syntaksen. COBOL IF CUST-CREDIT-SCORE < 650 MOVE 'DECLINED' TO WS-CREDIT-RESULT siger "en kunde med en kreditvurdering under 650 afvises." Java burde sige det samme i Javas sprog: if (customer.creditScore() < CreditThresholds.MINIMUM_SCORE) return CreditDecision.declined(...). Ikke if (CUST_CREDIT_SCORE < 650) { WS_CREDIT_RESULT = "DECLINED"; }.

Den praktiske konverteringsarbejdsgang, der producerer idiomatisk Java:

Trin 1: Strukturel analyse af COBOL-kilden, afhængighedsgraf, forretningslogikopgørelse, gendannelse af datamodel, identifikation af død kode. Dette trin producerer specifikationen. Intet konverteres, før dette trin er fuldført.

Trin 2: Domænemodeldesign, identificer de forretningsenheder (Kunde, Konto, Transaktion, Politik), som COBOL-programmerne opererer på. Disse bliver Java-domæneklasser. De WORKING-STORAGE-variabler, der repræsenterer entitetstilstand, bliver til instansfelter på den relevante domæneklasse.

Trin 3: Design af servicelag, identificer de forretningsaktiviteter (kredittjek, betalingsbehandling, saldoberegning), som COBOL-afsnittene og -sektionerne implementerer. Disse bliver Java-servicemetoder på den relevante domæneserviceklasse. PERFORM-kæden, der sekvenserer afsnit, bliver servicens orkestreringslogik.

Trin 4: Automatiseret konvertering som en kladdegenerator. Brug konverteringsværktøjet til at producere et Java-udkast som udgangspunkt for hvert program, baseret på domænemodellen designet i trin 2 og 3. Gennemgå udkastet i forhold til forretningslogikopgørelsen for at bekræfte, at alle beslutningsgrene er repræsenteret.

Trin 5: Systematisk refaktorering mod Jobol-mønstrene, anvendelse af de seks anti-mønsterrettelser: erstat statisk tilstand med instanstilstand, distribuer statiske metoder til domæneklasser, erstat statuskoder med undtagelser, erstat double med BigDecimal, erstat arrays med fast længde med String, opdele monolitiske klasser i tjenester med enkelt ansvar.

Trin 6: Test af adfærdsækvivalens, kør den refaktorerede Java mod det karakteriseringstestkorpus, der er genereret fra COBOL-analysen. Hver EVALUATE-betingelse og IF-gren skal have mindst én testcase, der udfører den og verificerer ækvivalens med COBOL-outputtet.

Hvordan SMART TS XL Forebygger Jobol ved kilden

Den strukturelle analyse, der er forudsat for Jobol-forebyggelse, afhængighedsgrafen, forretningslogikopgørelsen, identifikation af død kode og gendannelse af datamodeller, er præcis, hvad SMART TS XL's analyse af COBOL-kildekoden leveres, før et konverteringsværktøj kører.

SMART TS XL's statisk kodeanalyse opregner alle EVALUATE-betingelser, alle IF-grene, alle COMPUTE-formler og alle PERFORM-relationer i COBOL-kilden, det komplette forretningslogikinventar, som den refaktorerede Java skal repræsentere trofast. Når konverteringsværktøjet producerer Jobol, er forretningslogikinventaret den specifikation, som refaktoreringen valideres i forhold til: alle betingelser i inventaret skal vises i Java'en i den relevante domæneklasse, udtrykt i Java's idiom.

Applikationsafhængighedskortlægningen producerer PERFORM-grafen og programkaldsgrafen, der bestemmer Java-servicelagets struktur. Programmer , der kalder hinanden, hører ofte hjemme i den samme afgrænsede kontekst. Programmer, der kaldes af mange andre, er de delte tjenester, der skal designes omhyggeligt for at understøtte alle deres forbrugere i Java-arkitekturen. Afhængighedsgrafen er inputtet til servicelagets designtrin, hvor COBOLs implicitte koblingsstruktur konverteres til Javas eksplicitte afhængighedsinjektionsstruktur.

Analysen af ​​den ældre modernisering identificerer død kode, afsnit og sektioner, der aldrig er nået fra produktionsudførelsesstier, og som helt bør udelukkes fra konverteringsområdet. Konvertering af død COBOL til død Jobol og derefter refaktorering af død Jobol spilder den indsats, som identifikationstrinnet til død kode eliminerer, før konverteringen starter.

Effektanalysefunktionen validerer refaktoreringssekvensen: Når et COBOL-programs grænseflade ændres under refaktoreringen fra Jobol til idiomatisk Java, identificerer effektanalysen alle andre programmer, hvis Java-ækvivalent også skal opdateres for at forblive konsistent med den ændrede grænseflade. Refaktorering i afhængighedsrækkefølge, med færrest afhængige først, minimerer den nødvendige koordinering i hvert trin.

Konverteringsværktøjer oversætter syntaks. Modernisering transformerer betydning.

Automatiserede COBOL-til-Java-konvertere er hurtigere end menneskelig omskrivning. De er også optimeret til et andet mål: syntaktisk korrespondance snarere end semantisk ækvivalens i målsprogets idiom. Jobol er, hvad syntaktisk korrespondance producerer, når kildesproget (COBOL) og målsproget (Java) har fundamentalt forskellige strukturelle modeller.

De organisationer, der undgår Jobol, er ikke dem, der bruger bedre konvertere. Det er dem, der behandler konverteren som en kladdegenerator snarere end et færdigt produkt, investerer i strukturel analyse, før konverteringen starter, designer Java-domænemodellen, før konverteren kører, og anvender systematisk refactoring på konverterens output med forretningslogikinventaret som valideringsspecifikation.

Jobol kan undgås. Omkostningerne ved at undgå det er den strukturelle analyse og domænemodelleringsarbejde, der bør gå forud for enhver konvertering. Omkostningerne ved ikke at undgå det er et Java-system, der kræver COBOL-ekspertise at vedligeholde, hvilket betyder, at den forventede fordel ved moderniseringen, muligheden for at vedligeholde systemet med Java-udviklere, aldrig realiseres.

Konvertér intentionen. Ikke syntaksen. Forskellen er moderniseringens samlede værdi.