package com.example.services.impl;
import com.example.utils.SQSUtils;
import com.example.config.Config;
import com.example.services.Event;
import com.example.services.FileManager;
import com.example.services.FileOperation;
import com.example.services.Receiver;
import com.amazonaws.auth.policy.*;
import com.amazonaws.auth.policy.conditions.ArnCondition;
import com.amazonaws.auth.policy.conditions.ConditionFactory;
import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
import com.amazonaws.services.sqs.model.*;
import java.util.*;
import java.util.logging.Logger;
public class S3Receiver implements Receiver {
private final AmazonSQSAsyncClient client;
private final FileManager fileManager;
private final Config config;
private String queueUrl;
private final static Logger logger = Logger.getLogger(S3Receiver.class.getName());
private final static int RECEIVE_PERIOD = 5000;
private String queueArn;
public S3Receiver(Config config, FileManager fileManager) {
this.config = config;
this.fileManager = fileManager;
client = new AmazonSQSAsyncClient(config.getAWSCredentials());
}
public String createQueueForTopic(String topicArn) {
CreateQueueRequest request = new CreateQueueRequest();
request.setQueueName(config.getQueueName());
CreateQueueResult result = client.createQueue(request);
queueUrl = result.getQueueUrl();
logger.finest("Created queue " + config.getQueueName());
// Set message retention period
SetQueueAttributesRequest setAttributesRequest = new SetQueueAttributesRequest();
setAttributesRequest.setQueueUrl(queueUrl);
setAttributesRequest.addAttributesEntry("MessageRetentionPeriod", "1209600"); // 14 days, max is better
client.setQueueAttributes(setAttributesRequest);
// Get queueArn
List<String> attributeNames = new ArrayList<String>();
attributeNames.add("QueueArn");
GetQueueAttributesRequest attributesRequest = new GetQueueAttributesRequest();
attributesRequest.setAttributeNames(attributeNames);
attributesRequest.setQueueUrl(queueUrl);
GetQueueAttributesResult attributesResult = client.getQueueAttributes(attributesRequest);
queueArn = attributesResult.getAttributes().get("QueueArn");
logger.finest("Created QueueArn " + queueArn);
// Add permission
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(QueueAttributeName.Policy.toString(), makePolicy(topicArn).toJson());
client.setQueueAttributes(new SetQueueAttributesRequest(queueUrl, attributes));
logger.finest("Added permissions for queue");
return queueArn;
}
public void startListening() {
initializeTimer();
}
public void initializeTimer() {
ReceiveMessage receiveMessage = new ReceiveMessage();
Timer timer = new Timer();
timer.schedule(receiveMessage, 0, RECEIVE_PERIOD);
}
/**
* Generate a policy that will allow messages published to an SNS topic
* to be sent to all queues subscribed to that topic
* @param topicArn the topic to create policy for
* @return The policy
*/
private Policy makePolicy(String topicArn) {
//SQSActions.SendMessage does not work!!
Action sendMessageAction = new Action() {
@Override
public String getActionName() {
return "SQS:SendMessage";
}
};
return new Policy().withId("sns2sqs").withStatements(
new Statement(Statement.Effect.Allow)
.withPrincipals(Principal.AllUsers)
.withActions(sendMessageAction)
.withResources(new Resource(queueArn))
.withConditions(new ArnCondition(ArnCondition.ArnComparisonType.ArnEquals, ConditionFactory.SOURCE_ARN_CONDITION_KEY, topicArn)));
}
@Override
public void receive(Event event) {
if (event.getOriginator().equals(config.getQueueName())) {
logger.finest("Message from me, ignoring..");
return;
}
logger.info("Received in " + config.getQueueName() + " - " + event.getOperation() + " " + event.getFile().getAbsoluteFile());
if (event.isDirectory()) {
fileManager.createLocalDirectory(event.getFile());
} else {
if (event.getOperation().equals(FileOperation.DELETE)) {
fileManager.deleteLocalFile(event.getFile());
} else {
logger.finest("Downloading " + event.getFile().toString());
fileManager.download(event.getFile());
}
}
}
public void deleteMessage(String handle) {
DeleteMessageRequest deleteMessageRequest = new DeleteMessageRequest();
deleteMessageRequest.setQueueUrl(queueUrl);
deleteMessageRequest.setReceiptHandle(handle);
client.deleteMessageAsync(deleteMessageRequest);
}
private class ReceiveMessage extends TimerTask {
@Override
public void run() {
try {
ReceiveMessageRequest request = new ReceiveMessageRequest();
request.withAttributeNames("SenderId");
request.setQueueUrl(queueUrl);
client.receiveMessageAsync(request, new AsyncHandler<ReceiveMessageRequest, ReceiveMessageResult>() {
@Override
public void onError(Exception e) {
logger.severe(e.getMessage());
}
@Override
public void onSuccess(ReceiveMessageRequest receiveMessageRequest, ReceiveMessageResult receiveMessageResult) {
if (receiveMessageResult.getMessages().size() > 0) {
for (Message message: receiveMessageResult.getMessages()) {
Event event = SQSUtils.convertMessageToEvent(config.getBaseDir(), message);
// Receive the message and delete it
receive(event);
deleteMessage(message.getReceiptHandle());
}
}
}
});
} catch (Exception e) {
logger.severe("Lost connection while receiving message " + e.getMessage());
}
}
}
}