I'm not able to send message to sqs-queue from my java application.
I am running roribio16/alpine-sqs docker image for SQS in my local and I've created a standard queue.
Code snippets:
#Data
#Configuration
#EnableSqs
public class SQSConfig {
private final String amazonAWSAccessKey;
private final String amazonAWSSecretKey;
private final String amazonSQSRegion;
public SQSConfig(#Value("${aws.accesskey}") String amazonAWSAccessKey,
#Value("${aws.secretkey}") String amazonAWSSecretKey,
#Value("${sqs.aws.region}") String amazonSQSRegion) {
this.amazonAWSAccessKey = amazonAWSAccessKey;
this.amazonAWSSecretKey = amazonAWSSecretKey;
this.amazonSQSRegion = amazonSQSRegion;
}
#Bean
public QueueMessagingTemplate queueMessagingTemplate() {
return new QueueMessagingTemplate(amazonSQSClient());
}
#Bean
#Primary
public AmazonSQSAsync amazonSQSClient() {
return AmazonSQSAsyncClientBuilder.standard()
.withRegion(amazonSQSRegion)
.withCredentials(new AWSStaticCredentialsProvider(new DefaultAWSCredentialsProviderChain().getCredentials()))
.build();
}
private AWSStaticCredentialsProvider amazonAWSCredentials() {
return new AWSStaticCredentialsProvider(new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey));
}
}
MessageProducerService:
#Component
public class SQSMessageProducerService {
#Value("${sqs.endpoint}")
private String amazonSQSEndpoint;
#Autowired
private QueueMessagingTemplate queueMessagingTemplate;
public void sendMessage(String messageBody) {
Message messageBuilt = MessageBuilder.withPayload(messageBody).build();
try {
queueMessagingTemplate.send(amazonSQSEndpoint, messageBuilt);
} catch (MessagingException e) {
System.out.println(e);
}
System.out.println("message sent: " + messageBody);
}
}
Calling using -
sqsMessageProducerService.sendMessage(userEvent.toString());
Error stack -
InvalidAction; see the SQS docs. (Service: AmazonSQS; Status Code:
400; Error Code: InvalidAction; Request ID:
00000000-0000-0000-0000-000000000000)
Vars from properties file -
sqs.endpoint=${SQS_ENDPOINT:http://localhost:9324/test-sns-queue}
sqs.aws.region=${SQS_AWS_REGION:us-west-2}
Also, SQS instance is up in my local and can be accessed using http://localhost:9325/.
Is there some config that I'm missing here?
I think the problem might be that the endpoint is http://localhost:9324/queue/test-sns-queue
Related
#Value("${amazon.sqs.queue.endpoint}")
private String endpoint;
#Value("${amazon.sqs.queue.name}")
private String queueName;
#Autowired
private SQSListener sqsListener;
#Bean
public DefaultMessageListenerContainer jmsListenerContainer() throws JMSException {
SQSConnectionFactory sqsConnectionFactory = SQSConnectionFactory.builder()
.withAWSCredentialsProvider(new DefaultAWSCredentialsProviderChain())
.withEndpoint(endpoint)
.withAWSCredentialsProvider(new ClasspathPropertiesFileCredentialsProvider("AwsCredentials-sqs.properties"))
.withNumberOfMessagesToPrefetch(10).build();
DefaultMessageListenerContainer dmlc = new DefaultMessageListenerContainer();
dmlc.setConnectionFactory(sqsConnectionFactory);
dmlc.setDestinationName(queueName);
dmlc.setConcurrentConsumers(1);
dmlc.setMaxConcurrentConsumers(100);
dmlc.setMessageListener(sqsListener);
return dmlc;
}
#Component
public class SQSListener implements MessageListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SQSListener.class);
#Override
public void onMessage(Message message) {
try {
// Cast the recei
ved message as TextMessage and print the text to screen.
System.out.println("Received: " + ((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
I added a file in s3 then the message was sent to sqs queue.
after getting this message can I get the actual data, that was uploaded in s3?
It's not clear from your question what the message contains, but if the message contains the S3 bucket and key, then yes, you can just use the S3 API to download this.
Add the following dependency
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
This logic will create the S3 client and download the object.
AwsCredentials credentials = AwsBasicCredentials.create(
"<AWS Access Key>",
"<AWS Secret>"
);
S3Client s3client = S3Client.builder()
.region(Region.US_EAST_1) // or whatever region you're in
.credentialsProvider(() -> credentials) // credentials created above (or preferably injected)
.build();
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket("the bucket") // the bucket that contains the object, from message maybe?
.key("the key") // the key to the object, from message maybe?
.build();
ResponseInputStream<GetObjectResponse> responseInputStream = s3client.getObject(getObjectRequest);
This will give you an InputStream that you can read from.
i am making login function in android using retrofit. I have created an endpoint for login validation, then I have tested it using Postman using raw (json) and it worked. But when I enter the endpoint into android using retrofit I get an error message like this:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 39 path $.message
can anyone help me?
So here my source:
ApiClient
public class ApiClient {
public static final String BASE_URL = "";
public static Retrofit retrofit;
public static Retrofit getRetrofit() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
AuthInterface
public interface AuthInterface {
#Headers("Content-Type: application/json")
#POST("auth/login")
Call<AuthPost> authPostCall(#Body String body);
}
AuthPost
public class AuthPost {
#SerializedName("status")
private String status;
#SerializedName("error_code")
private int error_code;
#SerializedName("message")
private String message;
#SerializedName("token")
private String token;
...getter and setter
}
LoginActivity
JSONObject payload = new JSONObject();
try {
payload.put("login_username", loginUsernameText);
payload.put("login_password", loginPasswordText);
} catch (JSONException e) {
e.printStackTrace();
}
Call<AuthPost> authPostCall = authInterface.authPostCall(payload.toString());
authPostCall.enqueue(new Callback<AuthPost>() {
#Override
public void onResponse(Call<AuthPost> call, Response<AuthPost> response) {
if (response.code() == 200) {
} else {
}
}
#Override
public void onFailure(Call<AuthPost> call, Throwable t) {
t.printStackTrace();
}
});
Are you sure about:
#SerializedName("message")
private String message;
Usually this error appears if this field is Object.
Does your JSON looks like
"message":"test"
or something like:
"message":{"field":"value"}
If it is the second variant so you should simple change the field to necessary type.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am working on a project in Spring Boot that has a microservice architecture. I needed to make a call from one microservice to another and with data that I get as response do something. I am very new to all microservice architecture and Spring Boot, so I figured I need a small push :)
So I have this class:
HttpDataClient.java
public class HttpDataClient implements DataClient{
private final static Logger LOGGER = LoggerFactory.getLogger(HttpDataClient.class);
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
public HttpDataClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public DataResponse getData(String dataId) {
try{
JsonNode node = restTemplate.exchange(
String.format("/data/%s", dataId),
HttpMethod.POST,
new HttpEntity<>(buildRequest(dataId), headers()),
JsonNode.class
).getBody();
return dataResponse(node);
}catch (HttpStatusCodeException e) {
String msg = String.format(
"Error getting data for dataId: %s",
dataId,
e.getStatusCode(),
e.getResponseBodyAsString());
LOGGER.error(msg);
return dataResponse.failed();
}
}
private MultiValueMap<String, String> headers() {
final LinkedMultiValueMap<String, String> mv = new LinkedMultiValueMap<>();
mv.set(HttpHeaders.CONTENT_TYPE, "application/json");
return mv;
}
private DataResponse dataResponse(JsonNode node) {
return DataResponse.dataResponse(
asString(node, "dataId"),
asString(node, "author"),
asString(node, "authorDataId"),
asString(node, "serverSideDataId")
);
}
private JsonNode buildRequest(String dataId) {
ObjectNode root = objectMapper.createObjectNode();
root.put("dataId", dataId);
return root;
}
}
And the interface
public interface DataClient {
DataResponse getData(String dataId);
}
And from this class when I get response I should be able to do next if author is "Philadelphia" then athorDataId and serverSideId are the same and if author is not "Philadelphia" then athorDataId and serverSideId are not the same.
For now, I have created this class:
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
}
And in this class, I should execute all of this:
And from this class when I get response I should be able to do next if author is "Philadelphia" then athorDataId and serverSideId are the same and if author is not "Philadelphia" then athorDataId and serverSideId are not the same.
But I don't know how to start. I know I suppose to use client to get the response data. I am not sure how to use it.. And then I should probably do something like this :
if (author == Philadelphia) {
authorDataId == serverSideDataId
} elseif(author != Philadelphia) {
authorDataId != serverSideDataId
}
UPDATE
Is it possible to create new class like this
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
}
And in this class access data from response and manipulate it in some way?
Something like this:
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
public DataIdResolver idResolver() {
if (author == Philadelphia) {
authorDataId == serverSideDataId
} elseif(author != Philadelphia) {
authorDataId != serverSideDataId
}
}
}
If the response that you expect should contain the mentioned attributes, then you should create the following class and use it as the return type in restTemplate.exchange instead of JsonNode.class (assuming you have Jackson in your classpath):
public class DataClient {
private String dataId;
private String author;
private String authorDataId;
private String serverSideDataId;
// getters and setters
}
So you would have something like in the HttpDataClient class:
#Override
public DataClient getData(String dataId) {
try{
DataClient data = restTemplate.exchange(
String.format("/data/%s", dataId),
HttpMethod.POST,
new HttpEntity<>(buildRequest(dataId), headers()),
DataClient.class).getBody();
return data;
}catch (HttpStatusCodeException e) {
String msg = String.format(
"Error getting data for dataId: %s",
dataId,
e.getStatusCode(),
e.getResponseBodyAsString());
LOGGER.error(msg);
return dataResponse.failed();
}
}
And DataResolver:
#Component
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
public DataClient idResolver(String dataId) {
DataClient data = client.getData(dataId);
// Whatever logic you need
return data;
}
}
Maybe one important thing here is that you most likely would want to make HttpDataClient a Spring bean by adding the #Service annotation. By doing this you can autowire it to any other Spring bean you need.
I'm using akka with Java and Spring. And my main goal is notify other application in a asynchronous way. But sometimes this other app isn't notified. So, I implemented a Supervisor to make use of retries. But, when I receive an Exception, specifically InternalServerErrorException my supervisor does not make another retry. Seems that the RestTemplate and your treatement of exceptions is causing the problem.
Below follows my code:
#Scope(SCOPE_PROTOTYPE)
public class NotificacaoSupervisor extends AbstractActor {
private static final int RETRIES = 5;
private OneForOneStrategy ONE_FOR_ONE_STRATEGY = new OneForOneStrategy(
RETRIES,
Duration.create("5 minutes"),
true,
DeciderBuilder.match(NotificacaoException.class, ex -> SupervisorStrategy.restart())
.build());
#Inject #Qualifier("notifyActor")
private ActorRef notifyActor;
#Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(any -> notifyActor.forward(any, getContext()))
.build();
}
#Override
public SupervisorStrategy supervisorStrategy() {
return ONE_FOR_ONE_STRATEGY;
}
}
Block that sends a notification
try{
if(content.isPresent()) {
this.logInfo(content.get());
HttpEntity<String> entity = new HttpEntity<>(content.get(), headers);
String urlDeCallback = integracao.getUrlDeCallback();
URI uri = URI.create(urlDeCallback);
restTemplate.postForObject(uri, entity, Void.class);
}
} catch (Exception e) {
this.logError(new ErrorResponse(notificacao).toString(), e);
throw new NotificacaoException(e);
}
Is there a Spring Boot Actuator Health Check endpoint for SQS? I have built a SQS consumer and I want to check if SQS is up and running. I am not using JMSlistener for connecting to SQS but rather using Spring Cloud Libraries.
I implemented the below health check endpoint. This returns the below error when I delete the queue and try to hit the health check endpoint. If there is a connectivity issue or if the SQS service goes down , will I be getting a similar error which will eventually cause the health check endpoint to fail?
com.amazonaws.services.sqs.model.QueueDoesNotExistException: The
specified queue does not exist for this wsdl version. (Service:
AmazonSQS; Status Code: 400; Error Code:
AWS.SimpleQueueService.NonExistentQueue; Request ID:
cd8e205d-dc43-535e-931f-7332733bd16c)
public class SqsQueueHealthIndicator extends AbstractHealthIndicator {
private final AmazonSQSAsync amazonSQSAsync;
private final String queueName;
public SqsQueueHealthIndicator(AmazonSQSAsync amazonSQSAsync, String queueName) {
this.amazonSQSAsync = amazonSQSAsync;
this.queueName = queueName;
}
#Override
protected void doHealthCheck(Health.Builder builder) {
try {
amazonSQSAsync.getQueueUrl(queueName);
builder.up();
} catch (QueueDoesNotExistException e) {
e.printStackTrace();
builder.down(e);
}
}
}
Beans
#Bean
SqsQueueHealthIndicator queueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${sqs.queueName}") String queueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, queueName);
}
#Bean
SqsQueueHealthIndicator deadLetterQueueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${sqs.dlQueueName}") String deadLetterQueueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, deadLetterQueueName);
}
You have to write a custom health check like below to check your queue exists or not by calling getQueueUrl using AWS Java SDK lib.
#Component
public class SQSHealthCheck implements HealthIndicator {
#Override
public Health health() {
int errorCode = check(); // perform some specific health check
if (errorCode != 0) {
return Health.down()
.withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
public int check() {
/**
your logic to check queue exists or not using by calling getQueueUrl . e.g you will get queue url of a queue named "SampleQueue" like https://sqs.us-east-1.amazonaws.com/12XXX56789XXXX/SampleQueue
**/
return 0; // 0 or 1 based on result
}
}