Repository navigation
Fix mechanical CodeQL note findings: reflection, boxing, deprecated closes - #134
Conversation
AttributeTypeUtil.java, MultiOpTests.java, PrettyStringBuilder.java, StringUtil.java, ActiveDirectoryChangeLogSyncStrategy.java and XSDAnnotationParser.java were independently fixed here and in OpenIdentityPlatform#134 with byte-identical diffs; reverting them here to avoid merging the same change twice and to let OpenIdentityPlatform#134 own them. Drops AttributeTypeUtilTests.java too since it exercises the NumberFormatException-wrapping behavior that lived in the now-reverted AttributeTypeUtil.java (still present in OpenIdentityPlatform#134, just not in this PR anymore).
|
The red #126 fixes exactly those two lines (wraps the |
maximthomas
left a comment
There was a problem hiding this comment.
praise: The cleanup finds and fixes real behaviour along the way, and each fix has a test.
IOUtil.quietClose(Connection)gets back theisClosed()guard of theSQLUtil.closeQuietly(Connection)it replaces.DatabaseConnectionTest.testDisposeandSQLUtilTests.quietConnectionClosecaught the missing guard.AttributeTypeUtil.createInstantiatedObjectnow turns a malformed numeric value into aConnectorExceptionthat names the value and the type. The newAttributeTypeUtilTestpins this for all 8 types CodeQL flagged.
issue (blocking): getDeclaredConstructor().newInstance() wraps constructor exceptions in InvocationTargetException, so the catch-and-wrap sites now hide the original exception type.
OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/ConnectorPoolManager.java:169, OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/LocalConnectorInfoManagerImpl.java:329, OpenICF-java-framework/connector-framework-osgi/src/main/java/org/forgerock/openicf/framework/impl/api/osgi/internal/OsgiConnectorInfoManagerImpl.java:338, OpenICF-java-framework/connector-framework-server/src/main/java/org/forgerock/openicf/framework/osgi/internal/AsyncOsgiConnectorInfoManagerImpl.java:245, OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java:122, :125
Class.newInstance() rethrew whatever the constructor threw, unchanged. Constructor.newInstance() wraps every throwable, Errors included, in the checked InvocationTargetException. Each of these sites ends in catch (Exception e) { throw ConnectorException.wrap(e); } (ContractException.wrap in ConnectorHelper). wrap passes a RuntimeException through and rethrows an Error, but turns the ITE into new ConnectorException(ite).
The result: a ConnectionFailedException or ConfigurationException from a connector or configuration constructor now reaches the caller as a plain ConnectorException with the message java.lang.reflect.InvocationTargetException. A NoClassDefFoundError becomes a RuntimeException. You can see it by running the contract tests without -DconnectorName. That used to fail with GroovyDataProvider's "To run contract tests, you must specify valid [connectorName] ...". It now fails with ContractException("java.lang.reflect.InvocationTargetException"), and that message sits two causes down. ConnectorAPIOperationRunnerProxy:111 is the only site that already unwraps.
} catch (InvocationTargetException e) {
throw ConnectorException.wrap(e.getCause()); // ContractException.wrap in ConnectorHelper
} catch (Exception e) {
throw ConnectorException.wrap(e);
}Apply the same unwrap at the factory singletons (ConnectorFacadeFactory:55/:74, ConnectorInfoManagerFactory:51, ObjectSerializerFactory:54, ConnectorServer:113, TestHelpers:219/:312) to keep the old behaviour there too. Also put it ahead of the catch (RuntimeException e) branches in Log.java:149 and EncryptorFactory.java:43, which no longer see constructor exceptions.
issue (non-blocking): ScriptExecutorFactory.getFactoryCache now silently skips a factory whose constructor throws, and logs the cause as Exception:null.
OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/script/ScriptExecutorFactory.java:74, :80, :82, :173
The catch (RuntimeException e) { throw e; } at :80 no longer sees constructor exceptions. They arrive as InvocationTargetException in catch (Throwable) at :82, which logs e.getMessage(), and that is null for an ITE. Say JavaScriptExecutorFactory() throws "JavaScript Engine is not found". Every later newInstance("JavaScript") then fails with "Language not supported: JavaScript", and nothing in the log says why. newInstance(language) at :173 now throws RuntimeException(ITE) instead of the original exception.
} catch (Throwable e) {
Throwable cause = e instanceof InvocationTargetException ? e.getCause() : e;
logger.ok("ScriptExecutorFactory {0} can not be activated. Exception:{1}", factory, cause);
}Or: unwrap as in the blocking issue to restore the old propagation.
issue (non-blocking): In GroovyDataProvider, the new !createNewFile() throw only fires on a race or a dangling symlink. When it does, it logs "will not be stored" and the parameters are stored anyway.
OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/data/GroovyDataProvider.java:241, :250, :266
File.createNewFile() returns false only when the path already exists. A real I/O failure already threw IOException into the same catch before this PR. So the new branch fires in only two cases: the file appears between exists() and createNewFile(), or the path is a dangling symlink (exists() false, createNewFile() false). The catch warns "Unable to create ... the test parameters will not be stored" but leaves _queriedPropsOutFile / _propertyOutFile set. The writers check only for null, so they store the parameters anyway, through the symlink. Before this PR, the symlink case reached canWrite() == false, nulled the field and stored nothing.
_queriedPropsOutFile = new File(pOut);
if (!_queriedPropsOutFile.createNewFile() && !_queriedPropsOutFile.isFile()) {
throw new IOException("Could not create " + _queriedPropsOutFile);
}
// ...
} catch (IOException iOException) {
_queriedPropsOutFile = null;
LOG.warn("Unable to create ''{0}'' file, the test parameters will not be stored", pOut);
}Make the same change for _propertyOutFile at :266.
suggestion (non-blocking): AttributeTypeUtilTest checks only the exception class, not the message, and leaves out BigInteger/BigDecimal.
OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTest.java:45, :51, :55
The test's Javadoc promises an exception that names the value and the target type, but nothing checks the message. Change AttributeTypeUtil.java:50 to throw new ConnectorException(e) and all 16 rows stay green. The BIG_INTEGER/BIG_DECIMAL branches (AttributeTypeUtil.java:107-113) sit behind the same catch, and no test calls them.
@Test(dataProvider = "numericTypes")
public void createInstantiatedObjectWrapsMalformedNumber(String type) {
try {
AttributeTypeUtil.createInstantiatedObject("not-a-number", type);
fail("ConnectorException expected for " + type);
} catch (ConnectorException e) {
assertEquals(e.getMessage(), "Value 'not-a-number' is not a valid " + type);
assertTrue(e.getCause() instanceof NumberFormatException);
}
}Pin: the message assertion catches the mutant above. Add the rows { XmlHandlerUtil.BIG_INTEGER } and { XmlHandlerUtil.BIG_DECIMAL } to numericTypes() to cover the Big* branches.
suggestion (non-blocking): No test covers the new parentFile != null guard in XMLConfiguration.validate.
OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/XMLConfiguration.java:110
Take a relative xmlFilePath with no parent directory (getParentFile() == null). Before this PR it threw NullPointerException, and it now validates. Drop parentFile != null && and the suite stays green. Every setCreateFileIfNotExists(true) test uses a file whose parent directory exists.
@Test
public void shouldValidateXmlFilePathWithoutParentDirectory() throws IOException {
File xsd = File.createTempFile("schema", ".xsd");
xsd.deleteOnExit();
File xml = new File("xml-config-" + System.nanoTime() + ".xml"); // getParentFile() == null
config.setXsdFilePath(xsd);
config.setXmlFilePath(xml);
config.setCreateFileIfNotExists(true);
config.validate(); // NullPointerException before the guard
AssertJUnit.assertFalse(xml.exists());
}Pin: this test fails with NullPointerException as soon as the guard is removed.
…loses
Class.newInstance() is replaced with getDeclaredConstructor().newInstance()
at every call site (20): all are already inside a catch (Exception) or
catch (Throwable), or declared throws Exception, so the extra checked
NoSuchMethodException needs no new handling.
new Integer/Long/Boolean/Double/Float/Character(String) become valueOf/
parseX (19 sites); new String(x) becomes x.
SQLUtil.closeQuietly(Connection/Statement/ResultSet), deprecated in favour
of IOUtil.quietClose, is replaced at its 23 call sites, including its own
internal use. IOUtil.quietClose(Connection) was missing the isClosed()
check SQLUtil.closeQuietly(Connection) has, so it called close() on an
already-closed connection where the old code did not; two tests using a
strict-sequence mock caught it. IOUtil.quietClose(Connection) now checks
isClosed() first, matching what it replaces.
Plexus IOUtil.close(Closeable) ("deprecated: use try-with-resources
instead") is replaced by try-with-resources at its 3 call sites in the
maven plugin.
XMLConfiguration.validate and GroovyDataProvider no longer ignore the
return value of mkdir()/createNewFile(): a real failure now surfaces
through the existing IOException handling instead of being silently
masked by the following canWrite() check.
Small ones: PrettyStringBuilder iterates entrySet() instead of keySet()
followed by get(); StringUtil.isEmpty and an XSD annotation check use
isEmpty() instead of comparing to ""; a missing space in a concatenated
message and a stray comma in a @PARAM tag are fixed.
createInstantiatedObject used to let a NumberFormatException from Integer/Long/Double/Float.valueOf (and BigInteger/BigDecimal) escape uncaught, with no indication of which attribute or target type failed to parse. Converting the deprecated boxed constructors to valueOf earlier in this branch made CodeQL recognise the parse call and flag it (java/uncaught-number-format-exception) - the bug was already there, just invisible to the query through the old constructor form.
CodeQL's uncaught-number-format-exception query only sees a catch in the same method or a throws clause on the enclosing one; the wrapper added in the previous commit left all eight alerts open at the helper's lines.
… SecurityManager (#138) Closes the remaining `java/deprecated-call` alerts on real (non-`MessagesUtil.*Legacy`) code: `SSLContextConfigurator.createSSLContext` (3) and `System.getSecurityManager` (1). Together with the 67 `MessagesUtil.serializeLegacy`/`deserializeLegacy` alerts (dismissed as "won't fix" — see below) and the reflection/boxing/close alerts fixed in #134, this closes `java/deprecated-call` entirely. ## SSLContextConfigurator.createSSLContext() — 3 sites Grizzly's `SSLContextConfigurator.createSSLContext()` is deprecated in favour of `createSSLContext(boolean throwException)`. Checked the Grizzly source: the no-arg version is literally `return createSSLContext(false);` — identical behaviour, so `ConnectionManager.java` (×2) and `ConnectorServer.java` (grizzly, ×1) switch to `createSSLContext(false)` with no behaviour change. ## System.getSecurityManager() — CCLWatchThreadFactory This class's own comment says it is "Copied from java.util.concurrent.Executors.DefaultThreadFactory". Checked the current JDK's own copy of that class (OpenJDK 26 source): it has since dropped the `SecurityManager` branch entirely, now unconditionally `group = Thread.currentThread().getThreadGroup();` — matching the permanent disabling of the Security Manager in JEP 486. This PR makes the same change here, with a comment explaining why. ## MessagesUtil.serializeLegacy / deserializeLegacy — 67 alerts, dismissed, not touched These are the framework's only serialization path for payloads its own wire protocol defines as opaque `bytes` rather than structured protobuf messages — `scriptArguments` (`CommonObjectMessages.proto`), `connectorObject`, `attributes`, and the sync token `value` (`OperationMessages.proto`). There is no drop-in non-deprecated alternative without redefining the wire protocol, which would break compatibility with the .NET connector server and existing clients. Dismissed on GitHub as "won't fix" with that reasoning recorded on each alert. ## Tests No new tests: both changes are behaviour-preserving (verified against the Grizzly source and the current JDK source respectively), and neither has an observable difference a test could assert on. Local runs of the three touched modules, all green: connector-framework-internal 469 tests (2 skipped, same as on master), connector-framework-server 29, connector-server-grizzly 34.
AttributeTypeUtil.java, MultiOpTests.java, PrettyStringBuilder.java, StringUtil.java, ActiveDirectoryChangeLogSyncStrategy.java and XSDAnnotationParser.java were independently fixed here and in OpenIdentityPlatform#134 with byte-identical diffs; reverting them here to avoid merging the same change twice and to let OpenIdentityPlatform#134 own them. Drops AttributeTypeUtilTests.java too since it exercises the NumberFormatException-wrapping behavior that lived in the now-reverted AttributeTypeUtil.java (still present in OpenIdentityPlatform#134, just not in this PR anymore).
…ectively
getDeclaredConstructor().newInstance() wraps whatever the constructor
throws in InvocationTargetException, so the catch-and-wrap sites turned
e.g. a ConfigurationException from a connector constructor into a plain
ConnectorException("java.lang.reflect.InvocationTargetException"), and
ScriptExecutorFactory logged a failing factory as "Exception:null".
ReflectionUtil.newInstance(Lookup, Class) restores what Class.newInstance()
did: the constructor's exception reaches the caller as is, and access is
checked against the caller's lookup, so a package-private class such as
StdOutLogger stays instantiable from its own package.
GroovyDataProvider no longer keeps an out file it failed to create: a
dangling symlink or a directory at that path now stores nothing, as
before, instead of logging "will not be stored" and writing through it.
AttributeTypeUtilTest pins the exception message and cause and covers
BigInteger/BigDecimal; XMLConfigurationTests covers a file path without
a parent directory.
73a6a75 to
7500a98
Compare
|
@maximthomas addressed in 7500a98 (the branch is also rebased on current InvocationTargetException (blocking) and
|
maximthomas
left a comment
There was a problem hiding this comment.
praise: The round-1 exception-type regression is fixed in one place, with the old semantics kept exactly.
ReflectionUtil.newInstanceinvokes the constructor throughlookup.findConstructor(clazz, methodType(void.class)).invoke()and rethrowsException | Erroras is. AConfigurationExceptionfrom a connector constructor reachesConnectorException.wrap/ContractException.wrapunchanged again, as it did underClass.newInstance(). All 20 sites use it; no baregetDeclaredConstructor().newInstance()is left in main code.- Access is checked against the caller's
MethodHandles.lookup(), soLog.getLogstill instantiates the package-privateStdOutLogger. A probe with two sibling class loaders (the OSGi bundle layout) behaves the same asClass.newInstance(): a public class instantiates, a constructor exception passes through unchanged, and an abstract class throwsInstantiationException.
Closes the remaining 1320 open `java/missing-override-annotation` CodeQL alerts on `master` — the last large `note`-severity category from the ongoing CodeQL cleanup series (#122, #123, #126, #128, #130–#134). ## Change Every location CodeQL's `java/missing-override-annotation` flagged gets an `@Override` line inserted immediately above the method signature, at the same indentation, at the exact line the alert points to. 225 files, purely additive (`+1488 / −0`): - 1320 `@Override` insertions; - 164 `Portions Copyrighted 2026 3A Systems, LLC` header lines: 162 appended to an existing header, and 2 in a new `/* */` block for files that had no header at all (`AttributeMappingConfig`, `ContainsQuery`). The other 61 files already carried a 3A line covering 2026, so their headers are left alone. No other code changes: no renames, no logic touched, no reordering. ## Verification `@Override` is compiler-checked — `javac` fails hard when it is placed on a method that does not actually override or implement anything from a supertype ("method does not override a method from its superclass"). Rather than review 1320 insertions by hand, the real proof is a clean build: the entire reactor was built from the root `pom.xml` (`mvn install`, all 28 modules) after the insertions, with no manual fixes needed afterward. Result on the original head: **BUILD SUCCESS**, all 28 modules, **1442 tests, 0 failures, 0 errors** (164 skipped, same skips as on `master`) — including `connector-framework-internal` (469 tests) and `ldap-connector` with its embedded OpenDJ (159 tests). Also spot-checked ~30 random insertions by hand across different files and annotation styles (interface method declarations, anonymous inner classes, nested static classes) before the build, all correctly placed. After the rebase below, the whole reactor was rebuilt (`mvn install -DskipTests`, main and test sources) and `build.yml` runs on the new head. ## Rebase onto master The branch was rebased onto `master` at c7648cf. Two files conflicted with PRs merged in the meantime: - `RemoteFrameworkConnectionInfoConverter` (#122): #122 had already added its own 3A header line and replaced the anonymous `X509TrustManager` with an `X509ExtendedTrustManager` whose seven methods are annotated. Master's side is kept for both hunks; this PR now only adds `@Override` to `enableLogging`, `canConvert` and `fromConfiguration`. - `RemoteRequest` (#127): #127 rewrote the anonymous classes and already annotates all four methods, so the file is taken from master unchanged and drops out of this PR. In six files — the converter above plus `EncryptorImpl`, `LocalConnectorInfoManagerImpl`, `RemoteFrameworkConnection`, `CCLWatchThreadFactory` and `ConnectionManager` — other PRs of the series had already added a 3A header line; that line now comes from master, and no file carries two 3A lines. The remaining open PRs of the series may still touch files changed here; whichever merges second needs a rebase.
AttributeTypeUtil.java, MultiOpTests.java, PrettyStringBuilder.java, StringUtil.java, ActiveDirectoryChangeLogSyncStrategy.java and XSDAnnotationParser.java were independently fixed here and in OpenIdentityPlatform#134 with byte-identical diffs; reverting them here to avoid merging the same change twice and to let OpenIdentityPlatform#134 own them. Drops AttributeTypeUtilTests.java too since it exercises the NumberFormatException-wrapping behavior that lived in the now-reverted AttributeTypeUtil.java (still present in OpenIdentityPlatform#134, just not in this PR anymore).
## Summary Follow-up to the unused-parameter cleanup (#141): investigated all the newly-surfaced small `note`-severity CodeQL categories. - **`GuardedString`** now overrides `toString()` (returns `"GuardedString(...)"`), so it no longer inherits `Object`'s default. Fixes `call-to-object-tostring` at its root instead of patching the two current call sites (`SharedSecretPrincipal`, `ScriptOnResourceApiOpTests`) individually — any future logging of a `GuardedString` is safe by construction. This is more than cosmetic: the inherited output printed `hashCode()`, which is derived from the unsalted SHA-1 hash of the clear text, so a fingerprint of the secret reached log text — through `SharedSecretPrincipal.toString()`, and, with OK-level logging on, through `LoggingProxy`, which prints every API argument and return value: a `GuardedString` attribute value (e.g. `__PASSWORD__`) passed to create/update or returned by getObject was printed via `Attribute.toString()`. - **`GuardedByteArray`** gets the same override (`"GuardedByteArray(...)"`): its `hashCode()` is derived from the clear bytes the same way, and it is a supported attribute type, so the same `LoggingProxy` → `Attribute.toString()` path printed it. - Fixes a shadowed local in `LdapInternalSearch.execute()` (`local-shadows-field`). Also dismissed on GitHub: 3 `ignored-error-status-of-call` (the ignored return values are already covered by a subsequent check or exception path), 8 `jdk-internal-api-access` (AD DirSync's `com.sun.jndi.ldap.Ber*`, no public JDK alternative exists), 1 `confusing-method-signature` (`Log.log(..)` is long-standing, heavily-used public API — renaming to remove the overload is too invasive for the benefit). Covered by other PRs instead of this one: - #134 (opened independently, earlier) fixes byte-for-byte the same lines in `AttributeTypeUtil.java`, `MultiOpTests.java`, `PrettyStringBuilder.java`, `StringUtil.java`, `ActiveDirectoryChangeLogSyncStrategy.java`, `XSDAnnotationParser.java` (with `AttributeTypeUtilTests.java`): `uncaught-number-format-exception`, `inefficient-boxed-constructor`, `inefficient-empty-string-test`, `inefficient-key-set-iterator`, `unknown-javadoc-parameter`, `missing-space-in-concatenation`. Dropped here on 2026-09-21. - #132 removes the dead `ContractTestFactory` inner class from `ContractITCase` (`unused-reference-type`) along with more of that file's dead code; the two PRs conflicted there, so this PR no longer touches `ContractITCase`. Dropped here on 2026-10-04. **Update 2026-10-02 (review round 1):** rebased onto the current `master`; added the `GuardedByteArray` override; replaced the `toString()` test, which passed even without the override, by one that pins the fix; reworded the Javadoc to say why the override matters. **Update 2026-10-04 (review round 2):** rebased onto the current `master` (picks up #146, which fixes the `docs.groovy-lang.org` javadoc failure that turned the `windows-latest, 11` cell red); both `toString()` tests now also pin the exact output; corrected the description of the `GuardedByteArray` leak (it was reachable, not latent); dropped the `ContractITCase` change in favour of #132; aligned the added 3A copyright lines to the repository's `Portions Copyrighted 2026 3A Systems, LLC`. ## Test plan - [x] `GuardedStringTests.testToStringDoesNotDependOnTheSecret` and `GuardedByteArrayTests.testToStringDoesNotDependOnTheSecret`: two different secrets print the same `toString()`, and it is exactly `"GuardedString(...)"` / `"GuardedByteArray(...)"`. Verified red with the overrides removed (round 1) and with `toString()` returning `null` / `""` (round 2). - [x] `connector-framework` test suite after round 2 — 194 tests, 0 failures. - [x] `connector-framework`, `connector-framework-contract`, `OpenICF-ldap-connector` build after round 2. - [x] `mvn install` on `connector-framework`, `connector-framework-contract`, `OpenICF-ldap-connector` (incl. existing test suites, LDAP/OpenDJ integration tests included) — all green, 946 tests, 0 failures (before the review rounds).
… NumberFormatException (#139) Closes 48 of the 54 remaining `java/uncaught-number-format-exception` alerts (the 8 in `AttributeTypeUtil` were already fixed in #134). Dismisses the remaining 6 as unreachable in practice, with the reasoning recorded on each alert. ## AD/LDAP cluster — 26 of 28 sites `ADUserAccountControl`, `ADGroupType` and `ActiveDirectoryChangeLogSyncStrategy` share one root cause: Active Directory bitmask attributes (`userAccountControl`, `groupType`) and sync tokens are parsed with plain `Integer.parseInt`, nothing around it. `ADLdapUtil` gets `parseADInteger`/`parseADLong` (new, unit-tested), and all three classes route through it — a corrupted AD attribute, or a sync token tampered with by whoever calls the connector's `sync()` operation, now fails with a `ConnectorException` naming the value instead of a bare `NumberFormatException` three frames deep with no context. The sync strategy's USNs (`uSNChanged`, `highestCommittedUSN`) are 64-bit in AD, so they go through `parseADLong` and the changes are ordered in a `TreeMap<Long, SyncDelta>`; before, a domain controller past `Integer.MAX_VALUE` could not sync at all. `PagedSearchStrategy`'s paged-results cookie is `<base64 cookie>:<context index>`; the base64 half was already inside a `try`/`catch(RuntimeException)` reporting `ConnectorException`, the numeric half was one line below it, outside. Moved in, and an index outside `baseDNs` (`AAAA:5`, `AAAA:-1`, and `AAAA:1` against a single base DN) is now rejected the same way instead of escaping as an `IndexOutOfBoundsException`. Left alone and dismissed as unreachable (`ADLdapUtil.binarySIDtoString`, 2 sites): the hex strings it parses are built two lines above via `String.format("%02X...")` from raw bytes, so they are always valid hex. ## Elsewhere — 22 sites, same idiom as what already surrounds them - `PropertyBag.castValue` (maven-plugin, 8): POM `<configuration>` values converted to typed connector config properties at build time; the conversion chain is wrapped as a whole, reporting through the `MojoExecutionException` the method already uses two lines above for the equivalent blank-value case. - `SQLUtil.attribute2jdbcValue` (dbcommon, 6): converts an attribute value to a JDBC-bindable value for a numeric SQL column; wrapped with `ConnectorException`, already imported and used elsewhere in the file. - `XmlObjectDecoder` (connector-framework-internal, 5): the five primitive decoders of the XML wire protocol — the one genuinely network-facing trust boundary in this batch. Wrapped with `ConnectorException`, matching `decodeClass`'s existing convention two methods above; an empty element (no text node, which `#PCDATA` allows) is rejected the same way instead of an NPE. - `RemoteWSFrameworkConnectionInfo.loadSystemProxy` (1): a typo in `-Dhttp.proxyPort` now fails clearly at startup instead of with a bare NFE. - `ConnectorHelper` / `AuthenticationApiOpTests` (connector-framework-contract, 2): contract-test configuration (`-DserverPort`, a test-suite attribute) now reports through `ContractException`, the framework's own convention, instead of a bare NFE. ## Dismissed as unreachable — 6 - `ADLdapUtil.binarySIDtoString` ×2 (see above). - `TstStatefulConnectorConfig` (testbundlev1): parses a revision the same test connector generated two lines above via `AtomicInteger.getAndIncrement()`/`String.valueOf()` — no external input. - `JavaScriptExecutorFactory`: parses the JVM's own `java.specification.version`, guaranteed valid by the JDK. - `CSVFileConnector` ×2: parses a substring the regex `\.[0-9]{13}$` just matched immediately before — always exactly 13 ASCII digits. ## Tests New: `ADLdapUtilTest` (8), `ADUserAccountControlTest` (2), `ADGroupTypeTest` (2), `PagedSearchStrategyTest` (4), `SQLUtilTests` (+3), `XmlObjectDecoderTest` (11, corrupts or empties a value inside a real serialized document rather than hand-building the XML schema), `RemoteWSFrameworkConnectionInfoTest` (1), `PropertyBagTests` (2, through the public `mergeConfigurationProperties`) — 33 new/changed tests. Each is RED before its fix and green after, except four that are green on master by design: `decodesAValidInt`, `isAccountDisabledReadsTheBitmask` and `isScopeGlobalReadsTheBitmask` are positive controls for the parse path, and `convertsAnIntWithoutTheUnsupportedWarning` guards against the round-1 regression where a split `if` chain logged "Cast to targetType ... is not supported" for every numeric property. The wrapping tests (`ADLdapUtilTest`, `XmlObjectDecoderTest`, `SQLUtilTests`, `RemoteWSFrameworkConnectionInfoTest`, `PropertyBagTests`) pin the exact message and the `NumberFormatException` cause, so a catch that drops either fails. `ConnectorHelper`/`AuthenticationApiOpTests` are contract-test configuration parsers with no dedicated unit tests of their own; verified by compiling and by the module's existing suite. Local runs after rebasing onto current master (`1abfe741`, through #143), one reactor build of every touched module with its dependencies, all green: connector-framework 197, dbcommon 89, connector-framework-internal 520 (2 skipped, same as on master), connector-framework-contract 43, maven-plugin 3, connector-framework-server 33, ldap-connector 179 (embedded OpenDJ). The counts are higher than in earlier rounds because master gained tests meanwhile (e.g. `ADUserAccountControlTests` from #141).
…nterned-string locks, racy lazy init (#132) Closes the remaining 10 open CodeQL alerts of severity `error` that carry no security rating: `java/contradictory-type-checks` #1536 #1537, `java/dereferenced-value-is-always-null` #1560, `java/sync-on-boxed-types` #1554 #1555 #1556, `java/unsafe-double-checked-locking` #1550 #1551, `java/unsynchronized-getter` #1544 #1545. The eleventh, #1549 (`ScriptedConfiguration`), was closed by #133. Fixes #148 Fixes #149 | Where | Finding | Change | |---|---|---| | `SQLUtil.setParam` | `else if (val instanceof Integer)` appears twice; the second branch can never run | second branch removed | | `ContractITCase.createInstances` | `IObjectFactory objectFactory = null` is dereferenced for any test class with a `(String)` constructor. None of the default classes has one today, so the code only worked because the `NoSuchMethodException` path was always taken | the `(String)` constructor is called through plain reflection, so the method no longer depends on TestNG's internal `ObjectFactoryImpl`, which TestNG 7.5 moved and whose `IObjectFactory` 7.10.2 removed. The unused nested `ContractTestFactory` with its three never-read fields is gone | | `BatchRemoteCache` (testbundlev1) | `synchronized (resultLock)` on the interned literal `"resultLock"`, a monitor shared with any other code in the JVM that synchronises on the same string | `new Object()` | | `WebSocketConnectionGroup.operationContext`, `TstStatefulConnectorConfig.executorService` | double-checked lazy initialisation reads a non-volatile field outside the lock, so a thread may see a partially constructed object | fields declared `volatile`, which makes the pattern correct under the JMM | | `ScriptedConfiguration.getGroovyScriptEngine()` | #133 made the getter `synchronized` for the whole initialisation. That closed the double-checked-locking alert and #149 (the getter and `release()` share the monitor now), and concurrent callers wait for the customizer. But the getter still stored the engine before its customizer ran, so a failed customizer left an uncustomized engine in place for good (#148) | on top of #133's synchronised getter, the engine is published only after `initializeCustomizer()` succeeds. The customizer's own re-entrant calls get the engine under construction from a second lock-guarded field. If the customizer fails, nothing is published and the next call retries; the failed customizer class is removed with `InvokerHelper.removeClass`, so the metaclass the REST, CREST and SSH configurations register on it does not keep one class loader alive per retry. The customizer runs under the configuration's monitor, so it must not wait for another thread that uses the engine; the comment on `initializeCustomizer()` says so | | `customize` in `SSHConfiguration`, `ScriptedCRESTConfiguration` | with the retry above, a failed attempt that registered `release {}` left its closure for `release()` to run after a successful retry that registered none: the SSH `customize` never reset it, and CREST's `release = null` wrote a private field nothing reads | both reset it with `setReleaseClosure(null)`, as REST's `customize` already does; CREST's unread field is removed | | `FrameworkUtil.getFrameworkVersion()` | unsynchronised lazy init while `setFrameworkVersion` is synchronised | `static synchronized` (not a hot path). The `(ClassLoader)` overload reads `connectors-framework.properties` rather than the field and is only used by `FrameworkUtilTests`, so it is renamed to `readFrameworkVersion` and the getter/setter pairing no longer applies to it | `ScriptedConfigurationTest` covers #148: a customizer that fails once is retried on the next call, a second thread blocks until the customizer has finished, and a failed customizer class has no metaclass left registered. Each test fails when its part of the fix is reverted (engine published before the customizer, no `removeClass`, getter not synchronised). `ScriptedConfigurationTest` (CREST) and `SSHConfigurationCustomizerTest` (SSH) cover the release closure: a first attempt registers `release {}` and fails, the retry registers none, and `release()` must not run the stale closure; both fail without the `customize` reset. #149 is fixed on master by #133; it was a window between two adjacent field reads that no test can hold open, so it has no test. The other changes have no behaviour a unit test can observe: dead branches, `volatile`, a private monitor and a synchronised getter. `createInstances` takes its classes from a fixed list with no `(String)` constructors, so its reflective path cannot be reached from a test. The class of a customizer that **succeeded** is still never unregistered, so `release()` keeps its loader alive; master behaves the same, and that is tracked separately in #154. Local reactor run (rebased on current master, after #131 #133 #134 #135 #136 #139 #141 #142 #143 #145) of all touched modules and everything they depend on: connector-framework 200, dbcommon 89, framework-internal 524 (2 skipped), contract 43, connector-framework-server 33, groovy-connector 130 (42 skipped as on master, 4 new), ssh-connector 97 (82 skipped, 1 new), testbundlev1 compiles — all green.
Closes ~76 of the open
note-severity CodeQL alerts (no security rating) that are safe, mechanical fixes:java/deprecated-call(Class.newInstance, SQLUtil.closeQuietly, plexus IOUtil.close),java/inefficient-boxed-constructor,java/inefficient-string-constructor,java/ignored-error-status-of-call,java/inefficient-key-set-iterator,java/inefficient-empty-string-test,java/missing-space-in-concatenation,java/unknown-javadoc-parameter, plus the 8java/uncaught-number-format-exceptionalerts inAttributeTypeUtilthat the boxing change touched anyway.Class.newInstance() — 20 sites, 16 files
Replaced with the new
ReflectionUtil.newInstance(MethodHandles.lookup(), clazz).Class.newInstance()rethrew whatever the constructor threw, unchanged. A baregetDeclaredConstructor().newInstance()wraps it inInvocationTargetExceptioninstead, and the catch-and-wrap sites would then turn e.g. aConfigurationExceptionfrom a connector constructor intoConnectorException("java.lang.reflect.InvocationTargetException"). The helper invokes the constructor through aMethodHandle, so the exception reaches the caller as is. Access is checked against the caller's lookup, so a package-private class (StdOutLoggerfromLog) stays instantiable from its own package. Every site is already inside acatch (Exception …)/catch (Throwable …)or declaredthrows Exception, so the reflective exceptions need no new handling anywhere.Boxed constructors — 19 sites
new Integer/Long/Boolean/Double/Float/Character(String)→valueOf/parseXinAttributeTypeUtil,RandomGenerator,SQLUtil;new String(x)→xinAttributeTypeUtil.SQLUtil.string2Timestamp/string2DateuseLong.parseLong(a primitive is what theDate/Timestampconstructor needs, so no boxing round-trip at all).AttributeTypeUtil.createInstantiatedObject(xml-connector) also gets theNumberFormatExceptionhandling it was missing: a malformed numeric value in the XML data file now fails with aConnectorExceptionnaming the value and the target type instead of a bareNumberFormatExceptionwith no indication of which attribute it came from. This closes the 8java/uncaught-number-format-exceptionalerts in that class. The parse calls sit in a helper that declaresthrows NumberFormatException— CodeQL's query (NumberFormatException.ql) only accepts a catch in the same method or athrowsclause on the enclosing one, so the first cut with just a wrapper method left all eight alerts open on the PR's own CodeQL run.Deprecated closes — 26 sites
SQLUtil.closeQuietly(Connection/Statement/ResultSet)(deprecated in favour ofIOUtil.quietClose) is replaced at its 23 call sites, including its own 6 internal uses.IOUtil.quietClose(Connection)did not checkisClosed()before callingclose(), unlike theSQLUtil.closeQuietly(Connection)it replaces. Harmless against a real JDBC driver (Connection.close()on an already-closed connection is a spec-mandated no-op), but two tests using a strict-call-sequence mock (DatabaseConnectionTest.testDispose,SQLUtilTests.quietConnectionClose) caught the difference immediately.IOUtil.quietClose(Connection)now checksisClosed()first, matching what it replaces and what its own javadoc promises.IOUtil.close(Closeable)— its javadoc says "deprecated: use try-with-resources instead" — is replaced by try-with-resources at its 3 call sites in the maven plugin.ignored-error-status-of-call — 3 sites
XMLConfiguration.validate:getParentFile().mkdir()'s return value is checked now, distinguishing "the directory already existed" from a real failure.GroovyDataProvider(×2): an out file that cannot be created (a directory or a dangling symlink at that path) now throws into the existingcatch (IOException), which clears the field, so nothing is stored. An already existing file is still used, as before.Small mechanical ones
PrettyStringBuilder:map.keySet().iterator()+map.get(key)→map.entrySet().iterator().StringUtil.isEmptyand an XSD annotation check:"".equals(x)/x.equals("")→x.isEmpty(). A missing space in a concatenated log message (ActiveDirectoryChangeLogSyncStrategy) and a stray comma in a@paramjavadoc tag (MultiOpTests) are fixed.Tests
New:
AttributeTypeUtilTest(21) — each numeric type parses, and a malformed or blank value fails with aConnectorExceptionnaming the value and the type, caused by theNumberFormatException, for each of the 10 numeric types includingBigInteger/BigDecimal.ReflectionUtilTests(+5) — the constructor'sRuntimeException, checked exception andErrorreach the caller unchanged, and a class without a no-arg constructor fails.XMLConfigurationTests(+1) — a file path without a parent directory validates. Everything else is a mechanical replacement with identical behaviour, except theIOUtil.quietClose(Connection)fix, which is proven by the two existing tests that caught the regression and now pass.Local runs of all 11 touched modules, all green: connector-framework 197, dbcommon 86, connector-test-common 5, connector-framework-internal 499 (2 skipped, same as on master), connector-framework-osgi (compiles), connector-framework-contract 43, maven-plugin 1, connector-framework-server 32, databasetable-connector 78, ldap-connector 159 (embedded OpenDJ), xml-connector 103. (dbcommon, databasetable-connector and ldap-connector are from the first round; the review round did not touch them.)
Left open on purpose
java/missing-override-annotation(1327 — mechanically safe but would touch ~200 files and conflict with the six other open CodeQL PRs),java/deprecated-callonMessagesUtil.*Legacy(67 — the framework's own deprecated API, its only path for script arguments today),java/uncaught-number-format-exception(54 after the 8 closed here — needs a per-site judgment call, not mechanical; #139 takes them),java/unused-parameter/java/constants-only-interface/java/jdk-internal-api-access(public API or no JDK alternative),java/chained-type-tests(needs an actual refactor),java/call-to-object-tostring/java/local-shadows-field/java/confusing-method-signature(judgment calls, not mechanics).