44
55import com .amazonaws .services .lambda .runtime .Context ;
66import com .amazonaws .services .lambda .runtime .RequestStreamHandler ;
7- import com .fasterxml .jackson .core .JsonGenerator ;
8- import com .fasterxml .jackson .core .JsonParser ;
9- import com .fasterxml .jackson .core .JsonToken ;
10- import com .fasterxml .jackson .databind .DeserializationContext ;
11- import com .fasterxml .jackson .databind .DeserializationFeature ;
12- import com .fasterxml .jackson .databind .JsonDeserializer ;
13- import com .fasterxml .jackson .databind .JsonSerializer ;
14- import com .fasterxml .jackson .databind .MapperFeature ;
15- import com .fasterxml .jackson .databind .ObjectMapper ;
16- import com .fasterxml .jackson .databind .PropertyNamingStrategies ;
17- import com .fasterxml .jackson .databind .SerializationFeature ;
18- import com .fasterxml .jackson .databind .SerializerProvider ;
19- import com .fasterxml .jackson .databind .json .JsonMapper ;
20- import com .fasterxml .jackson .databind .module .SimpleModule ;
21- import com .fasterxml .jackson .datatype .jsr310 .JavaTimeModule ;
227import java .io .IOException ;
238import java .io .InputStream ;
249import java .io .OutputStream ;
2510import java .lang .reflect .ParameterizedType ;
26- import java .time .Instant ;
27- import java .time .format .DateTimeFormatter ;
28- import java .time .format .DateTimeFormatterBuilder ;
29- import java .util .Date ;
3011import org .slf4j .Logger ;
3112import org .slf4j .LoggerFactory ;
3213import software .amazon .lambda .durable .model .DurableExecutionInput ;
33- import software .amazon .lambda .durable .serde .AwsSdkV2Module ;
14+ import software .amazon .lambda .durable .serde .DurableInputOutputSerDes ;
3415
3516/**
3617 * Abstract base class for Lambda handlers that use durable execution.
@@ -46,7 +27,7 @@ public abstract class DurableHandler<I, O> implements RequestStreamHandler {
4627
4728 private final TypeToken <I > inputType ;
4829 private final DurableConfig config ;
49- private final ObjectMapper objectMapper = createObjectMapper (); // Internal ObjectMapper
30+ private final DurableInputOutputSerDes serDes = new DurableInputOutputSerDes (); // Internal ObjectMapper
5031 private static final Logger logger = LoggerFactory .getLogger (DurableHandler .class );
5132
5233 protected DurableHandler () {
@@ -58,7 +39,6 @@ protected DurableHandler() {
5839 throw new IllegalArgumentException ("Cannot determine input type parameter" );
5940 }
6041 this .config = createConfiguration ();
61- validateConfiguration ();
6242 }
6343
6444 /**
@@ -142,26 +122,22 @@ protected DurableConfig createConfiguration() {
142122 return DurableConfig .defaultConfig ();
143123 }
144124
145- private void validateConfiguration () {
146- if (config .getDurableExecutionClient () == null ) {
147- throw new IllegalStateException ("DurableExecutionClient configuration failed" );
148- }
149- if (config .getSerDes () == null ) {
150- throw new IllegalStateException ("SerDes configuration failed" );
151- }
152- if (config .getExecutorService () == null ) {
153- throw new IllegalStateException ("ExecutorService configuration failed" );
154- }
155- }
156-
125+ /**
126+ * Reads the request, executes the durable function handler and writes the response
127+ *
128+ * @param inputStream the input stream
129+ * @param outputStream the output stream
130+ * @param context the Lambda context
131+ * @throws IOException thrown when serialize/deserialize fails
132+ */
157133 @ Override
158134 public final void handleRequest (InputStream inputStream , OutputStream outputStream , Context context )
159135 throws IOException {
160136 var inputString = new String (inputStream .readAllBytes ());
161137 logger .debug ("Raw input from durable handler: {}" , inputString );
162- var input = this . objectMapper . readValue (inputString , DurableExecutionInput .class );
138+ var input = serDes . deserialize (inputString , TypeToken . get ( DurableExecutionInput .class ) );
163139 var output = DurableExecutor .execute (input , context , inputType , this ::handleRequest , config );
164- outputStream .write (objectMapper . writeValueAsBytes (output ));
140+ outputStream .write (serDes . serialize (output ). getBytes ( ));
165141 }
166142
167143 /**
@@ -172,66 +148,4 @@ public final void handleRequest(InputStream inputStream, OutputStream outputStre
172148 * @return Result
173149 */
174150 public abstract O handleRequest (I input , DurableContext context );
175-
176- /**
177- * Creates ObjectMapper for DAR backend communication (internal use only). This is for INTERNAL use only - handles
178- * Lambda Durable Functions backend protocol.
179- *
180- * <p>Customer-facing serialization uses SerDes from DurableConfig.
181- *
182- * @return Configured ObjectMapper for durable backend communication
183- */
184- public static ObjectMapper createObjectMapper () {
185- var dateModule = new SimpleModule ();
186- dateModule .addDeserializer (Date .class , new JsonDeserializer <>() {
187- @ Override
188- public Date deserialize (JsonParser jsonParser , DeserializationContext deserializationContext )
189- throws IOException {
190- // Timestamp is a double value represent seconds since epoch.
191- var timestamp = jsonParser .getDoubleValue ();
192- // Date expects milliseconds since epoch, so multiply by 1000.
193- return new Date ((long ) (timestamp * 1000 ));
194- }
195- });
196- dateModule .addSerializer (Date .class , new JsonSerializer <>() {
197- @ Override
198- public void serialize (Date date , JsonGenerator jsonGenerator , SerializerProvider serializerProvider )
199- throws IOException {
200- // Timestamp should be a double value representing seconds since epoch, so
201- // convert from milliseconds.
202- double timestamp = date .getTime () / 1000.0 ;
203- jsonGenerator .writeNumber (timestamp );
204- }
205- });
206-
207- // Needed for deserialization of timestamps for some SDK v2 objects
208- dateModule .addDeserializer (Instant .class , new JsonDeserializer <>() {
209- private static final DateTimeFormatter TIMESTAMP_FORMATTER = new DateTimeFormatterBuilder ()
210- .appendPattern ("yyyy-MM-dd HH:mm:ss.SSSSSSXXX" )
211- .toFormatter ();
212-
213- @ Override
214- public Instant deserialize (JsonParser jsonParser , DeserializationContext deserializationContext )
215- throws IOException {
216- if (jsonParser .hasToken (JsonToken .VALUE_NUMBER_INT )) {
217- return Instant .ofEpochMilli (jsonParser .getLongValue ());
218- }
219- var timestampStr = jsonParser .getValueAsString ();
220- return Instant .from (TIMESTAMP_FORMATTER .parse (timestampStr ));
221- }
222- });
223-
224- return JsonMapper .builder ()
225- .disable (SerializationFeature .FAIL_ON_EMPTY_BEANS )
226- .disable (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES )
227- // Looks pretty, and probably needed for tests to be deterministic.
228- .enable (MapperFeature .SORT_PROPERTIES_ALPHABETICALLY )
229- .enable (SerializationFeature .ORDER_MAP_ENTRIES_BY_KEYS )
230- // Data passed over the wire from the backend is UpperCamelCase
231- .propertyNamingStrategy (PropertyNamingStrategies .UPPER_CAMEL_CASE )
232- .addModule (new JavaTimeModule ())
233- .addModule (dateModule )
234- .addModule (new AwsSdkV2Module ())
235- .build ();
236- }
237151}
0 commit comments