Call Amazon notification service on Play framework - java

I want to send a message from Amazon Simple Notification Service (SNS) using Play framework, and I read that I have to use WS for REST APIs. However, I am confused on how to do this. Can anyone help me work this out?

First of all, you need to add SNS SDK to your project by adding the following line into your build.sbt:
libraryDependencies += "com.amazonaws" % "aws-java-sdk-sns" % "1.11.271"
then use your service by injecting it with your controller, your service should be something like:
#Singleton
public final class AmazonSNSService {
// logging always a good thing to do
private final Logger.ALogger logger = Logger.of(this.getClass());
// this is what you have to use
private final com.amazonaws.services.sns.AmazonSNS snsClient;
private final String AMAZON_ARN;
#Inject
public AmazonSNSService(Configuration configuration) { // or Config if play 2.6 and later
// I set the aws config in application.conf and I read them here
final String AWS_ACCESS_KEY = configuration.getString("aws_access_key");
final String AWS_SECRET_KEY = configuration.getString("aws_secrect_key");
snsClient = AmazonSNSClient.builder()
.withRegion(Regions.US_EAST_1)
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY)))
.build();
AMAZON_ARN = configuration.getString("amazon_arn_config_key");
}
}
then you can use snsClient with methods you do want, for creating a topic:
public void createTopic(String topicName) {
String topicARN = AMAZON_ARN + ":" + topicName;
if (doesTopicExists(topicARN)) {
logger.debug("TopicArn - already Exists" + topicARN);
} else {
//create a new SNS topic
CreateTopicRequest createTopicRequest = new CreateTopicRequest(topicName);
CreateTopicResult createTopicResult = snsClient.createTopic(createTopicRequest);
//get request id for CreateTopicRequest from SNS metadata
ResponseMetadata topicResponse = snsClient.getCachedResponseMetadata(createTopicRequest);
logger.debug("CreateTopicArn - " + createTopicResult.getTopicArn());
}
}
And another example to subscribe to the topic:
public String subscribeToTopic(String topicName, String deviceEndpointARN) {
String topicARN = AMAZON_ARN + ":" + topicName;
//if topic does not exists create topic then subscribe
if (!doesTopicExists(topicARN)) {
createTopic(topicName);
}
return subscribeToTopic(topicARN, deviceEndpointARN);
}
Want push to the topic?:
public void publishToTopic(String message, String topicName) {
String topicARN = AMAZON_ARN + ":" + topicName;
//if topic does not exists create topic then publish to topic
// or throw an exception, maybe it does not make sense to push to the topic that have no subscribers at all.
if (!doesTopicExists(topicARN)) {
createTopic(topicName);
}
//publish to an SNS topic
PublishRequest publishRequest = new PublishRequest(topicARN, message);
publishRequest.setMessageStructure("json");
PublishResult publishResult = snsClient.publish(publishRequest);
//print MessageId of message published to SNS topic
logger.debug("Push Notification sent to TOPIC [" + topicARN + "] MessageId - [" + publishResult.getMessageId() + "] Message Body: " + message);
}
And this is how I check if the topic exists or not:
private boolean doesTopicExists(String topicARN) {
String nextToken = null;
do {
ListTopicsRequest request = new ListTopicsRequest();
request.setNextToken(nextToken);
ListTopicsResult listTopicsResult = snsClient.listTopics();
List<Topic> topics = listTopicsResult.getTopics();
for (Topic topic : topics) {
if (topic.getTopicArn().equals(topicARN)) {
return true;
}
}
nextToken = request.getNextToken();
} while (nextToken != null);
return false;
}
For more, check java doc of them and search for examples,
Have fun!

Related

How to know which kafka is being consumed from list?

I have a function for consuming Kafka. I consume multiple topics in the same function. But I don't know which topic is consumed
#KafkaListener(topics = {"topic1","topic2"})
public void kafkaConsume(String message) {
//Print the kafka topic that is being consumed
return;
}
How do I print the kafka topic?
You can add #Headers parameter to get all the extra information you want within the message.
For example:
#KafkaListener(topics = "topicName")
public void listenWithHeaders(
#Payload String message,
#Header(KafkaHeaders.TOPIC) String topic) {
System.out.println("Received Message: " + message" + "from: " + topic);
}
You can get all this information in KafkaHeaders https://docs.spring.io/spring-kafka/api/org/springframework/kafka/support/KafkaHeaders.html
Or alternatively, you can consume a ConsumerRecord<K, V> which has most of the information as well
#KafkaListener(topics = "topicName")
public void listenConsumerRecord(ConsumerRecord<String, String> record) {
System.out.println("Received Message: " + record.value() + "from: " + record.topic());
}

Send push notification using Java Springboot server and Expo React native client

My serser uses Java with SpringBoot and my client is an expo react native app which uses typescript.
I am really blocked by this feature: I want to sens push notifications. I tried a lot of methods, but I didn't succeed.
On the client side I am using the method described in expo documentation: https://docs.expo.dev/push-notifications/overview/.
When I use their tool to send test notifications(https://expo.dev/notifications), I receive them on my device.
I didn't manage to send notifications from my client app. I tried all I found on the Inthernet. When I used FirebaseMessagingService and I used the server key from the firebase project as token, I received the SENDER_ID_MISMATCH error.
#Service
public class FirebaseMessagingService {
private final FirebaseMessaging firebaseMessaging;
public FirebaseMessagingService(FirebaseMessaging firebaseMessaging) {
this.firebaseMessaging = firebaseMessaging;
}
public String sendNotification(Note note, String topic) throws FirebaseMessagingException {
Notification notification = Notification
.builder()
.setTitle(note.getSubject())
.setBody(note.getContent())
.setImage(note.getImage())
.build();
Message message = Message
.builder()
.setNotification(notification)
.putAllData(note.getData())
.setToken(topic)
.build();
return firebaseMessaging.send(message);
}
}
I also found the expo-server-sdk-java but I couldn't manage to integrate it.
Any heeeeelp, pleaseeee?
not sure if it's the best practice but it works fine for me.
My pom
<dependency>
<groupId>io.github.jav</groupId>
<artifactId>expo-server-sdk</artifactId>
<version>1.1.0</version>
</dependency>
Then in the java class
private static void sendPushNotification(String token, String titulo, String mensaje, Map<String, Object> data) throws PushClientException {
if (!PushClient.isExponentPushToken(token)) throw new Error("Token:" + token + " is not a valid token.");
ExpoPushMessage expoPushMessage = new ExpoPushMessage();
expoPushMessage.getTo().add(token);
expoPushMessage.setTitle(titulo);
expoPushMessage.setBody(mensaje);
expoPushMessage.setData(data);
List<ExpoPushMessage> expoPushMessages = new ArrayList<>();
expoPushMessages.add(expoPushMessage);
PushClient client = new PushClient();
List<List<ExpoPushMessage>> chunks = client.chunkPushNotifications(expoPushMessages);
List<CompletableFuture<List<ExpoPushTicket>>> messageRepliesFutures = new ArrayList<>();
for (List<ExpoPushMessage> chunk : chunks) {
messageRepliesFutures.add(client.sendPushNotificationsAsync(chunk));
}
// Wait for each completable future to finish
List<ExpoPushTicket> allTickets = new ArrayList<>();
for (CompletableFuture<List<ExpoPushTicket>> messageReplyFuture : messageRepliesFutures) {
try {
allTickets.addAll(messageReplyFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
List<ExpoPushMessageTicketPair<ExpoPushMessage>> zippedMessagesTickets = client.zipMessagesTickets(expoPushMessages, allTickets);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> okTicketMessages = client.filterAllSuccessfulMessages(zippedMessagesTickets);
String okTicketMessagesString = okTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Id:" + p.ticket.getId()).collect(Collectors.joining(","));
LOGGER.info("Recieved OK ticket for " + okTicketMessages.size() + " messages: " + okTicketMessagesString);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> errorTicketMessages = client.filterAllMessagesWithError(zippedMessagesTickets);
String errorTicketMessagesString = errorTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Error: " + p.ticket.getDetails().getError()).collect(Collectors.joining(","));
LOGGER.error("Recieved ERROR ticket for " + errorTicketMessages.size() + " messages: " + errorTicketMessagesString);
/**
// Countdown 30s
int wait = 30;
for (int i = wait; i >= 0; i--) {
System.out.print("Waiting for " + wait + " seconds. " + i + "s\r");
Thread.sleep(1000);
}
System.out.println("Fetching reciepts...");
List<String> ticketIds = (client.getTicketIdsFromPairs(okTicketMessages));
CompletableFuture<List<ExpoPushReceipt>> receiptFutures = client.getPushNotificationReceiptsAsync(ticketIds);
List<ExpoPushReceipt> receipts = new ArrayList<>();
try {
receipts = receiptFutures.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Recieved " + receipts.size() + " receipts:");
for (ExpoPushReceipt reciept : receipts) {
System.out.println("Receipt for id: " + reciept.getId() + " had status: " + reciept.getStatus());
}
*/
}
In the App.js from react native project with EXPO 44 i take expo token in this way
async function registerForPushNotificationsAsync() {
let token;
if (isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
return token;
}
effect from App.js
useEffect(() => {
initFirebaseApp();
registerForPushNotificationsAsync().then(async (token) => {
//store in some place token
});
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener(handleNotification);
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener(
handleNotificationResponse
);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
notification handlers in App.js
const handleNotification = (response) => {
// console.log(response);
};
const handleNotificationResponse = (response) => {
// console.log(response)
};
I hope this helps you
Docs
Expo SDK documentation
Expo docs reference

Handling dead-letter queue message-broker independent way

I have a project that currently uses Spring Cloud Streams and RabbitMQ underneath. I've implemented a logic based on the documentation. See below:
#Component
public class ReRouteDlq {
private static final String ORIGINAL_QUEUE = "so8400in.so8400";
private static final String DLQ = ORIGINAL_QUEUE + ".dlq";
private static final String PARKING_LOT = ORIGINAL_QUEUE + ".parkingLot";
private static final String X_RETRIES_HEADER = "x-retries";
private static final String X_ORIGINAL_EXCHANGE_HEADER = RepublishMessageRecoverer.X_ORIGINAL_EXCHANGE;
private static final String X_ORIGINAL_ROUTING_KEY_HEADER = RepublishMessageRecoverer.X_ORIGINAL_ROUTING_KEY;
#Autowired
private RabbitTemplate rabbitTemplate;
#RabbitListener(queues = DLQ)
public void rePublish(Message failedMessage) {
Map<String, Object> headers = failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
if (retriesHeader == null) {
retriesHeader = Integer.valueOf(0);
}
if (retriesHeader < 3) {
headers.put(X_RETRIES_HEADER, retriesHeader + 1);
String exchange = (String) headers.get(X_ORIGINAL_EXCHANGE_HEADER);
String originalRoutingKey = (String) headers.get(X_ORIGINAL_ROUTING_KEY_HEADER);
this.rabbitTemplate.send(exchange, originalRoutingKey, failedMessage);
}
else {
this.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
#Bean
public Queue parkingLot() {
return new Queue(PARKING_LOT);
}
}
It does what it is expected, however, it is binded to RabbitMQ, and my company is planning to stop using this message broker in one year or two (don't know why, must be some crazy business). So, I want to implement the same thing, but detach it from any message broker.
I tried changing the rePublish method this way, but it does not work:
#StreamListener(Sync.DLQ)
public void rePublish(Message failedMessage) {
Map<String, Object> headers = failedMessage.getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
if (retriesHeader == null) {
retriesHeader = Integer.valueOf(0);
}
if (retriesHeader < 3) {
headers.put(X_RETRIES_HEADER, retriesHeader + 1);
String exchange = (String) headers.get(X_ORIGINAL_EXCHANGE_HEADER);
String originalRoutingKey = (String) headers.get(X_ORIGINAL_ROUTING_KEY_HEADER);
this.rabbitTemplate.send(exchange, originalRoutingKey, failedMessage);
}
else {
this.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
It fails because the Message class has immutable Headers - throws exception on the put attempt saying you can't change its values (uses org.springframework.messaging.Message class).
Is there a way to implement this dead-letter queue handler in a message broker independent way?
Use
MessageBuilder.fromMessage(message)
.setHeader("foo", "bar")
...
.build();
Note that the message in #StreamListener is a spring-messaging Message<?>, not a spring-amqp Message and can't be sent using the template that way; you need an output binding to send the message to.

Application using Java SDK Client for Hyperledger Fabric V1.0 is waiting indefinitely when invoking chaincode

I have my Hyperledger Fabric V1.0 network up and running by following the steps Building Your First Network.
And now I am able to create channel, install/instantiate/invoke/query chaincode etc.
Now I am trying to create some assets and query the same using Java SDK Client.
I have created the following methods to invoke and query the chaincode from my java application.
void createChannel() throws InvalidArgumentException, TransactionException, IOException, ProposalException{
Properties ordererProperties = getOrdererProperties("orderer.example.com");
ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});
Orderer orderer = client.newOrderer("orderer.example.com", "grpcs://192.168.99.100:7050",ordererProperties);
Properties peerProperties = getPeerProperties("peer0.org1.example.com"); //test properties for peer.. if any.
if (peerProperties == null) {
peerProperties = new Properties();
}
peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);
Peer peer = client.newPeer("peer0.org1.example.com", "grpcs://192.168.99.100:7051",peerProperties);
channel = client.newChannel("testchannel");
channel.addOrderer(orderer);
channel.addPeer(peer);
channel.initialize();
}
void creteTransactionalProposal(){
proposalRequest = client.newTransactionProposalRequest();
final ChaincodeID chaincodeID = ChaincodeID.newBuilder()
.setName("asset_test")
.setVersion("1.0")
.setPath("github.com/myuser/myfabricrepo/asset_chain")
.build();
proposalRequest.setChaincodeID(chaincodeID);
proposalRequest.setFcn("set");
proposalRequest.setProposalWaitTime(TimeUnit.SECONDS.toMillis(1));
proposalRequest.setArgs(new String[]{"a1", "a1_val"});
}
void sendProposal() throws ProposalException, InvalidArgumentException, InterruptedException, ExecutionException{
final Collection<ProposalResponse> responses = channel.sendTransactionProposal(proposalRequest);
CompletableFuture<BlockEvent.TransactionEvent> txFuture = channel.sendTransaction(responses, client.getUserContext());
BlockEvent.TransactionEvent event = txFuture.get();//waiting indefinitely
System.out.println(event.toString());
//query();
}
void query() throws InvalidArgumentException, ProposalException{
final ChaincodeID chaincodeID = ChaincodeID.newBuilder()
.setName(""asset_test"")
.setVersion("1.0")
.setPath("github.com/myuser/myfabricrepo/asset_chain")
.build();
QueryByChaincodeRequest queryByChaincodeRequest = client.newQueryProposalRequest();
queryByChaincodeRequest.setArgs(new String[] {"a1"});
queryByChaincodeRequest.setFcn("get");
queryByChaincodeRequest.setChaincodeID(chaincodeID);
Map<String, byte[]> tm2 = new HashMap<>();
tm2.put("HyperLedgerFabric", "QueryByChaincodeRequest:JavaSDK".getBytes(UTF_8));
tm2.put("method", "QueryByChaincodeRequest".getBytes(UTF_8));
queryByChaincodeRequest.setTransientMap(tm2);
Collection<ProposalResponse> queryProposals = channel.queryByChaincode(queryByChaincodeRequest, channel.getPeers());
for (ProposalResponse proposalResponse : queryProposals) {
if (!proposalResponse.isVerified()
|| proposalResponse.getStatus() != ProposalResponse.Status.SUCCESS) {
System.out.println("Failed query proposal from peer " + proposalResponse.getPeer().getName() + " status: "
+ proposalResponse.getStatus() + ". Messages: " + proposalResponse.getMessage()
+ ". Was verified : " + proposalResponse.isVerified());
} else {
String payload = proposalResponse.getProposalResponse().getResponse().getPayload()
.toStringUtf8();
System.out.printf("\nQuery payload of b from peer %s returned %s", proposalResponse.getPeer().getName(),
payload);
//assertEquals(payload, expect);
}
}
}
I am able to create Asset by calling
t.creteTransactionalProposal();
t.sendProposal();
But the line BlockEvent.TransactionEvent event = txFuture.get(); makes the application in an indefinite waiting state even after the completion of the transaction commit to ledger. Why it is behaving like this?
Once I to force exit and run the query() method it is listing the asset.
I ran into a similar issue to this, and many of the answers around the net are missing a key part of the code - assigning the EventHub to the channel. I added this before initializing the channel (which in this case would be in the createChannel mehtod), and my transactions were then processed successfully:
channel.addEventHub(client.newEventHub("eventhub0", "grpc://localhost:7053"));

Caused by: io.grpc.StatusRuntimeException: NOT_FOUND: Resource not found

I try to write a test of pubsub:
#Test
public void sendTopic() throws Exception {
CustomSubscriber customSubscriber = new CustomSubscriber();
customSubscriber.startAndWait();
CustomPublisher customPublisher = new CustomPublisher();
customPublisher.publish("123");
}
and:
public CustomSubscriber() {
this.subscriptionName = SubscriptionName.create(SdkServiceConfig.s.GCP_PROJECT_ID, SdkServiceConfig.s.TOPIC_ID );
this.receiveMsgAction = (message, consumer) -> {
// handle incoming message, then ack/nack the received message
System.out.println("Id : " + message.getMessageId());
System.out.println("Data : " + message.getData().toStringUtf8());
consumer.ack();
};
this.afterStopAction = new ApiFutureEmpty();
}
// [TARGET startAsync()]
public void startAndWait() throws Exception {
Subscriber subscriber = createSubscriberWithCustomCredentials();
subscriber.startAsync();
// Wait for a stop signal.
afterStopAction.get();
subscriber.stopAsync().awaitTerminated();
}
and:
public ApiFuture<String> publish(String message) throws Exception {
ByteString data = ByteString.copyFromUtf8(message);
PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build();
ApiFuture<String> messageIdFuture = publisher.publish(pubsubMessage);
ApiFutures.addCallback(messageIdFuture, new ApiFutureCallback<String>() {
public void onSuccess(String messageId) {
System.out.println("published with message id: " + messageId);
}
public void onFailure(Throwable t) {
System.out.println("failed to publish: " + t);
}
});
return messageIdFuture;
}
/**
* Example of creating a {#code Publisher}.
*/
// [TARGET newBuilder(TopicName)]
// [VARIABLE "my_project"]
// [VARIABLE "my_topic"]
public void createPublisher(String projectId, String topicId) throws Exception {
TopicName topic = TopicName.create(projectId, topicId);
try {
publisher = createPublisherWithCustomCredentials(topic);
} finally {
// When finished with the publisher, make sure to shutdown to free up resources.
publisher.shutdown();
}
}
When i run the code i get this error:
Caused by: io.grpc.StatusRuntimeException: NOT_FOUND: Resource not found (resource=add-partner-request).
What am i missing?
Whatever entity is named "add-partner-request" was not successfully created or does not belong to the project. If "add-partner-request" is the topic, then you actually need to create the topic; the line TopicName.create(projectId, topicId) is not sufficient for creating the topic itself. Typically, one would create the topic in the Cloud Pub/Sub portion of the Cloud console or via a gcloud command, e.g.,
gcloud pubsub topics create add-partner-request
Ensure that the project you are logged into in the console is the one used in the code. You should also set the project explicitly when creating the topic via the --project flag or verify that the default project is the correct one:
gcloud config list --format='text(core.project)'
For tests, it is typical to create and delete in code. For example, to create a topic:
Topic topic = null;
ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
TopicAdminClient topicAdminClient = TopicAdminClient.create();
try {
topic = topicAdminClient.createTopic(topicName);
} catch (APIException e) {
System.out.println("Issue creating topic!");
}
If "add-partner-request" is the subscription name, then the same things apply. The gcloud command would change a bit:
gcloud pubsub subscriptions create add-partner-request --topic=<topic name>
The command to create the subscription in Java would be as follows:
Subscription subscription = null;
ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(projectId, subscriptionId);
SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create();
try {
subscription = subscriptionAdminClient.createSubscription(
subscriptionName, topicName, PushConfig.newBuilder().build(), 600);
} catch (APIException e) {
System.out.println("Issue creating topic!");
}
I'm assuming TOPIC_ID is the name of your topic; you actually need to reference a subscription. You can easily create a subscription from the GCP console, then reference that name in the SubscriptionName.create(project,yoursubscriptionname)
I think that you forget to create a topic inside your project with the following name "add-partner-request".
You can create it using the following code:
try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) {
// projectId <= unique project identifier, eg. "my-project-id"
TopicName topicName = TopicName.create(projectId, "add-partner-request");
Topic topic = topicAdminClient.createTopic(topicName);
return topic;
}

Categories