Apache Beam : is it possible to comsume messages of RabbitMQ with exchange and routing key - rabbitmq

I defined a pipeline in Apache Beam to consume messages of a given queue in RabbitMQ message broker.
I defined an exchange and routing key in RabbitMQ.
I used AmqpIO.read() in Beam (version 2.9.0) but I did not found any API to set the echange and the routing key.
(Following this doc : https://beam.apache.org/releases/javadoc/2.4.0/org/apache/beam/sdk/io/amqp/AmqpIO.html)
Is there any possibility to do that ? Even with any other plugin.
Regards,
Ali

There is a new (experimental) IO connector for RabbitMQ shipped with the latest v2.9.0 Apache Beam release. The AMQP connector will not work for RabbitMQ.
If you are using Maven add the following dependency in your POM
<!-- Beam MongoDB I/O -->
<dependency>
<groupId>org.apache.beam</groupId>
<artifactId>beam-sdks-java-io-mongodb</artifactId>
<version>2.9.0</version>
</dependency>
and you can use it in a pipeline like
public class RabbitMQPipeline {
final static Logger log = LoggerFactory.getLogger(RabbitMQPipeline.class);
/**
* Mongo Pipeline options.
*/
public interface RabbitMQPipelineOptions extends PipelineOptions {
#Description("Path of the file to read from")
#Default.String("amqp://localhost")
#Required
String getUri();
void setUri(String uri);
}
/**
* #param args
*/
public static void main(String[] args) {
RabbitMQPipelineOptions options = PipelineOptionsFactory.fromArgs(args).withValidation()
.as(RabbitMQPipelineOptions.class);
Pipeline pipeline = Pipeline.create(options);
PCollection<RabbitMqMessage> messages = pipeline
.apply(RabbitMqIO2.read().withUri(options.getUri()).withQueue("test"));
messages.apply(ParDo.of(new DoFn<RabbitMqMessage, String>() {
#ProcessElement
public void process(#Element RabbitMqMessage msg) {
System.out.println(msg.toString());
}
}));
pipeline.run().waitUntilFinish();
}
}
The RabbitMqIO Javadoc has examples of how to use the reader and writer.
A word of caution
There is a known bug that has been fixed but scheduled for release in v2.11.0 that blocks the connector from working even in the simplest scenarios. The fix is really simple (see JIRA issue) but you will need to rebuild a new version of the class. In case you want to give it a try make sure you add the following Maven dependency
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.5.2</version>
<scope>provided</scope>
</dependency>
and add the following configuration in Maven Compiler plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessors>
<annotationProcessor>com.google.auto.value.processor.AutoValueProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
If you are using Eclipse make sure you install the m2-apt Maven plugin. Good luck!

Related

HttpComponentsClientHttpConnector is not accepting org.apache.http.impl.nio.client.CloseableHttpAsyncClient for Webclient with Apache Http Client

Im trying to run Webflux on Tomcat and try to create Sping WebClient with Apache Http Client.
Reference Documentation stated that theres built-in support:
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-builder-http-components
private ClientHttpConnector getApacheHttpClient(){
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(RequestConfig.DEFAULT);
CloseableHttpAsyncClient client = clientBuilder.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
return connector;
}
But Springs HttpComponentsClientHttpConnector is not accepting org.apache.http.impl.nio.client.CloseableHttpAsyncClient. It requires org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient. So there seems to be a package rename and I canĀ“t find a Maven Dependency that has the required class.
Does anybody know the right Maven Dependency for that class. Or how could I make it work?
Apache HTTP Client 5 is a separate artifact. You'll need to add the following dependencies to your pom.xml:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5-reactive</artifactId>
<version>5.1</version>
</dependency>
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.springframework.http.client.reactive.HttpComponentsClientHttpConnector;
public class ApacheHttp {
public static void main(String[] args) {
new HttpComponentsClientHttpConnector(HttpAsyncClients.custom().build())
}
}

spring.jackson properties not found

In my Spring Boot project it seems none of the spring.jackson properties are working. I want to take controll over date fields and I tried to add the following properties to my application.properties file:
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.joda-date-time-format=yyyy-MM-dd HH:mm:ss
Dates still come as timestamps (java.util.Date and joda.time). I have the following dependencies:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</dependency>
I didn't add version number but it says the managed version number is 2.8.7 (with all three).
I've read if I use #EnableWebMvc annotation on any class, it won't work. But I don't use such annotation. (However I have spring-webmvc on my classpath.)
When I start the application (in Tomcat), I got these log messages:
2017-04-23 16:54:55 DEBUG JndiTemplate:150 - Looking up JNDI object with name [java:comp/env/spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS]
2017-04-23 16:54:55 DEBUG JndiLocatorDelegate:101 - Converted JNDI name [java:comp/env/spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS] not found - trying original name [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS]. javax.naming.NameNotFoundException: Name [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS] is not bound in this Context. Unable to find [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS].
2017-04-23 16:54:55 DEBUG JndiTemplate:150 - Looking up JNDI object with name [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS]
2017-04-23 16:54:55 DEBUG JndiPropertySource:99 - JNDI lookup for name [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS] threw NamingException with message: Name [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS] is not bound in this Context. Unable to find [spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS].. Returning null.
2017-04-23 16:54:55 DEBUG PropertySourcesPropertyResolver:151 - Found key 'spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS' in [URL [file:C:/Program Files/Apache Software Foundation/Tomcat 9.0/conf/Catalina/localhost/Blowfish/config/application.properties]] with type [String]
Only one thing worked with both java.util.Date and joda.time:
#Configuration
public class AppConfig extends WebMvcConfigurerAdapter{
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
break;
}
}
}
}
But I don't like this solution, it would be more elegant to use some configuriation in my application.properties.
I use Spring Boot 1.5.2.

Unsatisfied dependencies with Weld during integration testing

I am able to deploy a RESTEasy application working well with Weld (meaning my CDI works) but I am having some trouble with my integration tests. I get this error:
org.jboss.weld.exceptions.DeploymentException:
WELD-001408: Unsatisfied dependencies for type SomeService with qualifiers #Default
while testing:
#RunWith(WeldJUnit4Runner.class)
public class SomeServiceIT {
#Inject
private SomeService service;
#Test
public void test() {
System.out.println(service);
}
}
The last message in my logs is
DEBUG::WELD-000100: Weld initialized. Validating beans
Content of src/test/resources/META-INF/beans.xml:
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
</beans>
By the way I tried the cdi-unit library and it works, but I need to use my own WeldJUnit4Runner which is currently:
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
private final Weld weld;
private final WeldContainer container;
public WeldJUnit4Runner(Class<?> klass) throws InitializationError {
super(klass);
this.weld = new Weld();
this.container = weld.initialize();
}
#Override
protected Object createTest() throws Exception {
return container.instance().select(getTestClass().getJavaClass()).get();
}
}
I use weld-se 2.4.1.Final for testing.
Thanks.
EDIT:
So it seems like Weld only looks into src/test/java (when I copy SomeService over to src/test/java it woks). This is silly, I am not going to duplicate all my classes to test them... How to tell Weld to retrieve classes from src/main/java?
So I was able to make it work by creating src/main/resources/META-INF/beans.xml in addition to the existing src/main/webapp/WEB-INF/beans.xml and src/test/resources/META-INF/beans.xml meaning now I have 3 times the exact same file in the same project which I find silly but I guess this is how it is in the Weld world...
Thanks all for your time.
EDIT:
Actually I am able to deploy the application with only src/main/resources/META-INF/beans.xml (I removed src/main/webapp/WEB-INF/beans.xml)
Sorry, I have no solution, but only a small clue: if you want to do some customizations of the BlockJUnit4ClassRunner - why don't you try to extend the org.jglue.cdiunit.CdiRunner or org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner? Or at least take a look at their source code.
Ps. I always find Weld's class-path scanning brittle & error prone. And try to avoid it as much as possible.
It should work so I post here what I did.
Firstly, I use :
Eclipse Luna
JDK 7
The tree of my project is the following one :
Here are my pom dependencies :
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>2.4.1.Final</version>
<scope>test</scope>
</dependency>
The SomeService interface :
package org.jvi.cdirunner;
public interface SomeService {
void test();
}
The SomeServiceImpl implementation :
package org.jvi.cdirunner;
public class SomeServiceImpl implements SomeService {
#Override
public void test() {
// TODO Auto-generated method stub
}
}
And the test to run :
package org.jvi.cdirunner.test;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvi.cdirunner.SomeService;
#RunWith(WeldJUnit4Runner.class)
public class SomeServiceIT {
#Inject
private SomeService service;
#Test
public void test() {
System.out.println(service);
}
}
And everything works fine if I run the test under Eclipse. I can't figure out why it doesn't work on your side.

Infinispan 6.0 CDI and default configuration

I'm having problems defining configuration for a CDI application (glassfish 4).
I have:
#CacheResult(cacheName = "example")
public String getSomething(String something){
logger.debug("getSomething "+something);
return "this is "+something;
}
This works as expected, the second time is called is not executed because it's cached
However, I want to specify a configuration for my caches. I have tried writing a infinispan.xml file (in src/main/resources), but it's ignored. I have also tried with both:
#Produces
#Default
public Configuration defaultEmbeddedCacheConfiguration() {
return new ConfigurationBuilder().expiration().lifespan(3000l)
.eviction()
.strategy(EvictionStrategy.LRU)
.maxEntries(2)
.build();
}
#Produces
#ApplicationScoped
public EmbeddedCacheManager defaultEmbeddedCacheManager() {
return new DefaultCacheManager(defaultEmbeddedCacheConfiguration());
}
But these methods are never called.
I have also tried with #ConfigureCache
My dependencies are:
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-cdi</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-jcache</artifactId>
<version>6.0.2.Final</version>
</dependency>
Any ideas?
Thx

Camel Redis Component subscribe to a channel not working

I've a simple route that listens to a Redis channel. For some reason it's not working.
Here is my route. I verified that data is being published into the Redis channel and I can read it back using a normal Jedis subscriber. I'm running Camel inside Jetty and it is deployed as a war.
public class RedisSubscriberRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("spring-redis://localhost:6379?command=SUBSCRIBE&channels=mychannel")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String res = exchange.getIn().getBody().toString();
System.out.println("************ " + res);
exchange.getOut().setBody(res);
}
})
.to("log:foo");
}
}
UPDATE (10-May-2013 9:56 AM EST): Adding version information
<properties>
<spring.version>3.2.2.RELEASE</spring.version>
<camel.version>2.11.0</camel.version>
<jetty.version>7.6.8.v20121106</jetty.version>
</properties>
Redis server version is 2.6.11
The sample git project is here.
https://github.com/soumyasd/camelredisdemo
UPDATE 10-May-2013 (10:18 PM EST):
As suggested in the comments below I changed the version of the spring-data to 1.0.0.RELEASE. Looks like the message is getting to the subscriber but I'm still getting an exception.
java.lang.RuntimeException: org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.StreamCorruptedException: invalid stream header: 77686174
at org.apache.camel.component.redis.RedisConsumer.onMessage(RedisConsumer.java:73)[camel-spring-redis-2.11.0.jar:2.11.0]
at org.springframework.data.redis.listener.RedisMessageListenerContainer.executeListener(RedisMessageListenerContainer.java:242)[spring-data-redis-1.0.0.RELEASE.jar:]
at org.springframework.data.redis.listener.RedisMessageListenerContainer.processMessage(RedisMessageListenerContainer.java:231)[spring-data-redis-1.0.0.RELEASE.jar:]
at org.springframework.data.redis.listener.RedisMessageListenerContainer$DispatchMessageListener$1.run(RedisMessageListenerContainer.java:726)[spring-data-redis-1.0.0.RELEASE.jar:]
at java.lang.Thread.run(Thread.java:680)[:1.6.0_45]
There is something broken in the consumer with v 1.0.3.RELEASE, use 1.0.0.RELEASE instead.
The exception you are getting is something different: Camel producer uses Spring RedisTemplate, which in turn uses JdkSerializationRedisSerializer. To make it symetric, the consumer by default also uses JdkSerializationRedisSerializer to deserialize data. So if you are using Camel producer to publish data, it should work fine w/o hustle. But if you are publishing data to redis using other redis clients (or as in your case some other libraries) you have to use another serializer for the consumer. Long explanation, but to make it work is actually two lines:
from("spring-redis://localhost:6379?command=SUBSCRIBE&channels=mychannel&serializer=#serializer")
Here is a summary of what I had to change to make this work.
As pointed out by #Bilgin Ibryam - you have to use the version 1.0.0.RELEASE of spring-data-redis (as on 11-May-2013)
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<!-- IMPORTANT - as of 10-May-2013 the Redis Camel
component only works with version 1.0.0.RELASE -->
<version>1.0.0.RELEASE</version>
</dependency>
Other versions that I used in my pom.xml are
3.2.2.RELEASE
2.11.0
7.6.8.v20121106
If you are publishing and consuming using the Camel Redis component you don't have to declare a different serializer. In my case I was publishing from python as well as plain old Java using Jedis. I had to change as my route to include the serializer and define the serializer in my spring/camel config.
#Override
public void configure() throws Exception {
from("spring-redis://localhost:6379?command=SUBSCRIBE&channels=mychannel&serializer=#redisserializer")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String res = exchange.getIn().getBody().toString();
System.out.println("************ " + res);
exchange.getOut().setBody(res);
}
})
.to("log:foo");
}