Skip to content

Fix CodeQL note-severity alerts: deprecated JDK calls and inefficient expressions - #823

Open
vharseko wants to merge 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:codeql/deprecated-and-inefficient
Open

Fix CodeQL note-severity alerts: deprecated JDK calls and inefficient expressions#823
vharseko wants to merge 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:codeql/deprecated-and-inefficient

Conversation

@vharseko

@vharseko vharseko commented Aug 3, 2026

Copy link
Copy Markdown
Member

Third batch of note-severity code scanning fixes (after #814 and #817). Mechanical migration away from deprecated JDK/third party API plus the java/inefficient-* cleanups — about 135 alerts, no behaviour change intended.

Deprecated JDK and third party calls (88 of the 180 java/deprecated-call alerts)

  • Class.newInstance()getDeclaredConstructor().newInstance() (53) — deprecated since Java 9 because it silently propagates the checked exceptions thrown by the constructor. The call sites are the reflective component loaders (*ConfigManager, AbstractLogger, TaskScheduler, RecurringTask, SchemaHandler, makeldif TemplateFile, …); all of them already catch Exception, so the only structural change is in the quicksetup application loader, whose narrow catch clauses now also handle NoSuchMethodException and InvocationTargetException.

    Review follow-up: the new call wraps constructor failures in InvocationTargetException, whose toString() is just the class name, so the ten sites that formatted the caught exception with %s (both TemplateFiles, ExtendedOperationConfigManager, LogRetentionPolicyConfigManager, LogRotationPolicyConfigManager, AbstractLogger) now format it via getExceptionMessage()/stackTraceToSingleLineString() — both unwrap the InvocationTargetException — so the error message shows the real cause.

  • X509Certificate.getSubjectDN()/getIssuerDN()getSubjectX500Principal()/getIssuerX500Principal() (20) — the deprecated accessors return a Principal implemented by the internal sun.security API. Every call site either prints the DN, logs it, or uses it as a trust store alias; Platform.isSelfSigned() now compares the canonical X.500 forms, which is stricter than the previous Principal.equals().

  • new Integer/Long/Byte(...)valueOf(...) (8, SNMP), the Provider(String, double, String) constructor → the version-string one (1), JList.getSelectedValues()getSelectedValuesList() (3, which also removes three @SuppressWarnings("deprecation")), JsonParser.getCurrentName()currentName() (2), InputEvent.getModifiers()getModifiersEx() (1).

Inefficient expressions and small defects (47)

  • s.equals("") / "".equals(s)s.isEmpty() — 30 of 35 alerts.
  • new String("literal") → the literal (1); two keySet() iterations that immediately call map.get(key)entrySet() iterations (3).
  • java/call-to-object-tostring (3): LDIF.toLDIF() called toString() on the writer it had just written to and discarded the result — removed; ReturnCode gained a toString() so that it no longer logs as ReturnCode@1a2b3c; the replication trace logs the DN of the initialize task instead of its identity hash.
  • java/inconsistent-javadoc-throws (2): removed two @throws tags for exceptions the methods cannot throw.

Alerts deliberately left open

  • 92 java/deprecated-call on the server's own deprecated API (ObjectClass.isPlaceHolder 27, AttributeDescription.getNameOrOID 20, Entry.addAttribute/removeAttribute 23, DirectoryServer.getConfigEntry 9, AttributeDescription.create 7, …). That is an internal API migration, not a mechanical change, and belongs in its own work.
  • Subject.getSubject() (1) — the replacement Subject.current() requires Java 18; this project targets Java 11.
  • Five "".equals(x) tests (ConsoleApplication, LDIFChangeRecordReader, NewSchemaElementsTask, InstallDS, Installer) where the literal-first form is deliberately null safe and x.isEmpty() would throw.

Testing

  • Full test suites: opendj-core 8173 tests, opendj-rest2ldap 531 tests, opendj-cli 46 tests — all passing.

  • opendj-server-legacy (-Pprecommit), classes exercising server startup — which is what loads every component reflectively — plus groups, password policies, schema, tasks, makeldif and LDIF import/export: SchemaBackendTestCase (165), PasswordPolicyTestCase (144), TaskBackendTestCase (62), HostPortTest (37), LDAPURLTest (29), GroupManagerTestCase (25), LDIFBackendTestCase (22), MakeLDIFTestCase (17), TestBackupAndRestore (12), TestImportAndExport (12), SubentryPasswordPolicyTestCase (11), LDAPURLTestCase (8) — all passing.

    Four of these classes initially failed in setUp/startServer with IOException(Address already in use) while binding the administration connector to its fixed port; another OpenDJ test JVM was running concurrently on the same machine and holding ports 65534/65528. All four pass when re-run against a free port.

  • After the review follow-up: both modules recompiled, EntryGeneratorTestCase (33) and TemplateTagTestCase (21) re-run — passing.

… expressions

Migrate away from JDK and third party API deprecated in Java 9 and later, and
apply the mechanical cleanups reported by the java/inefficient-* queries:

* Class.newInstance() is replaced with getDeclaredConstructor().newInstance() in
  the 53 places where components are loaded reflectively. All of these call sites
  already caught Exception, except the quicksetup application loader whose catch
  clauses now also handle NoSuchMethodException and InvocationTargetException.
* X509Certificate.getSubjectDN()/getIssuerDN(), which return a Principal from the
  internal sun.security API, are replaced with getSubjectX500Principal() and
  getIssuerX500Principal(). The call sites either print the DN, log it or use it
  as a trust store alias; Platform.isSelfSigned() now compares the canonical
  X.500 forms of the two DNs.
* new Integer/Long/Byte(...) are replaced with valueOf(...), the Provider
  constructor taking a double version with the one taking a version string,
  JList.getSelectedValues() with getSelectedValuesList(),
  JsonParser.getCurrentName() with currentName(), and InputEvent.getModifiers()
  with getModifiersEx().
* equals("") is replaced with isEmpty(), new String(literal) with the literal
  itself, and two key set iterations with entry set iterations.
* LDIF.toLDIF() no longer calls toString() on the writer it has just written to,
  ReturnCode gets a toString(), and the replication trace logs the DN of the
  initialize task instead of its identity hash.
* Two @throws tags documenting an exception which cannot be thrown are removed.

Left alone: the calls to the deprecated internal API of the server itself
(ObjectClass.isPlaceHolder, AttributeDescription.getNameOrOID, Entry.addAttribute,
DirectoryServer.getConfigEntry and friends), which need a real migration rather
than a mechanical change; Subject.getSubject(), whose replacement Subject.current()
requires Java 18 while the project targets Java 11; and five "".equals(x) tests
which are deliberately null safe.

@maximthomas maximthomas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

One thing worth fixing: getDeclaredConstructor().newInstance() wraps constructor failures, and InvocationTargetException.toString() is just the class name with a null message. Ten sites format the throwable with %s, so:

before: ...does not contain a valid extended operation handler implementation:
        java.lang.IllegalStateException: keystore file /opt/opendj/keys.p12 is unreadable
after:  ...does not contain a valid extended operation handler implementation:
        java.lang.reflect.InvocationTargetException

…tiation error messages

getDeclaredConstructor().newInstance() wraps constructor failures in
InvocationTargetException, whose toString() is just the class name. At the
ten sites that format the caught exception with %s, format it via
getExceptionMessage()/stackTraceToSingleLineString() instead — both unwrap
the InvocationTargetException — so the message shows the real cause again.
@vharseko

vharseko commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

@maximthomas Good catch, fixed in 9d5b399. Exactly ten sites formatted the caught exception with %s: the two TemplateFiles now format it via getExceptionMessage(e) and ExtendedOperationConfigManager, LogRetentionPolicyConfigManager, LogRotationPolicyConfigManager, AbstractLogger (two sites each) via stackTraceToSingleLineString(e) — the same helper their sibling config managers already use. Both helpers unwrap InvocationTargetException to its cause, so the message shows the real constructor failure again. The remaining migrated sites were already safe: they either use one of these two helpers, don't put the exception into the message at all, or attach it as the rethrown exception's cause.

@vharseko
vharseko requested a review from maximthomas August 3, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

java Pull requests that update java code security Security fixes / CodeQL code-scanning alerts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants