Skip to content

Actor DurationUtils parsing of duration throws an exception #712

Description

@mukundansundar

Expected Behavior

Duration is parsed correctly and no exception is thrown

Actual Behavior

Following Actor API reference in docs : https://docs.dapr.io/reference/api/actors_api/#examples-3

if a reminder is created in the format

curl http://localhost:3500/v1.0/actors/stormtrooper/50/reminders/checkRebels \
  -H "Content-Type: application/json"
-d '{
      "data": "someData",
      "dueTime": "1m",
      "period": "20s"
    }'

And used in a Java with an Actor Type names stormtrooper and the Actor class implements Remindable data ...

public Mono<Void> receiveReminder(String reminderName, String state, Duration dueTime, Duration period) {

When receiveReminder is called by the actor runtime, the dueTime field is parsed and converted to Java Duration using the class
DurationUtils.java

 public static Duration convertDurationFromDaprFormat(String valueString) {
    // Convert the format returned by the Dapr runtime into Duration
    // An example of the format is: 4h15m50s60ms. It does not include days.
    int hourIndex = valueString.indexOf('h');
    int minuteIndex = valueString.indexOf('m');
    int secondIndex = valueString.indexOf('s');
    int milliIndex = valueString.indexOf("ms");

    String hoursSpan = valueString.substring(0, hourIndex);

    int hours = Integer.parseInt(hoursSpan);
    int days = hours / 24;
    hours = hours % 24;

    String minutesSpan = valueString.substring(hourIndex + 1, minuteIndex);
    int minutes = Integer.parseInt(minutesSpan);

    String secondsSpan = valueString.substring(minuteIndex + 1, secondIndex);
    int seconds = Integer.parseInt(secondsSpan);

    String millisecondsSpan = valueString.substring(secondIndex + 1, milliIndex);
    int milliseconds = Integer.parseInt(millisecondsSpan);

    return Duration.ZERO
        .plusDays(days)
        .plusHours(hours)
        .plusMinutes(minutes)
        .plusSeconds(seconds)
        .plusMillis(milliseconds);
  }

In this an exception is thrown as such:

== APP == 2022-03-23 12:26:12,023 {HH:mm:ss.SSS} [http-nio-3000-exec-5] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 3] with root cause
== APP == java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 3
== APP == 	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
== APP == 	at java.base/java.lang.String.substring(String.java:1874)
== APP == 	at io.dapr.utils.DurationUtils.convertDurationFromDaprFormat(DurationUtils.java:34)
== APP == 	at io.dapr.actors.runtime.ActorObjectSerializer.extractDurationOrNull(ActorObjectSerializer.java:229)
== APP == 	at io.dapr.actors.runtime.ActorObjectSerializer.deserializeActorReminder(ActorObjectSerializer.java:209)
== APP == 	at io.dapr.actors.runtime.ActorObjectSerializer.deserialize(ActorObjectSerializer.java:168)
== APP == 	at io.dapr.actors.runtime.ActorManager.lambda$invokeReminder$4(ActorManager.java:104)
== APP == 	at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:85)
== APP == 	at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:126)
== APP == 	at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53)
== APP == 	at reactor.core.publisher.Mono.subscribe(Mono.java:4198)
....

Dapr parses the dueTime correctly and reminds the app on time ... But the DurationUtils class expects the full format of ISO 8601 duration.

Steps to Reproduce the Problem

Create any actor type and create reminder with only partial dueTime example "1m", "1s" etc.

Release Note

RELEASE NOTE:

Metadata

Metadata

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions