Java tutorial
/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.aws.inbound; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.cloud.aws.messaging.endpoint.NotificationStatus; import org.springframework.cloud.aws.messaging.endpoint.NotificationStatusHandlerMethodArgumentResolver; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.integration.aws.support.AwsHeaders; import org.springframework.integration.expression.ValueExpression; import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway; import org.springframework.integration.http.inbound.RequestMapping; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.messaging.Message; import org.springframework.util.Assert; import org.springframework.web.multipart.MultipartResolver; import com.amazonaws.services.sns.AmazonSNS; import com.fasterxml.jackson.databind.JsonNode; /** * The {@link HttpRequestHandlingMessagingGateway} extension for the Amazon WS SNS HTTP(S) endpoints. * Accepts all {@code x-amz-sns-message-type}s, converts the received Topic JSON message to the * {@link Map} using {@link MappingJackson2HttpMessageConverter} and send it to the provided * {@link #requestChannel} as {@link Message} {@code payload}. * <p> * The mapped url must be configured inside the Amazon Web Service platform as a subscription. * Before receiving any notification itself this HTTP endpoint must confirm the subscription. * <p> * The {@link #handleNotificationStatus} flag (defaults to {@code false}) indicates that * this endpoint should send the {@code SubscriptionConfirmation/UnsubscribeConfirmation} * messages to the the provided {@link #requestChannel}. If that, the {@link AwsHeaders#NOTIFICATION_STATUS} * header is populated with the {@link NotificationStatus} value. In that case it is a responsibility of * the application to {@link NotificationStatus#confirmSubscription()} or not. * <p> * By default this endpoint just does {@link NotificationStatus#confirmSubscription()} * for the {@code SubscriptionConfirmation} message type. * And does nothing for the {@code UnsubscribeConfirmation}. * <p> * For the convenience on the underlying message flow routing a {@link AwsHeaders#SNS_MESSAGE_TYPE} * header is present. * * @author Artem Bilan * @author Kamil Przerwa */ public class SnsInboundChannelAdapter extends HttpRequestHandlingMessagingGateway { private final NotificationStatusResolver notificationStatusResolver; private final MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); private volatile boolean handleNotificationStatus; private volatile Expression payloadExpression; private EvaluationContext evaluationContext; public SnsInboundChannelAdapter(AmazonSNS amazonSns, String... path) { super(false); Assert.notNull(amazonSns, "'amazonSns' must not be null."); Assert.notNull(path, "'path' must not be null."); Assert.noNullElements(path, "'path' must not contain null elements."); this.notificationStatusResolver = new NotificationStatusResolver(amazonSns); RequestMapping requestMapping = new RequestMapping(); requestMapping.setMethods(HttpMethod.POST); requestMapping.setHeaders("x-amz-sns-message-type"); requestMapping.setPathPatterns(path); this.jackson2HttpMessageConverter .setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8, MediaType.TEXT_PLAIN)); super.setRequestMapping(requestMapping); super.setStatusCodeExpression(new ValueExpression<>(HttpStatus.NO_CONTENT)); super.setMessageConverters( Collections.<HttpMessageConverter<?>>singletonList(this.jackson2HttpMessageConverter)); super.setRequestPayloadType(HashMap.class); } public void setHandleNotificationStatus(boolean handleNotificationStatus) { this.handleNotificationStatus = handleNotificationStatus; } @Override protected void onInit() throws Exception { super.onInit(); if (this.payloadExpression != null) { this.evaluationContext = createEvaluationContext(); } } @Override public String getComponentType() { return "aws:sns-inbound-channel-adapter"; } @Override @SuppressWarnings("unchecked") protected void send(Object object) { Message<?> message = (Message<?>) object; Map<String, String> payload = (HashMap<String, String>) message.getPayload(); AbstractIntegrationMessageBuilder<?> messageToSendBuilder; if (this.payloadExpression != null) { messageToSendBuilder = getMessageBuilderFactory() .withPayload(this.payloadExpression.getValue(this.evaluationContext, message)) .copyHeaders(message.getHeaders()); } else { messageToSendBuilder = getMessageBuilderFactory().fromMessage(message); } String type = payload.get("Type"); if ("SubscriptionConfirmation".equals(type) || "UnsubscribeConfirmation".equals(type)) { JsonNode content = this.jackson2HttpMessageConverter.getObjectMapper().valueToTree(payload); NotificationStatus notificationStatus = this.notificationStatusResolver .resolveNotificationStatus(content); if (this.handleNotificationStatus) { messageToSendBuilder.setHeader(AwsHeaders.NOTIFICATION_STATUS, notificationStatus); } else { if ("SubscriptionConfirmation".equals(type)) { notificationStatus.confirmSubscription(); } return; } } messageToSendBuilder.setHeader(AwsHeaders.SNS_MESSAGE_TYPE, type).setHeader(AwsHeaders.MESSAGE_ID, payload.get("MessageId")); super.send(messageToSendBuilder.build()); } @Override public void setPayloadExpression(Expression payloadExpression) { this.payloadExpression = payloadExpression; } @Override public void setHeaderExpressions(Map<String, Expression> headerExpressions) { throw new UnsupportedOperationException(); } @Override public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) { throw new UnsupportedOperationException(); } @Override public void setMergeWithDefaultConverters(boolean mergeWithDefaultConverters) { throw new UnsupportedOperationException(); } @Override public void setHeaderMapper(HeaderMapper<HttpHeaders> headerMapper) { throw new UnsupportedOperationException(); } @Override public void setRequestMapping(RequestMapping requestMapping) { throw new UnsupportedOperationException(); } @Override public void setRequestPayloadType(Class<?> requestPayloadType) { throw new UnsupportedOperationException(); } @Override public void setExtractReplyPayload(boolean extractReplyPayload) { throw new UnsupportedOperationException(); } @Override public void setMultipartResolver(MultipartResolver multipartResolver) { throw new UnsupportedOperationException(); } @Override public void setStatusCodeExpression(Expression statusCodeExpression) { throw new UnsupportedOperationException(); } private static class NotificationStatusResolver extends NotificationStatusHandlerMethodArgumentResolver { NotificationStatusResolver(AmazonSNS amazonSns) { super(amazonSns); } NotificationStatus resolveNotificationStatus(JsonNode content) { return (NotificationStatus) doResolveArgumentFromNotificationMessage(content, null, null); } } }