[java] JSpecify annotations for `Credential` and `MBean` by mk868 · Pull Request #16481 · SeleniumHQ/selenium
User description
🔗 Related Issues
Related #14291
💥 What does this PR do?
JSpecify annotations added to the:
org.openqa.selenium.grid.jmx.MBeanorg.openqa.selenium.virtualauthenticator.Credential
🔧 Implementation Notes
💡 Additional Considerations
🔄 Types of changes
- New feature (non-breaking change which adds functionality and tests!)
PR Type
Enhancement
Description
-
Add JSpecify null-safety annotations to
MBeanclass -
Add JSpecify null-safety annotations to
Credentialclass -
Improve null-safety handling in attribute and operation invocation methods
-
Add defensive null checks for map lookups in getter/setter/invoke operations
Diagram Walkthrough
flowchart LR
A["MBean.java<br/>Credential.java"] -->|Add @NullMarked| B["Class-level<br/>null-safety"]
A -->|Add @Nullable| C["Method returns<br/>and parameters"]
A -->|Add null checks| D["getAttribute/setAttribute<br/>invoke methods"]
C --> E["Improved type<br/>safety"]
D --> E
File Walkthrough
| Relevant files | |||||
|---|---|---|---|---|---|
| Enhancement |
|
PR Compliance Guide 🔍
Below is a summary of compliance checks for this PR:
| Security Compliance | ||||||
| ⚪ | Incomplete error handlingDescription: The change makes DynamicMBean#getAttribute return null when the attribute is missing or Referred Code@Override public @Nullable Object getAttribute(String attribute) { try { AttributeInfo attributeInfo = attributeMap.get(attribute); if (attributeInfo == null || attributeInfo.getter == null) { return null; } Object res = attributeInfo.getter.invoke(bean); if (res instanceof Map<?, ?>) { return ((Map<?, ?>) res) .entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString())); } else if (res instanceof Number) { return res; } else { return res.toString(); } } catch (IllegalAccessException | InvocationTargetException e) { LOG.severe("Error during execution: " + e.getMessage()); return null; } ... (clipped 1 lines) | |||||
Incomplete error handlingDescription: The invoke method now returns null when an operation is not found, potentially hiding Referred Code@Override public @Nullable Object invoke(String actionName, Object[] params, String[] signature) { try { OperationInfo operationInfo = operationMap.get(actionName); if (operationInfo == null) { return null; } return operationInfo.method.invoke(bean, params); } catch (IllegalAccessException | InvocationTargetException e) { LOG.severe("Error during execution: " + e.getMessage()); return null; } | ||||||
| Ticket Compliance | ||||||
| 🟢 |
🎫 #14291
| |||||
| Codebase Duplication Compliance | ||||||
| ⚪ | Codebase context is not definedFollow the guide to enable codebase context checks. | |||||
| Custom Compliance | ||||||
| ⚪ | No custom compliance providedFollow the guide to enable custom compliance check. | |||||
Compliance status legend
🟢 - Fully Compliant🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label
PR Code Suggestions ✨
Explore these optional code suggestions:
| Category | Suggestion | Impact |
| Possible issue |
Missing return statements in methodAdd missing java/src/org/openqa/selenium/grid/jmx/MBean.java [158-175] private @Nullable Method findSetter(Method annotatedMethod) {
ManagedAttribute ma = annotatedMethod.getAnnotation(ManagedAttribute.class);
if (!"".equals(ma.setter())) {
return findMethod(annotatedMethod.getDeclaringClass(), ma.setter());
} else {
String name = annotatedMethod.getName();
if (name.startsWith("set")) {
return annotatedMethod;
}
if (name.startsWith("get")) {
- findMethod(annotatedMethod.getDeclaringClass(), "s" + name.substring(1));
+ return findMethod(annotatedMethod.getDeclaringClass(), "s" + name.substring(1));
}
if (name.startsWith("is")) {
- findMethod(annotatedMethod.getDeclaringClass(), "set" + name.substring(2));
+ return findMethod(annotatedMethod.getDeclaringClass(), "set" + name.substring(2));
}
}
return null;
}
Suggestion importance[1-10]: 9__ Why: The suggestion correctly identifies a significant bug in the | High |
| Learned best practice |
Fail on invalid attribute setsValidate and throw precise exceptions when an attribute is unknown or not java/src/org/openqa/selenium/grid/jmx/MBean.java [244-254] public void setAttribute(Attribute attribute) {
+ String name = attribute.getName();
+ AttributeInfo attributeInfo = attributeMap.get(name);
+ if (attributeInfo == null) {
+ throw new IllegalArgumentException("Unknown attribute: " + name);
+ }
+ if (attributeInfo.setter == null) {
+ throw new IllegalStateException("Attribute not writable: " + name);
+ }
try {
- AttributeInfo attributeInfo = attributeMap.get(attribute.getName());
- if (attributeInfo == null || attributeInfo.setter == null) {
- return;
- }
attributeInfo.setter.invoke(bean, attribute.getValue());
} catch (IllegalAccessException | InvocationTargetException e) {
LOG.severe("Error during execution: " + e.getMessage());
+ throw new IllegalStateException("Failed to set attribute: " + name, e);
}
}
Suggestion importance[1-10]: 6__ Why: | Low |
Throw explicit errors on invalid attributesFail fast with precise exceptions when an attribute is missing or not readable java/src/org/openqa/selenium/grid/jmx/MBean.java [221-241] -public @Nullable Object getAttribute(String attribute) { +public Object getAttribute(String attribute) { + AttributeInfo attributeInfo = attributeMap.get(attribute); + if (attributeInfo == null) { + throw new IllegalArgumentException("Unknown attribute: " + attribute); + } + if (attributeInfo.getter == null) { + throw new IllegalStateException("Attribute not readable: " + attribute); + } try { - AttributeInfo attributeInfo = attributeMap.get(attribute); - if (attributeInfo == null || attributeInfo.getter == null) { - return null; + Object res = attributeInfo.getter.invoke(bean); + if (res instanceof Map<?, ?>) { + return ((Map<?, ?>) res) + .entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString())); + } else if (res instanceof Number) { + return res; + } else { + return res.toString(); } - Object res = attributeInfo.getter.invoke(bean); - ... } catch (IllegalAccessException | InvocationTargetException e) { LOG.severe("Error during execution: " + e.getMessage()); - return null; + throw new IllegalStateException("Failed to get attribute: " + attribute, e); } }
Suggestion importance[1-10]: 5__ Why: | Low | |
| ||
mk868
deleted the
jspecify-Credential-MBean
branch
This was referenced
Oct 25, 2025This was referenced
Dec 16, 2025This was referenced
Jan 19, 2026This was referenced
Feb 11, 2026This was referenced
Feb 20, 2026This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters