diff --git a/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnection.java b/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnection.java index 5797c1265..779a29fdf 100644 --- a/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnection.java +++ b/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnection.java @@ -38,6 +38,7 @@ import java.util.Map; import org.identityconnectors.common.StringUtil; +import org.identityconnectors.common.IOUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.databasetable.mapping.AttributeConvertor; @@ -289,7 +290,7 @@ public void test() { // nothing to do, just invalidate the connection throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, sql), ex); } finally { - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(stmt); } } } diff --git a/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnector.java b/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnector.java index ed3722c3b..ea7ff3661 100644 --- a/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnector.java +++ b/OpenICF-databasetable-connector/src/main/java/org/identityconnectors/databasetable/DatabaseTableConnector.java @@ -58,6 +58,7 @@ import org.identityconnectors.common.Assertions; import org.identityconnectors.common.CollectionUtil; +import org.identityconnectors.common.IOUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; @@ -323,7 +324,7 @@ public Uid create(ObjectClass oclass, Set attrs, OperationOptions opt } } finally { // clean up... - SQLUtil.closeQuietly(pstmt); + IOUtil.quietClose(pstmt); closeConnection(); } log.ok("Account {0} created", accountName); @@ -404,7 +405,7 @@ public void delete(final ObjectClass oclass, final Uid uid, final OperationOptio throw new ConnectorException(config.getMessage(MSG_CAN_NOT_DELETE, accountUid), e); } finally { // clean up.. - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(stmt); closeConnection(); } log.ok("Account Uid {0} deleted", accountUid); @@ -489,7 +490,7 @@ public Uid update(ObjectClass oclass, Uid uid, Set attrs, OperationOp } } finally { // clean up.. - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(stmt); closeConnection(); } @@ -562,8 +563,8 @@ public void executeQuery(ObjectClass oclass, FilterWhereBuilder where, ResultsHa throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, tblname), e); } } finally { - SQLUtil.closeQuietly(result); - SQLUtil.closeQuietly(statement); + IOUtil.quietClose(result); + IOUtil.quietClose(statement); closeConnection(); } log.ok("Query Account commited"); @@ -643,8 +644,8 @@ public void sync(ObjectClass oclass, SyncToken token, SyncResultsHandler handler SQLUtil.rollbackQuietly(getConn()); throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, tblname), e); } finally { - SQLUtil.closeQuietly(result); - SQLUtil.closeQuietly(statement); + IOUtil.quietClose(result); + IOUtil.quietClose(statement); closeConnection(); } @@ -701,8 +702,8 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, tblname), e); } finally { // clean up.. - SQLUtil.closeQuietly(rset); - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(rset); + IOUtil.quietClose(stmt); closeConnection(); } @@ -840,8 +841,8 @@ public Uid authenticate(ObjectClass oclass, String username, GuardedString passw SQLUtil.rollbackQuietly(getConn()); throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, config.getTable()), e); } finally { - SQLUtil.closeQuietly(result); - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(result); + IOUtil.quietClose(stmt); closeConnection(); } @@ -904,8 +905,8 @@ public Uid resolveUsername(ObjectClass oclass, String username, OperationOptions SQLUtil.rollbackQuietly(getConn()); throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, config.getTable()), e); } finally { - SQLUtil.closeQuietly(result); - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(result); + IOUtil.quietClose(stmt); closeConnection(); } @@ -1059,8 +1060,8 @@ private Set buildSelectBasedAttributeInfos() { SQLUtil.rollbackQuietly(getConn()); throw new ConnectorException(config.getMessage(MSG_CAN_NOT_READ, config.getTable()), ex); } finally { - SQLUtil.closeQuietly(rset); - SQLUtil.closeQuietly(stmt); + IOUtil.quietClose(rset); + IOUtil.quietClose(stmt); } log.ok("schema created"); return attrInfo; diff --git a/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/DatabaseConnection.java b/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/DatabaseConnection.java index 7671eff5c..b7b23f508 100644 --- a/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/DatabaseConnection.java +++ b/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/DatabaseConnection.java @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.dbcommon; @@ -30,6 +31,7 @@ import java.util.List; import org.identityconnectors.common.logging.Log; +import org.identityconnectors.common.IOUtil; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.spi.Configuration; @@ -76,9 +78,8 @@ public DatabaseConnection(final Connection conn) { /** * Closes the internal {@link java.sql.Connection}. */ - @SuppressWarnings("deprecation") public void dispose() { - SQLUtil.closeQuietly(nativeConn); + IOUtil.quietClose(nativeConn); } /** diff --git a/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/SQLUtil.java b/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/SQLUtil.java index 6aef2ed80..2d75528a1 100644 --- a/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/SQLUtil.java +++ b/OpenICF-dbcommon/src/main/java/org/identityconnectors/dbcommon/SQLUtil.java @@ -320,7 +320,7 @@ public static void closeQuietly(final Connection connection) { */ public static void closeQuietly(final DatabaseConnection conn) { if (conn != null) { - closeQuietly(conn.getConnection()); + IOUtil.quietClose(conn.getConnection()); } } @@ -434,7 +434,7 @@ public static java.sql.Date string2Date(String param) { } catch (IllegalArgumentException e) { // Wrong string, cloud be a string number try { - parsedDate = new java.sql.Date(new Long(param)); + parsedDate = new java.sql.Date(Long.parseLong(param)); } catch (NumberFormatException expected) { // Locale parsed date, possible lost of precision try { @@ -462,7 +462,7 @@ public static java.sql.Timestamp string2Timestamp(String param) { } catch (IllegalArgumentException e) { // Wrong string, cloud be a number try { - parsedTms = new java.sql.Timestamp(new Long(param)); + parsedTms = new java.sql.Timestamp(Long.parseLong(param)); } catch (NumberFormatException expected) { // Locale parsed date, possible lost of precision try { @@ -1051,8 +1051,8 @@ public static Object selectSingleValue(Connection conn, String sql, SQLParam... } return null; } finally { - closeQuietly(rs); - closeQuietly(st); + IOUtil.quietClose(rs); + IOUtil.quietClose(st); } } @@ -1096,8 +1096,8 @@ public static List selectRows(Connection conn, String sql, SQLParam... } return rows; } finally { - closeQuietly(rs); - closeQuietly(st); + IOUtil.quietClose(rs); + IOUtil.quietClose(st); } } @@ -1128,7 +1128,7 @@ public static int executeUpdateStatement(Connection conn, String sql, SQLParam.. setParams(st, Arrays.asList(params)); return st.executeUpdate(); } finally { - closeQuietly(st); + IOUtil.quietClose(st); } } } diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/GroovyDataProvider.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/GroovyDataProvider.java index 0263c309b..a64a73228 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/GroovyDataProvider.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/GroovyDataProvider.java @@ -238,8 +238,8 @@ private void initQueriedPropsDump() { if (StringUtil.isNotBlank(pOut)) { try { _queriedPropsOutFile = new File(pOut); - if (!_queriedPropsOutFile.exists()) { - _queriedPropsOutFile.createNewFile(); + if (!_queriedPropsOutFile.createNewFile() && !_queriedPropsOutFile.isFile()) { + throw new IOException("Could not create " + _queriedPropsOutFile); } if (!_queriedPropsOutFile.canWrite()) { _queriedPropsOutFile = null; @@ -248,6 +248,7 @@ private void initQueriedPropsDump() { LOG.info("Storing parameter values to ''{0}'', you can rerun the test with the same parameters later", pOut); } } catch (IOException iOException) { + _queriedPropsOutFile = null; LOG.warn("Unable to create ''{0}'' file, the test parameters will not be stored", pOut); } } @@ -263,8 +264,8 @@ private void initSnapshot() { if (StringUtil.isNotBlank(pOut)) { try { _propertyOutFile = new File(pOut); - if (!_propertyOutFile.exists()) { - _propertyOutFile.createNewFile(); + if (!_propertyOutFile.createNewFile() && !_propertyOutFile.isFile()) { + throw new IOException("Could not create " + _propertyOutFile); } if (!_propertyOutFile.canWrite()) { _propertyOutFile = null; @@ -273,6 +274,7 @@ private void initSnapshot() { LOG.info("Storing parameter values to ''{0}'', you can rerun the test with the same parameters later", pOut); } } catch (IOException iOException) { + _propertyOutFile = null; LOG.warn("Unable to create ''{0}'' file, the test parameters will not be stored", pOut); } } diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/RandomGenerator.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/RandomGenerator.java index 0f77cb6e4..ca03d4819 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/RandomGenerator.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/RandomGenerator.java @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.contract.data; @@ -154,11 +155,11 @@ private static Map> getDefaultCharacterSetMap() { alpha_mixed.addAll(alpha_upper); // setup the mappings - characterSetMap.put(new Character('#'), numeric); - characterSetMap.put(new Character('a'), alpha_lower); - characterSetMap.put(new Character('A'), alpha_upper); - characterSetMap.put(new Character('?'), alpha_mixed); - characterSetMap.put(new Character('.'), alpha_numeric); + characterSetMap.put(Character.valueOf('#'), numeric); + characterSetMap.put(Character.valueOf('a'), alpha_lower); + characterSetMap.put(Character.valueOf('A'), alpha_upper); + characterSetMap.put(Character.valueOf('?'), alpha_mixed); + characterSetMap.put(Character.valueOf('.'), alpha_numeric); return characterSetMap; } @@ -179,7 +180,7 @@ private static void addRange(Set s, char min, char max) { } for (char i = min; i <= max; i++) { - s.add(new Character(i)); + s.add(Character.valueOf(i)); } } @@ -212,7 +213,7 @@ private static String createRandomString(String pattern, Map> characterSetMap) { StringBuffer replacement = new StringBuffer(); for (int i = 0; i < pattern.length(); i++) { - Set characterSet = characterSetMap.get(new Character( + Set characterSet = characterSetMap.get(Character.valueOf( pattern.charAt(i))); if (pattern.charAt(i) == '\\') { // do escape, and print the next character diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java index ae9be0048..5e763fb60 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java @@ -31,6 +31,7 @@ import static org.testng.Assert.assertTrue; import java.io.File; +import java.lang.invoke.MethodHandles; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; @@ -44,6 +45,7 @@ import org.identityconnectors.common.CollectionUtil; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.contract.data.DataProvider; @@ -119,10 +121,10 @@ public static DataProvider createDataProvider() { throw new Exception("Class " + customDataProvider + " is not of type " + DataProvider.class.getName()); } - dp = (DataProvider) dpClass.newInstance(); + dp = (DataProvider) ReflectionUtil.newInstance(MethodHandles.lookup(), dpClass); } else { logger.info("DataProvider class not specified, using default ''"+DEFAULT_DATA_PROVIDER+"''."); - dp = (DataProvider) DEFAULT_DATA_PROVIDER.newInstance(); + dp = (DataProvider) ReflectionUtil.newInstance(MethodHandles.lookup(), DEFAULT_DATA_PROVIDER); } } catch (Exception ex) { throw ContractException.wrap(ex); diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java index dabb83b72..b273ef188 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java @@ -21,6 +21,7 @@ * ==================== * * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2026 3A Systems, LLC * */ package org.identityconnectors.contract.test; @@ -580,7 +581,7 @@ public void testPasswordChangeIntervalPredAttribute(ObjectClass objectClass) { * @param createValue value used for create * @param updateValue value used for update * @param type expected type of the value - * @param addPassword, add password to attributes in the update + * @param addPassword add password to attributes in the update */ private void checkOpAttribute(ObjectClass objectClass, String attrName, Object createValue, Object updateValue, Class type, boolean addPassword) { final int iteration = 0; diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/ConnectorPoolManager.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/ConnectorPoolManager.java index d580a08e1..1513ae19a 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/ConnectorPoolManager.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/ConnectorPoolManager.java @@ -20,15 +20,18 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.framework.impl.api.local; +import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.identityconnectors.common.Pair; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.pooling.ObjectPoolConfiguration; import org.identityconnectors.framework.api.ConfigurationPropertyChangeListener; @@ -165,7 +168,7 @@ public PoolableConnector makeObject() { config = context.getConfiguration(); } - connector = (PoolableConnector) clazz.newInstance(); + connector = (PoolableConnector) ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); connector.init(config); } else { throw new ConnectorException("The Connector is not PoolableConnector: " diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/JavaClassProperties.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/JavaClassProperties.java index 1769a5b1f..34f614237 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/JavaClassProperties.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/JavaClassProperties.java @@ -28,6 +28,7 @@ import java.beans.IndexedPropertyDescriptor; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.text.MessageFormat; @@ -216,7 +217,7 @@ private static Set> translateOperations( private static Configuration createBean2(ConfigurationPropertiesImpl properties, Class configClass) throws Exception { - Configuration rv = configClass.newInstance(); + Configuration rv = ReflectionUtil.newInstance(MethodHandles.lookup(), configClass); rv.setConnectorMessages(properties.getParent().getConnectorInfo().getMessages()); mergeIntoBean2(properties, rv); return rv; diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/LocalConnectorInfoManagerImpl.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/LocalConnectorInfoManagerImpl.java index 18c5701d6..7be5c3605 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/LocalConnectorInfoManagerImpl.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/LocalConnectorInfoManagerImpl.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -326,7 +327,7 @@ public static APIConfigurationImpl createDefaultAPIConfiguration( try { final Class connectorClass = localInfo.getConnectorClass(); final APIConfigurationImpl rv = new APIConfigurationImpl(); - final Configuration config = localInfo.getConnectorConfigurationClass().newInstance(); + final Configuration config = ReflectionUtil.newInstance(MethodHandles.lookup(), localInfo.getConnectorConfigurationClass()); final boolean pooling = PoolableConnector.class.isAssignableFrom(connectorClass); rv.setConnectorPoolingSupported(pooling); rv.setConfigurationProperties(JavaClassProperties.createConfigurationProperties(config)); diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ConnectorAPIOperationRunnerProxy.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ConnectorAPIOperationRunnerProxy.java index ad5ba971e..6f4cc9ee3 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ConnectorAPIOperationRunnerProxy.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ConnectorAPIOperationRunnerProxy.java @@ -20,15 +20,18 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2015 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.framework.impl.api.local.operations; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.objects.Subscription; import org.identityconnectors.framework.impl.api.local.ObjectPool; @@ -95,7 +98,7 @@ public Object invoke(Object proxy, Method method, Object[] args) } else { // get a new instance of the connector.. - connector = connectorClazz.newInstance(); + connector = ReflectionUtil.newInstance(MethodHandles.lookup(), connectorClazz); // initialize the connector.. connector.init(context.getConfiguration()); } diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/server/ConnectorServer.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/server/ConnectorServer.java index 40d0f72e1..80e673cae 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/server/ConnectorServer.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/server/ConnectorServer.java @@ -25,6 +25,7 @@ package org.identityconnectors.framework.server; +import java.lang.invoke.MethodHandles; import java.net.InetAddress; import java.net.URL; import java.util.List; @@ -32,6 +33,7 @@ import javax.net.ssl.KeyManager; import org.identityconnectors.common.CollectionUtil; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; @@ -110,7 +112,7 @@ public abstract class ConnectorServer { public static ConnectorServer newInstance() { try { final Class clazz = Class.forName(IMPL_NAME); - return (ConnectorServer) clazz.newInstance(); + return (ConnectorServer) ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); } catch (Exception e) { throw ConnectorException.wrap(e); } diff --git a/OpenICF-java-framework/connector-framework-osgi/src/main/java/org/forgerock/openicf/framework/impl/api/osgi/internal/OsgiConnectorInfoManagerImpl.java b/OpenICF-java-framework/connector-framework-osgi/src/main/java/org/forgerock/openicf/framework/impl/api/osgi/internal/OsgiConnectorInfoManagerImpl.java index 1f8805904..ffcd7483d 100644 --- a/OpenICF-java-framework/connector-framework-osgi/src/main/java/org/forgerock/openicf/framework/impl/api/osgi/internal/OsgiConnectorInfoManagerImpl.java +++ b/OpenICF-java-framework/connector-framework-osgi/src/main/java/org/forgerock/openicf/framework/impl/api/osgi/internal/OsgiConnectorInfoManagerImpl.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandles; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -335,7 +336,7 @@ private APIConfigurationImpl createDefaultAPIConfiguration(LocalConnectorInfoImp try { Class connectorClass = localInfo.getConnectorClass(); APIConfigurationImpl rv = new APIConfigurationImpl(); - Configuration config = localInfo.getConnectorConfigurationClass().newInstance(); + Configuration config = ReflectionUtil.newInstance(MethodHandles.lookup(), localInfo.getConnectorConfigurationClass()); boolean pooling = PoolableConnector.class.isAssignableFrom(connectorClass); rv.setConnectorPoolingSupported(pooling); rv.setConfigurationProperties(JavaClassProperties.createConfigurationProperties(config)); diff --git a/OpenICF-java-framework/connector-framework-server/src/main/java/org/forgerock/openicf/framework/osgi/internal/AsyncOsgiConnectorInfoManagerImpl.java b/OpenICF-java-framework/connector-framework-server/src/main/java/org/forgerock/openicf/framework/osgi/internal/AsyncOsgiConnectorInfoManagerImpl.java index 03b139fd8..513a99785 100644 --- a/OpenICF-java-framework/connector-framework-server/src/main/java/org/forgerock/openicf/framework/osgi/internal/AsyncOsgiConnectorInfoManagerImpl.java +++ b/OpenICF-java-framework/connector-framework-server/src/main/java/org/forgerock/openicf/framework/osgi/internal/AsyncOsgiConnectorInfoManagerImpl.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandles; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -242,7 +243,7 @@ private APIConfigurationImpl createDefaultAPIConfiguration(OsgiConnectorInfoImpl try { Class connectorClass = localInfo.getConnectorClass(); APIConfigurationImpl rv = new APIConfigurationImpl(); - Configuration config = localInfo.getConnectorConfigurationClass().newInstance(); + Configuration config = ReflectionUtil.newInstance(MethodHandles.lookup(), localInfo.getConnectorConfigurationClass()); boolean pooling = PoolableConnector.class.isAssignableFrom(connectorClass); rv.setConnectorPoolingSupported(pooling); rv.setConfigurationProperties(JavaClassProperties.createConfigurationProperties(config)); diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/IOUtil.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/IOUtil.java index 9a9921718..a1814f9da 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/IOUtil.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/IOUtil.java @@ -183,7 +183,7 @@ public static void quietClose(final Statement stmt) { */ public static void quietClose(final Connection conn) { try { - if (conn != null) { + if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java index 4477f8c50..743919d8b 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2015 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common; @@ -96,13 +97,13 @@ protected String toPrettyString(final Object obj) { s.append(')'); } else if (obj instanceof Map) { final Map map = (Map) obj; - final Iterator it = map.keySet().iterator(); + final Iterator it = map.entrySet().iterator(); int i = 0; s.append('{'); while ((it.hasNext() && (i++ < maxArrayLen))) { - final Object key = it.next(); - s.append(key).append(':'); - s.append(toPrettyString(map.get(key))); + final Map.Entry entry = (Map.Entry) it.next(); + s.append(entry.getKey()).append(':'); + s.append(toPrettyString(entry.getValue())); if (it.hasNext()) { s.append(","); } diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/ReflectionUtil.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/ReflectionUtil.java index 7fef46a69..acfc24908 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/ReflectionUtil.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/ReflectionUtil.java @@ -19,10 +19,15 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyright 2026 3A Systems, LLC. */ package org.identityconnectors.common; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -116,6 +121,46 @@ public static boolean overridesEqualsAndHashcode(final Class clazz) { } } + /** + * Creates a new instance of the class through its no-argument constructor, + * as the deprecated {@link Class#newInstance()} did. + *

+ * Unlike a bare {@code getDeclaredConstructor().newInstance()}, whatever + * the constructor throws reaches the caller unchanged instead of wrapped + * in an {@link java.lang.reflect.InvocationTargetException}, so callers + * that pass a {@link RuntimeException} through or wrap a checked exception + * keep seeing the original exception type and message. + *

+ * Access to the constructor is checked against {@code lookup}, so the + * caller passes its own {@link MethodHandles#lookup()}: a package-private + * class stays instantiable from its own package, as it was with + * {@link Class#newInstance()}. + * + * @param + * the type of the instance. + * @param lookup + * the caller's lookup, {@code MethodHandles.lookup()}. + * @param clazz + * class to instantiate. + * @return the new instance. + * @throws Exception + * whatever the constructor throws, or the reflective exception + * if the class has no no-argument constructor accessible to + * {@code lookup}. + */ + public static T newInstance(final MethodHandles.Lookup lookup, final Class clazz) + throws Exception { + final MethodHandle constructor = + lookup.findConstructor(clazz, MethodType.methodType(void.class)); + try { + return clazz.cast(constructor.invoke()); + } catch (Exception | Error e) { + throw e; + } catch (Throwable t) { + throw new UndeclaredThrowableException(t); + } + } + /** * Returns the package the class is associated with. * diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java index d17023730..fb821d9fb 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common; @@ -285,7 +286,7 @@ public static int indexOf(final String src, final char[] ch, final int idx) { * @return true if the string is empty else false. */ public static boolean isEmpty(final String val) { - return (val == null) ? true : "".equals(val) ? true : false; + return (val == null) || val.isEmpty(); } /** diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/logging/Log.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/logging/Log.java index ce848b981..ffce1aca0 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/logging/Log.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/logging/Log.java @@ -20,12 +20,14 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common.logging; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.text.MessageFormat; import java.util.Collection; import java.util.Properties; @@ -143,7 +145,7 @@ public static Log getLog(final Class clazz) { throw new IllegalArgumentException(); } // attempt to get an instance.. - final LogSpi logImpl = (LogSpi) getSpiClass().newInstance(); + final LogSpi logImpl = (LogSpi) ReflectionUtil.newInstance(MethodHandles.lookup(), getSpiClass()); return new Log(clazz, logImpl); } catch (RuntimeException e) { throw e; diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/script/ScriptExecutorFactory.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/script/ScriptExecutorFactory.java index 8b65b7586..7c179de54 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/script/ScriptExecutorFactory.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/script/ScriptExecutorFactory.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2015 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common.script; @@ -27,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -36,6 +38,7 @@ import java.util.Set; import org.identityconnectors.common.CollectionUtil; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.operations.ScriptOnConnectorApiOp; @@ -70,7 +73,7 @@ private static synchronized Map> getFactoryCache() { Class clazz = Class.forName(factory); // Create an instance in order to get the supported // language. - ScriptExecutorFactory instance = (ScriptExecutorFactory) clazz.newInstance(); + ScriptExecutorFactory instance = (ScriptExecutorFactory) ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); String language = instance.getLanguageName(); // Do not override a factory earlier in the classpath. if (!factoryCache.containsKey(language)) { @@ -169,7 +172,7 @@ public static ScriptExecutorFactory newInstance(String language) { } // exceptions here should not happened because of the register try { - return (ScriptExecutorFactory) clazz.newInstance(); + return (ScriptExecutorFactory) ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); } catch (RuntimeException e) { throw e; } catch (Exception e) { diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/EncryptorFactory.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/EncryptorFactory.java index d235db190..db5766299 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/EncryptorFactory.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/EncryptorFactory.java @@ -19,9 +19,14 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common.security; +import java.lang.invoke.MethodHandles; + +import org.identityconnectors.common.ReflectionUtil; + public abstract class EncryptorFactory { // At some point we might make this pluggable, but for now, hard-code @@ -37,7 +42,7 @@ public static synchronized EncryptorFactory getInstance() { if (instance == null) { try { Class clazz = Class.forName(IMPL_NAME); - Object object = clazz.newInstance(); + Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); instance = EncryptorFactory.class.cast(object); } catch (RuntimeException e) { throw e; diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorFacadeFactory.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorFacadeFactory.java index 100aabccb..2f4317f9e 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorFacadeFactory.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorFacadeFactory.java @@ -20,9 +20,13 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.framework.api; +import java.lang.invoke.MethodHandles; + +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.framework.common.exceptions.ConnectorException; /** @@ -51,7 +55,7 @@ public static synchronized ConnectorFacadeFactory getInstance() { if (instance == null) { try { final Class clazz = Class.forName(IMPL_NAME); - final Object object = clazz.newInstance(); + final Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); instance = ConnectorFacadeFactory.class.cast(object); } catch (Exception e) { throw ConnectorException.wrap(e); @@ -70,7 +74,7 @@ public static synchronized ConnectorFacadeFactory getManagedInstance() { if (managedInstance == null) { try { final Class clazz = Class.forName(IMPL_NAME_MANAGED); - final Object object = clazz.newInstance(); + final Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); managedInstance = ConnectorFacadeFactory.class.cast(object); } catch (Exception e) { throw ConnectorException.wrap(e); diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorInfoManagerFactory.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorInfoManagerFactory.java index e374921c4..8445da198 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorInfoManagerFactory.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/api/ConnectorInfoManagerFactory.java @@ -23,8 +23,10 @@ */ package org.identityconnectors.framework.api; +import java.lang.invoke.MethodHandles; import java.net.URL; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.framework.common.exceptions.ConnectorException; /** @@ -48,7 +50,7 @@ public static synchronized ConnectorInfoManagerFactory getInstance() { if (instance == null) { try { Class clazz = Class.forName(IMPL_NAME); - Object object = clazz.newInstance(); + Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); instance = ConnectorInfoManagerFactory.class.cast(object); } catch (Exception e) { throw ConnectorException.wrap(e); diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/serializer/ObjectSerializerFactory.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/serializer/ObjectSerializerFactory.java index 93622eef5..bbe1840be 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/serializer/ObjectSerializerFactory.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/serializer/ObjectSerializerFactory.java @@ -26,7 +26,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; +import java.lang.invoke.MethodHandles; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.xml.sax.InputSource; @@ -51,7 +53,7 @@ public static ObjectSerializerFactory getInstance() { if (instance == null) { try { final Class clazz = Class.forName(IMPL_NAME); - final Object object = clazz.newInstance(); + final Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); instance = ObjectSerializerFactory.class.cast(object); } catch (Exception e) { throw ConnectorException.wrap(e); diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/ReflectionUtilTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/ReflectionUtilTests.java index 32c290121..5d5214435 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/ReflectionUtilTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/ReflectionUtilTests.java @@ -19,14 +19,20 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyright 2026 3A Systems, LLC. */ package org.identityconnectors.common; import static org.identityconnectors.common.ReflectionUtil.containsInterface; import static org.identityconnectors.common.ReflectionUtil.getAllInterfaces; +import static org.identityconnectors.common.ReflectionUtil.newInstance; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.Set; import org.testng.annotations.Test; @@ -46,6 +52,46 @@ public void functional() { assertFalse(containsInterface(C1.class, I3.class)); } + @Test + public void newInstanceCallsNoArgConstructor() throws Exception { + assertTrue(newInstance(MethodHandles.lookup(), Constructible.class) instanceof Constructible); + } + + @Test + public void newInstancePassesRuntimeExceptionThroughUnchanged() throws Exception { + try { + newInstance(MethodHandles.lookup(), ThrowsRuntimeException.class); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "from constructor"); + } + } + + @Test + public void newInstancePassesCheckedExceptionThroughUnchanged() throws Exception { + try { + newInstance(MethodHandles.lookup(), ThrowsCheckedException.class); + fail("IOException expected"); + } catch (IOException e) { + assertEquals(e.getMessage(), "from constructor"); + } + } + + @Test + public void newInstancePassesErrorThroughUnchanged() throws Exception { + try { + newInstance(MethodHandles.lookup(), ThrowsError.class); + fail("NoClassDefFoundError expected"); + } catch (NoClassDefFoundError e) { + assertEquals(e.getMessage(), "from constructor"); + } + } + + @Test(expectedExceptions = NoSuchMethodException.class) + public void newInstanceFailsWithoutNoArgConstructor() throws Exception { + newInstance(MethodHandles.lookup(), NoNoArgConstructor.class); + } + // ======================================================================= // Helper Classes/Interfaces // ======================================================================= @@ -72,4 +118,31 @@ interface I5 extends I1 { class C2 implements I5 { } + + public static class Constructible { + + } + + public static class ThrowsRuntimeException { + public ThrowsRuntimeException() { + throw new IllegalArgumentException("from constructor"); + } + } + + public static class ThrowsCheckedException { + public ThrowsCheckedException() throws IOException { + throw new IOException("from constructor"); + } + } + + public static class ThrowsError { + public ThrowsError() { + throw new NoClassDefFoundError("from constructor"); + } + } + + public static class NoNoArgConstructor { + public NoNoArgConstructor(String value) { + } + } } diff --git a/OpenICF-java-framework/connector-test-common/src/main/java/org/identityconnectors/test/common/TestHelpers.java b/OpenICF-java-framework/connector-test-common/src/main/java/org/identityconnectors/test/common/TestHelpers.java index 67190cc84..47c033e2a 100644 --- a/OpenICF-java-framework/connector-test-common/src/main/java/org/identityconnectors/test/common/TestHelpers.java +++ b/OpenICF-java-framework/connector-test-common/src/main/java/org/identityconnectors/test/common/TestHelpers.java @@ -20,10 +20,12 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.test.common; import java.io.File; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; @@ -37,6 +39,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.framework.api.APIConfiguration; import org.identityconnectors.framework.api.operations.SearchApiOp; @@ -215,7 +218,7 @@ private static synchronized TestHelpersSpi getSpi() { if (instance == null) { try { Class clazz = Class.forName(IMPL_NAME); - Object object = clazz.newInstance(); + Object object = ReflectionUtil.newInstance(MethodHandles.lookup(), clazz); instance = TestHelpersSpi.class.cast(object); } catch (Exception e) { throw ConnectorException.wrap(e); @@ -308,7 +311,7 @@ public static PropertyBag getProperties(Class clazz, String environment) { Class configObject = Class.forName("groovy.util.ConfigObject"); Object slurpInstance = null; if (StringUtil.isBlank(environment)) { - slurpInstance = slurper.newInstance(); + slurpInstance = ReflectionUtil.newInstance(MethodHandles.lookup(), slurper); } else { slurpInstance = slurper.getConstructor(String.class).newInstance(environment); } diff --git a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java index b9b6ccdd4..41adde35f 100644 --- a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java +++ b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java @@ -20,6 +20,7 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.ldap.sync.activedirectory; @@ -321,7 +322,7 @@ private String gethighestCommittedUSN() { Attributes attrs = conn.getInitialContext().getAttributes("", new String[]{HCU_CHANGED_ATTR}); hcUSN = getStringAttrValue(attrs, HCU_CHANGED_ATTR); if (hcUSN == null) { - String error = "Unable to read the highestCommittedUSN attribute" + String error = "Unable to read the highestCommittedUSN attribute " + "from the rootDSE of Active Directory "; throw new ConnectorException(error); } diff --git a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorDocBuilder.java b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorDocBuilder.java index 6562b28c3..ac9ec7cbb 100644 --- a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorDocBuilder.java +++ b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorDocBuilder.java @@ -26,6 +26,7 @@ package org.forgerock.openicf.maven; import java.io.Writer; +import java.lang.invoke.MethodHandles; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +57,7 @@ import org.codehaus.plexus.util.ReaderFactory; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.Pair; +import org.identityconnectors.common.ReflectionUtil; import org.identityconnectors.framework.api.APIConfiguration; import org.identityconnectors.framework.api.ConfigurationProperty; import org.identityconnectors.framework.api.ConnectorFacadeFactory; @@ -200,7 +202,7 @@ protected void executeReport() throws MojoExecutionException { Class connectorClass = ((LocalConnectorInfoImpl) info).getConnectorClass(); try { - SchemaOp connector = (SchemaOp) connectorClass.newInstance(); + SchemaOp connector = (SchemaOp) ReflectionUtil.newInstance(MethodHandles.lookup(), connectorClass); schema = connector.schema(); } catch (Throwable t) { handler.getLog().debug("Getting Schema with Connector Instance", t); diff --git a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorInfoReportMojo.java b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorInfoReportMojo.java index 62ff4edae..f09c507d5 100644 --- a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorInfoReportMojo.java +++ b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ConnectorInfoReportMojo.java @@ -352,12 +352,8 @@ protected void executeReport(Locale locale) throws MavenReportException { } } else if (!name.endsWith(".xml")) { if (!destination.exists()) { - FileOutputStream output = null; - try { - output = new FileOutputStream(destination); + try (FileOutputStream output = new FileOutputStream(destination)) { IOUtil.copy(zip, output); - } finally { - IOUtil.close(output); } } } diff --git a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/DocBookResourceMojo.java b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/DocBookResourceMojo.java index 4e258c802..a74519e49 100644 --- a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/DocBookResourceMojo.java +++ b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/DocBookResourceMojo.java @@ -423,12 +423,8 @@ public boolean accept(File pathname) { } } else { if (!destination.exists()) { - FileOutputStream output = null; - try { - output = new FileOutputStream(destination); + try (FileOutputStream output = new FileOutputStream(destination)) { IOUtil.copy(zip, output); - } finally { - IOUtil.close(output); } } } diff --git a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ReducePomMojo.java b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ReducePomMojo.java index 957395355..a5dfa1b0b 100644 --- a/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ReducePomMojo.java +++ b/OpenICF-maven-plugin/src/main/java/org/forgerock/openicf/maven/ReducePomMojo.java @@ -144,15 +144,11 @@ public void execute() throws MojoExecutionException { f.delete(); } - Writer w = null; - try { - w = WriterFactory.newXmlWriter(f); + try (Writer w = WriterFactory.newXmlWriter(f)) { MavenXpp3Writer pomWriter = new MavenXpp3Writer(); pomWriter.write(w, model); } catch (IOException exception) { throw new MojoExecutionException("Cannot generate reduced POM", exception); - } finally { - IOUtil.close(w); } /* diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/XMLConfiguration.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/XMLConfiguration.java index 8bcd444d0..e33352bed 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/XMLConfiguration.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/XMLConfiguration.java @@ -20,6 +20,7 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted 2010 [name of copyright owner]" + * Portions Copyrighted 2026 3A Systems, LLC * * $Id$ */ @@ -106,7 +107,10 @@ public void validate() { } else if (!xmlFilePath.exists()) { if (createFileIfNotExists) { try { - xmlFilePath.getParentFile().mkdir(); + File parentFile = xmlFilePath.getParentFile(); + if (parentFile != null && !parentFile.isDirectory() && !parentFile.mkdir()) { + throw new IOException("Could not create " + parentFile); + } if (xmlFilePath.createNewFile()) { xmlFilePath.delete(); } diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java index b6bc1296b..23da35814 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java @@ -20,6 +20,7 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted 2010 [name of copyright owner]" + * Portions Copyrighted 2026 3A Systems, LLC * * $Id$ */ @@ -43,48 +44,56 @@ public class AttributeTypeUtil { public static Object createInstantiatedObject(String attrValue, String javaclass) { + try { + return createInstantiatedObjectUnchecked(attrValue, javaclass); + } catch (NumberFormatException e) { + throw new ConnectorException("Value '" + attrValue + "' is not a valid " + javaclass, e); + } + } + + private static Object createInstantiatedObjectUnchecked(String attrValue, String javaclass) + throws NumberFormatException { if (javaclass.equals(XmlHandlerUtil.STRING)) { - String s = new String(attrValue); - return s; + return attrValue; } else if (javaclass.equals(XmlHandlerUtil.INT_PRIMITIVE)) { - int i = new Integer(attrValue); + int i = Integer.valueOf(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.INTEGER)) { - Integer i = new Integer(attrValue); + Integer i = Integer.valueOf(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.LONG)) { - Long l = new Long(attrValue); + Long l = Long.valueOf(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.LONG_PRIMITIVE)) { - long l = new Long(attrValue); + long l = Long.valueOf(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN)) { - Boolean b = new Boolean(attrValue); + Boolean b = Boolean.valueOf(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN_PRIMITIVE)) { - boolean b = new Boolean(attrValue); + boolean b = Boolean.valueOf(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE)) { - Double d = new Double(attrValue); + Double d = Double.valueOf(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE_PRIMITIVE)) { - double d = new Double(attrValue); + double d = Double.valueOf(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.FLOAT)) { - Float f = new Float(attrValue); + Float f = Float.valueOf(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.FLOAT_PRIMITIVE)) { - float f = new Float(attrValue); + float f = Float.valueOf(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.CHARACTER)) { diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java index e7e0661fd..7515acc68 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java @@ -20,6 +20,7 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted 2010 [name of copyright owner]" + * Portions Copyrighted 2026 3A Systems, LLC * * $Id$ */ @@ -71,7 +72,7 @@ public void characters(char[] chars, int start, int length) throws SAXException if(parse){ StringBuilder sb = new StringBuilder(); sb.append(chars, start, length); - if(!sb.toString().replace(" ", "").trim().equals("")){ + if(!sb.toString().replace(" ", "").trim().isEmpty()){ String stringToAppend = addValue + " " + sb.toString().trim(); stringBuilder.append(stringToAppend); stringBuilder.append("\n"); diff --git a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/XMLConfigurationTests.java b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/XMLConfigurationTests.java index 2c3b7e69f..cf882a079 100644 --- a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/XMLConfigurationTests.java +++ b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/XMLConfigurationTests.java @@ -20,6 +20,7 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted 2010 [name of copyright owner]" + * Portions Copyright 2026 3A Systems, LLC. * * $Id$ */ @@ -29,6 +30,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.AssertJUnit; import java.io.File; +import java.io.IOException; public class XMLConfigurationTests { @@ -74,4 +76,21 @@ public void shouldThrowIllegalArgumentExceptionWhenValidatingWithBlankFilepath() config.setXmlFilePath(new File("")); config.validate(); } + + @Test + public void shouldValidateXmlFilePathWithoutParentDirectory() throws IOException { + File xsd = File.createTempFile("schema", ".xsd"); + File xml = new File("xml-config-" + System.nanoTime() + ".xml"); + try { + AssertJUnit.assertNull(xml.getParentFile()); + config.setXsdFilePath(xsd); + config.setXmlFilePath(xml); + config.setCreateFileIfNotExists(true); + config.validate(); + AssertJUnit.assertFalse(xml.exists()); + } finally { + xml.delete(); + xsd.delete(); + } + } } diff --git a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTest.java b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTest.java new file mode 100644 index 000000000..f6de3982e --- /dev/null +++ b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTest.java @@ -0,0 +1,85 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2026 3A Systems, LLC. + */ +package org.forgerock.openicf.connectors.xml.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.identityconnectors.framework.common.exceptions.ConnectorException; +import org.testng.annotations.Test; + +public class AttributeTypeUtilTest { + + @Test + public void createInstantiatedObjectParsesEachNumericType() { + assertEquals(AttributeTypeUtil.createInstantiatedObject("3", XmlHandlerUtil.INT_PRIMITIVE), 3); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3", XmlHandlerUtil.INTEGER), 3); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3", XmlHandlerUtil.LONG_PRIMITIVE), 3L); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3", XmlHandlerUtil.LONG), 3L); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3.5", XmlHandlerUtil.DOUBLE_PRIMITIVE), 3.5); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3.5", XmlHandlerUtil.DOUBLE), 3.5); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3.5", XmlHandlerUtil.FLOAT_PRIMITIVE), 3.5f); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3.5", XmlHandlerUtil.FLOAT), 3.5f); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3", XmlHandlerUtil.BIG_INTEGER), new BigInteger("3")); + assertEquals(AttributeTypeUtil.createInstantiatedObject("3.5", XmlHandlerUtil.BIG_DECIMAL), new BigDecimal("3.5")); + } + + /** + * A malformed numeric value in the XML data file must fail with a + * ConnectorException naming the value and the target type, not a bare + * NumberFormatException with no indication of which attribute it came + * from. + */ + @Test(dataProvider = "numericTypes") + public void createInstantiatedObjectWrapsMalformedNumber(String type) { + assertWrapsNumberFormatException("not-a-number", type); + } + + @Test(dataProvider = "numericTypes") + public void createInstantiatedObjectWrapsBlankNumber(String type) { + assertWrapsNumberFormatException("", type); + } + + private static void assertWrapsNumberFormatException(String value, String type) { + try { + AttributeTypeUtil.createInstantiatedObject(value, type); + fail("ConnectorException expected for " + type); + } catch (ConnectorException e) { + assertEquals(e.getMessage(), "Value '" + value + "' is not a valid " + type); + assertTrue(e.getCause() instanceof NumberFormatException); + } + } + + @org.testng.annotations.DataProvider + public Object[][] numericTypes() { + return new Object[][] { + { XmlHandlerUtil.INT_PRIMITIVE }, + { XmlHandlerUtil.INTEGER }, + { XmlHandlerUtil.LONG_PRIMITIVE }, + { XmlHandlerUtil.LONG }, + { XmlHandlerUtil.DOUBLE_PRIMITIVE }, + { XmlHandlerUtil.DOUBLE }, + { XmlHandlerUtil.FLOAT_PRIMITIVE }, + { XmlHandlerUtil.FLOAT }, + { XmlHandlerUtil.BIG_INTEGER }, + { XmlHandlerUtil.BIG_DECIMAL }, + }; + } +}