
com.amazonaws.services.lambda.runtime.serialization.events.serializers.S3EventSerializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-lambda-java-serialization Show documentation
Show all versions of aws-lambda-java-serialization Show documentation
Serialization logic for the AWS Lambda Java Runtime
The newest version!
/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
package com.amazonaws.services.lambda.runtime.serialization.events.serializers;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions;
import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
import com.amazonaws.services.lambda.runtime.serialization.util.SerializeUtil;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Serializer for S3 event
* NOTE: Because the s3 event class provided by the SDK does not play well with Jackson through a class laoder,
* this class uses the low level org json library to serialize and deserialize the event. If new events are added
* that do not work well with Jackson or GSON, this is the fallback method that will always work but is more verbose.
*/
public class S3EventSerializer implements OrgJsonSerializer {
/**
* As part of https://github.com/aws/aws-lambda-java-libs/issues/74 the `com.amazonaws.services.s3.event.S3EventNotification`
* class used by the aws-lambda-java-events library was adapted from the AWSS3JavaClient library into
* `com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification`, hence the need to support both classes
* in the runtime
* @see com.amazonaws.services.lambda.runtime.events.S3Event;
* @see com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification;
* @see com.amazonaws.services.s3.event.S3EventNotification;
*/
private static final String S3_EVENT_NOTIFICATION_CLASS_V3 = "com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification";
private static final String S3_EVENT_NOTIFICATION_CLASS_V2 = "com.amazonaws.services.s3.event.S3EventNotification";
/**
* S3 event class
* @see com.amazonaws.services.lambda.runtime.events.S3Event;
* @see com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification;
* @see com.amazonaws.services.s3.event.S3EventNotification;
*/
private Class eventClass;
/**
* ClassLoader to be used when loading S3 event classes
*/
private ClassLoader classLoader;
/**
* Construct s3Event Serialize from specific s3 event class from user
* @param eventClass s3 event class
* @see com.amazonaws.services.lambda.runtime.events.S3Event;
* @see com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification;
* @see com.amazonaws.services.s3.event.S3EventNotification;
*/
@Override
public S3EventSerializer withClass(Class eventClass) {
this.eventClass = eventClass;
return this;
}
/**
* Sets the ClassLoader that will be used to load S3 event classes
* @param classLoader - ClassLoader that S3 event classes will be loaded from
*/
@Override
public S3EventSerializer withClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
/**
* deserialize an instance of an s3 event from an input stream
* @param input InputStream reading from
* @return S3Event Object
*/
public T fromJson(InputStream input) {
return fromJson(SerializeUtil.convertStreamToString(input));
}
/**
* deserialize an instance of an s3 event from a string
* @param input String with JSON
* @return s3Event object
*/
public T fromJson(String input) {
JSONObject jsonObject = new JSONObject((input));
return deserializeEvent(jsonObject);
}
/**
* serialize an S3 event object to the output stream
* @param value S3 event object
* @param output OutputStream serializing to
*/
public void toJson(T value, OutputStream output) {
JSONObject jsonObject;
try {
// Try to load newer version of S3EventNotification from aws-lambda-java-events v3+
Class eventNotificationRecordClass = SerializeUtil.loadCustomerClass(
S3_EVENT_NOTIFICATION_CLASS_V3 + "$S3EventNotificationRecord", classLoader);
jsonObject = serializeEvent(eventNotificationRecordClass, value, S3_EVENT_NOTIFICATION_CLASS_V3);
} catch (Exception ex) {
// Fallback to aws-lambda-java-events pre-v3 (relies on aws-s3-sdk)
Class eventNotificationRecordClass = SerializeUtil.loadCustomerClass(
S3_EVENT_NOTIFICATION_CLASS_V2 + "$S3EventNotificationRecord", classLoader);
jsonObject = serializeEvent(eventNotificationRecordClass, value, S3_EVENT_NOTIFICATION_CLASS_V2);
}
// Writer in try block so that writer gets flushed and closed
try (Writer writer = new OutputStreamWriter(output)) {
writer.write(jsonObject.toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* serialize an s3 event
* @param eventNotificationRecordClass class holding the s3 event notification record
* @param value s3 event object
* @param baseClassName base class name
* @return JSONObject that contains s3 event
*/
@SuppressWarnings({"unchecked"})
private JSONObject serializeEvent(Class eventNotificationRecordClass, T value, String baseClassName) {
JSONObject jsonObject = new JSONObject();
Functions.R0 getRecordsMethod = ReflectUtil.bindInstanceR0(value, "getRecords", true, List.class);
jsonObject.put("Records", serializeEventNotificationRecordList(getRecordsMethod.call(), eventNotificationRecordClass, baseClassName));
return jsonObject;
}
/**
* deserialize an s3 event
* @param jsonObject JSONObject with s3 data
* @return S3 Event Object
*/
@SuppressWarnings({"unchecked"})
private T deserializeEvent(JSONObject jsonObject) {
Functions.R1 constructor = ReflectUtil.loadConstructor1(eventClass, true, List.class);
JSONArray records = jsonObject.optJSONArray("Records");
try {
// Try to load newer version of S3EventNotification from aws-lambda-java-events v3+
Class recordClass = SerializeUtil.loadCustomerClass(
S3_EVENT_NOTIFICATION_CLASS_V3 + "$S3EventNotificationRecord", classLoader);
return (T) constructor.call(deserializeEventNotificationRecordList(records, recordClass,
S3_EVENT_NOTIFICATION_CLASS_V3));
} catch (Exception ex) {
// Fallback to aws-lambda-java-events pre-v3 (relies on aws-s3-sdk)
Class eventNotificationRecordClass = SerializeUtil.loadCustomerClass(
S3_EVENT_NOTIFICATION_CLASS_V2 + "$S3EventNotificationRecord", classLoader);
return (T) constructor.call(deserializeEventNotificationRecordList(records, eventNotificationRecordClass,
S3_EVENT_NOTIFICATION_CLASS_V2));
}
}
/**
* serialize an s3 event notification record list
* @param eventNotificationRecords List of event notification records
* @param EventNotificationRecord
* @return JSONArray with s3 event records
*/
@SuppressWarnings({"unchecked"})
private JSONArray serializeEventNotificationRecordList(List eventNotificationRecords,
Class eventNotificationRecordClass,
String baseClassName) {
JSONArray jsonRecords = new JSONArray();
for (Object eventNotificationRecord: eventNotificationRecords) {
jsonRecords.put(serializeEventNotificationRecord((A) eventNotificationRecord, baseClassName));
}
return jsonRecords;
}
/**
* deserialize an s3 event notification record
* @param jsonRecords JSONArray of event notification records
* @param eventNotificiationRecordClass Event notification record class
* @param Event notification record type
* @return List of event notification records
*/
@SuppressWarnings({"unchecked"})
private List deserializeEventNotificationRecordList(JSONArray jsonRecords,
Class eventNotificiationRecordClass,
String baseClassName) {
if (jsonRecords == null) {
jsonRecords = new JSONArray();
}
Class s3EntityClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3Entity", classLoader);
Class s3BucketClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3BucketEntity", classLoader);
Class s3ObjectClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3ObjectEntity", classLoader);
Class requestParametersClass = SerializeUtil.loadCustomerClass(baseClassName + "$RequestParametersEntity", classLoader);
Class responseElementsClass = SerializeUtil.loadCustomerClass(baseClassName + "$ResponseElementsEntity", classLoader);
Class userIdentityClass = SerializeUtil.loadCustomerClass(baseClassName + "$UserIdentityEntity", classLoader);
List records = new ArrayList<>();
for (int i=0; i < jsonRecords.length(); i++) {
records.add((A) deserializeEventNotificationRecord(
jsonRecords.getJSONObject(i), eventNotificiationRecordClass, s3EntityClass, s3BucketClass,
s3ObjectClass, requestParametersClass, responseElementsClass, userIdentityClass));
}
return records;
}
/**
* serialize an s3 event notification record
* @param eventNotificationRecord Event notification record
* @param Event notification record type
* @return JSONObject
*/
private JSONObject serializeEventNotificationRecord(A eventNotificationRecord, String baseClassName) {
// reflect load all the classes we need
Class s3EntityClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3Entity", classLoader);
Class requestParametersClass = SerializeUtil.loadCustomerClass(baseClassName + "$RequestParametersEntity", classLoader);
Class responseElementsClass = SerializeUtil.loadCustomerClass(baseClassName + "$ResponseElementsEntity", classLoader);
Class userIdentityClass = SerializeUtil.loadCustomerClass(baseClassName + "$UserIdentityEntity", classLoader);
// Workaround not to let maven shade plugin relocating string literals https://issues.apache.org/jira/browse/MSHADE-156
Class dateTimeClass = SerializeUtil.loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.DateTime", classLoader);
// serialize object
JSONObject jsonObject = new JSONObject();
Functions.R0 getAwsRegionMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getAwsRegion", true, String.class);
jsonObject.put("awsRegion", getAwsRegionMethod.call());
Functions.R0 getEventNameMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getEventName", true, String.class);
jsonObject.put("eventName", getEventNameMethod.call());
Functions.R0 getEventSourceMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getEventSource", true, String.class);
jsonObject.put("eventSource", getEventSourceMethod.call());
Functions.R0> getEventTimeMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getEventTime", true, dateTimeClass);
jsonObject.put("eventTime", SerializeUtil.serializeDateTime(getEventTimeMethod.call(), classLoader));
Functions.R0 getEventVersionMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getEventVersion", true, String.class);
jsonObject.put("eventVersion", getEventVersionMethod.call());
Functions.R0> getRequestParametersMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getRequestParameters", true, requestParametersClass);
jsonObject.put("requestParameters", serializeRequestParameters(getRequestParametersMethod.call()));
Functions.R0> getResponseElementsMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getResponseElements", true, responseElementsClass);
jsonObject.put("responseElements", serializeResponseElements(getResponseElementsMethod.call()));
Functions.R0> getS3EntityMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getS3", true, s3EntityClass);
jsonObject.put("s3", serializeS3Entity(getS3EntityMethod.call(), baseClassName));
Functions.R0> getUserIdentityMethod =
ReflectUtil.bindInstanceR0(eventNotificationRecord, "getUserIdentity", true, userIdentityClass);
jsonObject.put("userIdentity", serializeUserIdentity(getUserIdentityMethod.call()));
return jsonObject;
}
/**
* deserialize an event notification record
* NOTE: Yes there are a lot of generics. They are needed for the compiler to correctly associate instance types
* with class types
* @param jsonObject JSONObject to deserialize from
* @param eventNotificationRecordClass event notification record class
* @param s3EntityClass s3 entity class
* @param s3BucketClass s3 bucket class
* @param s3ObjectClass s3 object class
* @param requestParametersClass request parameters class
* @param responseElementsClass response elements class
* @param userIdentityClass user identity class
* @param event notification record type
* @param s3 entity type
* @param s3 bucket type
* @param s3 object type
* @param request parameters type
* @param response elements type
* @param user identity class
* @return event notification record object
*/
private A deserializeEventNotificationRecord(JSONObject jsonObject,
Class eventNotificationRecordClass,
Class s3EntityClass,
Class s3BucketClass,
Class s3ObjectClass,
Class requestParametersClass,
Class responseElementsClass,
Class userIdentityClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String awsRegion = jsonObject.optString("awsRegion");
String eventName = jsonObject.optString("eventName");
String eventSource = jsonObject.optString("eventSource");
String eventTime = jsonObject.optString("eventTime");
String eventVersion = jsonObject.optString("eventVersion");
E requestParameters = deserializeRequestParameters(jsonObject.optJSONObject("requestParameters"), requestParametersClass);
F responseElements = deserializeResponseElements(jsonObject.optJSONObject("responseElements"), responseElementsClass);
B s3 = deserializeS3Entity(jsonObject.optJSONObject("s3"), s3EntityClass, s3BucketClass, s3ObjectClass, userIdentityClass);
G userIdentity = deserializeUserIdentity(jsonObject.optJSONObject("userIdentity"), userIdentityClass);
Functions.R9 constructor =
ReflectUtil.loadConstuctor9(eventNotificationRecordClass, true, String.class, String.class,
String.class, String.class, String.class, requestParametersClass, responseElementsClass,
s3EntityClass, userIdentityClass);
return constructor.call(awsRegion, eventName, eventSource, eventTime, eventVersion, requestParameters,
responseElements, s3, userIdentity);
}
/**
* serialize an s3 entity
* @param s3Entity S3 entity object
* @param S3 entity type
* @return JSONObject with serialized s3 entity
*/
private JSONObject serializeS3Entity(A s3Entity, String baseClassName) {
Class s3BucketClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3BucketEntity", classLoader);
Class s3ObjectClass = SerializeUtil.loadCustomerClass(baseClassName + "$S3ObjectEntity", classLoader);
JSONObject jsonObject = new JSONObject();
Functions.R0 getConfigurationIdMethod =
ReflectUtil.bindInstanceR0(s3Entity, "getConfigurationId", true, String.class);
jsonObject.put("configurationId", getConfigurationIdMethod.call());
Functions.R0> getBucketMethod =
ReflectUtil.bindInstanceR0(s3Entity, "getBucket", true, s3BucketClass);
jsonObject.put("bucket", serializeS3Bucket(getBucketMethod.call(), baseClassName));
Functions.R0> getObjectMethod =
ReflectUtil.bindInstanceR0(s3Entity, "getObject", true, s3ObjectClass);
jsonObject.put("object", serializeS3Object(getObjectMethod.call()));
Functions.R0 getSchemaVersionMethod =
ReflectUtil.bindInstanceR0(s3Entity, "getS3SchemaVersion", true, String.class);
jsonObject.put("s3SchemaVersion", getSchemaVersionMethod.call());
return jsonObject;
}
/**
* deserialize an S3 entity object
* @param jsonObject json object to deserialize from
* @param s3EntityClass s3 entity class
* @param s3BucketClass s3 bucket class
* @param s3ObjectClass s3 object class
* @param userIdentityClass s3 user identity class
* @param s3 entity type
* @param s3 bucket type
* @param s3 object type
* @param s3 user identity type
* @return s3 entity object
*/
private A deserializeS3Entity(JSONObject jsonObject, Class s3EntityClass, Class s3BucketClass,
Class s3ObjectClass, Class userIdentityClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String configurationId = jsonObject.optString("configurationId");
B bucket = deserializeS3Bucket(jsonObject.optJSONObject("bucket"), s3BucketClass, userIdentityClass);
C object = deserializeS3Object(jsonObject.optJSONObject("object"), s3ObjectClass);
String schemaVersion = jsonObject.optString("s3SchemaVersion");
Functions.R4 constructor =
ReflectUtil.loadConstuctor4(s3EntityClass, true, String.class, s3BucketClass, s3ObjectClass, String.class);
return constructor.call(configurationId, bucket, object, schemaVersion);
}
/**
* serialize an s3 bucket object
* @param s3Bucket S3 bucket object
* @param S3 bucket type
* @return JSONObject
*/
private JSONObject serializeS3Bucket(A s3Bucket, String baseClassName) {
Class userIdentityClass = SerializeUtil.loadCustomerClass(baseClassName + "$UserIdentityEntity", classLoader);
JSONObject jsonObject = new JSONObject();
Functions.R0 getNameMethod =
ReflectUtil.bindInstanceR0(s3Bucket, "getName", true, String.class);
jsonObject.put("name", getNameMethod.call());
Functions.R0> getOwnerIdentityMethod =
ReflectUtil.bindInstanceR0(s3Bucket, "getOwnerIdentity", true, userIdentityClass);
jsonObject.put("ownerIdentity", serializeUserIdentity(getOwnerIdentityMethod.call()));
Functions.R0 getArnMethod = ReflectUtil.bindInstanceR0(s3Bucket, "getArn", true, String.class);
jsonObject.put("arn", getArnMethod.call());
return jsonObject;
}
/**
* deserialize an s3 bucket object
* @param jsonObject JSONObject to deserialize from
* @param s3BucketClass S3Bucket class
* @param userIdentityClass user identity class
* @param s3 bucket type
* @param user identity type
* @return s3 bucket object
*/
private A deserializeS3Bucket(JSONObject jsonObject, Class s3BucketClass, Class userIdentityClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String name = jsonObject.optString("name");
B ownerIdentity = deserializeUserIdentity(jsonObject.optJSONObject("ownerIdentity"), userIdentityClass);
String arn = jsonObject.optString("arn");
Functions.R3 constructor =
ReflectUtil.loadConstuctor3(s3BucketClass, true, String.class, userIdentityClass, String.class);
return constructor.call(name, ownerIdentity, arn);
}
/**
* serialize an s3 object
* @param s3Object s3Object object
* @param s3 object type
* @return s3Object object
*/
private JSONObject serializeS3Object(A s3Object) {
JSONObject jsonObject = new JSONObject();
Functions.R0 getKeyMethod =
ReflectUtil.bindInstanceR0(s3Object, "getKey", true, String.class);
jsonObject.put("key", getKeyMethod.call());
Functions.R0 getSizeMethod =
ReflectUtil.bindInstanceR0(s3Object, "getSizeAsLong", true, Long.class);
jsonObject.put("size", getSizeMethod.call().longValue());
Functions.R0 getETagMethod =
ReflectUtil.bindInstanceR0(s3Object, "geteTag", true, String.class);
jsonObject.put("eTag", getETagMethod.call());
Functions.R0 getVersionIdMethod =
ReflectUtil.bindInstanceR0(s3Object, "getVersionId", true, String.class);
jsonObject.put("versionId", getVersionIdMethod.call());
// legacy s3 event models do not have these methods
try {
Functions.R0 getUrlEncodedKeyMethod =
ReflectUtil.bindInstanceR0(s3Object, "getUrlDecodedKey", true, String.class);
jsonObject.put("urlDecodedKey", getUrlEncodedKeyMethod.call());
Functions.R0 getSequencerMethod =
ReflectUtil.bindInstanceR0(s3Object, "getSequencer", true, String.class);
jsonObject.put("sequencer", getSequencerMethod.call());
} catch (Exception ignored) {}
return jsonObject;
}
/**
* deserialize an s3Object
* @param jsonObject json object to deserialize from
* @param s3ObjectClass class of s3Object
* @param s3Object type
* @return s3Object object
*/
private A deserializeS3Object(JSONObject jsonObject, Class s3ObjectClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String key = jsonObject.optString("key");
Long size = jsonObject.optLong("size");
String eTag = jsonObject.optString("eTag");
String versionId = jsonObject.optString("versionId");
String sequencer = jsonObject.optString("sequencer");
// legacy s3 event uses constructor in catch statement
try {
Functions.R5 constructor =
ReflectUtil.loadConstuctor5(s3ObjectClass, true, String.class, Long.class, String.class, String.class, String.class);
return constructor.call(key, size, eTag, versionId, sequencer);
} catch (Exception e) {
Functions.R4 constructor =
ReflectUtil.loadConstuctor4(s3ObjectClass, true, String.class, Long.class, String.class, String.class);
return constructor.call(key, size, eTag, versionId);
}
}
/**
* serialize an s3 user identity
* @param userIdentity user identity object
* @param user identity type
* @return JSONObject with serialized user identity
*/
private JSONObject serializeUserIdentity(A userIdentity) {
JSONObject jsonObject = new JSONObject();
Functions.R0 getPrincipalIdMethod =
ReflectUtil.bindInstanceR0(userIdentity, "getPrincipalId", true, String.class);
jsonObject.put("principalId", getPrincipalIdMethod.call());
return jsonObject;
}
/**
* deserialize a user identity
* @param jsonObject JSONObject to deserialize from
* @param userIdentityClass User Identity Class
* @param User Identity Type
* @return User Identity Object
*/
private A deserializeUserIdentity(JSONObject jsonObject, Class userIdentityClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String principalId = jsonObject.optString("principalId");
Functions.R1 constructor =
ReflectUtil.loadConstructor1(userIdentityClass, true, String.class);
return constructor.call(principalId);
}
/**
* serialize request parameters
* @param requestParameters request parameters object
* @param request parameters type
* @return JSONObject with serialized request parameters
*/
private JSONObject serializeRequestParameters(A requestParameters) {
JSONObject jsonObject = new JSONObject();
Functions.R0 getSourceIpMethod =
ReflectUtil.bindInstanceR0(requestParameters, "getSourceIPAddress", true, String.class);
jsonObject.put("sourceIPAddress", getSourceIpMethod.call());
return jsonObject;
}
/**
* deserialize request parameters
* @param jsonObject JSONObject to deserialize from
* @param requestParametersClass RequestParameters class
* @param RequestParameters type
* @return RequestParameters object
*/
private A deserializeRequestParameters(JSONObject jsonObject, Class requestParametersClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String sourceIpAddress = jsonObject.optString("sourceIPAddress");
Functions.R1 constructor = ReflectUtil.loadConstructor1(requestParametersClass, true, String.class);
return constructor.call(sourceIpAddress);
}
/**
* serialize response elements object
* @param responseElements response elements object
* @param response elements type
* @return JSONObject with serialized responseElements
*/
private JSONObject serializeResponseElements(A responseElements) {
JSONObject jsonObject = new JSONObject();
Functions.R0 getXAmzId2Method =
ReflectUtil.bindInstanceR0(responseElements, "getxAmzId2", true, String.class);
jsonObject.put("x-amz-id-2", getXAmzId2Method.call());
Functions.R0 getXAmzRequestId =
ReflectUtil.bindInstanceR0(responseElements, "getxAmzRequestId", true, String.class);
jsonObject.put("x-amz-request-id", getXAmzRequestId.call());
return jsonObject;
}
/**
* deserialize response elements
* @param jsonObject JSONObject deserializing from
* @param responseElementsClass response elements class
* @param response elements type
* @return Response elements object
*/
private A deserializeResponseElements(JSONObject jsonObject, Class responseElementsClass) {
if (jsonObject == null) {
jsonObject = new JSONObject();
}
String xAmzId2 = jsonObject.optString("x-amz-id-2");
String xAmzRequestId = jsonObject.optString("x-amz-request-id");
Functions.R2 constructor =
ReflectUtil.loadConstructor2(responseElementsClass, true, String.class, String.class);
return constructor.call(xAmzId2, xAmzRequestId);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy