Skip to content

Comments

[hueemulation] Fix NullPointerExceptions#19589

Merged
lsiepel merged 1 commit intoopenhab:mainfrom
jlaur:hueemulation-npe
Nov 23, 2025
Merged

[hueemulation] Fix NullPointerExceptions#19589
lsiepel merged 1 commit intoopenhab:mainfrom
jlaur:hueemulation-npe

Conversation

@jlaur
Copy link
Contributor

@jlaur jlaur commented Oct 30, 2025

E.g. on PUT request for http://openhab:8080/api/userid/config with empty body:

2025-10-29 20:55:12.568 [ERROR] [.AbstractFaultChainInitiatorObserver] - An unexpected error occurred during error handling. No further error processing will occur.
org.apache.cxf.interceptor.Fault: Cannot read field "devicename" because "changes" is null
 at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162) ~[bundleFile:3.6.8]
 at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:128) ~[bundleFile:3.6.8]
 at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.6.8]
 at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.6.8]
 at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.6.8]
 at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.6.8]
 at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:267) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:304) ~[bundleFile:3.6.8]
 at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPut(AbstractHTTPServlet.java:234) ~[bundleFile:3.6.8]
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:520) ~[bundleFile:4.0.4]
 at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:279) ~[bundleFile:3.6.8]
 at org.ops4j.pax.web.service.spi.servlet.OsgiInitializedServlet.service(OsgiInitializedServlet.java:102) ~[bundleFile:?]
 at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) ~[bundleFile:9.4.57.v20241219]
 at org.ops4j.pax.web.service.spi.servlet.OsgiFilterChain.doFilter(OsgiFilterChain.java:113) ~[bundleFile:?]
 at org.ops4j.pax.web.service.jetty.internal.PaxWebServletHandler.doHandle(PaxWebServletHandler.java:334) ~[bundleFile:?]
 at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[bundleFile:9.4.57.v20241219]
 at org.ops4j.pax.web.service.jetty.internal.PrioritizedHandlerCollection.handle(PrioritizedHandlerCollection.java:96) ~[bundleFile:?]
 at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:722) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) ~[bundleFile:9.4.57.v20241219]
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) ~[bundleFile:9.4.57.v20241219]
 at java.lang.Thread.run(Thread.java:1583) [?:?]
Caused by: java.lang.NullPointerException: Cannot read field "devicename" because "changes" is null
 at org.openhab.io.hueemulation.internal.rest.ConfigurationAccess.putFullConfigApi(ConfigurationAccess.java:114) ~[?:?]
 at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
 at java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
 at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.6.8]
 at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.6.8]
 ... 53 more

Also fix warnings.

@jlaur jlaur requested a review from digitaldan as a code owner October 30, 2025 21:06
@jlaur jlaur requested a review from a team October 30, 2025 21:06
@jlaur jlaur added the bug An unexpected problem or unintended behavior of an add-on label Nov 10, 2025
@lsiepel lsiepel requested a review from Copilot November 22, 2025 13:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request fixes NullPointerExceptions that occur when processing HTTP requests with empty request bodies in the Hue emulation service. The primary fix adds null checks after deserializing JSON request bodies using Gson, which returns null for empty bodies. Additionally, the PR includes code cleanup by removing unused @SuppressWarnings annotations, improving test assertions consistency, and applying modern Java patterns.

Key changes:

  • Added null checks after gson.fromJson() calls in REST endpoints to prevent NPEs when request bodies are empty
  • Standardized test assertions from assertEquals() to assertThat() with Hamcrest matchers
  • Modernized code using pattern matching for instanceof and Stream API improvements (toList() instead of Collectors.toList())

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
ConfigurationAccess.java Added null check for empty request body in putFullConfigApi() to fix the NPE described in the PR
UserManagement.java Added null check for empty body in createNewUser() and removed unnecessary @SuppressWarnings
Sensors.java Added null check in renameLightApi() and removed @SuppressWarnings annotations
Schedules.java Added null check and improved validation logic in postNewSchedule()
Scenes.java Added null checks, improved lights list handling with defensive null check, modernized stream collection
Rules.java Added null check in modifyRuleApi() and simplified description handling in postNewRule()
LightsAndGroups.java Added null checks, improved updateGroup0() with defensive null check, removed unused variable
UpnpServer.java Improved resource management with proper try-with-resources, added null check in handleEvent(), fixed inline link documentation
HueEmulationConfigWithRuntime.java Removed redundant implements Runnable, added unnecessary Objects.requireNonNull()
HttpActionHandler.java Added unnecessary Objects.requireNonNull() around Integer.getInteger()
TimerTriggerHandler.java Modernized with pattern matching for instanceof
RemoveRuleActionHandler.java Improved code clarity with local variable extraction
HueRuleConditionHandler.java Removed redundant null check and unused import
AbsoluteDateTimeTriggerHandler.java Added proper nullable annotations, improved field organization, used pattern matching
StateUtils.java Modernized with pattern matching for instanceof checks
RuleUtils.java Removed unnecessary @SuppressWarnings annotations
HueEmulationService.java Fixed inline link documentation for EventAdmin
DeviceType.java Added @NonNullByDefault annotation
ConfigStore.java Improved null handling with local variable extraction
Test files (multiple) Standardized assertions to use assertThat() instead of assertEquals(), added assertNotNull() checks for null safety

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@lsiepel lsiepel left a comment

Choose a reason for hiding this comment

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

Thanks, no new comments besides some of the copilot suggestions. Otherwise LGTM

Fix warnings

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
@jlaur
Copy link
Contributor Author

jlaur commented Nov 23, 2025

no new comments besides some of the copilot suggestions

I have removed unneeded null checks in SceneTests as suggested by Copilot.

Copy link
Contributor

@lsiepel lsiepel left a comment

Choose a reason for hiding this comment

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

Thanks, LGTM

(awaiting 5.0.3 release)

@lsiepel lsiepel self-assigned this Nov 23, 2025
@lsiepel lsiepel merged commit acfce6b into openhab:main Nov 23, 2025
2 checks passed
@lsiepel lsiepel added this to the 5.1 milestone Nov 23, 2025
@jlaur jlaur deleted the hueemulation-npe branch November 27, 2025 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug An unexpected problem or unintended behavior of an add-on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants