+ All Categories
Home > Documents > WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing...

WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing...

Date post: 03-Aug-2020
Category:
Upload: others
View: 1 times
Download: 0 times
Share this document with a friend
24
REFACTORING WITH AUTOMATED CONVERSION: COBOL TO JAVA OR C# TECHNICAL DEEP-DIVE INTO MODERN SYSTEMS’ AUTOMATED TRANSLATION TECHNOLOGY WHITEPAPER
Transcript
Page 1: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

REFACTORING WITH AUTOMATED CONVERSION: COBOL TO JAVA OR C#TECHNICAL DEEP-DIVE INTO MODERN SYSTEMS’ AUTOMATED TRANSLATION TECHNOLOGY

WHITEPAPER

Page 2: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 02 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Growing maintenance fees, scarcity of trained developers, expected end-of-life announcements, cumbersome deployment processes, platform dependencies – these are just a few reasons companies are migrating from legacy Mainframe COBOL applications to modern Java/C# solutions. Whether migrating management support systems, operations infrastructure or reporting applications, the COBOL to Java/C# migration process is usually highly complex, risky, expensive and time consuming.

CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy application from COBOL to Java/C#, producing object oriented Java/C# that can be easily maintainable and can be efficiently integrated with other Java/C# applications. CTU supports the full environment of the Mainframe-based COBOL application, including databases (e.g.,DB2, IDMS, IMS) and VSAM, TP monitors (e.g., CICS), JCL, utilities (e.g., IDCAMS), and SORT. CTU is a fully automated solution that captures the entire source base of the application and automatically migrates it to object oriented Java/C#. The resulting code has the exact functionality of the original legacy application and can easily be tested and implemented in production.

Introduction

• Automated migration of the legacy mainframe applications in their entirety• Extremely powerful and versatile automated conversion tool backed by decades of modernization experience• Resulting code is object oriented, understandable and can be maintained and fully integrated with other Java / C# applications

CTU provides significant cost savings in hardware and software (no need for post-conversion emulation software) and positions the entire organization for continued growth and success.

Benefits of Refactoring with Automated Conversion using CTU This whitepaper provides a detailed analysis of the

methodology used by Modern Systems to design and develop the CTU Refactoring tool-set. It provides the reader with a deep understanding of the capabilities of the software and its merits, with special emphasis on its object-oriented translation methodology.

What to Expect Ahead

Page 3: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 03 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Translating a procedural language like COBOL to object oriented Java presents many challenges. As such, CTU’s automated conversion process was designed with the following requirements in mind:

• The resulting application should work exactly the same as the original application and produce exactly the same results• The resulting application should be maintainable and follow the object oriented concepts and paradigms: Encapsulation, abstraction, modularization, loose coupling, etc.• The resulting application should perform as well, or better than, the original application on the open systems platform

The example below shows how a simple COBOL application with sequential file processing is translated to Java, using CTU.

A First Look Into Refactoring Through Automated Conversion

IDENTIFICATION DIVISION. PROGRAM-ID. FILES. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL.

SELECT PROFILES ASSIGN TO PROFILEFILE. SELECT WEDDINGS ASSIGN TO WEDDINGFILE ORGANIZATION IS SEQUENTIAL. DATA DIVISION. FILE SECTION.

FD PROFILES. 01 PROFILES-RECORD. 05 NAME PIC X(20). 05 SURNAME PIC X(20). 05 MARITAL-STATUS PIC X(01) VALUE ‘M’. 88 MARRIED VALUE ‘M’. 88 SINGLE VALUE ‘S’.

FD WEDDINGS. 01 WEDDINGS-RECORD. 05 NAME PIC X(20). 05 SURNAME PIC X(20).

WORKING-STORAGE SECTION.

01 END-OF-FILE PIC X(01) VALUE ‘N’. 88 EOF-Y VALUE ‘Y’. 88 EOF-N VALUE ‘N’. 01 MARRIED-PEOPLE PIC 9(5) COMP VALUE ‘0’.

PROCEDURE DIVISION. 0100-OPEN-FILES. * OPEN PROFILES FILE OPEN INPUT PROFILES. * OPEN WEDDINGS FILE OPEN OUTPUT WEDDINGS.

0200-PROCESS-FILES. * READ RECORDS FROM PROFILES FILE AND * WRITE THEM INTO THE WEDDING FILE IF MARRIED PERFORM UNTIL EOF-Y READ PROFILES NEXT RECORD AT END SET EOF-Y TO TRUE NOT AT END IF MARRIED PERFORM 0800-WRITE-WEDDING-RECORD ADD 1 TO MARRIED-PEOPLE END-IF END-PERFORM. * DISPLAY NUMBER OF MARRIED PEOPLE DISPLAY ‘MARRIED PEOPLE:’ MARRIED-PEOPLE.

package com.bphx.ctu.samples.files;

import com.bphx.ctu.af.core.*;import com.bphx.ctu.af.io.*;import com.bphx.ctu.af.util.*;

public class Files {

private static final String PROGRAM_ID = “FILES”;

public ProfilesTO profilesTO = new ProfilesTO(); public ProfilesDAO profilesDAO = new ProfilesDAO(); public WeddingsTO weddingsTO = new WeddingsTO(); public WeddingsDAO weddingsDAO = new WeddingsDAO();

protected boolean endOfFile = false; protected int marriedPeople = 0;

public void run() { try { mainSubroutine(); } catch (ReturnException re) { // normal termination of the program } } private void mainSubroutine() { openFiles(); processFiles(); closeFiles(); }

private void openFiles() { // OPEN PROFILES FILE profilesDAO.open(OpenMode.READ, “Files”); // OPEN WEDDINGS FILE weddingsDAO.open(OpenMode.WRITE, “Files”); }

private void processFiles() { // READ RECORDS FROM PROFILES FILE AND // WRITE THEM INTO THE WEDDING FILE IF MARRIED while (!endOfFile) { profilesTO = profilesDAO.read(profilesTO); if (profilesDAO.getFileStatus().isEOF()) { endOfFile = true; } else { if (profilesTO.maritalStatus.isMarried()) { writeWeddingRecord(); marriedPeople = marriedPeople + 1; } } } // DISPLAY NUMBER OF MARRIED PEOPLE

Page 4: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 04 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

A First Look Into Application Conversion

Alternatively, the following example shows how a simple COBOL application with sequential file processing is translated to C#, using CTU.

0700-CLOSE-FILES. * CLOSE FILES CLOSE PROFILES. CLOSE WEDDINGS. GOBACK.

0800-WRITE-WEDDING-RECORD. MOVE CORRESPONDING PROFILES-RECORD TO WEDDINGS-RECORD. WRITE WEDDINGS-RECORD.

AfSystem.trace(“MARRIED PEOPLE:”, marriedPeople); }

private void closeFiles() { // CLOSE FILES profilesDAO.close(); weddingsDAO.close(); throw new ReturnException(); }

private void writeWeddingRecord() { weddingsTO.setName(profilesTO.getName()); weddingsTO.setSurname(profilesTO.getSurname()); weddingsDAO.write(weddingsTO); }}

IDENTIFICATION DIVISION. PROGRAM-ID. FILES. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL.

SELECT PROFILES ASSIGN TO PROFILEFILE. SELECT WEDDINGS ASSIGN TO WEDDINGFILE ORGANIZATION IS SEQUENTIAL. DATA DIVISION. FILE SECTION.

FD PROFILES. 01 PROFILES-RECORD. 05 NAME PIC X(20). 05 SURNAME PIC X(20). 05 MARITAL-STATUS PIC X(01) VALUE ‘M’. 88 MARRIED VALUE ‘M’. 88 SINGLE VALUE ‘S’.

FD WEDDINGS. 01 WEDDINGS-RECORD. 05 NAME PIC X(20). 05 SURNAME PIC X(20).

WORKING-STORAGE SECTION.

01 END-OF-FILE PIC X(01) VALUE ‘N’. 88 EOF-Y VALUE ‘Y’. 88 EOF-N VALUE ‘N’. 01 MARRIED-PEOPLE PIC 9(5) COMP VALUE ‘0’.

PROCEDURE DIVISION. 0100-OPEN-FILES. * OPEN PROFILES FILE OPEN INPUT PROFILES. * OPEN WEDDINGS FILE OPEN OUTPUT WEDDINGS.

0200-PROCESS-FILES. * READ RECORDS FROM PROFILES FILE AND * WRITE THEM INTO THE WEDDING FILE IF MARRIED PERFORM UNTIL EOF-Y READ PROFILES NEXT RECORD AT END SET EOF-Y TO TRUE NOT AT END IF MARRIED PERFORM 0800-WRITE-WEDDING-RECORD

namespace com.bphx.ctu.samples.files{

public class Files : AbstractProgramTemplate<FilesTO>{ private const string PROGRAM_ID = “FILES”;

public ProfilesTO profilesTO = new ProfilesTO(); public ProfilesDAO profilesDAO = new ProfilesDAO(new FileAccessStatus()); public WeddingsTO weddingsTO = new WeddingsTO(); public WeddingsDAO weddingsDAO = new WeddingsDAO(new FileAccessStatus());

protected bool endOfFile = false; protected int marriedPeople = 0;

protected override void EntryPoint() { mainSubroutine(); }

private void mainSubroutine() { openFiles(); processFiles(); closeFiles(); }

private void openFiles() { // OPEN PROFILES FILE profilesDAO.Open(OpenMode.READ, “Files”); // OPEN WEDDINGS FILE weddingsDAO.Open(OpenMode.WRITE, “Files”); }

private void processFiles() { // READ RECORDS FROM PROFILES FILE AND // WRITE THEM INTO THE WEDDING FILE IF MARRIED while (!getEndOfFile()) {

Page 5: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 05 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

A First Look Into Application Conversion ADD 1 TO MARRIED-PEOPLE END-IF END-PERFORM. * DISPLAY NUMBER OF MARRIED PEOPLE DISPLAY ‘MARRIED PEOPLE:’ MARRIED-PEOPLE.

0700-CLOSE-FILES. * CLOSE FILES CLOSE PROFILES. CLOSE WEDDINGS. GOBACK.

0800-WRITE-WEDDING-RECORD. MOVE CORRESPONDING PROFILES-RECORD TO WEDDINGS-RECORD. WRITE WEDDINGS-RECORD.

profilesTO = profilesDAO.Read(profilesTO); if (profilesDAO.GetFileStatus().IsEOF()) { setEndOfFile(true); } else { if (profilesTO.maritalStatus.isMarried()) { writeWeddingRecord(); setMarriedPeople(System.Math.Abs(1 + getMarriedPeople()) % 100000); } } } // DISPLAY NUMBER OF MARRIED PEOPLE DisplayUtil.Sysout.Write(“MARRIED PEOPLE:”, getMarriedPeopleAsString()); }

private void closeFiles() { // CLOSE FILES profilesDAO.Close(); weddingsDAO.Close(); throw new ReturnException(); }

private void writeWeddingRecord() { weddingsTO.setNameFormatted( profilesTO.getNameFormatted()); weddingsTO.setSurnameFormatted( profilesTO.getSurnameFormatted()); weddingsDAO.Write(weddingsTO); }}

The converted code is readable and maintainable, following Java and C# standards and object-oriented concepts. COBOLbusiness logic and comments are preserved so employees familiar with the original legacy application can easily understand the converted one. In this example, there is a separation between business logic (the program)and data layer (ProfilesDAO, ProfilesTO, WeddingsDAO, WeddingsTO). The sections highlight the architectureand the set of rules that act as the base of the CTU object-oriented conversion tool-set.

Page 6: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 06 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

The Modern Systems automated code/data migration process re-architects the code into three layers: A data access layer (files and database access), a presentation layer (screens), and a business logic layer (programs).

Object-Oriented Conversion

Online/CICS ArchitectureThe converted online/CICS application is a 3-tier application, running in an application server with a thin client in the browser.

Converted online/CICS application architecture

Each COBOL program will be converted to a Java or C# class providing the same functionality exposed as a service (Business Logic Layer). All database/files operations from the original COBOL code are externalized in DAO classes and Hibernate (NHibernate for C#) mapping files (the Data Access Layer). The Presentation Layer is generated by converting the legacy screens to JSF (Java Server Faces) or ASP .NET MVC for Java or C# respectively.

These layers can be implemented via a number of products in order to provide the robustness necessary for high volume, high uptime applications. The typical overall high level architecture is depicted in the following diagram:

Typical High-Level Architecture

Page 7: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 07 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Batch ArchitectureThe converted batch application doesn’t depend on any Java EE or web APIs, so it can run as a normal Java or C# application. For running batch applications, BatchSession implementation is used, keeping information regarding the JCL context.

Basic MappingCTU includes a very strong mapping capability that produces an object oriented model that best represents the procedural code. This mapping component produces code that can be further refined and refactored after the migration project to better fit into the overall framework and architecture of the system.

The table below illustrates the basic mapping from the procedural COBOL language to its object oriented counterparts.

Procedural COBOL Java C#Legacy program Java class C# classParagraph within program Java method C# methodFile Data Access Object (DAO) Data Access Object (DAO)File record definition Data Transfer Object (DTO) Data Transfer Object (DTO)

TableData Access Object (DAO)Data Transfer Object (DTO)

Data Access Object (DAO)Data Transfer Object (DTO)

Copybook Data classes Data classes

Working storageData fields: Class propertiesGroup items: Class methodsRedefines: Java classes

Data fields: Class propertiesGroup items: Class methodsRedefines: C# classes

Map Web client (JSF, beans) Web client (ASP .NET MVC)

In some cases specific COBOL statements can be translated to different Java/C# objects (e.g., Table to DAO / DTO). CTU includes the logic that enables it to make the right decision and use the most relevant object which best fits the overall architecture of the full system, taking performance and maintainability into account.

Data Access LayerThe logic used to handle files and databases is decoupled from the main COBOL structure. To do this, the DAO/DTO design pattern has been implemented. Data Access Objects (DAO) assure the communication with external data (files & databases) and Data Transfer Objects (DTO) are used to transport data between main COBOL program and external data. DAO only uses DTO to communicate with the program.

Page 8: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

Using this approach, the resulting code is much easier to understand and offers many simple ways to maintain the code. Moreover, DAO and DTO code can be reused for future enhancements made in Java or C#. Modifications can be made to the DAO implementation without altering other decoupled modules of the application.

page - 08 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Database AccessFor database access, Hibernate or NHibernate is used in Java or C# conversions respectively, to facilitate use of multiple database engines. All used queries in a DAO are externalized to a Hibernate/NHibernate XML mapping file, further simplifying maintenance of the queries. Database DTOs are created from the SQL table definitions.

CTU also handles cloning at the SQL level. This means that if the COBOL code contains more than one SQL statement of the same type, even in different programs, only one Java/C# method will be created inside the specific DAO. The DTOs contain all the information related to a specific SQL table and the DAOs represent a collection of different SQL statements used in COBOL project related to that SQL table.

Code SamplesFor the DDL below, CTU creates four artifacts: DAO, TO, Hibernate or NHibernate class definition, and Hibernate or NHibernate queries.

Java C#

DTO Class: Transfer object used in all operations with ACCOUNT DAO.

CREATE TABLE ACCOUNT (ACCOUNT_ID VARCHAR(8) NOT NULL ,SURNAME VARCHAR(20) ,FIRST_NAME VARCHAR(20) ,SOLD INTEGER ,CURRENCY_SIGN VARCHAR(3) ,ACCOUNT_LEVEL CHAR(1) ,BONUS_INTERVAL SMALLINT ,PRIMARY KEY (ACCOUNT_ID) )

public class AccountTO {

private DbAccessStatus dbAccessStatus; private AccountID sql_id = new AccountID(); private int sold; private char accountLevel; private short bonusInterval; private AfVarchar surname = new AfVarchar(20); private AfVarchar firstName = new AfVarchar(20); private AfVarchar currencySign = new AfVarchar(3);

... //getters and setters}

public class AccountTO{ private DbAccessStatus dbAccessStatus; private AccountID sql_id = new AccountID(); private int _sold; private char _accountLevel; private short _bonusInterval; private AfVarchar _surname = new AfVarchar(20); private AfVarchar _firstName = new AfVarchar(20); private AfVarchar _currencySign = new AfVarchar(3);

// properties ...}

Page 9: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

DAO Class: Contains all of the operations with the ACCOUNT table, from all programs.

page - 09 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Java

C#

public class AccountDAO {

public ScrollableResults sqldemoAcctCur; public AfSqlca sqlca = new AfSqlca();

public static DbAccessStatus deleteByAccountIdRec(AfVarchar accountId) { ... } public DbAccessStatus openSqldemoAcctCur() { ... } public AccountTO fetchSqldemoAcctCur(AccountTO accountTO) { ... }

public static DbAccessStatus updateByAccountIdRec(short bonusInterval, AfVarchar accountId) { AfSqlca sqlca = new AfSqlca(); DbAccessStatus dbStatus = new DbAccessStatus(); try { Session session = HibernateSessionFactory.current().getSession(“Account”); Query query = session.getNamedQuery(“com.bphx.ctu.samples.sqldemo.data.dao.AccountDAO.updateByAccountIdRec”); query.setShort(“bonusInterval”, bonusInterval); query.setParameter(“accountId”, accountId.clone(), HibernateTypes.VarCharType()); if (sqlca.collectAffectedRecords(query.executeUpdate()) == 0) { sqlca.collect(AfSqlError.NO_UPDATED_RECORDS); } dbStatus.setSqlCa(sqlca); } catch (HibernateException ex) { dbStatus.collect(ex); } return dbStatus; }}

public class AccountDAO{ public ISqlCa sqlCa; public IList sqldemoAcctCurList; public ICursorEnumerator sqldemoAcctCur;

public virtual DbAccessStatus deleteByAccountIdRec(AfVarchar accountId) {...} public virtual AccountTO fetchSqldemoAcctCur(AccountTO accountTO) {...} public virtual DbAccessStatus openSqldemoAcctCur(){...}

public virtual DbAccessStatus updateByAccountIdRec(short bonusInterval, AfVarchar accountId) { DbAccessStatus dbStatus = new DbAccessStatus(); try { dbStatus.Clear(); ISession session = HibernateSessionFactory.Current().GetSession(“Account”); IQuery query = session.GetNamedQuery( “com.bphx.ctu.samples.sqldemo.data.dao.AccountDAO.updateByAccountIdRec”); query.SetInt16(“bonusInterval”, bonusInterval); query.SetParameter(“accountId”, accountId, HibernateTypes.VarCharType()); if (dbStatus.CollectAffectedRecords(query.ExecuteUpdate()) == 0) { dbStatus.Collect(AfSqlError.NO_UPDATED_RECORDS); } } catch (HibernateException ex) { dbStatus.Collect(ex); } return dbStatus; }}

Page 10: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

Hibernate (Java) class definition for ACCOUNT table.

page - 10 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

NHibernate (C#) class definition for ACCOUNT table.

<hibernate-mapping> <class name=”com.bphx.ctu.samples.sqldemo.data.to.AccountTO” table=”ACCOUNT”> <composite-id name=”sql_id” class=”com.bphx.ctu.samples.sqldemo.data.id.AccountID”> <key-property name=”accountId” type=”com.bphx.ctu.af.lang.hibernate.types.AfVarcharType”> <column name=”ACCOUNT_ID” length=”8”/> </key-property> </composite-id> <property name=”sold” type=”integer” not-null=”false”> <column name=”SOLD”/> </property> <property name=”accountLevel” type=”character” not-null=”false”> <column name=”ACCOUNT_LEVEL”/> </property> <property name=”bonusInterval” type=”short” not-null=”false”> <column name=”BONUS_INTERVAL”/> </property> <property name=”surname” type=”com.bphx.ctu.af.lang.hibernate.types.AfVarcharType” not-null=”false”> <column name=”SURNAME” length=”20”/> </property> <property name=”firstName” type=”com.bphx.ctu.af.lang.hibernate.types.AfVarcharType” not-null=”false”> <column name=”FIRST_NAME” length=”20”/> </property> <property name=”currencySign” type=”com.bphx.ctu.af.lang.hibernate.types.AfVarcharType” not-null=”false”> <column name=”CURRENCY_SIGN” length=”3”/> </property> </class></hibernate-mapping>

<hibernate-mapping> <class name=”com.bphx.ctu.samples.sqldemo.data.to.AccountTO” table=”ACCOUNT”> <composite-id name=”sql_id” class=”com.bphx.ctu.samples.sqldemo.data.id.AccountID”> <key-property name=”accountId” type=”Com.Bphx.Ctu.Af.Lang.Hibernate.Types.AfVarcharType”> <column name=”ACCOUNT_ID” length=”8”/> </key-property> </composite-id> <property name=”sold” type=”int” not-null=”false”> <column name=”SOLD”/> </property> <property name=”accountLevel” type=”char” not-null=”false”> <column name=”ACCOUNT_LEVEL”/> </property> <property name=”bonusInterval” type=”short” not-null=”false”> <column name=”BONUS_INTERVAL”/> </property> <property name=”surname” type=”Com.Bphx.Ctu.Af.Lang.Hibernate.Types.AfVarcharType” not-null=”false”> <column name=”SURNAME” length=”20”/> </property> <property name=”firstName” type=”Com.Bphx.Ctu.Af.Lang.Hibernate.Types.AfVarcharType” not-null=”false”> <column name=”FIRST_NAME” length=”20”/> </property> <property name=”currencySign” type=”Com.Bphx.Ctu.Af.Lang.Hibernate.Types.AfVarcharType” not-null=”false”> <column name=”CURRENCY_SIGN” length=”3”/> </property> </class> </hibernate-mapping>

Page 11: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

Hibernate queries (Java): All queries for ACCOUNT table will be in an Hibernate mappings file.

page - 11 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

NHibernate queries (C#): All queries for ACCOUNT table will be in an NHibernate mappings file.

Sequential File AccessFor sequential file access, DAOs use framework calls to IFileOperations in order to emulate the COBOL operations. File DTOs are created from COBOL variables associated with each file descriptor. The same cloning approach for databases is used for the file access layer.

VSAM File AccessVSAM files are migrated to a relational database model. During the conversion process, all VSAM records are analyzed and the best layout is chosen. For each layout chosen, a DDL is generated. VSAM DAOs use Hibernate or NHibernate (for Java or C# respectively) object relation mapping. The queries from .hbm.xml files are written in HQL language, further abstracting the database usage.

<hibernate-mapping> <sql-query name=”com.bphx.ctu.samples.sqldemo.data.dao.AccountDAO.updateByAccountIdRec”> <query-param name=”bonusInterval” type=”short”/> <query-param name=”accountId” type=”com.bphx.ctu.af.lang.hibernate.types.AfVarcharType”/> <synchronize table=”ACCOUNT”/>update ACCOUNT set BONUS_INTERVAL = :bonusInterval where ACCOUNT_ID = :accountId </sql-query></hibernate-mapping>

<hibernate-mapping> <sql-query name=”com.bphx.ctu.samples.sqldemo.data.dao.AccountDAO.updateByAccountIdRec”> <query-param name=”bonusInterval” type=”short”/> <query-param name=”accountId” type=””/> <synchronize table=”ACCOUNT”/> update ACCOUNT set BONUS_INTERVAL = :bonusInterval where ACCOUNT_ID = :accountId </sql-query></hibernate-mapping>

Page 12: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 12 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedBusiness Logic LayerEach COBOL program is converted to a Java or C# class. This class encapsulates all working storage fields and paragraphs, exposed as a service to the main entry point in the EntryPoint() for C#, or run() for Java, method.

Data RemodelingCOBOL data structures are analyzed and remodeled to meet the following requirements:

• Encapsulation• Reuse• Readability• Small memory footprint for better utilization

Deeply nested COBOL records are collapsed to the primary data fields. The information will be kept only in those fields. When a group access is needed, a method will be generated that knows how to read and write information from the primary data fields. Decision to generate a class or collapse fields in the parent are customizable (by using different thresholds, such as number of records in a program, number of copybook uses, etc).

public class CobolProgram{ protected workingStorageField1; protected workingStorageField2; ...

public static CobolProgram getInstance() {...}

public CobolProgramTO run(ExecContext execContext, CobolProgramTO cobolProgramTO){...}

public void run(ExecContext execContext, byte[] dynamic_dfhcommarea){...}

public void run(){ //call first paragraph }

private void paragraph1() {...} private void paragraph2() {...}}

Java

public class CobolProgram : AbstractProgramTemplate<CobolProgramTO>{ private const string PROGRAM_ID = “COBOLPROGRAM”;

protected workingStorageField1; protected workingStorageField2; ...

public static CobolProgram getInstance() {...}

protected override void EntryPoint() { //Entry point: Call first paragraph }

private void paragraph1() {...} private void paragraph2() {...}}

C#

01 PERSONAL-INFO. 05 ACCT-INFO. 10 ACCT-TYPE PIC X. 10 NAME PIC X(30). 10 AGE PIC 9(3). 10 ACCT-ID PIC 9(7). 10 AMOUNT PIC 9(12). 05 ADDITIONAL-INFO. 10 TRAVEL-EXPENSES PIC 9(6)V9(2). 10 SHOE-SIZE PIC 9(2) COMP-3. 10 SHIRT-SIZE PIC 9(2) COMP-2.

Page 13: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 13 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Java C#

Each copybook that contains only data will be translated to a class. Copybook classes are reused as a single class across the entire application.

When a new COBOL program is developed and uses same data structures as an old program, the developer has two ways to use the existing information related to data information. The most clever way is to move the data definition from the old COBOL program into a copybook and use the copybook in both the old and new COBOL programs. But most COBOL developers just copy the COBOL data definitions in the new program. Complex COBOL systems can have a lot of these kinds of structures defined again and again. CTU can easily handle both situations and detects data clones. Only one Java or C# class is created and is used in all places.

public class PersonalInfo { protected char acctType; protected String name; protected short age; protected int acctId; protected long amount; protected AfDecimal travelExpenses; protected AfDecimal shoeSize; protected double shirtSize;

//Group methods public void setAcctInfo(char[] buf) { //marshalls from char[] to fields ... } public char[] getAcctInfo() { //marshalls from fields to char[] ... }}

public class PersonalInfo{ public AcctInfo acctInfo = new AcctInfo(); public AdditionalInfo additionalInfo = new AdditionalInfo();}

public class AcctInfo{ protected char acctType = DefaultValues.CHAR_VAL; protected string name = DefaultValues.StringVal(30); protected short age = DefaultValues.SHORT_VAL; protected int acctId = DefaultValues.INT_VAL; protected long amount = DefaultValues.LONG_VAL;

//Group methods public virtual void setAcctInfo(char[] buffer) { //marshalls from char[] to fields } public virtual char[] getAcctInfo() { //marshalls from fields to char[] } ...}

public class AdditionalInfo{ protected AfDecimal travelExpenses = new AfDecimal(DefaultValues.DEC_VAL, 8, 2); protected AfDecimal shoeSize = new AfDecimal(DefaultValues.DEC_VAL, 2, 0); protected double shirtSize = DefaultValues.DOUBLE_VAL; ...}

Page 14: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 14 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedAll data types are translated to native Java or C# types. The only exception is the decimal type, which is translated to the CTU Framework class AfDecimal in order to keep the maintainability of the resulting code and to assure the same functionality of the converted application. The table below shows how some COBOL data types are translated into Java and C#.

COBOL Java C#PIC 9 - PIC 9(4) short short

PIC 9(5) - PIC 9(9) int int

PIC 9(10) - PIC 9(18) long long

PIC X char char

PIC X(2) - PIC X(n) java.lang.String string

Numeric-edited java.lang.String string

Alpha-numeric-edited java.lang.String string

9(n)V9(m) com.bphx.ctu.af.lang.types.AfDecimal Com.Bphx.Ctu.Af.Lang.Types.AfDecimal

USAGE IS PACKED-DECIMAL com.bphx.ctu.af.lang.types.AfDecimal Com.Bphx.Ctu.Af.Lang.Types.AfDecimal

USAGE IS COMP-2 double double

USAGE IS INDEX int int

Handling 88 Level - An example of in-depth, smart translationDepending on the usage of an elementary COBOL item which contains 88 level conditions the structures can have dual translation. They can be converted to Java boolean, C# bool, or to classes.

COBOL Java C#GRP88 PIC X(1) VALUE ‘Y’. 88 Y VALUE ‘Y’. 88 N VALUE ‘N’.

boolean/class (depending on usage)

bool/class (depending on usage)

GRP88 PIC 9(2). 88 PERSONAL VALUE 5 THRU 55. 88 COMMERCIAL VALUE 99. 88 OTHER-DATA VALUE 100.

class (enumeration) class (enumeration)

88 levels with multiple values are translated to an enum like class.

88 levels with only two values that can signify a boolean or bool (Java or C# respectively) value (Y/N, YES/NO, TRUE/FALSE) are translated to boolean/bool (Java/C#) or to an enum like class, depending on the usage in the programs. The legacy application is static analyzed and if and only if the usage of that field contains only approved values, then a Java native boolean type or C# native bool type is used for translation.

Page 15: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 15 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Program normalization is a code transformation that replaces GOTOs and fall through behavior with a statically determined code. The transformation involves minimal changes to the program structure without changing the original conditional statements or constructs. A simple processing loop is shown below.

Program Normalization

Code RestructuringEach paragraph becomes a private method in the Java or C# program class. Program structure and comments are preserved during conversion, simplifying maintenance. During code restructuring, program flow is normalized and GOTOs are removed.

For each “perform range” (e.g. statements like PERFORM READ-INPUT THRU PROCESSING-END), a method (rngReadInput) is built that will subsequently call the paragraphs that are in the flow order within the original program. This way, the fall through is ensured. Other perform statements will use the same method (if the context doesn’t have a different set of perform mines). Below is the Java equivalent.

P1. PERFORM READ-INPUT THRU PROCESSING-END UNTIL EOF. STOP RUN.

READ-INPUT. DISPLAY “READING INPUT FILE”* AT END, SKIP PROCESSING IF EOF GO TO PROCESSING-END.

PROCESSING.* DO SOME PROCESSING DISPLAY “PROCESSING”.

PROCESSING-END. DISPLAY “DONE”.

private void rngReadInput() { String retcode; retcode = readInput(); if (!retcode.equals(“PROCESSING-END”) ){ processing(); } processingEnd(); }

Page 16: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 16 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

The GOTO statements within the paragraphs are transformed to return “paragraph_name” (return “PROCESSING-END”), and the flow is rebuilt within the calling “perform range” method.

The implicit GOTOs within the subroutine are normalized by building helper control structures:

• While loops for forward or before in the program order GOTOs • If guarded blocks for backward or after in the program order GOTOs

Alternatively, the C# equivalent is shown below.

Java C#

In order to simplify the resulting code, the EXIT type paragraphs and the dead code paragraphs (both flow and data) are removed.

private void p1() { while (!getEndOfFile()) { rngReadInput(); } throw new TerminateException(Session.GetReturnCode()); }

private void rngReadInput() { string retcode = “”; retcode = readInput(); if (retcode != “PROCESSING-END”) { processing(); } processingEnd(); }

private void processing() {...} private void processingEnd() {...}

private String readInput() { ABOSystem.trace(“READING INPUT FILE”); if (eof) { return “PROCESSING-END”; } return “”; }

private string readInput() { DisplayUtil.Sysout.Write(“READING INPUT FILE”); // AT END, SKIP PROCESSING if (getEndOfFile()) { return “PROCESSING-END”; } return “”; }

Page 17: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 17 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Presentation LayerTo maintain the same user experience, BMS Maps are migrated to equivalent web pages. In all cases, the actual data fed into each new web interface is the same as the data fed into the legacy screen. When the application screen display logic is executed, the resulting HTML web page produced mimics the original 3270 screen as closely as possible. The actual screen layouts are maintained, so users of the application do not have to be retrained.

Legacy screens are automatically converted to standard JSF or ASP.NET MVC (for Java and C# conversions respectively) in order to be easily deployable in any application server. Each screen is converted into the following Java components:

• JSF Facelet (XHTML UI representation) – the View• Managed Data Bean – the Model (data fields/attributes)• Static text labels property file (separating content from layout)

Alternatively, in a C# conversion, screens are represented with the following components:

• Razor view (cshtml UI representation) - the View• Data class – the Model (data fields/attributes)• Static text labels resx file (separating content from layout)

As stated, the converted code will preserve the look and feel of the original application. In order to achieve this, the generated JSF or ASP .NET defaults to using fixed-width fonts and absolute positioning of fields and text labels. The web application will have the same navigational features as the original one.

PF Keys and return keys are fully supported using JavaScript event handling routines. In addition, navigation using arrow keys is also supported allowing the user to move from one field to another just like in the original application (without involving mouse operations or TAB key – although these are available as well).

Further, the authentication module is customizable. An API is provided to the end user that can be further customized to accommodate all authentication requirements. A basic authentication mechanism is provided out of the box.

Page 18: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 18 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Presentation Layer

While the migrated screens retain the same screen layout and functionality, the resulting generated web pages can be structured to take advantage of the more graphic web interface.

Before Conversion

After Conversion

Page 19: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 19 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedEverything is based on CSS3 and can be customized by the end user. Out of the box, the application has a settings menu, PFKEYS toolbar (on/off) and theme support (light and dark).

FrameworkThe migrated Java or C# also references an included framework library. Modern Systems provides a framework library to handle common functions that existed in the COBOL language, but are not be directly translatable to the Java language. The framework has two components: The core and translation layers.

Core LayerThe core layer contains core concepts and utilities.

• Logging: CTU uses the SL4J logging library for Java conversions and the Log4Net logging library for C# conversions, to log all framework events. The amount of information recorded is configurable in both cases.• Configuration settings for: marshaling, locations, implementations (TSQueue, TDQueue, display), database, Hibernate (Java conversions) or NHibernate (C# conversions)• Hibernate (Java) or NHibernate (C#) and SQL: helpers for database access• Data types: support data types that don’t have equivalent in Java or C# (e.g.: com.bphx.ctu.af.lang.types.AfDecimal)• Marshalling: reads and writes byte/char buffers from CTU data types. Support for EBCDIC to ASCII encoding. Support for COBOL REDEFINES• Sessions: com.bphx.ctu.af.core.Session is a core concept of the CTU framework that retains the context of the application on a per thread basis. Most notable specializations are: com.bphx.ctu. af.batch.BatchSession for batch/JCL running, and com.bphx.ctu.af.tp.TpSession for CICS transaction processing

Translation LayerThe translation layer contains emulation of COBOL functionalities.

• COBOL: translation of COBOL functions to equivalent Java or C# functions• Batch: support for JCL, batch session and DD Cards• Files: support for sequential files• VSAM: support for VSAM to relational database runtime• Transaction Processing: support CICS command API, TDQueues, TSQueues, TP session• Screens: JSF (for Java) and ASP.NET MVC (for C#) implementation of screens

The framework can be licensed in source code format and can be further customized and extended by the client as required.

Page 20: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 20 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedJCL ConversionThe aim of the universal JCL migrator (a part of CTU) is to translate JCL to Java or C#, within the following guidelines:

• A one-to-one translation• Each JOB is translated to a Java or C# class • Each PROC is translated to a Java or C# class• The scripts logic is preserved• Resulting code is readable and maintainable

Each job class inherits from JobScript and each Proc from ProcScript. These classes constitute a runtime framework. Each step becomes an inner class implementation of one of the following:

• ProgStep• ClassStep • ProcStep• Each DD card becomes a call to a setDDCard method or a dataCard method.

• Among other features, setDDCard provided support for GDG files, &&temp files, DUMMY, SYSOUT= files • Record length and record format is supported, by the use of meta-data files that contain record formatting information. These are created for DISP=NEW files and created/copied/deleted as required by the runtime framework and utility replacement classes.

Basic Mapping

A typical Mainframe DSNAME has the format aa.bb.cc.dd, while a typical Unix filename has the format /aa/bb/cc.dd and a windows filename has the format C:\aa\bb\cc.dd. It is not always practical to simply change all periods to slashes, nor is it always practical to change all filenames in exactly the same manner. To solve this issue, the universal JCL migrator supports a powerful rule based filename translation algorithm, allowing different filename translation rules to be applied to different DSN patterns. In the code sample following this section, the DSNs AA.AA and BB.BB have been modified by a rule that adds the ../files/ prefix.

Filename Translation

The universal JCL migrator uses Program name translation rules that can modify the name of a called program, and determine if it is called by a ClassStep or a ProgStep. In the code sample below, a call to the program IKJEFT01 is translated to a call to DbaseRunner (Modern Systems’ equivalent of IKJEFT01) and it is called via a ClassStep.

Program Name Translation

The universal JCL runtime framework is supplemented by implementations of the most common IBM and 3rd party utilities such as IKJEFT01, IDCAMS, IEBGENR, IEBCOPY, etc. In addition, the JCL migrator has its own SORT utility that can be used as a substitute of the Mainframe SORT utility to great effect.

Utilities

Page 21: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 21 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

Original input code://SAMPLE JOB i(ACT),’BPHX’//*//OPEN EXEC PGM=ECHO,PARM=’SAMPLE CODE’//SYSOUT DD SYSOUT=’*’//CALLP EXEC P1,ABC=DEF//DD1 DD DSN=AA.AA,DISP=NEW,DCB=(LRECL=80,RECFM=FB)//DD2 DD *Example of multi-lineinline data//IKJEFT EXEC PGM=IKJEFT01

//SYSTSIN DD DSN=BB.BB,DISP=OLD

import com.bphx.runtime.jcl.*;

public class SAMPLE extends JobScript { public void runProcess() { OPEN.run(); CALLP.run(); IKJEFT.run(); }

public ProgStep OPEN = new ProgStep(this, “OPEN”) { public void defineStep() { setDDCard(“name=SYSOUT”, “sysout=’*’”); perform(“ECHO”, “SAMPLE CODE”); } };

public ProcStep CALLP = new ProcStep(this, “CALLP”) { public void defineStep() { setDDCard(“name=DD1”, “dsn=../files/AA.AA”, “dispStatus=NEW”, “lrecl=80”, “recfm=FB”); dataCard(“DD2”, “Example of multi-line”, “inline data”); perform(“P1”, “ABC=DEF”); } };

public ClassStep IKJEFT = new ClassStep(this, “IKJEFT”) { public void defineStep() { setDDCard(“name=SYSTSIN”, “dsn=../files/BB.BB”, “dispStatus=OLD”); perform(“DBaseRunner”); } };}

Java Equivalent

JCL Code Conversion Sample

Page 22: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 22 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continued

using System;using com.bphx.runtime.jcl;

public class SAMPLE : JobScript{

public override void Initialize() { }

public SAMPLE() { this.OPEN = new OPEN_Step(this, “OPEN”); this.CALLP = new CALLP_Step(this, “CALLP”); this.IKJEFT = new IKJEFT_Step(this, “IKJEFT”); }

public override void RunProcess() { OPEN.Run(); CALLP.Run(); IKJEFT.Run(); }

public ProgStep OPEN; public sealed class OPEN_Step : ProgStep { public OPEN_Step(Script inParent, String name) : base(inParent, name) { }

public override void DefineStep() { SetDDCard(“name=SYSOUT”, “sysout=’*’”); Perform(“ECHO”, “SAMPLE CODE”); } };

public ProcStep CALLP; public sealed class CALLP_Step : ProcStep { public CALLP_Step(Script inParent, String name) : base(inParent, name) { }

public override void DefineStep() { SetDDCard(“name=DD1”, “dsn=../files/AA.AA”, “dispStatus=NEW”, “lrecl=80”, “recfm=FB”); DataCard(“DD2”, “Example of multi-line”, “inline data”); Perform(“P1”, “ABC=DEF”); } };

public ProgStep IKJEFT; public sealed class IKJEFT_Step : ProgStep { public IKJEFT_Step(Script inParent, String name) : base(inParent, name) { }

public override void DefineStep() { SetDDCard(“name=SYSTSIN”, “dsn=../files/BB.BB”, “dispStatus=OLD”); Perform(“IKJEFT01”); } };}

C# Equivalent

JCL Code Conversion Sample

Page 23: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 23 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedMaintenance

It is very easy for an employee familiar with the old legacy application to understand the new translated application source. Readability and maintainability is one of the main capabilities of Modern Systems’ Java and C# conversions. The following apply for the migration:

• The general structure of the program is preserved and you can easily navigate from legacy code to new code• Original comments are preserved throughout the translated code• All code is formatted following Java or C# coding conventions• The code is object oriented (examples):

• Three separated layers: Data, Business, and Presentation• Use of Java or C# primitive types (int, char, string) enhances the readability of the code• Data records are translated to a class. Similar data records are detected, and only one class is generated for them• Copybook classes are reused as a single class throughout the application when possible• Transfer between programs is done using the TransferObject pattern• Data access layer is separated. For each program, SQL statements are moved to separate classes, Data Access Object. Programs are moving data to DAOs using a TransferObject• Program flow is normalized• Each COBOL program is a service, exposing only main entry point. All working storage fields are private to the program class• Design of the statement translation leverages, as much as possible, from the existing J2SE or .NET library for Java or C# conversions respectively

• The application is modular• Each application module is translated to a Java package or C# namespace • Each module package has a standard structure

Understanding

The CTU modernization approach focuses on a pragmatic layering of the original application and full code conversion, rather than totally re-architecting the application. This pragmatic approach has two major benefits:

• Risk-free, fast migration to a new platform and higher ROI• The CTU solution completely automates the migration process which translates into equivalent functionality (including transfer of existing bugs, if there are any in the old code) to the current system

Existing inventory components will be processed through the CTU migration toolset, converted to operate in the new technology environment and then thoroughly tested to prove “functional equivalency.” This means that although the application now operates in newer, proven technologies, it produces equivalent results when presented with equal inputs in terms of data, control inputs, and user keystrokes.

Re-Architecture

Page 24: WHITEPAPER - Modern Systems€¦ · CTU (COBOL-to-Universal), is a Refactoring tool utilizing Automated Conversion that transforms a Mainframe-based legacy ... including databases

page - 24 | WHITEPAPER: REFACTOR WITH AUTOMATED CONVERSION

Object-Oriented Conversion, continuedMaintenance

After conversion, the resulting application is already maintainable and object oriented, separated in three layers: Data, Business and Presentation:

• Data layer is separated• Data structures are true Java or C# structures, using native Java or C# types• Each COBOL program is a service• Each map is a JSF (Java) or ASP.NET (C#) screen• Each application module is a package (Java) or namespace (C#)

The resulting application has a working set of tests verifying the original functionality. In addition, further re-architecting of the application from this point is possible using standard Java or C# re-factory tools. This process will benefit from the tests already done, significantly lowering re-architecting risks.

Re-Architecture, continued


Recommended