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)