SQSlistener not receiving messages - amazon-web-services

I am able to send messages to SQS queue from my springboot but not able to receive using sqslistener annotation, can someone help?
public void send(String message) {
queueMessagingTemplate.convertAndSend("test-queue", MessageBuilder.withPayload(message).build());
}
#SqsListener(value = "test-queue", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
public void receive(String message)
{
System.out.println("message: " + message);
}
I have verified send by goign to AWS console, i can see my messages in queue, but they are not coming to receive method.
config:
#Bean
public AmazonSQSAsyncClient amazonSQSAsyncClient()
{
AmazonSQSAsyncClient amazonSQSAsyncClient= new AmazonSQSAsyncClient(amazonAWSCredentials());
if (!StringUtils.isEmpty(amazonSqsEndpoint)) {
amazonSQSAsyncClient.setEndpoint(amazonSqsEndpoint);
}
}
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory() {
SimpleMessageListenerContainerFactory msgListenerContainerFactory = new SimpleMessageListenerContainerFactory();
msgListenerContainerFactory.setAmazonSqs(amazonSQSAsyncClient());
return msgListenerContainerFactory;
}
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs) {
return new QueueMessagingTemplate(amazonSQSAsyncClient());
}
#Bean
public BasicAWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
}

In my case i was missing an annotation #EnableSqs in config class

Turns out i had a typo in the queue names in SQS console and my code, my bad.

Though it is late, it may help someone. In my case, I used gradle with these configuration
implementation "io.awspring.cloud:spring-cloud-starter-aws-messaging:2.3.0"
implementation "io.awspring.cloud:spring-cloud-aws-dependencies:2.3.0"
It doesn't work , below works perfectly for me:
compile "io.awspring.cloud:spring-cloud-starter-aws-messaging:2.3.0"
compile "io.awspring.cloud:spring-cloud-aws-dependencies:2.3.0"

In my case, the wrong region is specified. For some reason, sending didn't complain & pushed the message to the queue, but the listener is not getting called. Fixing the region in the application.yml file solved the issue.

In my case QueueMessageHandler bean wasn't initialized.
In the documentation they are using maven dependency:
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-messaging</artifactId>
<version>{spring-cloud-version}</version>
</dependency>
But in my case I'm using gradle, and also require dependency:
implementation(platform("io.awspring.cloud:spring-cloud-aws-dependencies:2.4.2"))
implementation("io.awspring.cloud:spring-cloud-aws-messaging")
implementation("io.awspring.cloud:spring-cloud-aws-autoconfigure")
Or you could define QueueMessageHandler in your own config file
#Bean
public QueueMessageHandler queueMessageHandler(QueueMessageHandlerFactory factory) {
return factory.createQueueMessageHandler();
}

Related

AWS .Net Core SDK Simple Email Service Suppression List Not Working

I am trying to retrieve the SES account-level suppression list using AWS SDK in .Net Core:
Below is my code:
public class SimpleEmailServiceUtility : ISimpleEmailServiceUtility
{
private readonly IAmazonSimpleEmailServiceV2 _client;
public SimpleEmailServiceUtility(IAmazonSimpleEmailServiceV2 client)
{
_client = client;
}
public async Task<ListSuppressedDestinationsResponse> GetSuppressionList()
{
ListSuppressedDestinationsRequest request = new ListSuppressedDestinationsRequest();
request.PageSize = 10;
ListSuppressedDestinationsResponse response = new ListSuppressedDestinationsResponse();
try
{
response = await _client.ListSuppressedDestinationsAsync(request);
}
catch (Exception ex)
{
Console.WriteLine("ListSuppressedDestinationsAsync failed with exception: " + ex.Message);
}
return response;
}
}
But it doesn't seem to be working. The request takes too long and then returns empty response or below error if I remove try/catch:
An unhandled exception occurred while processing the request.
TaskCanceledException: A task was canceled.
System.Threading.Tasks.TaskCompletionSourceWithCancellation<T>.WaitWithCancellationAsync(CancellationToken cancellationToken)
TimeoutException: A task was canceled.
Amazon.Runtime.HttpWebRequestMessage.GetResponseAsync(CancellationToken cancellationToken)
Can anyone please guide if I am missing something?
Thank you!
I have tested your code and everything works correctly.
using Amazon;
using Amazon.SimpleEmailV2;
using Amazon.SimpleEmailV2.Model;
internal class Program
{
private async static Task Main(string[] args)
{
var client = new AmazonSimpleEmailServiceV2Client("accessKeyId", "secrectAccessKey", RegionEndpoint.USEast1);
var utility = new SimpleEmailServiceUtility(client);
var result = await utility.GetSuppressionList();
}
}
<PackageReference Include="AWSSDK.SimpleEmailV2" Version="3.7.1.127" />
Things that you can check:
Try again, maybe it was a temporary problem.
Try with the latest version that I am using(if not already)
How far are you from the region that you try to get the list? Try making the same request from an EC2 instance in that region.
Finally found the issue, I was using awsConfig.DefaultClientConfig.UseHttp = true;' in startup` which was causing the issue. Removing it fixed the issue and everything seems to be working fine now.

Springboot SOAP service unmarshalling Issue with the response from WebServiceTemplate

I am really bugged with an unmarshalling issue with the response from the SOAP service. I am using springboot application and WebServiceTemplate for calling an existing SOAP service. I am using below code to set up beans for marshalling and webservicetemplate. Any help is highly appreciated.
On calling webServiceTemplate.marshalSendAndReceive(request); I am expecting TravelResponse object but it is giving me JAXBElement<TravelResponse> object as response. I need help to understand
1) why is it giving above response instead of TravelResponse
2) How to convert to TravelResponse
Code snippet below:
#Bean
Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.cater.trip.simple_api.trip.v1");
return jaxb2Marshaller;
}
#Bean
public WebServiceTemplate webServiceTemplate() throws Exception {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMessageFactory(getMessageFactory());
webServiceTemplate.setMarshaller(jaxb2Marshaller());
webServiceTemplate.setUnmarshaller(jaxb2Marshaller());
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageSender(getMessageSender());
return webServiceTemplate;
}
#Bean
public SaajSoapMessageFactory getMessageFactory() {
return new SaajSoapMessageFactory();
}
#Bean
public HttpComponentsMessageSender getMessageSender() {
return new HttpComponentsMessageSender();
}
#Override
public Object getData( ) {
ObjectFactory clientFac = new ObjectFactory();
TravelRequest request = populateRequest(clientFac);
TravelResponse res = (TravelResponse) webServiceTemplate.marshalSendAndReceive(request);
return res;
}
As per Spring's doc, WebServiceTemplate.marshalSendAndReceive(Object requestPayload)
Sends a web service message that contains the given payload, marshalled by the configured Marshaller. Returns the unmarshalled payload of the response message, if any.
This will only work with a default uri specified!
So, you can do this to return the expected response.
JAXBElement<TravelResponse> res = (JAXBElement<TravelResponse>) webServiceTemplate.marshalSendAndReceive(request);
return res.getValue();
Try JaxbIntrospector.getValue to get the actual response from JAXB element.
TravelResponse response = JaxbIntrospector.getValue(jaxbElement);

Same-Site flag for session cookie in Spring Security

Is it possible to set Same-site Cookie flag in Spring Security?
And if not, is it on a roadmap to add support, please? There is already support in some browsers (i.e. Chrome).
New Tomcat version support SameSite cookies via TomcatContextCustomizer. So you should only customize tomcat CookieProcessor, e.g. for Spring Boot:
#Configuration
public class MvcConfiguration implements WebMvcConfigurer {
#Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
}
For SameSiteCookies.NONE be aware, that cookies are also Secure (SSL used), otherwise they couldn't be applied.
By default since Chrome 80 cookies considered as SameSite=Lax!
See SameSite Cookie in Spring Boot and SameSite cookie recipes.
For nginx proxy it could be solved easily in nginx config:
if ($scheme = http) {
return 301 https://$http_host$request_uri;
}
proxy_cookie_path / "/; secure; SameSite=None";
UPDATE from #madbreaks:
proxy_cookie_flags iso proxy_cookie_path
proxy_cookie_flags ~ secure samesite=none;
Instead of a Filter, In your Authentication Success Handler, you can mention in this way.
#Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
clearAuthenticationAttributes(request);
addSameSiteCookieAttribute(response);
handle(request, response);
}
private void addSameSiteCookieAttribute(HttpServletResponse response) {
Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
boolean firstHeader = true;
// there can be multiple Set-Cookie attributes
for (String header : headers) {
if (firstHeader) {
response.setHeader(HttpHeaders.SET_COOKIE,
String.format("%s; %s", header, "SameSite=Strict"));
firstHeader = false;
continue;
}
response.addHeader(HttpHeaders.SET_COOKIE,
String.format("%s; %s", header, "SameSite=Strict"));
}
}
It was mentioned in one of the answers. Couldn't find the link after I've implemented it.
All possible solutions here failed for me. Every time I tried a filter or interceptor, the Set-Cookie header had not yet been added. The only way I was able to make this work was by adding Spring Session and adding this bean into one of my #Configuration files:
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite("none");
return serializer;
}
Anyway hope this helps someone else in my same situation.
You can always set cookie values by yourself in the Java world if you can get an instance of the HttpServletResponse.
Then you can do:
response.setHeader("Set-Cookie", "key=value; HttpOnly; SameSite=strict")
In spring-security you can easily do this with a filter, here is an example:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Set-Cookie", "locale=de; HttpOnly; SameSite=strict");
chain.doFilter(request, response);
}
}
Add this filter to your SecurityConfig like this:
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class)
Or via XML:
<http>
<custom-filter after="BASIC_AUTH_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="org.bla.CustomFilter"/>
It isn't possible. There is support for this feature in Spring Session: https://spring.io/blog/2018/10/31/spring-session-bean-ga-released
I came up with a solution similar to Ron's one. But there is one important thing to note:
Cookies for cross-site usage must specify SameSite=None; Secure
to enable inclusion in third party context.
So I've included Secure attribute in header. Also, you don't have to override all three methods when you don't use them. It is only required when you are implementing HandlerInterceptor.
import org.apache.commons.lang.StringUtils;
public class CookiesInterceptor extends HandlerInterceptorAdapter {
final String sameSiteAttribute = "; SameSite=None";
final String secureAttribute = "; Secure";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
addEtagHeader(request, response);
Collection<String> setCookieHeaders = response.getHeaders(HttpHeaders.SET_COOKIE);
if (setCookieHeaders == null || setCookieHeaders.isEmpty())
return;
setCookieHeaders
.stream()
.filter(StringUtils::isNotBlank)
.map(header -> {
if (header.toLowerCase().contains("samesite")) {
return header;
} else {
return header.concat(sameSiteAttribute);
}
})
.map(header -> {
if (header.toLowerCase().contains("secure")) {
return header;
} else {
return header.concat(secureAttribute);
}
})
.forEach(finalHeader -> response.setHeader(HttpHeaders.SET_COOKIE, finalHeader));
}
}
I used xml in my project so I had to add this to my configuration file:
<mvc:interceptors>
<bean class="com.zoetis.widgetserver.mvc.CookiesInterceptor"/>
</mvc:interceptors>
Using the interceptor in SpringBoot.
I'm looking for a resolution for adding SameSite as you, and I only want to add the attribute to the existing "Set-Cookie" instead of creating a new "Set-Cookie".
I have tried several ways to meet this requirement, including:
adding a custom filter as #unwichtich said,
and more I overrode basicAuthenticationFilter. It does add the SameSite attribute. While the timing when Spring will add the "Set-Cookie" is hard to catch. I thought in onAuthenticationSuccess() method, the response must have this header, but it doesn't. I'm not sure whether it's the fault of my custom basicAuthenticationFilter's order.
using cookieSerializer, but the spring-session version comes up to a problem. Seems only the latest version support it, but I still can't figure out the version number should be added into the dependency list.
Unfortunately, none of them above can add the samesite well as expected.
Finally, I found the interceptor in spring can help me to make it.
It took me a week to get it. Hope this can help you if anyone has the same problem.
#Component
public class CookieServiceInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//check whether it has "set-cookie" in the response, if it has, then add "SameSite" attribute
//it should be found in the response of the first successful login
Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
boolean firstHeader = true;
for (String header : headers) { // there can be multiple Set-Cookie attributes
if (firstHeader) {
response.setHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=strict"));
firstHeader = false;
continue;
}
response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=strict"));
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
}
}
and you also need to make this interceptor work in your application, which means you should add a bean as below:
#Autowired
CookieServiceInterceptor cookieServiceInterceptor;
#Bean
public MappedInterceptor myInterceptor() {
return new MappedInterceptor(null, cookieServiceInterceptor);
}
This interceptor has a flaw, it can't add samesite when the request is redirected(ex.return 302) or failed(ex. return 401), while it makes my app fail when SSO. Eventually, I have to use the Tomcat cookie, because I don't embed tomcat in my springboot app. I add
<Context>
<CookieProcessor sameSiteCookies="none" />
</Context>
in a context.xml under /META-INF of my app. It will add SameSite attribute in set-cookie header for each response. Note that this behavior is possible since Tomcat 9.0.21 and 8.5.42. according to https://stackoverflow.com/a/57622508/4033979
For Spring Webflux (reactive environment) this worked for me:
#Configuration
#EnableSpringWebSession
public class SessionModule {
#Bean
public ReactiveSessionRepository<MapSession> reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
#Bean
public WebSessionIdResolver webSessionIdResolver() {
CookieWebSessionIdResolver resolver = new CookieWebSessionIdResolver();
resolver.setCookieName("SESSION");
resolver.addCookieInitializer((builder) -> {
builder.path("/")
.httpOnly(true)
.secure(true)
.sameSite("None; Secure");
});
return resolver;
}
}
You can add cookie by yourself by using ResponseCookie and adding it to your HttpServletResponse.
ResponseCookie cookie = ResponseCookie.from("cookiename", "cookieValue")
.maxAge(3600) // one hour
.domain("test.com")
.sameSite("None")
.secure(true)
.path("/")
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
I have tested this solution for spring-webmvc without spring-security, but I think it should also work for spring-boot.
Using the SessionRepositoryFilter bean from spring-session-core
You can extend default java HttpSession with a spring Session and replace JSESSIONID cookie with a custom one, like this:
Set-Cookie: JSESSIONID=NWU4NzY4NWUtMDY3MC00Y2M1LTg1YmMtNmE1ZWJmODcxNzRj; Path=/; Secure; HttpOnly; SameSite=None
Additional spring Session cookie flags can be set using DefaultCookieSerializer:
#Configuration
#EnableSpringHttpSession
public class WebAppConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
servletContext
.addFilter("sessionRepositoryFilter", DelegatingFilterProxy.class)
.addMappingForUrlPatterns(null, false, "/*");
}
#Bean
public MapSessionRepository sessionRepository() {
final Map<String, Session> sessions = new ConcurrentHashMap<>();
MapSessionRepository sessionRepository =
new MapSessionRepository(sessions) {
#Override
public void save(MapSession session) {
sessions.entrySet().stream()
.filter(entry -> entry.getValue().isExpired())
.forEach(entry -> sessions.remove(entry.getKey()));
super.save(session);
}
};
sessionRepository.setDefaultMaxInactiveInterval(60*5);
return sessionRepository;
}
#Bean
public SessionRepositoryFilter<?> sessionRepositoryFilter(MapSessionRepository sessionRepository) {
SessionRepositoryFilter<?> sessionRepositoryFilter =
new SessionRepositoryFilter<>(sessionRepository);
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setCookieName("JSESSIONID");
cookieSerializer.setSameSite("None");
cookieSerializer.setUseSecureCookie(true);
CookieHttpSessionIdResolver cookieHttpSessionIdResolver =
new CookieHttpSessionIdResolver();
cookieHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
sessionRepositoryFilter.setHttpSessionIdResolver(cookieHttpSessionIdResolver);
return sessionRepositoryFilter;
}
}
I have extended a bit MapSessionRepository implementation, since it does NOT support firing SessionDeletedEvent or SessionExpiredEvent - I have added clearing of expired sessions before adding new ones. I think this might be enough for a small application.
Apparently, with spring boot you can write this and it gets picked up.
#Configuration
public static class WebConfig implements WebMvcConfigurer {
#Bean
public CookieSameSiteSupplier cookieSameSiteSupplier(){
return CookieSameSiteSupplier.ofNone();
}
}
Or ... even simpler, spring boot since 2.6.0 supports setting it in application.properties.
Spring documentation about SameSite Cookies
server.servlet.session.cookie.same-site = none

Camel exchange expired via jetty continuation

Is there a possibility in Apache Camel to register a handler for managing exchanges that cannot be written to jetty endpoint http response because continuation timeout has been reached?
I'll just add my notes on that because I made it available in my project by modifying CamelContinuationServlet in the if (continuation.isExpired()) block like this
if (continuation.isExpired()) {
String id = (String) continuation.getAttribute(EXCHANGE_ATTRIBUTE_ID);
// remember this id as expired
expiredExchanges.put(id, id);
log.warn("Continuation expired of exchangeId: {}", id);
consumer.getBinding().doWriteExceptionResponse(new TimeoutException(), response);
return;
}
in combination with a custom HttpBinding called ErrorHandlingHttpBinding in my code like this
public class ErrorHandlingHttpBinding extends DefaultHttpBinding {
#Override
public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException {
if (exception instanceof TimeoutException) {
response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
response.getWriter().write("Continuation timed out...");
} else {
super.doWriteExceptionResponse(exception, response);
}
}
}
registered as spring bean with id="errorHandlingHttpBinding" and referred in the component string as jetty:http://localhost:21010/?useContinuation=true&continuationTimeout=1&httpBindingRef=errorHandlingHttpBinding.
No this is not possible. Maybe you need to set a higher timeout if you have some slow processing exchanges.
You are welcome to dive in the Jetty APIs to see if you can find a hook for such a onTimeout event and see what it takes to support that in camel-jetty.

Azure Webjobs - Use INameResolver with TimerTrigger Function

I've tried to configure a job with a simple function with a TimerTrigger.
public class Processor
{
/// <summary>
/// Initializes a new instance of the <see cref="Processor"/> class.
/// </summary>
public Processor()
{
}
/// <summary>
/// Process the Leads to Marketo.
/// </summary>
[Disable("Processor.Disable")]
public async Task ProcessMessages([TimerTrigger("%Processor.TimerTrigger%")] TimerInfo timerInfo, TextWriter log)
{
// TODO : remove
await Task.FromResult(0);
}
}
My settings are defined in my app.config file:
<add key="Processor.TimerTrigger" value="00:01:00" />
<add key="Processor.Disable" value="false" />
When Starting my webjob, I've configure the job to use INameResolver and timertrigger:
static void Main()
{
// Configure the job host
var config = new JobHostConfiguration
{
NameResolver = new ConfigNameResolver() // Resolve name from the config file.
};
config.UseTimers();
var host = new JobHost(config);
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
When executing the line host.RunAndBlock(), I've got this exception :
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException: Error indexing method 'ProcessMessages' ---> System.FormatException: String was not recognized as a valid TimeSpan.
I've put a break point in the class that implements the INameResolver interface but never hit.
Is there any way to configure a NameResolver with TimerTrigger ?
Thanks.
TimerTrigger does not currently support INameResolver. Please open an issue in the public repo here and we'll add that support. The other extension bindings support INameResolver. If it's important to you, we can get out a pre-release build for you to use/verify ahead of the actual next release.
Confirmation that INameResolver is now supported in Timer Triggers using the technique in the original question and a resolver that looks like this:
public class ConfigNameResolver : INameResolver
{
public string Resolve(string name)
{
return ConfigurationManager.AppSettings.Get(name);
}
}