From 0cdeeefa596ff3cc4d572115df9adfc7a72e7d18 Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:09:35 +0100 Subject: [PATCH 1/6] virtual threads creation Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- .../ibm/as400/access/AS400ThreadedServer.java | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java b/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java index 815acf37..e5164309 100644 --- a/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java +++ b/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java @@ -14,12 +14,18 @@ package com.ibm.as400.access; import java.io.IOException; +import java.lang.reflect.Method; import java.net.SocketException; import java.util.Hashtable; final class AS400ThreadedServer extends AS400Server implements Runnable { - private static int threadCount_ = 0; + private static final boolean VIRTUAL_THREADS_AVAILABLE; + private static final Method OF_VIRTUAL_METHOD; + private static final Method UNSTARTED_METHOD; + private static final Method NAME_METHOD; + + private static int threadCount_ = 0; private AS400ImplRemote system_; private boolean disconnecting_ = false; @@ -184,6 +190,50 @@ private class ReceiveLock extends Object {} private final CorrelationIdLock correlationIdLock_ = new CorrelationIdLock(); private final ReceiveLock receiveLock_ = new ReceiveLock(); + static { + boolean available = false; + Method ofVirtual = null; + Method unstarted = null; + Method name = null; + + try { + Class> threadClass = Thread.class; + + // Check if Thread.ofVirtual() exists + ofVirtual = threadClass.getMethod("ofVirtual"); + // Check if unstarted(Runnable) exists on the returned object + Class> ofVirtualClass = ofVirtual.getReturnType(); + name = ofVirtualClass.getMethod("name", String.class); + unstarted = ofVirtualClass.getMethod("unstarted", Runnable.class); + + available = true; + } catch (NoSuchMethodException e) { + available = false; + } + + VIRTUAL_THREADS_AVAILABLE = available; + OF_VIRTUAL_METHOD = ofVirtual; + NAME_METHOD = name; + UNSTARTED_METHOD = unstarted; + } + + public static Thread newThread(Runnable r, String name, boolean virtual) { + if (virtual && VIRTUAL_THREADS_AVAILABLE) { + try { + Object ofVirtual = OF_VIRTUAL_METHOD.invoke(null); // Thread.ofVirtual() + ofVirtual = NAME_METHOD.invoke(ofVirtual, name); + return (Thread) UNSTARTED_METHOD.invoke(ofVirtual, r); // unstarted(r) + } catch (Exception e) { + // silently fall back to normal daemon threads + } + } + // Fallback to normal thread if reflection fails + Thread t = new Thread(r, name); + t.setDaemon(true); + + return t; + } + AS400ThreadedServer(AS400ImplRemote system, int service, SocketContainer socket, String jobString) throws IOException { system_ = system; @@ -204,8 +254,7 @@ private class ReceiveLock extends Object {} if (jobString != null && jobString.length() != 0) jobID = jobString; else jobID = AS400.getServerName(service) + "/" + (++threadCount_); - readDaemon_ = new Thread(this, "AS400 Read Daemon [system:"+system.getSystemName() + ";job:" + jobID + "]"); - readDaemon_.setDaemon(true); + readDaemon_ = newThread(this, "AS400 Read Daemon [system:"+system.getSystemName() + ";job:" + jobID + "]", true); readDaemon_.start(); } From b848101541c9801e66524e091eb8f315085eb145 Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:10:47 +0100 Subject: [PATCH 2/6] properties Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- .../as400/access/AS400JDBCDataSourceBeanInfo.java | 8 +++++++- src/main/java/com/ibm/as400/access/JDMRI.java | 2 ++ src/main/java/com/ibm/as400/access/JDProperties.java | 12 +++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCDataSourceBeanInfo.java b/src/main/java/com/ibm/as400/access/AS400JDBCDataSourceBeanInfo.java index 59dd4635..e14fc4a7 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCDataSourceBeanInfo.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCDataSourceBeanInfo.java @@ -500,7 +500,13 @@ public class AS400JDBCDataSourceBeanInfo extends SimpleBeanInfo { useSock5.setDisplayName(AS400JDBCDriver.getResource("PROP_NAME_USE_SOCK5", null)); useSock5.setShortDescription(AS400JDBCDriver.getResource("USE_SOCK5_DESC", null)); - + PropertyDescriptor virtualThreads = new PropertyDescriptor("virtualThreads", beanClass, "isVirtualThreads", + "setVirtualThreads"); + virtualThreads.setBound(true); + virtualThreads.setConstrained(false); + virtualThreads.setDisplayName(AS400JDBCDriver.getResource("PROP_NAME_VIRTUAL_THREADS", null)); + virtualThreads.setShortDescription(AS400JDBCDriver.getResource("VIRTUAL_THREADS_DESC", null)); + PropertyDescriptor keepAlive = new PropertyDescriptor("keepAlive", beanClass, "isKeepAlive", "setKeepAlive"); keepAlive.setBound(true); diff --git a/src/main/java/com/ibm/as400/access/JDMRI.java b/src/main/java/com/ibm/as400/access/JDMRI.java index 489fa0e9..2d222294 100644 --- a/src/main/java/com/ibm/as400/access/JDMRI.java +++ b/src/main/java/com/ibm/as400/access/JDMRI.java @@ -154,6 +154,7 @@ public Object[][] getContents() { "PROP_NAME_TLS_TRUSTSTORE_PASSWORD", "tlsTruststorePassword"}, { "PROP_NAME_STAY_ALIVE", "stayAlive"}, { "PROP_NAME_USE_SOCK5", "useSock5"}, + { "PROP_NAME_VIRTUAL_THREADS", "virtualThreads"}, // #TRANNOTE JDBC property descriptions. { "ACCESS_DESC", "Specifies the level of database access for the connection." }, { "BEHAVIOR_OVERRIDE_DESC", "Specifies the Toolbox JDBC driver behavior to override." }, //@J5A @@ -272,6 +273,7 @@ public Object[][] getContents() {"TLS_TRUSTSTORE_DESC","Specifies a file to be used as the truststore for TLS connections."}, {"TLS_TRUSTSTORE_PASSWORD_DESC","Specifies the password associated with the configured TLS truststore."}, {"USE_SOCK5_DESC","Specifies that Socks5 should be used for the proxy support."}, + {"VIRTUAL_THREADS_DESC","Specifies that virtual threads should be used when available."}, // JDBC 2 - Optional Package support - RowSet @E5 { "PROP_NAME_RS_COMMAND", "command" }, { "PROP_NAME_RS_CONCURRENCY", "concurrency" }, diff --git a/src/main/java/com/ibm/as400/access/JDProperties.java b/src/main/java/com/ibm/as400/access/JDProperties.java index 4b2eea91..86733d37 100644 --- a/src/main/java/com/ibm/as400/access/JDProperties.java +++ b/src/main/java/com/ibm/as400/access/JDProperties.java @@ -191,10 +191,11 @@ public class JDProperties implements Serializable, Cloneable { static final int TLS_TRUSTSTORE = 103; static final int TLS_TRUSTSTORE_PASSWORD = 104; static final int USE_SOCK5 = 105; // @greenscreens + static final int VIRTUAL_THREADS = 106; // @greenscreens // always add to the end of the array! - private static final int NUMBER_OF_ATTRIBUTES_ = 106; + private static final int NUMBER_OF_ATTRIBUTES_ = 107; // Property names. static final String ACCESS_ = "access"; @@ -303,6 +304,7 @@ public class JDProperties implements Serializable, Cloneable { static final String DESCRIBE_OPTION_ = "describe option"; // @F6A static final String DECIMAL_DATA_ERRORS_ = "decimal data errors"; static final String USE_SOCK5_ = "use sock5"; // @greenscreens + static final String VIRTUAL_THREADS_ = "use virtual threads"; // Common String objects. Using these will theoretically // cut down on the number of String allocations. @@ -1646,6 +1648,14 @@ public class JDProperties implements Serializable, Cloneable { dpi_[i].choices[1] = TRUE_; defaults_[i] = EMPTY_; + i = VIRTUAL_THREADS; + dpi_[i] = new DriverPropertyInfo(VIRTUAL_THREADS_, ""); + dpi_[i].description = "VIRTUAL_THREADS_DESC"; + dpi_[i].required = false; + dpi_[i].choices = new String[2]; + dpi_[i].choices[0] = FALSE_; + dpi_[i].choices[1] = TRUE_; + defaults_[i] = EMPTY_; } /** From 200722b2db3e6df7a175399cbfc565df92a85baa Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:21:26 +0100 Subject: [PATCH 3/6] settings Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- .../ibm/as400/access/AS400JDBCDataSource.java | 25 +++++++++++++++++++ .../access/AS400JDBCManagedDataSource.java | 18 +++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java index 27ea7d89..afef8124 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java @@ -1211,6 +1211,15 @@ public boolean isUseSock5() return properties_.getBoolean(JDProperties.USE_SOCK5); } + /** + * Indicates whether virtual threads are used. + * @return Returns true if the driver will try to use virtual threads + **/ + public boolean isVirtualThreads() + { + return properties_.getBoolean(JDProperties.VIRTUAL_THREADS); + } + /** * Returns the Reference object for the data source object. * This is used by JNDI when bound in a JNDI naming service. @@ -3911,7 +3920,23 @@ public void setUseSock5(boolean value) if (JDTrace.isTraceOn()) JDTrace.logInformation (this, property + ": " + value); } + /** + * Sets the flag to use virtual threads + * @param value Flag how proxy property is used. + **/ + public void setVirtualThreads(boolean value) + { + String property = JDProperties.VIRTUAL_THREADS_ ; + Boolean oldValue = Boolean.valueOf(isVirtualThreads()); + Boolean newValue = Boolean.valueOf(value); + + properties_.setString(JDProperties.VIRTUAL_THREADS, value ? TRUE_ : FALSE_); + + changes_.firePropertyChange(property, oldValue, newValue); + if (JDTrace.isTraceOn()) + JDTrace.logInformation (this, property + ": " + value); + } /** * Sets the source of the text for REMARKS columns in ResultSets returned diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCManagedDataSource.java b/src/main/java/com/ibm/as400/access/AS400JDBCManagedDataSource.java index 5a077de3..f02e1d42 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCManagedDataSource.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCManagedDataSource.java @@ -2450,6 +2450,15 @@ public boolean isSecureCurrentUser() { public boolean isUseSock5() { return properties_.getBoolean(JDProperties.USE_SOCK5); } + /** + * Indicates whether virtual threads are used + * + * @return true if virtual threads are used; false otherwise. The default value + * is false. + **/ + public boolean isVirtualThreads() { + return properties_.getBoolean(JDProperties.VIRTUAL_THREADS); + } /** * Indicates whether a thread is used. @@ -4546,6 +4555,15 @@ public void setUseSock5(boolean useSock5) { properties_.setString(JDProperties.USE_SOCK5, FALSE_); } + /** + * Sets whether virtual threads should be used + * + * @param virtualThreads true if virtual threads should be used. The + * default value is false. + **/ + public void setVirtualThreads(boolean virtualThreads) { + properties_.setString(JDProperties.VIRTUAL_THREADS, virtualThreads ? TRUE_ : FALSE_); + } /** * Sets whether lock sharing is allowed for loosely coupled transaction From 753b9f5fd28b594b117034bf1ef3f75fd121f51f Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:46:11 +0100 Subject: [PATCH 4/6] plumbing Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- src/main/java/com/ibm/as400/access/AS400.java | 57 ++++++++++++++++++- .../java/com/ibm/as400/access/AS400Impl.java | 2 +- .../com/ibm/as400/access/AS400ImplProxy.java | 4 +- .../com/ibm/as400/access/AS400ImplRemote.java | 9 ++- .../ibm/as400/access/AS400JDBCDataSource.java | 52 +++++++++++------ .../com/ibm/as400/access/AS400JDBCDriver.java | 8 +++ .../ibm/as400/access/AS400ThreadedServer.java | 4 +- .../com/ibm/as400/access/ConnectionList.java | 3 +- .../access/ConnectionPoolProperties.java | 12 +++- .../java/com/ibm/as400/access/PoolItem.java | 7 ++- 10 files changed, 128 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/AS400.java b/src/main/java/com/ibm/as400/access/AS400.java index 1d843c83..43cfcef7 100644 --- a/src/main/java/com/ibm/as400/access/AS400.java +++ b/src/main/java/com/ibm/as400/access/AS400.java @@ -173,6 +173,8 @@ public class AS400 implements Serializable, AutoCloseable private static boolean defaultMustUseSuppliedProfile_ = false; // Default setting for threadUsed property. private static boolean defaultThreadUsed_ = true; + // Default setting for virtualThreads property. + private static boolean defaultVirtualThreads_ = false; boolean skipSignonServer_ = false; public String currentLib_ = "*CURUSR"; @@ -351,6 +353,8 @@ public class AS400 implements Serializable, AutoCloseable private boolean mustUseSuppliedProfile_ = defaultMustUseSuppliedProfile_; // Flag that indicates if we use threads in communication with the host servers. private boolean threadUsed_ = defaultThreadUsed_; + // Flag that indicates if we use virtual threads + private boolean virtualThreads_ = defaultVirtualThreads_; // Locale object to use for determining NLV. private Locale locale_ = Locale.getDefault(); // The NLV set or determined from the locale. @@ -1082,6 +1086,7 @@ public AS400(AS400 system) mustUseNetSockets_ = system.mustUseNetSockets_; mustUseSuppliedProfile_ = system.mustUseSuppliedProfile_; threadUsed_ = system.threadUsed_; + virtualThreads_ = system.virtualThreads_; locale_ = system.locale_; nlv_ = system.nlv_; socketProperties_.copyValues(system.socketProperties_); @@ -1813,7 +1818,7 @@ private synchronized void chooseImpl() if (!propertiesFrozen_) { - impl_.setState(useSSLConnection_, canUseNativeOptimizations(), threadUsed_, ccsid_, nlv_, + impl_.setState(useSSLConnection_, canUseNativeOptimizations(), threadUsed_, virtualThreads_, ccsid_, nlv_, socketProperties_, ddmRDB_, mustUseNetSockets_, mustUseSuppliedProfile_, mustAddLanguageLibrary_); propertiesFrozen_ = true; } @@ -3645,6 +3650,18 @@ public boolean isThreadUsed() if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Checking if thread is used:", threadUsed_); return threadUsed_; } + + /** + * Indicates whether threads are used in communication with the host servers. + * + * @return true if threads are used; false otherwise. + **/ + public boolean isVirtualThreads() + { + if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Checking if virtual threads are used:", virtualThreads_); + return virtualThreads_; + } + /** * Indicates if the default user should be used by this object. If the default user is not used and a user ID was @@ -5362,6 +5379,44 @@ public void setThreadUsed(boolean useThreads) throws PropertyVetoException propertyChangeListeners_.firePropertyChange("threadUsed", oldValue, newValue); } } + + /** + * Sets whether the IBM Toolbox for Java uses virtual threads in communication with the host servers. The default is false. + * The virtual thread used property cannot be changed once a connection to the system has been established. + * + *
+ * Note: This property may also be set by specifying 'true' or 'false' in Java system property + * com.ibm.as400.access.AS400.virtualThreads + * + * @param virtualThreads true to use virtual threads; false otherwise. + * @exception ExtendedIllegalStateException If a connection has already been made. + * @exception PropertyVetoException If any of the registered listeners vetos the property change. + **/ + public void setVirtualThreads(boolean virtualThreads) throws PropertyVetoException + { + if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Setting thread used:", virtualThreads); + + if (propertiesFrozen_) + { + Trace.log(Trace.ERROR, "Cannot set thread used after connection has been made."); + throw new ExtendedIllegalStateException("threadUsed", ExtendedIllegalStateException.PROPERTY_NOT_CHANGED); + } + + if (propertyChangeListeners_ == null && vetoableChangeListeners_ == null) + virtualThreads_ = virtualThreads; + else + { + Boolean oldValue = Boolean.valueOf(virtualThreads_); + Boolean newValue = Boolean.valueOf(virtualThreads); + + if (vetoableChangeListeners_ != null) + vetoableChangeListeners_.fireVetoableChange("virtualThreads", oldValue, newValue); + + virtualThreads_ = virtualThreads; + if (propertyChangeListeners_ != null) + propertyChangeListeners_.firePropertyChange("virtualThreads", oldValue, newValue); + } + } /** * Sets the indicator for whether the default user is used. The default user is used if a system name is provided, diff --git a/src/main/java/com/ibm/as400/access/AS400Impl.java b/src/main/java/com/ibm/as400/access/AS400Impl.java index 8528961e..85520b20 100644 --- a/src/main/java/com/ibm/as400/access/AS400Impl.java +++ b/src/main/java/com/ibm/as400/access/AS400Impl.java @@ -66,7 +66,7 @@ interface AS400Impl // Set the service ports to their default values. void setServicePortsToDefault(String systemName); // Set significant instance variables into implementation object. - void setState(SSLOptions useSSLConnection, boolean canUseNativeOptimization, boolean threadUsed, int ccsid, String nlv, SocketProperties socketProperties, String ddmRDB, boolean mustUseNetSockets, boolean mustUseSuppliedProfile, boolean mustAddLanguageLibrary); + void setState(SSLOptions useSSLConnection, boolean canUseNativeOptimization, boolean threadUsed, boolean virtualThreads, int ccsid, String nlv, SocketProperties socketProperties, String ddmRDB, boolean mustUseNetSockets, boolean mustUseSuppliedProfile, boolean mustAddLanguageLibrary); SignonInfo setState(AS400Impl impl, CredentialVault credVault); // Sign-on to system. SignonInfo signon(String systemName, boolean systemNameLocal, String userId, CredentialVault vault, String gssName) throws AS400SecurityException, IOException; diff --git a/src/main/java/com/ibm/as400/access/AS400ImplProxy.java b/src/main/java/com/ibm/as400/access/AS400ImplProxy.java index f68ec4e1..77e1d2ed 100644 --- a/src/main/java/com/ibm/as400/access/AS400ImplProxy.java +++ b/src/main/java/com/ibm/as400/access/AS400ImplProxy.java @@ -316,11 +316,11 @@ public void setServicePortsToDefault(String systemName) // Set the significant instance variables for the AS400ImplRemote object. @Override - public void setState(SSLOptions useSSLConnection, boolean canUseNativeOptimization, boolean threadUsed, int ccsid, String nlv, SocketProperties socketProperties, String ddmRDB, boolean mustUseNetSockets, boolean mustUseSuppliedProfile, boolean mustAddLanguageLibrary) + public void setState(SSLOptions useSSLConnection, boolean canUseNativeOptimization, boolean threadUsed, boolean virtualThreads, int ccsid, String nlv, SocketProperties socketProperties, String ddmRDB, boolean mustUseNetSockets, boolean mustUseSuppliedProfile, boolean mustAddLanguageLibrary) { try { - connection_.callMethod(pxId_, "setState", new Class[] { SSLOptions.class, Boolean.TYPE, Boolean.TYPE, Integer.TYPE, String.class, SocketProperties.class, String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE }, new Object[] { useSSLConnection, Boolean.valueOf(canUseNativeOptimization), Boolean.valueOf(threadUsed), Integer.valueOf(ccsid), nlv, socketProperties, ddmRDB, Boolean.valueOf(mustUseNetSockets), Boolean.valueOf(mustUseSuppliedProfile), Boolean.valueOf(mustAddLanguageLibrary) } ); + connection_.callMethod(pxId_, "setState", new Class[] { SSLOptions.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Integer.TYPE, String.class, SocketProperties.class, String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE }, new Object[] { useSSLConnection, Boolean.valueOf(canUseNativeOptimization), Boolean.valueOf(threadUsed), Boolean.valueOf(virtualThreads), Integer.valueOf(ccsid), nlv, socketProperties, ddmRDB, Boolean.valueOf(mustUseNetSockets), Boolean.valueOf(mustUseSuppliedProfile), Boolean.valueOf(mustAddLanguageLibrary) } ); } catch (InvocationTargetException e) { diff --git a/src/main/java/com/ibm/as400/access/AS400ImplRemote.java b/src/main/java/com/ibm/as400/access/AS400ImplRemote.java index 23628678..68387266 100644 --- a/src/main/java/com/ibm/as400/access/AS400ImplRemote.java +++ b/src/main/java/com/ibm/as400/access/AS400ImplRemote.java @@ -109,6 +109,8 @@ public class AS400ImplRemote implements AS400Impl private boolean canUseNativeOptimization_ = true; // Flag that indicates if we use threads in communication with the host servers. private boolean threadUsed_ = true; + // Flag that indicates if we use virtual threads in communication with the host servers. + private boolean virtualThreads_ = true; // CCSID to use in conversations with the system. private int ccsid_ = 0; @@ -1684,7 +1686,7 @@ synchronized AS400Server getConnection(int service, int overridePort, boolean fo // the AS400Server object before passing it back to the caller. // Construct a new server... - server = (threadUsed_) ? new AS400ThreadedServer(this, service, socketContainer, jobString) + server = (threadUsed_) ? new AS400ThreadedServer(this, service, socketContainer, jobString, virtualThreads_) : new AS400NoThreadServer(this, service, socketContainer, jobString); } else @@ -1795,7 +1797,7 @@ private AS400Server getConnectionViaHOSTCNN(int service, int overridePort, bool jobString = obtainJobIdForConnection(HCSRouteReply.getJobNameBytes()); // Construct a new server... - return (threadUsed_) ? new AS400ThreadedServer(this, service, socketContainer, jobString) + return (threadUsed_) ? new AS400ThreadedServer(this, service, socketContainer, jobString, virtualThreads_) : new AS400NoThreadServer(this, service, socketContainer, jobString); } catch (IOException | AS400SecurityException | RuntimeException e) @@ -3251,7 +3253,7 @@ public void setServicePortsToDefault(String systemName) { // Set the state variables for this implementation object. @Override public void setState(SSLOptions useSSLConnection, - boolean canUseNativeOptimization, boolean threadUsed, int ccsid, + boolean canUseNativeOptimization, boolean threadUsed, boolean virtualThreads, int ccsid, String nlv, SocketProperties socketProperties, String ddmRDB, boolean mustUseNetSockets, boolean mustUseSuppliedProfile, boolean mustAddLanguageLibrary) @@ -3274,6 +3276,7 @@ public void setState(SSLOptions useSSLConnection, useSSLConnection_ = useSSLConnection; canUseNativeOptimization_ = canUseNativeOptimization; threadUsed_ = threadUsed; + virtualThreads_ = virtualThreads; if (ccsid != 0) { userOverrideCcsid_ = true; diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java index afef8124..4d18454b 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java @@ -691,6 +691,14 @@ public Connection getConnection(String user, char[] password, char[] additionalA { /*ignore*/ //@PDA } //@PDA + try + { + if(as400_.isVirtualThreads()) + as400Object.setVirtualThreads(true); + } catch (PropertyVetoException pve) + { /*ignore*/ + } + //set gui available on the new object to false if user turned prompting off try { //@C2A @@ -3920,23 +3928,6 @@ public void setUseSock5(boolean value) if (JDTrace.isTraceOn()) JDTrace.logInformation (this, property + ": " + value); } - /** - * Sets the flag to use virtual threads - * @param value Flag how proxy property is used. - **/ - public void setVirtualThreads(boolean value) - { - String property = JDProperties.VIRTUAL_THREADS_ ; - Boolean oldValue = Boolean.valueOf(isVirtualThreads()); - Boolean newValue = Boolean.valueOf(value); - - properties_.setString(JDProperties.VIRTUAL_THREADS, value ? TRUE_ : FALSE_); - - changes_.firePropertyChange(property, oldValue, newValue); - - if (JDTrace.isTraceOn()) - JDTrace.logInformation (this, property + ": " + value); - } /** * Sets the source of the text for REMARKS columns in ResultSets returned @@ -4399,6 +4390,33 @@ public void setThreadUsed(boolean threadUsed) JDTrace.logInformation (this, "threadUsed: " + threadUsed); //@A8C } + /** + * Sets the flag to use virtual threads + * @param threadUsed true if a virtual thread is used; false otherwise. + * The default value is false. + **/ + public void setVirtualThreads(boolean virtualThreads) + { + String property = JDProperties.VIRTUAL_THREADS_ ; + Boolean oldValue = Boolean.valueOf(isVirtualThreads()); + Boolean newValue = Boolean.valueOf(virtualThreads); + + properties_.setString(JDProperties.VIRTUAL_THREADS, virtualThreads ? TRUE_ : FALSE_); + + try + { + as400_.setVirtualThreads(virtualThreads); + } + catch (PropertyVetoException pve) + { /* Will never happen */ + } + + changes_.firePropertyChange(property, oldValue, newValue); + + if (JDTrace.isTraceOn()) + JDTrace.logInformation (this, property + ": " + virtualThreads); + } + /** * Sets the time format used in time literals with SQL statements. * @param timeFormat The time format. diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCDriver.java b/src/main/java/com/ibm/as400/access/AS400JDBCDriver.java index 902bf670..b5f558a1 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCDriver.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCDriver.java @@ -1106,6 +1106,7 @@ static AS400 initializeAS400(JDDataSourceURL dataSourceUrl, String prompt = jdProperties.getString (JDProperties.PROMPT); // @B8C boolean secure = jdProperties.getBoolean (JDProperties.SECURE); boolean useThreads = jdProperties.getBoolean(JDProperties.THREAD_USED); + boolean virtualThreads = jdProperties.getBoolean(JDProperties.VIRTUAL_THREADS); // Updated 2023 to not pass old Properties information. // Everything should be in the JDProperties object @@ -1236,6 +1237,13 @@ else if (clearPassword == null) } catch(java.beans.PropertyVetoException e){ } + //Determine if virtual threads should be used in communication with the host servers + try{ + if(virtualThreads) + as400.setVirtualThreads(virtualThreads); + } + catch(java.beans.PropertyVetoException e){ + } if(forcePrompt) //@prompt as400.forcePrompt(); //@prompt return as400; diff --git a/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java b/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java index e5164309..d58a2868 100644 --- a/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java +++ b/src/main/java/com/ibm/as400/access/AS400ThreadedServer.java @@ -234,7 +234,7 @@ public static Thread newThread(Runnable r, String name, boolean virtual) { return t; } - AS400ThreadedServer(AS400ImplRemote system, int service, SocketContainer socket, String jobString) throws IOException + AS400ThreadedServer(AS400ImplRemote system, int service, SocketContainer socket, String jobString, boolean virtual) throws IOException { system_ = system; service_ = service; @@ -254,7 +254,7 @@ public static Thread newThread(Runnable r, String name, boolean virtual) { if (jobString != null && jobString.length() != 0) jobID = jobString; else jobID = AS400.getServerName(service) + "/" + (++threadCount_); - readDaemon_ = newThread(this, "AS400 Read Daemon [system:"+system.getSystemName() + ";job:" + jobID + "]", true); + readDaemon_ = newThread(this, "AS400 Read Daemon [system:"+system.getSystemName() + ";job:" + jobID + "]", virtual); readDaemon_.start(); } diff --git a/src/main/java/com/ibm/as400/access/ConnectionList.java b/src/main/java/com/ibm/as400/access/ConnectionList.java index 01585b05..adcd3e9b 100644 --- a/src/main/java/com/ibm/as400/access/ConnectionList.java +++ b/src/main/java/com/ibm/as400/access/ConnectionList.java @@ -152,8 +152,9 @@ private PoolItem createNewConnection(int service, boolean connect, boolean secur } boolean threadUse = properties_.isThreadUsed(); + boolean virtualThreads = properties_.isVirtualThreads(); // create a new connection - PoolItem sys = new PoolItem (systemName_, userID_, poolAuth, secure, locale, service, connect, threadUse, socketProperties, ccsid, rootSystem); + PoolItem sys = new PoolItem (systemName_, userID_, poolAuth, secure, locale, service, connect, threadUse, virtualThreads, socketProperties, ccsid, rootSystem); // set the item is in use since we are going to return it to caller sys.setInUse(true); diff --git a/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java b/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java index b58cd0ef..71c4af81 100644 --- a/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java +++ b/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java @@ -60,6 +60,7 @@ class ConnectionPoolProperties implements Serializable private long maxUseTime_ = -1; // maximum usage time, release after this period, -1 for never private boolean pretestConnections_ = defaultPretestConnections_; private boolean useThreads_ = true; + private boolean virtualThreads_ = false; private int ccsid_ = -1; // CCSID to use when creating connections transient private PropertyChangeSupport changes_; @@ -109,7 +110,16 @@ public boolean isThreadUsed() return useThreads_; } - + /** + * Indicates whether threads are used in communication with the host servers. + * The default value is true. + * @return true if threads are used; false otherwise. + **/ + public boolean isVirtualThreads() + { + return virtualThreads_; + } + /** * Returns the CCSID used for connections in the pool. * Special value {@link #CCSID_DEFAULT CCSID_DEFAULT} is the default. diff --git a/src/main/java/com/ibm/as400/access/PoolItem.java b/src/main/java/com/ibm/as400/access/PoolItem.java index 0302e8d5..8f60d379 100644 --- a/src/main/java/com/ibm/as400/access/PoolItem.java +++ b/src/main/java/com/ibm/as400/access/PoolItem.java @@ -50,7 +50,7 @@ class PoolItem * **/ PoolItem(String systemName, String userID, AS400ConnectionPoolAuthentication poolAuth, boolean secure, Locale locale, - int service, boolean connect, boolean threadUse, SocketProperties socketProperties, int ccsid, AS400 rootSystem) throws AS400SecurityException, IOException + int service, boolean connect, boolean threadUse, boolean virtualThreads, SocketProperties socketProperties, int ccsid, AS400 rootSystem) throws AS400SecurityException, IOException { char[] password = null; @@ -93,7 +93,10 @@ else if (poolAuth.getAuthenticationScheme() == AS400.AUTHENTICATION_SCHEME_PROFI AS400object_.setGuiAvailable(false); if (!threadUse) - AS400object_.setThreadUsed(false); + AS400object_.setThreadUsed(false); + + if (virtualThreads) + AS400object_.setVirtualThreads(true); if (socketProperties != null) AS400object_.setSocketProperties(socketProperties); From ddd288d07fa4995b374ab51d884a0ea2d2e076e9 Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:52:43 +0100 Subject: [PATCH 5/6] plumbing / 2 Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- .../ibm/as400/access/AS400ConnectionPool.java | 4 +++ .../com/ibm/as400/access/ConnectionPool.java | 9 +++++ .../access/ConnectionPoolProperties.java | 35 +++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/AS400ConnectionPool.java b/src/main/java/com/ibm/as400/access/AS400ConnectionPool.java index 8e3fb111..e218acc3 100644 --- a/src/main/java/com/ibm/as400/access/AS400ConnectionPool.java +++ b/src/main/java/com/ibm/as400/access/AS400ConnectionPool.java @@ -104,6 +104,7 @@ public class AS400ConnectionPool extends ConnectionPool implements Serializable, private static final String PRETEST_CONNECTIONS_PROPERTY = "pretestConnections"; private static final String RUN_MAINTENANCE_PROPERTY = "runMaintenance"; private static final String THREAD_USED_PROPERTY = "threadUsed"; + private static final String VIRTUAL_THREADS_PROPERTY = "virtualThreads"; /** Indicates that the CCSID used for new connections is the same as the system default CCSID. @@ -172,6 +173,9 @@ public AS400ConnectionPool() case THREAD_USED_PROPERTY: setThreadUsed(Boolean.parseBoolean(value)); break; + case VIRTUAL_THREADS_PROPERTY: + setVirtualThreads(Boolean.parseBoolean(value)); + break; default: if (SocketProperties.isSocketProperty(property)) { if (socketProperties_ == null) { diff --git a/src/main/java/com/ibm/as400/access/ConnectionPool.java b/src/main/java/com/ibm/as400/access/ConnectionPool.java index 26dc31ea..2fae042c 100644 --- a/src/main/java/com/ibm/as400/access/ConnectionPool.java +++ b/src/main/java/com/ibm/as400/access/ConnectionPool.java @@ -451,4 +451,13 @@ public void setRunMaintenance(boolean cleanup) public void setThreadUsed(boolean useThreads) { properties_.setThreadUsed(useThreads, isInUse()); } + + /** + * Sets whether the IBM Toolbox for Java uses virtual threads. + * The default value is false. + * @param useThreads true to use virtual threads; false otherwise. + **/ + public void setVirtualThreads(boolean virtualThreads) { + properties_.setVirtualThreads(virtualThreads, isInUse()); + } } diff --git a/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java b/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java index 71c4af81..6de28682 100644 --- a/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java +++ b/src/main/java/com/ibm/as400/access/ConnectionPoolProperties.java @@ -111,9 +111,9 @@ public boolean isThreadUsed() } /** - * Indicates whether threads are used in communication with the host servers. + * Indicates whether virtual threads are used in communication with the host servers. * The default value is true. - * @return true if threads are used; false otherwise. + * @return true if virtual threads are used; false otherwise. **/ public boolean isVirtualThreads() { @@ -382,6 +382,37 @@ public void setThreadUsed(boolean useThreads, boolean isInUse) useThreads_ = useThreads; if (changes_ != null) changes_.firePropertyChange(property, Boolean.valueOf(oldValue), Boolean.valueOf(useThreads)); + } + + /** + * Sets whether the IBM Toolbox for Java uses virtual threads in communication with the host servers + * and whether the pool uses threads for running maintenance. + * The default value is false. + * @param useThreads true to use threads; false otherwise. + * @param isInUse + **/ + public void setVirtualThreads(boolean virtualThreads, boolean isInUse) + { + if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "ConnectionPoolProperties.setThreadUsed("+virtualThreads+","+isInUse+")"); + + String property = "threadUsed"; + // + // If the value does not change, just return + // + if (virtualThreads_ == virtualThreads) { + return; + } + + if (isInUse) + throw new ExtendedIllegalStateException(property, ExtendedIllegalStateException.PROPERTY_NOT_CHANGED); + +// Boolean oldValue = Boolean.valueOf(isThreadUsed()); +// Boolean newValue = Boolean.valueOf(useThreads); + boolean oldValue = virtualThreads_; + + virtualThreads_ = virtualThreads; + if (changes_ != null) changes_.firePropertyChange(property, Boolean.valueOf(oldValue), Boolean.valueOf(virtualThreads)); + } /** From 605cfda61e15c0b9ba0a288c609cc1b8a3e5a11c Mon Sep 17 00:00:00 2001 From: mdg1349 <142481417+mdg1349@users.noreply.github.com> Date: Thu, 8 Jan 2026 13:06:56 +0100 Subject: [PATCH 6/6] javadoc Signed-off-by: mdg1349 <142481417+mdg1349@users.noreply.github.com> --- src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java | 2 +- src/main/java/com/ibm/as400/access/ConnectionPool.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java index 4d18454b..7c815444 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCDataSource.java @@ -4392,7 +4392,7 @@ public void setThreadUsed(boolean threadUsed) /** * Sets the flag to use virtual threads - * @param threadUsed true if a virtual thread is used; false otherwise. + * @param virtualThreads true if a virtual thread is used; false otherwise. * The default value is false. **/ public void setVirtualThreads(boolean virtualThreads) diff --git a/src/main/java/com/ibm/as400/access/ConnectionPool.java b/src/main/java/com/ibm/as400/access/ConnectionPool.java index 2fae042c..60de27c9 100644 --- a/src/main/java/com/ibm/as400/access/ConnectionPool.java +++ b/src/main/java/com/ibm/as400/access/ConnectionPool.java @@ -455,7 +455,7 @@ public void setThreadUsed(boolean useThreads) { /** * Sets whether the IBM Toolbox for Java uses virtual threads. * The default value is false. - * @param useThreads true to use virtual threads; false otherwise. + * @param virtualThreads true to use virtual threads; false otherwise. **/ public void setVirtualThreads(boolean virtualThreads) { properties_.setVirtualThreads(virtualThreads, isInUse());