[
https://issues.apache.org/jira/browse/FINERACT-2315?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Peter Thua updated FINERACT-2315:
---------------------------------
Description:
{color:#0000ff}# SMS Campaign Reports API Returns 500 Internal Server
Error{color}
{color:#0000ff}## Summary{color}
{color:#000000}API endpoint for retrieving SMS campaign reports by status
returns a 500 Internal Server Error due to HK2 dependency injection failure
with record-based parameter classes.{color}
{color:#0000ff}## Environment{color}
{color:#0000ff}- {color}{color:#000000}*{*}API
Endpoint:{*}*{color}{color:#000000}
{color}{color:#001188}`/fineract-provider/api/v1/sms/\{id}/messageByStatus`{color}
{color:#0000ff}- {color}{color:#000000}*{*}Framework:{*}*{color}{color:#000000}
Jersey with HK2 dependency injection{color}
{color:#0000ff}- {color}{color:#000000}*{*}Java
Version:{*}*{color}{color:#000000} 21{color}
{color:#0000ff}## Steps to Reproduce{color}
{color:#0000ff}### Prerequisites{color}
{color:#0000ff}- {color}{color:#000000}Access to Fineract SMS API{color}
{color:#0000ff}- {color}{color:#000000}Valid SMS campaign ID{color}
{color:#0000ff}- {color}{color:#000000}API authentication credentials{color}
{color:#0000ff}### Reproduction Steps{color}
{color:#0000ff}1. {color}{color:#000000}*{*}Create or identify an existing SMS
campaign{*}*{color}
{color:#0000ff} - {color}{color:#000000}Ensure the campaign has sent
messages{color}
{color:#0000ff} - {color}{color:#000000}Note the campaign ID (e.g.,
{color}{color:#001188}`2`{color}{color:#000000}){color}
{color:#0000ff}2. {color}{color:#000000}*{*}Make API request to retrieve
campaign reports{*}*{color}
{color:#a31515} ```bash{color}
{color:#000000} GET
[https://localhost:8443/fineract-provider/api/v1/sms/\|https://localhost:8443/fineract-provider/api/v1/sms/]{{{}id{}}}/messageByStatus?status=100&locale=en&dateFormat=dd
MMMM yyyy&fromDate=01 June 2025&toDate=17 June 2025{color}
{color:#000000} {color}{color:#a31515}```{color}
{color:#000000} {color}
{color:#000000} Replace
{color}{color:#001188}`{{{}id{}}}`{color}{color:#000000} with your actual
campaign ID.{color}
{color:#0000ff}3. {color}{color:#000000}*{*}Sample query parameters:{*}*{color}
{color:#0000ff} - {color}{color:#001188}`status=100`{color}{color:#000000}
(or any valid status code){color}
{color:#0000ff} - {color}{color:#001188}`locale=en`{color}
{color:#0000ff} - {color}{color:#001188}`dateFormat=dd MMMM yyyy`{color}
{color:#0000ff} - {color}{color:#001188}`fromDate=01 June
2025`{color}{color:#000000} (adjust date as needed){color}
{color:#0000ff} - {color}{color:#001188}`toDate=17 June
2025`{color}{color:#000000} (adjust date as needed){color}
{color:#0000ff}4. {color}{color:#000000}*{*}Execute the request{*}*{color}
{color:#0000ff} - {color}{color:#000000}Use any HTTP client (Postman, curl,
browser, etc.){color}
{color:#0000ff} - {color}{color:#000000}Include necessary authentication
headers{color}
{color:#0000ff}### Expected Result{color}
{color:#000000}API should return SMS message data filtered by the specified
status and date range.{color}
{color:#0000ff}### Actual Result{color}
{color:#000000}API returns a 500 Internal Server Error with the following
response:{color}
{color:#a31515}```json{color}
{color:#000000}{{color}
{color:#000000} "timestamp": "2025-06-17T12:06:20.745Z",{color}
{color:#000000} "status": 500,{color}
{color:#000000} "error": "Internal Server Error",{color}
{color:#000000} "path":
"/fineract-provider/api/v1/sms/2/messageByStatus"{color}
{color:#000000}}
```
{color:#0000ff}## Error Details{color}
{color:#0000ff}### Server Console Error{color}
{color:#a31515}```{color}
{color:#001188}org.glassfish.hk2.api.MultiException: A MultiException has 2
exceptions. They are:{color}
{color:#001188}1. java.lang.NoSuchMethodException: Could not find a suitable
constructor in org.apache.fineract.infrastructure.sms.param.SmsRequestParam
class.{color}
{color:#001188}2. java.lang.IllegalArgumentException: Errors were discovered
while reifying SystemDescriptor(...){color}
{color:#a31515}```{color}
{color:#0000ff}### Error Analysis{color}
{color:#000000}The error occurs in the following sequence:{color}
{color:#0000ff}1. {color}{color:#000000}Jersey receives the HTTP request with
query parameters{color}
{color:#0000ff}2. {color}{color:#000000}Jersey attempts to create an instance
of {color}{color:#001188}`SmsRequestParam`{color}{color:#000000} (annotated
with {color}{color:#001188}`@BeanParam`{color}{color:#000000}){color}
{color:#0000ff}3. {color}{color:#000000}HK2 dependency injection container
tries to instantiate the parameter class{color}
{color:#0000ff}4. {color}{color:#000000}HK2 fails to find a suitable
constructor in the record-based
{color}{color:#001188}`SmsRequestParam`{color}{color:#000000} class{color}
{color:#0000ff}5.
{color}{color:#001188}`NoSuchMethodException`{color}{color:#000000} is thrown,
causing the 500 error{color}
{color:#0000ff}## Root Cause{color}
{color:#0000ff}### Technical Details{color}
{color:#0000ff}- {color}{color:#000000}*{*}Framework
Incompatibility:{*}*{color}{color:#000000} Jersey uses HK2 for dependency
injection, which requires classes to have default constructors for
instantiation{color}
{color:#0000ff}- {color}{color:#000000}*{*}Record
Limitation:{*}*{color}{color:#000000} Java records only provide constructors
that accept all declared parameters; they do not have default (no-argument)
constructors{color}
{color:#0000ff}- {color}{color:#000000}*{*}Parameter Binding
Process:{*}*{color}{color:#000000} The
{color}{color:#001188}`@BeanParam`{color}{color:#000000} annotation instructs
Jersey to:{color}
{color:#0000ff} 1. {color}{color:#000000}Create an instance of the parameter
class using dependency injection{color}
{color:#0000ff} 2. {color}{color:#000000}Populate the instance fields using
query parameter values{color}
{color:#0000ff} 3. {color}{color:#000000}Pass the populated instance to the
endpoint method{color}
{color:#0000ff}### Why Records Don't Work{color}
{color:#000000}Records are immutable data carriers that:{color}
{color:#0000ff}- {color}{color:#000000}Automatically generate constructors
requiring all parameters{color}
{color:#0000ff}- {color}{color:#000000}Do not provide default
constructors{color}
{color:#0000ff}- {color}{color:#000000}Cannot be instantiated without providing
all field values upfront{color}
{color:#000000}This conflicts with HK2's instantiation process, which expects
to create empty instances first and then populate them through setter methods
or field injection.{color}
{color:#0000ff}## Solution Implemented{color}
{color:#0000ff}### Code Changes{color}
{color:#000000}Converted
{color}{color:#001188}`SmsRequestParam`{color}{color:#000000} from a record to
a regular class with:{color}
{color:#000000}*{*}Before (Record):{*}*{color}
{color:#a31515}```java{color}
{color:#0000ff}public{color}{color:#000000}
{color}{color:#0000ff}record{color}{color:#000000} SmsRequestParam(Long status,
DateParam fromDate, DateParam toDate, String locale, String rawDateFormat,
Integer offset,{color}
{color:#000000} Integer limit, String orderBy, String sortOrder)
{}{color}
{color:#a31515}```{color}
{color:#000000}*{*}After (Regular Class):{*}*{color}
{color:#a31515}```java{color}
{color:#808080}@Data{color}
{color:#808080}@NoArgsConstructor{color}
{color:#0000ff}public{color}{color:#000000}
{color}{color:#0000ff}class{color}{color:#000000} SmsRequestParam {{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"status"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Long
status;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"fromDate"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
DateParam fromDate;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"toDate"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
DateParam toDate;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"locale"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
locale;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"dateFormat"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
rawDateFormat;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"offset"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Integer
offset;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"limit"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Integer
limit;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"orderBy"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
orderBy;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"sortOrder"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
sortOrder;{color}
{color:#000000}}
```
{color:#0000ff}### How the Fix Works{color}
{color:#0000ff}1. {color}{color:#000000}*{*}Default
Constructor:{*}*{color}{color:#000000} Allows HK2 to create an empty
instance{color}
{color:#0000ff}2. {color}{color:#000000}*{*}Setter
Methods:{*}*{color}{color:#000000} Enable Jersey to populate fields with query
parameter values{color}
{color:#0000ff}3. {color}{color:#000000}*{*}Field
Annotations:{*}*{color}{color:#000000}
{color}{color:#001188}`@QueryParam`{color}{color:#000000} annotations directly
map HTTP parameters to class fields{color}
{color:#0000ff}4. {color}{color:#000000}*{*}Dependency
Injection:{*}*{color}{color:#000000} HK2 can now successfully instantiate and
populate the parameter object{color}
{color:#0000ff}## Verification{color}
{color:#000000}After implementing the fix:{color}
{color:#0000ff}1. {color}{color:#000000}The API endpoint successfully processes
requests{color}
{color:#0000ff}2. {color}{color:#000000}Query parameters are correctly mapped
to the parameter object{color}
{color:#0000ff}3. {color}{color:#000000}SMS campaign reports are returned as
expected{color}
{color:#0000ff}4. {color}{color:#000000}No more 500 Internal Server Error
responses{color}
{color:#0000ff}## Impact{color}
{color:#0000ff}- {color}{color:#000000}*{*}Severity:{*}*{color}{color:#000000}
High - API endpoint completely non-functional{color}
{color:#0000ff}- {color}{color:#000000}*{*}Affected
Users:{*}*{color}{color:#000000} All users attempting to retrieve SMS campaign
reports{color}
{color:#0000ff}-
{color}{color:#000000}*{*}Workaround:{*}*{color}{color:#000000} None available
before fix implementation{color}
{color:#0000ff}## Related Issues{color}
{color:#0000ff}- {color}{color:#000000}Consider reviewing other record-based
parameter classes in the codebase{color}
{color:#0000ff}- {color}{color:#000000}Evaluate compatibility of Java records
with Jersey/HK2 framework combination{color}
{color:#0000ff}- {color}{color:#000000}Document best practices for parameter
binding in the project
{color}
was:
{color:#0000ff}# SMS Campaign Reports API Returns 500 Internal Server
Error{color}
{color:#0000ff}## Summary{color}
{color:#000000}API endpoint for retrieving SMS campaign reports by status
returns a 500 Internal Server Error due to HK2 dependency injection failure
with record-based parameter classes.{color}
{color:#0000ff}## Environment{color}
{color:#0000ff}- {color}{color:#000000}**API Endpoint:**{color}{color:#000000}
{color}{color:#001188}`/fineract-provider/api/v1/sms/\{id}/messageByStatus`{color}
{color:#0000ff}- {color}{color:#000000}**Framework:**{color}{color:#000000}
Jersey with HK2 dependency injection{color}
{color:#0000ff}- {color}{color:#000000}**Java Version:**{color}{color:#000000}
21{color}
{color:#0000ff}## Steps to Reproduce{color}
{color:#0000ff}### Prerequisites{color}
{color:#0000ff}- {color}{color:#000000}Access to Fineract SMS API{color}
{color:#0000ff}- {color}{color:#000000}Valid SMS campaign ID{color}
{color:#0000ff}- {color}{color:#000000}API authentication credentials{color}
{color:#0000ff}### Reproduction Steps{color}
{color:#0000ff}1. {color}{color:#000000}**Create or identify an existing SMS
campaign**{color}
{color:#0000ff} - {color}{color:#000000}Ensure the campaign has sent
messages{color}
{color:#0000ff} - {color}{color:#000000}Note the campaign ID (e.g.,
{color}{color:#001188}`2`{color}{color:#000000}){color}
{color:#0000ff}2. {color}{color:#000000}**Make API request to retrieve campaign
reports**{color}
{color:#a31515} ```bash{color}
{color:#000000} GET
https://localhost:8443/fineract-provider/api/v1/sms/\{{id}}/messageByStatus?status=100&locale=en&dateFormat=dd
MMMM yyyy&fromDate=01 June 2025&toDate=17 June 2025{color}
{color:#000000} {color}{color:#a31515}```{color}
{color:#000000} {color}
{color:#000000} Replace {color}{color:#001188}`\{{id}}`{color}{color:#000000}
with your actual campaign ID.{color}
{color:#0000ff}3. {color}{color:#000000}**Sample query parameters:**{color}
{color:#0000ff} - {color}{color:#001188}`status=100`{color}{color:#000000}
(or any valid status code){color}
{color:#0000ff} - {color}{color:#001188}`locale=en`{color}
{color:#0000ff} - {color}{color:#001188}`dateFormat=dd MMMM yyyy`{color}
{color:#0000ff} - {color}{color:#001188}`fromDate=01 June
2025`{color}{color:#000000} (adjust date as needed){color}
{color:#0000ff} - {color}{color:#001188}`toDate=17 June
2025`{color}{color:#000000} (adjust date as needed){color}
{color:#0000ff}4. {color}{color:#000000}**Execute the request**{color}
{color:#0000ff} - {color}{color:#000000}Use any HTTP client (Postman, curl,
browser, etc.){color}
{color:#0000ff} - {color}{color:#000000}Include necessary authentication
headers{color}
{color:#0000ff}### Expected Result{color}
{color:#000000}API should return SMS message data filtered by the specified
status and date range.{color}
{color:#0000ff}### Actual Result{color}
{color:#000000}API returns a 500 Internal Server Error with the following
response:{color}
{color:#a31515}```json{color}
{color:#000000}{{color}
{color:#000000} "timestamp": "2025-06-17T12:06:20.745Z",{color}
{color:#000000} "status": 500,{color}
{color:#000000} "error": "Internal Server Error",{color}
{color:#000000} "path":
"/fineract-provider/api/v1/sms/2/messageByStatus"{color}
{color:#000000}}{color}
{color:#a31515}```{color}
{color:#0000ff}## Error Details{color}
{color:#0000ff}### Server Console Error{color}
{color:#a31515}```{color}
{color:#001188}org.glassfish.hk2.api.MultiException: A MultiException has 2
exceptions. They are:{color}
{color:#001188}1. java.lang.NoSuchMethodException: Could not find a suitable
constructor in org.apache.fineract.infrastructure.sms.param.SmsRequestParam
class.{color}
{color:#001188}2. java.lang.IllegalArgumentException: Errors were discovered
while reifying SystemDescriptor(...){color}
{color:#a31515}```{color}
{color:#0000ff}### Error Analysis{color}
{color:#000000}The error occurs in the following sequence:{color}
{color:#0000ff}1. {color}{color:#000000}Jersey receives the HTTP request with
query parameters{color}
{color:#0000ff}2. {color}{color:#000000}Jersey attempts to create an instance
of {color}{color:#001188}`SmsRequestParam`{color}{color:#000000} (annotated
with {color}{color:#001188}`@BeanParam`{color}{color:#000000}){color}
{color:#0000ff}3. {color}{color:#000000}HK2 dependency injection container
tries to instantiate the parameter class{color}
{color:#0000ff}4. {color}{color:#000000}HK2 fails to find a suitable
constructor in the record-based
{color}{color:#001188}`SmsRequestParam`{color}{color:#000000} class{color}
{color:#0000ff}5.
{color}{color:#001188}`NoSuchMethodException`{color}{color:#000000} is thrown,
causing the 500 error{color}
{color:#0000ff}## Root Cause{color}
{color:#0000ff}### Technical Details{color}
{color:#0000ff}- {color}{color:#000000}**Framework
Incompatibility:**{color}{color:#000000} Jersey uses HK2 for dependency
injection, which requires classes to have default constructors for
instantiation{color}
{color:#0000ff}- {color}{color:#000000}**Record
Limitation:**{color}{color:#000000} Java records only provide constructors that
accept all declared parameters; they do not have default (no-argument)
constructors{color}
{color:#0000ff}- {color}{color:#000000}**Parameter Binding
Process:**{color}{color:#000000} The
{color}{color:#001188}`@BeanParam`{color}{color:#000000} annotation instructs
Jersey to:{color}
{color:#0000ff} 1. {color}{color:#000000}Create an instance of the parameter
class using dependency injection{color}
{color:#0000ff} 2. {color}{color:#000000}Populate the instance fields using
query parameter values{color}
{color:#0000ff} 3. {color}{color:#000000}Pass the populated instance to the
endpoint method{color}
{color:#0000ff}### Why Records Don't Work{color}
{color:#000000}Records are immutable data carriers that:{color}
{color:#0000ff}- {color}{color:#000000}Automatically generate constructors
requiring all parameters{color}
{color:#0000ff}- {color}{color:#000000}Do not provide default
constructors{color}
{color:#0000ff}- {color}{color:#000000}Cannot be instantiated without providing
all field values upfront{color}
{color:#000000}This conflicts with HK2's instantiation process, which expects
to create empty instances first and then populate them through setter methods
or field injection.{color}
{color:#0000ff}## Solution Implemented{color}
{color:#0000ff}### Code Changes{color}
{color:#000000}Converted
{color}{color:#001188}`SmsRequestParam`{color}{color:#000000} from a record to
a regular class with:{color}
{color:#000000}**Before (Record):**{color}
{color:#a31515}```java{color}
{color:#0000ff}public{color}{color:#000000}
{color}{color:#0000ff}record{color}{color:#000000} SmsRequestParam(Long status,
DateParam fromDate, DateParam toDate, String locale, String rawDateFormat,
Integer offset,{color}
{color:#000000} Integer limit, String orderBy, String sortOrder)
{}{color}
{color:#a31515}```{color}
{color:#000000}**After (Regular Class):**{color}
{color:#a31515}```java{color}
{color:#808080}@Data{color}
{color:#808080}@NoArgsConstructor{color}
{color:#0000ff}public{color}{color:#000000}
{color}{color:#0000ff}class{color}{color:#000000} SmsRequestParam {{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"status"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Long
status;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"fromDate"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
DateParam fromDate;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"toDate"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
DateParam toDate;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"locale"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
locale;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"dateFormat"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
rawDateFormat;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"offset"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Integer
offset;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"limit"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Integer
limit;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"orderBy"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
orderBy;{color}
{color:#000000}
{color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"sortOrder"{color}{color:#000000}){color}
{color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
sortOrder;{color}
{color:#000000}}{color}
{color:#a31515}```{color}
{color:#0000ff}### How the Fix Works{color}
{color:#0000ff}1. {color}{color:#000000}**Default
Constructor:**{color}{color:#000000} Allows HK2 to create an empty
instance{color}
{color:#0000ff}2. {color}{color:#000000}**Setter
Methods:**{color}{color:#000000} Enable Jersey to populate fields with query
parameter values{color}
{color:#0000ff}3. {color}{color:#000000}**Field
Annotations:**{color}{color:#000000}
{color}{color:#001188}`@QueryParam`{color}{color:#000000} annotations directly
map HTTP parameters to class fields{color}
{color:#0000ff}4. {color}{color:#000000}**Dependency
Injection:**{color}{color:#000000} HK2 can now successfully instantiate and
populate the parameter object{color}
{color:#0000ff}## Verification{color}
{color:#000000}After implementing the fix:{color}
{color:#0000ff}1. {color}{color:#000000}The API endpoint successfully processes
requests{color}
{color:#0000ff}2. {color}{color:#000000}Query parameters are correctly mapped
to the parameter object{color}
{color:#0000ff}3. {color}{color:#000000}SMS campaign reports are returned as
expected{color}
{color:#0000ff}4. {color}{color:#000000}No more 500 Internal Server Error
responses{color}
{color:#0000ff}## Impact{color}
{color:#0000ff}- {color}{color:#000000}**Severity:**{color}{color:#000000} High
- API endpoint completely non-functional{color}
{color:#0000ff}- {color}{color:#000000}**Affected
Users:**{color}{color:#000000} All users attempting to retrieve SMS campaign
reports{color}
{color:#0000ff}- {color}{color:#000000}**Workaround:**{color}{color:#000000}
None available before fix implementation{color}
{color:#0000ff}## Related Issues{color}
{color:#0000ff}- {color}{color:#000000}Consider reviewing other record-based
parameter classes in the codebase{color}
{color:#0000ff}- {color}{color:#000000}Evaluate compatibility of Java records
with Jersey/HK2 framework combination{color}
{color:#0000ff}- {color}{color:#000000}Document best practices for parameter
binding in the project
!image-2025-06-19-09-26-39-710.png!
{color}
> Error Reading SMS Campaing Reports e.g. Pending or Sent SMS
> -----------------------------------------------------------
>
> Key: FINERACT-2315
> URL: https://issues.apache.org/jira/browse/FINERACT-2315
> Project: Apache Fineract
> Issue Type: Bug
> Reporter: Peter Thua
> Priority: Major
> Attachments: image-2025-06-19-09-26-39-710.png
>
>
> {color:#0000ff}# SMS Campaign Reports API Returns 500 Internal Server
> Error{color}
> {color:#0000ff}## Summary{color}
> {color:#000000}API endpoint for retrieving SMS campaign reports by status
> returns a 500 Internal Server Error due to HK2 dependency injection failure
> with record-based parameter classes.{color}
> {color:#0000ff}## Environment{color}
> {color:#0000ff}- {color}{color:#000000}*{*}API
> Endpoint:{*}*{color}{color:#000000}
> {color}{color:#001188}`/fineract-provider/api/v1/sms/\{id}/messageByStatus`{color}
> {color:#0000ff}-
> {color}{color:#000000}*{*}Framework:{*}*{color}{color:#000000} Jersey with
> HK2 dependency injection{color}
> {color:#0000ff}- {color}{color:#000000}*{*}Java
> Version:{*}*{color}{color:#000000} 21{color}
> {color:#0000ff}## Steps to Reproduce{color}
> {color:#0000ff}### Prerequisites{color}
> {color:#0000ff}- {color}{color:#000000}Access to Fineract SMS API{color}
> {color:#0000ff}- {color}{color:#000000}Valid SMS campaign ID{color}
> {color:#0000ff}- {color}{color:#000000}API authentication credentials{color}
> {color:#0000ff}### Reproduction Steps{color}
> {color:#0000ff}1. {color}{color:#000000}*{*}Create or identify an existing
> SMS campaign{*}*{color}
> {color:#0000ff} - {color}{color:#000000}Ensure the campaign has sent
> messages{color}
> {color:#0000ff} - {color}{color:#000000}Note the campaign ID (e.g.,
> {color}{color:#001188}`2`{color}{color:#000000}){color}
> {color:#0000ff}2. {color}{color:#000000}*{*}Make API request to retrieve
> campaign reports{*}*{color}
> {color:#a31515} ```bash{color}
> {color:#000000} GET
> [https://localhost:8443/fineract-provider/api/v1/sms/\|https://localhost:8443/fineract-provider/api/v1/sms/]{{{}id{}}}/messageByStatus?status=100&locale=en&dateFormat=dd
> MMMM yyyy&fromDate=01 June 2025&toDate=17 June 2025{color}
> {color:#000000} {color}{color:#a31515}```{color}
> {color:#000000} {color}
> {color:#000000} Replace
> {color}{color:#001188}`{{{}id{}}}`{color}{color:#000000} with your actual
> campaign ID.{color}
> {color:#0000ff}3. {color}{color:#000000}*{*}Sample query
> parameters:{*}*{color}
> {color:#0000ff} - {color}{color:#001188}`status=100`{color}{color:#000000}
> (or any valid status code){color}
> {color:#0000ff} - {color}{color:#001188}`locale=en`{color}
> {color:#0000ff} - {color}{color:#001188}`dateFormat=dd MMMM yyyy`{color}
> {color:#0000ff} - {color}{color:#001188}`fromDate=01 June
> 2025`{color}{color:#000000} (adjust date as needed){color}
> {color:#0000ff} - {color}{color:#001188}`toDate=17 June
> 2025`{color}{color:#000000} (adjust date as needed){color}
> {color:#0000ff}4. {color}{color:#000000}*{*}Execute the request{*}*{color}
> {color:#0000ff} - {color}{color:#000000}Use any HTTP client (Postman, curl,
> browser, etc.){color}
> {color:#0000ff} - {color}{color:#000000}Include necessary authentication
> headers{color}
> {color:#0000ff}### Expected Result{color}
> {color:#000000}API should return SMS message data filtered by the specified
> status and date range.{color}
> {color:#0000ff}### Actual Result{color}
> {color:#000000}API returns a 500 Internal Server Error with the following
> response:{color}
> {color:#a31515}```json{color}
> {color:#000000}{{color}
> {color:#000000} "timestamp": "2025-06-17T12:06:20.745Z",{color}
> {color:#000000} "status": 500,{color}
> {color:#000000} "error": "Internal Server Error",{color}
> {color:#000000} "path":
> "/fineract-provider/api/v1/sms/2/messageByStatus"{color}
> {color:#000000}}
> ```
> {color:#0000ff}## Error Details{color}
> {color:#0000ff}### Server Console Error{color}
> {color:#a31515}```{color}
> {color:#001188}org.glassfish.hk2.api.MultiException: A MultiException has 2
> exceptions. They are:{color}
> {color:#001188}1. java.lang.NoSuchMethodException: Could not find a suitable
> constructor in org.apache.fineract.infrastructure.sms.param.SmsRequestParam
> class.{color}
> {color:#001188}2. java.lang.IllegalArgumentException: Errors were discovered
> while reifying SystemDescriptor(...){color}
> {color:#a31515}```{color}
> {color:#0000ff}### Error Analysis{color}
> {color:#000000}The error occurs in the following sequence:{color}
> {color:#0000ff}1. {color}{color:#000000}Jersey receives the HTTP request with
> query parameters{color}
> {color:#0000ff}2. {color}{color:#000000}Jersey attempts to create an instance
> of {color}{color:#001188}`SmsRequestParam`{color}{color:#000000} (annotated
> with {color}{color:#001188}`@BeanParam`{color}{color:#000000}){color}
> {color:#0000ff}3. {color}{color:#000000}HK2 dependency injection container
> tries to instantiate the parameter class{color}
> {color:#0000ff}4. {color}{color:#000000}HK2 fails to find a suitable
> constructor in the record-based
> {color}{color:#001188}`SmsRequestParam`{color}{color:#000000} class{color}
> {color:#0000ff}5.
> {color}{color:#001188}`NoSuchMethodException`{color}{color:#000000} is
> thrown, causing the 500 error{color}
> {color:#0000ff}## Root Cause{color}
> {color:#0000ff}### Technical Details{color}
> {color:#0000ff}- {color}{color:#000000}*{*}Framework
> Incompatibility:{*}*{color}{color:#000000} Jersey uses HK2 for dependency
> injection, which requires classes to have default constructors for
> instantiation{color}
> {color:#0000ff}- {color}{color:#000000}*{*}Record
> Limitation:{*}*{color}{color:#000000} Java records only provide constructors
> that accept all declared parameters; they do not have default (no-argument)
> constructors{color}
> {color:#0000ff}- {color}{color:#000000}*{*}Parameter Binding
> Process:{*}*{color}{color:#000000} The
> {color}{color:#001188}`@BeanParam`{color}{color:#000000} annotation instructs
> Jersey to:{color}
> {color:#0000ff} 1. {color}{color:#000000}Create an instance of the parameter
> class using dependency injection{color}
> {color:#0000ff} 2. {color}{color:#000000}Populate the instance fields using
> query parameter values{color}
> {color:#0000ff} 3. {color}{color:#000000}Pass the populated instance to the
> endpoint method{color}
> {color:#0000ff}### Why Records Don't Work{color}
> {color:#000000}Records are immutable data carriers that:{color}
> {color:#0000ff}- {color}{color:#000000}Automatically generate constructors
> requiring all parameters{color}
> {color:#0000ff}- {color}{color:#000000}Do not provide default
> constructors{color}
> {color:#0000ff}- {color}{color:#000000}Cannot be instantiated without
> providing all field values upfront{color}
> {color:#000000}This conflicts with HK2's instantiation process, which expects
> to create empty instances first and then populate them through setter methods
> or field injection.{color}
> {color:#0000ff}## Solution Implemented{color}
> {color:#0000ff}### Code Changes{color}
> {color:#000000}Converted
> {color}{color:#001188}`SmsRequestParam`{color}{color:#000000} from a record
> to a regular class with:{color}
> {color:#000000}*{*}Before (Record):{*}*{color}
> {color:#a31515}```java{color}
> {color:#0000ff}public{color}{color:#000000}
> {color}{color:#0000ff}record{color}{color:#000000} SmsRequestParam(Long
> status, DateParam fromDate, DateParam toDate, String locale, String
> rawDateFormat, Integer offset,{color}
> {color:#000000} Integer limit, String orderBy, String sortOrder)
> {}{color}
> {color:#a31515}```{color}
> {color:#000000}*{*}After (Regular Class):{*}*{color}
> {color:#a31515}```java{color}
> {color:#808080}@Data{color}
> {color:#808080}@NoArgsConstructor{color}
> {color:#0000ff}public{color}{color:#000000}
> {color}{color:#0000ff}class{color}{color:#000000} SmsRequestParam {{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"status"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000} Long
> status;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"fromDate"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
> DateParam fromDate;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"toDate"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
> DateParam toDate;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"locale"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
> locale;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"dateFormat"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
> rawDateFormat;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"offset"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
> Integer offset;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"limit"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000}
> Integer limit;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"orderBy"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
> orderBy;{color}
> {color:#000000}
> {color}{color:#808080}@QueryParam{color}{color:#000000}({color}{color:#a31515}"sortOrder"{color}{color:#000000}){color}
> {color:#000000} {color}{color:#0000ff}private{color}{color:#000000} String
> sortOrder;{color}
> {color:#000000}}
> ```
> {color:#0000ff}### How the Fix Works{color}
> {color:#0000ff}1. {color}{color:#000000}*{*}Default
> Constructor:{*}*{color}{color:#000000} Allows HK2 to create an empty
> instance{color}
> {color:#0000ff}2. {color}{color:#000000}*{*}Setter
> Methods:{*}*{color}{color:#000000} Enable Jersey to populate fields with
> query parameter values{color}
> {color:#0000ff}3. {color}{color:#000000}*{*}Field
> Annotations:{*}*{color}{color:#000000}
> {color}{color:#001188}`@QueryParam`{color}{color:#000000} annotations
> directly map HTTP parameters to class fields{color}
> {color:#0000ff}4. {color}{color:#000000}*{*}Dependency
> Injection:{*}*{color}{color:#000000} HK2 can now successfully instantiate and
> populate the parameter object{color}
> {color:#0000ff}## Verification{color}
> {color:#000000}After implementing the fix:{color}
> {color:#0000ff}1. {color}{color:#000000}The API endpoint successfully
> processes requests{color}
> {color:#0000ff}2. {color}{color:#000000}Query parameters are correctly mapped
> to the parameter object{color}
> {color:#0000ff}3. {color}{color:#000000}SMS campaign reports are returned as
> expected{color}
> {color:#0000ff}4. {color}{color:#000000}No more 500 Internal Server Error
> responses{color}
> {color:#0000ff}## Impact{color}
> {color:#0000ff}-
> {color}{color:#000000}*{*}Severity:{*}*{color}{color:#000000} High - API
> endpoint completely non-functional{color}
> {color:#0000ff}- {color}{color:#000000}*{*}Affected
> Users:{*}*{color}{color:#000000} All users attempting to retrieve SMS
> campaign reports{color}
> {color:#0000ff}-
> {color}{color:#000000}*{*}Workaround:{*}*{color}{color:#000000} None
> available before fix implementation{color}
> {color:#0000ff}## Related Issues{color}
> {color:#0000ff}- {color}{color:#000000}Consider reviewing other record-based
> parameter classes in the codebase{color}
> {color:#0000ff}- {color}{color:#000000}Evaluate compatibility of Java records
> with Jersey/HK2 framework combination{color}
> {color:#0000ff}- {color}{color:#000000}Document best practices for parameter
> binding in the project
> {color}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)