[ 
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)

Reply via email to