How to know which kafka is being consumed from list? - java

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());
}

Related

Send an acknowledgment to the method call using a Boolean variable (here sent) in Kafka Producer

I'm implementing a sendStringMessage method that returns true if the message is being sent to the topic. But I'm getting error( as indicated in code) Local variable sent defined in an enclosing scope must be final or effectively final.
Or can we create a custom callback function that returns a boolean when the message is sent to the topic.
private KafkaProducer<String, String> kafkaProducerFactory(String message){
Properties producerConfigs = new Properties();
producerConfigs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, Bootstrap_Server);
producerConfigs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
producerConfigs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
KafkaProducer<String, String> producer = new KafkaProducer<String, String>(producerConfigs);
return producer;
}
private boolean sendStringMessage (String topic , String message) throws InterruptedException, ExecutionException {
KafkaProducer<String, String> producer = kafkaProducerFactory(message);
ProducerRecord<String, String> record = new ProducerRecord<String, String>(topic , message);
boolean sent = false;
try {
producer.send(record, new Callback() {
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e == null) {
sent = true; // Error --> Local variable sent defined in an enclosing scope must be final or effectively final
logger.info("Kafka Message Successfully received the details by the topic : \n"
+ "\tTopic : {}\n"+ "\tPartition : {}\n" + "\tOffset : {}\n" + "\tTimestamp : {}",
recordMetadata.topic(), recordMetadata.partition(), recordMetadata.offset(),
recordMetadata.timestamp());
logger.info("kafka Message send by the producer : "+ message );
}
else {
sent = false; // Error --> Local variable sent defined in an enclosing scope must be final or effectively final
logger.info("Kafka Message Not received - System Error Message :");
}
}
}).get();
}
finally {
producer.flush();
producer.close();
}
return sent;
}
The short answer is that onCompletion is asynchronous and producer.send will likely complete before the return line is hit (despite you calling .get()), and thus sent would not be changed.
One solution would be to not return and relocate the new Callback to a method parameter of sendStringMessage, and then you don't need a blocking producer call
void sendStringMessage(String topic, String message, Callback completionEvent) {
...
producer.send(topic, message, completionEvent);
...
}

Call Amazon notification service on Play framework

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!

Trouble casting JMS TopicSession as AQjmsSession

I'm trying to browse messages in a topic using TopicBrowser. To call the correct createBrowser method that will return a TopicBrowser object, I need to cast my topicSession variable to AQjmsSession.
private InitialContext initialContext;
private TopicConnection topicConnection;
private TopicSession topicSession;
private Topic topic;
private TopicConnectionFactory topicFactory;
private TextMessage message;
private void initTopic(Context context, String topicName) throws NamingException, JMSException {
System.out.println("initializing the queue...");
topicFactory = (TopicConnectionFactory) context.lookup(JMS_FACTORY);
topicConnection = topicFactory.createTopicConnection();
//queueConnection.setExceptionListener(this);
topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("session created, lookup queue: " + topicName);
topic = (Topic)context.lookup(topicName);
System.out.println("done...");
}
private void readTopicMessage() throws JMSException {
int counter = 0;
System.out.println("Starting the subscriber");
TopicBrowser topicBrowser = ((AQjmsSession) topicSession).createBrowser(topic, SUBSCRIBER);
Enumeration msgs = topicBrowser.getEnumeration();
//topicConnection.start();
System.out.println("Topic started\n");
if (!msgs.hasMoreElements()) {
System.out.println("No messages in topic");
} else {
while (msgs.hasMoreElements()) {
System.out.println(">>> message count: " + ++counter);
Message message = (Message) msgs.nextElement();
System.out.println("MessageID: " + message.getJMSMessageID() + "\tPriority: " + message.getJMSPriority() + "\tDeliveryMode: " + message.getJMSDeliveryMode());
System.out.println("Timestamp: " + message.getJMSTimestamp() + "\tJMSDestination: " + message.getJMSDestination() + "\tReplyTo: " + message.getJMSReplyTo());
if (message instanceof TextMessage)
System.out.println("Data: " + ((TextMessage) message).getText());
System.out.println("\n");
if (counter >= 5) break;
}
}
System.out.println("stopping the topic");
topicConnection.stop();
}
Attempting to make this cast gives me this error:
java.lang.ClassCastException: weblogic.jms.client.WLSessionImpl cannot be cast to oracle.jms.AQjmsSession
You can't cast it to that type because the client that your application is using isn't from the provider you think it is. According to the exception the TopicSession is of a type provided by the WebLogic JMS client so that cast is doomed to failure. You should look at your JNDI configuration and see if it is configured the way you seem to think it is configured.
Your JNDI Properties is telling the code where to create the ConnectionFactory from and that is getting you WebLogic client bits, you seem to want Oracle AQ bits so the ConnectionFactory that's looked up in the JNDI step needs to be an Oracle one.

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;
}

How to find senders Bitcoin Address in BitcoinJ after receiving a transaction

So in my app I have the following functionality for receiving bitcoins
kit.wallet().addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
#Override
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
txtLog.append("-----> coins resceived: " + tx.getHashAsString() + "\n");
txtLog.append("received: " + tx.getValue(wallet) + "\n");
Futures.addCallback(tx.getConfidence().getDepthFuture(1), new FutureCallback<TransactionConfidence>() {
#Override
public void onSuccess(TransactionConfidence result) {
txtLog.append("\nSuccess! Recieved: " + tx.getValue(wallet) + "\n");
//Find address of sender here
}
#Override
public void onFailure(Throwable t) {
throw new RuntimeException(t);
}
});
}
});
This works great, OnSuccess triggers properly once a transaction is confirmed and added to my wallet. txtLog is just a textArea in my java frame which displays some text output for me. What I need to do now is find the address of the sender at this point, can i do this with the Transaction object tx? Any help would be appreciated.
Found the solution! Unfortunately it uses a depreciated method. I just added the following in the appropriate spot.
String address = "";
for (TransactionInput txin : tx.getInputs()){
address = txin.getFromAddress().toString();
}

Categories