2

I'm quite new to work with IBM i (an AS/400). I want to write a DDM File via JTOpen/JT400. Here is what I already did, with the use of the classes inside the jtopenlite.jar from here http://sourceforge.net/projects/jt400/files/JTOpen-full/8.5/

    final String HELLO_WORLD = "Hello World";
    final String LOREM_IPSUM_DOLORES = "lorem ipsum dolores"; 
    String library = "KEKRU1";
    String file = "QRPGLESRC3";
    String member = "DDSTEST2";

    DDMConnection connection = DDMConnection.getConnection("myas400.de", "username", "password");
    DDMRecordFormat recordFormat = connection.getRecordFormat(library, file);

    DDMWriteCallback writeCallback = new DDMWriteCallback() {

        @Override
        public int getRecordDataOffset(DDMCallbackEvent event, int recordIndex) {
            System.out.println(recordIndex);
            return 0;
        }

        @Override
        public byte[] getRecordData(DDMCallbackEvent event, int recordIndex) {              
            byte[] result = new byte[120]; //120 is the ddmfile.getRecordLength()

            byte[] src;
            if (recordIndex == 0){
                src = HELLO_WORLD.getBytes();
            }else{
                src = LOREM_IPSUM_DOLORES.getBytes();
            }

            System.arraycopy(src, 0, result, 0, src.length); //Copy from src to result
            return result;
        }

        @Override
        public int getNumberOfRecords(DDMCallbackEvent event) {
            return 2;
        }

        @Override
        public boolean[] getNullFieldValues(DDMCallbackEvent event, int recordIndex) {
            return null;
        }
    };

    DDMFile ddmFile = connection.open(library, file, member, recordFormat.getName(), DDMFile.WRITE_ONLY, false, 10, 1);

    System.out.println(ddmFile.getRecordLength()); //prints 120     

    connection.write(ddmFile, writeCallback);
    connection.close();

The program enters connection.write(ddmFile, writeCallback); writes the data do the stream and finally arrives at handleReply(file, "ddmS38PUTM", null); (inside connection.write) where it waits for an answer of the Server.

Here's the write method from the Library

public class DDMConnection extends HostServerConnection
{
...

 public void write(DDMFile file, DDMWriteCallback listener) throws IOException
  {
final DDMCallbackEvent event = file.getEventBuffer();
event.setEventType(DDMCallbackEvent.EVENT_WRITE);

int blockingFactor = file.getBatchSize();
int numRecords = listener.getNumberOfRecords(event);
int startingRecordNumber = 0;
int batchSize = numRecords > blockingFactor ? blockingFactor : numRecords;
int id = newCorrelationID();
while (startingRecordNumber < numRecords)
{
  if (startingRecordNumber+batchSize >= numRecords) batchSize = numRecords-startingRecordNumber;
  sendS38PUTMRequest(out_, file.getDCLNAM(), id);
  sendS38BUFRequest(file, out_, id, file.getRecordIncrement(), listener, file.getRecordLength(), startingRecordNumber, batchSize);
  out_.flush();

  handleReply(file, "ddmS38PUTM", null); //here the program waits for the server
  startingRecordNumber += batchSize;
}
}
}

But the Server does not send anything.

Do you have suggestions or another way to write DDM Files?

mike
  • 1,233
  • 1
  • 15
  • 36
KeKru
  • 444
  • 3
  • 13
  • It looks like you mean DDM instead of DDS. I don't see handleReply() in your code, and the 8.5 documentation for DDMConnection.write() doesn't mention a reply. Why do you expect a reply from the server at all? The remote file is write-only. – Buck Calabro Jul 09 '15 at 16:32
  • 1
    Are you writing this application for a mobile app? If not, then I would suggest using the full JT400.jar instead of jtopenlite.jar. Also, why use a DDM connection? It's much more straight forward to use a JDBC connection. – David G Jul 09 '15 at 16:38
  • @BuckCalabro I just added the codelines from DDMConnection.write(...) there is the handleReply(...). Yes I mean DDS, but as I understood DDS is part of DDM, isn't it? – KeKru Jul 09 '15 at 16:56
  • @david no I don't write a mobile app, but I thought the classes sound quite good for my purpose :D Is it possible to work with DDS via JDBC? JDBC works well for the internal DB400, but will it work with DDS? – KeKru Jul 09 '15 at 16:59
  • 1
    DDS is Data Description Language. It is source code used by commands like CRTPF, CRTDSPF, CRTLF and so on. DDM is a client/server mechanism that allows mostly transparent file access between several IBM i systems. Using RPG as an example, on SYSTEMA, CRTDDMF THISLIB/THISDDMF which points to MYLIB/MYFILE at SYSTEMB. RPG does an OPEN to this DDM file, and the operating system connects to SYSTEMB instead and does all of the file I/O on SYSTEMB, even though the RPG program is running on SYSTEMA. – Buck Calabro Jul 09 '15 at 17:05
  • I thin you need to clarify your terms. You code shows you're trying to access DDM files ... but you keep referencing the term DDS. As @BuckCalabro said, DDS is a data definition language (like SQL DDL). DDM is a remote database access protocol. – David G Jul 09 '15 at 17:19
  • What does happen? A timeout? Nothing? Does the DDM file get updated without a reply from the system? If not, is the DDM server job started? (I expect it is, if getRecordFormat() returns valid info.) – user2338816 Jul 09 '15 at 20:17
  • ah okay, now I understand, thanks for your explanations :) – KeKru Jul 10 '15 at 08:43
  • @user2338816 After more than 10 minutes my java program still waits for the server, no timeout. The DDM file is not written, but it is locked while my program runs (I can not edit it on my 5250 terminal). There is no job running wich has my username as user, so I think it is not started. But I'll try the JDBC way to make it run – KeKru Jul 10 '15 at 08:57
  • The *DDM server job wouldn't be under your (job)user name. though you would show as 'current' user. The jobs are QRWTSRVR and QRWTLSTN. Their joblogs, job locks, etc., will be where you should look while you run your process. Though JDBC is more common for data access, it sounds like DDM might be appropriate for what you're working with, members in a source file. – user2338816 Jul 10 '15 at 14:13
  • ok, I'm still a little confused :D I created an Alias: "CREATE ALIAS kekru1/ddstestxy FOR kekru1/qrpglesrc3(ddstest)", Select * kekru1.ddstesxy works and I get these Rows: {1.00,150706, "A R #SALESREP "}, {2.00,150706,"A SALESR2 2P 0"}, {...} and so on. That is DDS, isn't it? And it tells me, that my data records have a field "SALESR2" and so on. I already created the "ddstest" and run "compile". Now I need to create the correspondent DDM file, which holds the data records, right? How do I create the DDM and link it to the DDS? So that I can do INSERT INTO myddm(SALESR2)VALUES('Hello') – KeKru Jul 10 '15 at 16:04
  • CREATE ALIAS actually creates a DDM *file object. I had not realized that you were using the term "DDS file" to mean a source member *containing* DDS specs, as opposed to a file *created* from DDS specs. (By conventional practice, DDS specs are typically stored in QDDSSRC. RPGLE source files generally have longer records). If you want to work further on this, you may want to post it as a new question, where you can have a clean slate to format code (indent first with 4 spaces) – WarrenT Jul 11 '15 at 16:47
  • Thanks, I'll created the DDS and DDM files using this tutorial https://www.youtube.com/watch?v=OGCtj6JGqXs and now CREATE ALIAS and INSERT INTO works :) – KeKru Jul 13 '15 at 09:18
  • I really think this is being treated like it's more complex than it really is. Unless there's an overriding need to use DDM, I would suggest just using JDBC to connect directly to the system that hosts the database. – David G Jul 14 '15 at 16:37

2 Answers2

2

Rather than accessing the files using DDM on a remote system, just use JDBC to access the files on the system the database actually resides on.

David G
  • 3,940
  • 1
  • 22
  • 30
1

Perhaps you concerned about whether there is any difference between files created with DDS vs those created with SQL. The system treats them almost identically. Use JDBC on any described physical database file.

SQL/JDBC does not generally have much ability to work with specific file members. You will need to either

1) CREATE ALIAS to the specific member, and you would INSERT INTO your alias.

Or

2) establish an override in your connection session, probably with the CL command OVRDBF.

You can execute the SQL statement

CALL QCMDEXC('OVRDBF QRPGLESRC3 KEKRU1/QRPGLESRC3 TOMBR(DDSTEST2)')

QCMDEXC is a stored procedure that executes CL commands.

If the file member does not already exist, then you may first need to

CALL QCMDEXC('ADDPFM  KEKRU1/QRPGLESRC3 DDSTEST2')

If IBM I has not been updated on your system in a while, you may need to add a second parameter for the length of the CL command string, such as 0000000049.00000

WarrenT
  • 4,502
  • 19
  • 27
  • Thanks, it works with "CREATE ALIAS kekru1/ddstestxy FOR kekru1/qrpglesrc3(ddstest)" :) – KeKru Jul 10 '15 at 14:56
  • Unless the DB2 for i SQL feature made a specific external QCMDEXC [different than the *PGM object QSYS/QCMDEXC] that specifically effects for the Override feature an ability to effect scoping to the SQL as invoker, only the Override Scope (OVRSCOPE) of *JOB will suffic in the given OVRDBF example; i.e. OVRSCOPE(*JOB) should be coded [if not the default; thus Delete Override (DLTOVR) is recommended to pair the OVRxxx request]. Any other scoping would need to be effected by invoking OVRxxx outside of [usu. before invoking] the SQL that requires the override to be in effect. – CRPence Aug 12 '15 at 19:31