• Spring Batch

    From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Tue Mar 18 15:51:54 2025
    I am happy to report that Spring Batch 4.3 works fine on
    VMS x86-64 (and probably also on VMS Itanium, but not on
    VMS Alpha due to too old Java version).

    For database, XML, JSON and CSV the following jar files:

    antlr-2_7_7.jar;1
    aspectjrt-1_9_9_1.jar;1
    aspectjweaver-1_9_9_1.jar;1
    byte-buddy-1_12_7.jar;1
    classmate-1_5_1.jar;1
    commons-logging-1_1.jar;1
    hibernate-commons-annotations-5_1_2_Final.jar;1 hibernate-core-5_6_5_Final.jar;1
    jackson-annotations-2_9_9.jar;1
    jackson-core-2_9_9.jar;1
    jackson-databind-2_9_9.jar;1
    jandex-2_4_2_Final.jar;1
    javax_batch-api-1_0.jar;1
    javax_persistence-api-2_2.jar;1
    jboss-logging-3_4_3_Final.jar;1 jboss-transaction-api_1_2_spec-1_1_1_Final.jar;1
    micrometer-core-1_9_17.jar;1
    mysql-connector-j-8_0_33.jar;1
    spring-aop-5_3_31.jar;1
    spring-batch-core-4_3_10.jar;1
    spring-batch-infrastructure-4_3_10.jar;1
    spring-beans-5_3_31.jar;1
    spring-context-5_3_31.jar;1
    spring-core-5_3_31.jar;1
    spring-expression-5_3_31.jar;1
    spring-jdbc-5_3_31.jar;1
    spring-orm-5_3_31.jar;1
    spring-oxm-5_3_31.jar;1
    spring-retry-1_3_4.jar;1
    spring-tx-5_3_31.jar;1
    xstream-1_4_20.jar;1

    The code and configuration is the same as on any other platform.

    Spring Batch 5.x requires Java 17, so for that we have to wait
    until Java 17 VMS x86-64 becomes available.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Tue Mar 18 17:45:53 2025
    On 3/18/25 2:51 PM, Arne Vajhøj wrote:

    Spring Batch 5.x requires Java 17, so for that we have to wait
    until Java 17 VMS x86-64 becomes available.

    Which looks like it's delayed until December 2025 per the current
    roadmap. On the plus side, it looks like quite a lot of other things
    will be released in April and May.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John Dallman@21:1/5 to All on Fri Mar 21 17:40:00 2025
    In article <67dd9ee9$0$713$14726298@news.sunsite.dk>, arne@vajhoej.dk
    (Arne Vajhøj) wrote:

    Java 25 will come out in September, so in December Java 17 will
    be two versions behind.

    Yup. Java 21 is what people are moving to now. The good side is that the backlog of VMS-specific changes should be getting smaller now.

    John

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Fri Mar 21 13:16:25 2025
    On 3/18/2025 6:45 PM, Craig A. Berry wrote:
    On 3/18/25 2:51 PM, Arne Vajhøj wrote:
    Spring Batch 5.x requires Java 17, so for that we have to wait
    until Java 17 VMS x86-64 becomes available.

    Which looks like it's delayed until December 2025 per the current
    roadmap.  On the plus side, it looks like quite a lot of other things
    will be released in April and May.

    Java 25 will come out in September, so in December Java 17 will
    be two versions behind.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Fri Mar 28 08:51:24 2025
    On 3/18/2025 3:51 PM, Arne Vajhøj wrote:
    I am happy to report that Spring Batch 4.3 works fine on
    VMS x86-64 (and probably also on VMS Itanium, but not on
    VMS Alpha due to too old Java version).

    For database, XML, JSON and CSV the following jar files:

    The code and configuration is the same as on any other platform.

    Spring Batch can read/write from/to relational databases and a few NoSQL databases
    as well: MongoDB, Neo4J and Redis.

    Obviously it does not read/write VMS index-sequential files out of the box.

    But since I happen to have a library allowing JVM languages to access VMS index-sequential files, then creating a wrapper allowing Sping Batch to
    access
    them was tempting.

    So I did.

    First the simple model with only one index-sequential file.

    import org.springframework.batch.item.ExecutionContext;
    import org.springframework.batch.item.NonTransientResourceException;
    import org.springframework.batch.item.ParseException;
    import org.springframework.batch.item.UnexpectedInputException;
    import org.springframework.batch.item.support.AbstractItemStreamItemReader;

    import dk.vajhoej.isam.IsamException;
    import dk.vajhoej.isam.IsamResult;
    import dk.vajhoej.isam.IsamSource;
    import dk.vajhoej.record.RecordException;

    public class SingleIsamItemReader<T> extends
    AbstractItemStreamItemReader<T> {
    private IsamSource source;
    private Class<T> type;
    private IsamResult<T> result;
    public IsamSource getSource() {
    return source;
    }
    public void setSource(IsamSource source) {
    this.source = source;
    }
    public Class<T> getType() {
    return type;
    }
    public void setType(Class<T> type) {
    this.type = type;
    }
    @Override
    public void open(ExecutionContext ec) {
    try {
    result = source.readStart(type);
    } catch (IsamException e) {
    throw new RuntimeException(e);
    } catch (RecordException e) {
    throw new RuntimeException(e);
    }
    }
    @Override
    public T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
    if(result.read()) {
    return result.current();
    } else {
    return null;
    }
    }
    @Override
    public void close() {
    try {
    result.close();
    source.close();
    } catch (IsamException e) {
    throw new RuntimeException(e);
    }
    }
    }

    <bean id="foobarSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="foobar.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="true"/>
    </bean>
    <bean id="foobarReader" class="SingleIsamItemReader">
    <property name="source" ref="foobarSource" />
    <property name="type" value="somepackage.Foobar"/>
    </bean>

    import java.util.List;

    import org.springframework.batch.item.support.AbstractItemStreamItemWriter;

    import dk.vajhoej.isam.IsamSource;

    public class SingleIsamItemWriter<T> extends
    AbstractItemStreamItemWriter<T> {
    private IsamSource source;
    public IsamSource getSource() {
    return source;
    }
    public void setSource(IsamSource source) {
    this.source = source;
    }
    @Override
    public void write(List<? extends T> items) throws Exception {
    for(T item : items) {
    source.create(item);
    }
    }
    }

    <bean id="foobarSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="foobar.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="false"/>
    </bean>
    <bean id="foobarReader" class="SingleIsamItemWriter">
    <property name="source" ref="foobarSource" />
    </bean>

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Fri Mar 28 09:02:40 2025
    On 3/28/2025 8:51 AM, Arne Vajhøj wrote:
    On 3/18/2025 3:51 PM, Arne Vajhøj wrote:
    I am happy to report that Spring Batch 4.3 works fine on
    VMS x86-64 (and probably also on VMS Itanium, but not on
    VMS Alpha due to too old Java version).

    For database, XML, JSON and CSV the following jar files:

    The code and configuration is the same as on any other platform.

    Spring Batch can read/write from/to relational databases and a few NoSQL databases
    as well: MongoDB, Neo4J and Redis.

    Obviously it does not read/write VMS index-sequential files out of the box.

    But since I happen to have a library allowing JVM languages to access VMS index-sequential files, then creating a wrapper allowing Sping Batch to access
    them was tempting.

    So I did.

    First the simple model with only one index-sequential file.

    The more tricky case is multiple index-sequential files similar to
    one-to-many
    relations in relational databases.

    (index-sequentiel files allow for arrays within records, but that is not
    always
    possible to utilize due to the 32 KB limit)

    import java.lang.reflect.Method;

    import dk.vajhoej.isam.IsamSource;

    public class ReaderSecondarySource<T> {
    private String primaryKey; // name of key in primary source used to
    lookup in secondary source
    private String primaryList; // name of list in primary source
    containing records from secondary source
    private Class<T> type; // type of records in secondary source
    private int index; // index of key in secondary source used to
    lookup in secondary source
    private IsamSource source; // secondary source
    private Method primaryKeyGetter;
    private Method primaryListGetter;
    public String getPrimaryKey() {
    return primaryKey;
    }
    public void setPrimaryKey(String primaryKey) {
    this.primaryKey = primaryKey;
    }
    public String getPrimaryList() {
    return primaryList;
    }
    public void setPrimaryList(String primaryList) {
    this.primaryList = primaryList;
    }
    public Class<T> getType() {
    return type;
    }
    public void setType(Class<T> type) {
    this.type = type;
    }
    public int getIndex() {
    return index;
    }
    public void setIndex(int index) {
    this.index = index;
    }
    public IsamSource getSource() {
    return source;
    }
    public void setSource(IsamSource source) {
    this.source = source;
    }
    public Method getPrimaryKeyGetter() {
    return primaryKeyGetter;
    }
    public void setPrimaryKeyGetter(Method primaryKeyGetter) {
    this.primaryKeyGetter = primaryKeyGetter;
    }
    public Method getPrimaryListGetter() {
    return primaryListGetter;
    }
    public void setPrimaryListGetter(Method primaryListGetter) {
    this.primaryListGetter = primaryListGetter;
    }
    }

    import java.lang.reflect.Method;
    import java.util.List;

    import org.springframework.batch.item.ExecutionContext;
    import org.springframework.batch.item.NonTransientResourceException;
    import org.springframework.batch.item.ParseException;
    import org.springframework.batch.item.UnexpectedInputException;
    import org.springframework.batch.item.support.AbstractItemStreamItemReader;

    import dk.vajhoej.isam.IsamException;
    import dk.vajhoej.isam.IsamResult;
    import dk.vajhoej.isam.IsamSource;
    import dk.vajhoej.isam.Key;
    import dk.vajhoej.isam.KeyInfoArrayCache;
    import dk.vajhoej.record.RecordException;

    public class MultiIsamItemReader<T> extends
    AbstractItemStreamItemReader<T> {
    private IsamSource primarySource;
    private Class<T> type;
    private List<ReaderSecondarySource> secondarySources;
    private IsamResult<T> result;
    public IsamSource getPrimarySource() {
    return primarySource;
    }
    public void setPrimarySource(IsamSource primarySource) {
    this.primarySource = primarySource;
    }
    public Class<T> getType() {
    return type;
    }
    public void setType(Class<T> type) {
    this.type = type;
    }
    public List<ReaderSecondarySource> getSecondarySources() {
    return secondarySources;
    }
    public void setSecondarySources(List<ReaderSecondarySource> secondarySources) {
    this.secondarySources = secondarySources;
    }
    private Method getGetter(String fldnam) {
    try {
    return type.getMethod("get" + fldnam.substring(0,
    1).toUpperCase() + fldnam.substring(1));
    } catch (NoSuchMethodException e) {
    throw new RuntimeException(e);
    } catch (SecurityException e) {
    throw new RuntimeException(e);
    }
    }
    @Override
    public void open(ExecutionContext ec) {
    try {
    result = primarySource.readStart(type);
    for(ReaderSecondarySource<?> src2 : secondarySources) {
    src2.setPrimaryKeyGetter(getGetter(src2.getPrimaryKey()));

    src2.setPrimaryListGetter(getGetter(src2.getPrimaryList()));
    }
    } catch (IsamException e) {
    throw new RuntimeException(e);
    } catch (RecordException e) {
    throw new RuntimeException(e);
    }
    }
    @Override
    public T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
    if(result.read()) {
    T o = result.current();
    for(ReaderSecondarySource src2 : secondarySources) {
    int keyix = src2.getIndex();
    Comparable keyval = (Comparable)src2.getPrimaryKeyGetter().invoke(o);
    List lst2 = (List)src2.getPrimaryListGetter().invoke(o);
    Class t = src2.getType();
    IsamSource is = src2.getSource();
    IsamResult ir = is.readGE(t, new Key(keyix, keyval));
    while(ir.read()) {
    Object o2 = ir.current();
    Comparable keyval2 = (Comparable)KeyInfoArrayCache.analyze(t)[src2.getIndex()].getField().get(o2);
    if(keyval.compareTo(keyval2) == 0) {
    lst2.add(o2);
    } else {
    break;
    }
    }
    }
    return o;
    } else {
    return null;
    }
    }
    @Override
    public void close() {
    try {
    result.close();
    primarySource.close();
    for(ReaderSecondarySource src2 : secondarySources) {
    src2.getSource().close();
    }
    } catch (IsamException e) {
    throw new RuntimeException(e);
    }
    }
    }

    <bean id="secSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="sec.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="true"/>
    </bean>
    <bean id="secReader" class="ReaderSecondarySource">
    <property name="primaryKey" value="id"/>
    <property name="primaryList" value="secList"/>
    <property name="type" value="somepackage.SecClass"/>
    <property name="index" value="1"/>
    <property name="source" ref="secSource"/>
    </bean>
    <bean id="priSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="pri.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="true"/>
    </bean>
    <bean id="priReader" class="MultiIsamItemReader">
    <property name="primarySource" ref="priSource" />
    <property name="type" value="somepackage.PriClass"/>
    <property name="secondarySources">
    <list>
    <ref bean="secReader"/>
    </list>
    </property>
    </bean>

    import java.lang.reflect.Method;

    import dk.vajhoej.isam.IsamSource;

    public class WriterSecondarySource<T> {
    private String primaryList; // name of list in primary source
    containing records from secondary source
    private IsamSource source; // secondary source
    private Method primaryListGetter;
    public String getPrimaryList() {
    return primaryList;
    }
    public void setPrimaryList(String primaryList) {
    this.primaryList = primaryList;
    }
    public IsamSource getSource() {
    return source;
    }
    public void setSource(IsamSource source) {
    this.source = source;
    }
    public Method getPrimaryListGetter() {
    return primaryListGetter;
    }
    public void setPrimaryListGetter(Method primaryListGetter) {
    this.primaryListGetter = primaryListGetter;
    }
    }

    import java.lang.reflect.Method;
    import java.util.List;

    import org.springframework.batch.item.ExecutionContext;
    import org.springframework.batch.item.support.AbstractItemStreamItemWriter;

    import dk.vajhoej.isam.IsamException;
    import dk.vajhoej.isam.IsamSource;

    public class MultiIsamItemWriter<T> extends
    AbstractItemStreamItemWriter<T> {
    private IsamSource primarySource;
    private Class<T> type;
    private List<WriterSecondarySource> secondarySources;
    public IsamSource getPrimarySource() {
    return primarySource;
    }
    public void setPrimarySource(IsamSource primarySource) {
    this.primarySource = primarySource;
    }
    public Class<T> getType() {
    return type;
    }
    public void setType(Class<T> type) {
    this.type = type;
    }
    public List<WriterSecondarySource> getSecondarySources() {
    return secondarySources;
    }
    public void setSecondarySources(List<WriterSecondarySource> secondarySources) {
    this.secondarySources = secondarySources;
    }
    private Method getGetter(String fldnam) {
    try {
    return type.getMethod("get" + fldnam.substring(0,
    1).toUpperCase() + fldnam.substring(1));
    } catch (NoSuchMethodException e) {
    throw new RuntimeException(e);
    } catch (SecurityException e) {
    throw new RuntimeException(e);
    }
    }
    @Override
    public void open(ExecutionContext ec) {
    for(WriterSecondarySource<?> src2 : secondarySources) {
    src2.setPrimaryListGetter(getGetter(src2.getPrimaryList()));
    }
    }
    @Override
    public void write(List<? extends T> items) throws Exception {
    for(T item : items) {
    primarySource.create(item);
    for(WriterSecondarySource<?> src2 : secondarySources) {
    for(Object o : (List<?>)src2.getPrimaryListGetter().invoke(item) ) {
    src2.getSource().create(o);
    }
    }
    }
    }
    @Override
    public void close() {
    try {
    primarySource.close();
    for(WriterSecondarySource src2 : secondarySources) {
    src2.getSource().close();
    }
    } catch (IsamException e) {
    throw new RuntimeException(e);
    }
    }
    }

    <bean id="secSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="sec.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="false"/>
    </bean>
    <bean id="secWriter" class="WriterSecondarySource">
    <property name="primaryList" value="secList"/>
    <property name="source" ref="secSource"/>
    </bean>
    <bean id="priSource" class="dk.vajhoej.isam.local.LocalIsamSource">
    <constructor-arg value="pri.isq"/>
    <constructor-arg value="dk.vajhoej.vms.rms.IndexSequential"/>
    <constructor-arg value="false"/>
    </bean>
    <bean id="priWriter" class="MultiIsamItemWriter">
    <property name="primarySource" ref="priSource" />
    <property name="type" value="somepackage.PriClass"/>
    <property name="secondarySources">
    <list>
    <ref bean="secWriter"/>
    </list>
    </property>
    </bean>

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Mar 28 20:44:27 2025
    On Fri, 28 Mar 2025 08:51:24 -0400, Arne Vajhøj wrote:

    First the simple model with only one index-sequential file.

    Well, I have to say one thing about your Java code: at least you don’t use wildcard imports!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Sun Mar 30 10:07:31 2025
    On 3/28/2025 9:02 AM, Arne Vajhøj wrote:
    On 3/28/2025 8:51 AM, Arne Vajhøj wrote:
    On 3/18/2025 3:51 PM, Arne Vajhøj wrote:
    I am happy to report that Spring Batch 4.3 works fine on
    VMS x86-64 (and probably also on VMS Itanium, but not on
    VMS Alpha due to too old Java version).

    For database, XML, JSON and CSV the following jar files:

    The code and configuration is the same as on any other platform.

    Spring Batch can read/write from/to relational databases and a few
    NoSQL databases
    as well: MongoDB, Neo4J and Redis.

    Obviously it does not read/write VMS index-sequential files out of the
    box.

    But since I happen to have a library allowing JVM languages to access VMS
    index-sequential files, then creating a wrapper allowing Sping Batch
    to access
    them was tempting.

    So I did.

    First the simple model with only one index-sequential file.

    The more tricky case is multiple index-sequential files similar to one- to-many
    relations in relational databases.

    (index-sequentiel files allow for arrays within records, but that is not always
    possible to utilize due to  the 32 KB limit)

    To illustrate:

    VMS Tech Demo 21 - data export with Spring Batch
    https://www.vajhoej.dk/arne/articles/vmstd21.html

    VMS Tech Demo 22 - data import with Spring Batch
    https://www.vajhoej.dk/arne/articles/vmstd22.html

    Which are repeats of:

    VMS Tech Demo 18 - getting data out of VMS
    https://www.vajhoej.dk/arne/articles/vmstd18.html

    VMS Tech Demo 19 - getting data into VMS
    https://www.vajhoej.dk/arne/articles/vmstd19.html

    but with the custom Groovy/Jython code replaced with
    Spring Batch and XML config - in reality coding in
    XML instead of Groovy/Jython.

    For index-sequential file it is using two files, so it
    is the MultiIsamItemReader/Writer being used.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)