Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
</dependencies>
</dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/software.amazon.cloudformation/aws-cloudformation-resource-schema -->
<dependency>
<groupId>software.amazon.cloudformation</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
throw new TerminalException("No request object received");
}

String input = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
String input = this.serializer.decompress(IOUtils.toString(inputStream, StandardCharsets.UTF_8));

JSONObject rawInput = new JSONObject(new JSONTokener(input));

// deserialize incoming payload to modelled request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/
package software.amazon.cloudformation.resource;

import com.amazonaws.util.IOUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
Expand All @@ -22,14 +23,26 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.codec.binary.Base64;
import software.amazon.cloudformation.proxy.aws.AWSServiceSerdeModule;

public class Serializer {

public static final String COMPRESSED = "__COMPRESSED__";
private static final String COMPRESSION_METHOD = "__COMPRESSION_METHOD__";
private static final String COMPRESSION_GZIP_BASE64 = "gzip_base64";
private static final ObjectMapper OBJECT_MAPPER;

private static final ObjectMapper STRICT_OBJECT_MAPPER;
private static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() {
};

/**
* Configures the specified ObjectMapper with the (de)serialization behaviours
Expand Down Expand Up @@ -76,10 +89,36 @@ public <T> String serialize(final T modelObject) throws JsonProcessingException
return OBJECT_MAPPER.writeValueAsString(modelObject);
}

public <T> String compress(final String modelInput) throws IOException {
final Map<String, String> map = new HashMap<>();
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
try (GZIPOutputStream gzip = new GZIPOutputStream(byteArrayOutputStream)) {
gzip.write(modelInput.getBytes(StandardCharsets.UTF_8));
}
map.put(COMPRESSED, Base64.encodeBase64String(byteArrayOutputStream.toByteArray()));
map.put(COMPRESSION_METHOD, COMPRESSION_GZIP_BASE64);
}
return OBJECT_MAPPER.writeValueAsString(map);
}

public <T> T deserialize(final String s, final TypeReference<T> reference) throws IOException {
return OBJECT_MAPPER.readValue(s, reference);
}

public String decompress(final String s) throws IOException {
final Map<String, Object> map = deserialize(s, MAP_TYPE_REFERENCE);

if (!map.containsKey(COMPRESSED)) {
return s;
}

final byte[] bytes = Base64.decodeBase64((String) map.get(COMPRESSED));
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);) {
return new String(IOUtils.toByteArray(gzipInputStream), StandardCharsets.UTF_8);
}
}

public <T> T deserializeStrict(final String s, final TypeReference<T> reference) throws IOException {
return STRICT_OBJECT_MAPPER.readValue(s, reference);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/
package software.amazon.cloudformation.scheduler;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
import lombok.Data;
Expand Down Expand Up @@ -101,14 +101,15 @@ public <ResourceT, CallbackT> void rescheduleAfterMinutes(final String functionA
String jsonRequest;
try {
// expect return type to be non-null
jsonRequest = serializer.serialize(handlerRequest);
} catch (JsonProcessingException e) {
jsonRequest = serializer.compress(serializer.serialize(handlerRequest));
} catch (IOException e) {
throw new TerminalException("Unable to serialize the request for callback", e);
}
this.log(String.format("Scheduling re-invoke at %s (%s)%n", cronRule, rescheduleId));

PutRuleRequest putRuleRequest = PutRuleRequest.builder().name(ruleName).scheduleExpression(cronRule)
.state(RuleState.ENABLED).build();

this.client.putRule(putRuleRequest);

Target target = Target.builder().arn(functionArn).id(targetId).input(jsonRequest).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
Expand Down Expand Up @@ -148,7 +148,7 @@ public void test_cleanupCloudWatchEventsWithErrorDeletingRule() {
}

@Test
public void test_rescheduleAfterMinutes_1MinuteFloor() {
public void test_rescheduleAfterMinutes_1MinuteFloor() throws IOException {
final CloudWatchEventsProvider provider = mock(CloudWatchEventsProvider.class);
final CloudWatchEventsClient client = getCloudWatchEvents();
when(provider.get()).thenReturn(client);
Expand All @@ -166,8 +166,9 @@ public void test_rescheduleAfterMinutes_1MinuteFloor() {
verify(requestContext, times(1)).setCloudWatchEventsRuleName(startsWith("reinvoke-handler-"));
verify(requestContext, times(1)).setCloudWatchEventsTargetId(startsWith("reinvoke-target-"));

final List<TargetMatcher> targetMatchers = Arrays
.asList(new TargetMatcher(FUNCTION_ARN, "reinvoke-target-", new JSONObject(request).toString()));
final List<TargetMatcher> targetMatchers = Collections.singletonList(
new TargetMatcher(FUNCTION_ARN, "reinvoke-target-", serializer.compress(serializer.serialize(request))));

verify(client, times(1))
.putTargets(argThat(new PutTargetsRequestMatcher("reinvoke-handler-", new TargetsListMatcher(targetMatchers))));
verify(client, times(1))
Expand Down