I have an Apache Apex application DAG which reads RabbitMQ message from a queue. Which Apache Apex Malhar operator should I use? There are several operators but it's not clear which one to use and how to use it.
Have you looked at https://github.com/apache/apex-malhar/tree/master/contrib/src/main/java/com/datatorrent/contrib/rabbitmq ? There are also tests in https://github.com/apache/apex-malhar/tree/master/contrib/src/test/java/com/datatorrent/contrib/rabbitmq that show how to use the operator
https://github.com/apache/apex-malhar/blob/master/contrib/src/main/java/com/datatorrent/contrib/rabbitmq/AbstractRabbitMQInputOperator.java
That is the main operator code where the tuple type is a generic parameter and emitTuple() is an abstract method that subclasses need to implement.
AbstractSinglePortRabbitMQInputOperator is a simple subclass that provides a single output port and implements emitTuple() using another abstract method getTuple() which needs an implementation in its subclasses.
The tests that Sanjay pointed to show how to use these classes.
I also had problems finding out how to read messages from RabbitMQ to Apache Apex. With the help of the provided links of Sanjay's answer (https://stackoverflow.com/a/42210636/2350644) I finally managed to get it running. Here's how it works all together:
1. Setup a RabbitMQ Server
There are lot of ways installing RabbitMQ that are described here: https://www.rabbitmq.com/download.html
The simplest way for me was using docker (See: https://store.docker.com/images/rabbitmq)
docker pull rabbitmq
docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-management
To check if RabbitMQ is working, open a browser and navigate to: http://localhost:15672/. You should see the Management page of RabbitMQ.
2. Write a Producer program
To send messages to the queue you can write a simple JAVA program like this:
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.util.ArrayList;
public class Send {
private final static String EXCHANGE = "myExchange";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.FANOUT);
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE, "");
List<String> messages = Arrays.asList("Hello", "World", "!");
for (String msg : messages) {
channel.basicPublish(EXCHANGE, "", null, msg.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + msg + "'");
}
channel.close();
connection.close();
}
}
If you execute the JAVA program you should see some outputs in the Management UI of RabbitMQ.
3. Implement a sample Apex Application
3.1 Bootstrap a sample apex application
Follow the official apex documentation http://docs.datatorrent.com/beginner/
3.2 Add additional dependencies to pom.xml
To use the classes provided by malhar add the following dependencies:
<dependency>
<groupId>org.apache.apex</groupId>
<artifactId>malhar-contrib</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.2.0</version>
</dependency>
3.3 Create a Consumer
We first need to create an InputOperator that consumes messages from RabbitMQ using available code from apex-malhar.
import com.datatorrent.contrib.rabbitmq.AbstractSinglePortRabbitMQInputOperator;
public class MyRabbitMQInputOperator extends AbstractSinglePortRabbitMQInputOperator<String> {
#Override
public String getTuple(byte[] message) {
return new String(message);
}
}
You only have to override the getTuple() method. In this case we simply return the message that was received from RabbitMQ.
3.4 Setup an Apex DAG
To test the application we simply add an InputOperator (MyRabbitMQInputOperator that we implemented before) that consumes data from RabbitMQ and a ConsoleOutputOperator that prints the received messages.
import com.rabbitmq.client.BuiltinExchangeType;
import org.apache.hadoop.conf.Configuration;
import com.datatorrent.api.annotation.ApplicationAnnotation;
import com.datatorrent.api.StreamingApplication;
import com.datatorrent.api.DAG;
import com.datatorrent.api.DAG.Locality;
import com.datatorrent.lib.io.ConsoleOutputOperator;
#ApplicationAnnotation(name="MyFirstApplication")
public class Application implements StreamingApplication
{
private final static String EXCHANGE = "myExchange";
#Override
public void populateDAG(DAG dag, Configuration conf)
{
MyRabbitMQInputOperator consumer = dag.addOperator("Consumer", new MyRabbitMQInputOperator());
consumer.setHost("localhost");
consumer.setExchange(EXCHANGE);
consumer.setExchangeType(BuiltinExchangeType.FANOUT.getType());
ConsoleOutputOperator cons = dag.addOperator("console", new ConsoleOutputOperator());
dag.addStream("myStream", consumer.outputPort, cons.input).setLocality(Locality.CONTAINER_LOCAL);
}
}
3.5 Test the Application
To simply test the created application we can write a UnitTest, so there is no need to setup a Hadoop/YARN cluster.
In the bootstrap application there is already a UnitTest namely ApplicationTest.java that we can use:
import java.io.IOException;
import javax.validation.ConstraintViolationException;
import org.junit.Assert;
import org.apache.hadoop.conf.Configuration;
import org.junit.Test;
import com.datatorrent.api.LocalMode;
/**
* Test the DAG declaration in local mode.
*/
public class ApplicationTest {
#Test
public void testApplication() throws IOException, Exception {
try {
LocalMode lma = LocalMode.newInstance();
Configuration conf = new Configuration(true);
//conf.addResource(this.getClass().getResourceAsStream("/META-INF/properties.xml"));
lma.prepareDAG(new Application(), conf);
LocalMode.Controller lc = lma.getController();
lc.run(10000); // runs for 10 seconds and quits
} catch (ConstraintViolationException e) {
Assert.fail("constraint violations: " + e.getConstraintViolations());
}
}
}
Since we don't need any properties for this application the only thing changed in this file is uncommenting the line:
conf.addResource(this.getClass().getResourceAsStream("/META-INF/properties.xml"));
If you execute the ApplicationTest.java and send messages to RabbitMQ using the Producer program as described in 2., the Test should output all the messages.
You might need to increase the time of the test to see all messages (It is set to 10sec currently).
Related
I am currently trying to learn consumer-driven contract testing in the context of Services that use ActiveMQ Messages to communicate.
Spring offers a documentation for Spring Cloud Contract Verifier Messaging
https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_spring_cloud_contract_verifier_messaging
but I was not able to follow it with the setting I have ( Spring Services and ActiveMQ ).
My question is:
Is it a good idea to use consumer-driven contract testing for messaging ?
What are the best practices ?
If its a good idea, do you have any good tutorials on this for consumer-driven contract testing spring services that communicate via JMS and ActiveMQ ?
I managed to create a custom MessageVerifier for JMS and ActiveMQ which looks like this:
package de.itemis.seatreservationservice;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.itemis.seatreservationservice.domain.ReservationRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
#Component
#Primary
public class JmsMessageVerifier implements MessageVerifier {
#Autowired
JmsTemplate jmsTemplate;
#Autowired
ObjectMapper mapper;
#Override
public void send(Object message, String destination) {
jmsTemplate.convertAndSend(destination, message, new ReplyToProcessor());
}
#Override
public Object receive(String destination, long timeout, TimeUnit timeUnit) {
jmsTemplate.setReceiveTimeout(timeout);
return receiveMessage(destination);
}
#Override
public Object receive(String destination) {
return receiveMessage(destination);
}
#Override
public void send(Object payload, Map headers, String destination) {
ReservationRequest request = null;
try {
request = mapper.readValue((String) payload, ReservationRequest.class);
} catch (IOException e) {
e.printStackTrace();
}
jmsTemplate.convertAndSend(destination, request, new ReplyToProcessor());
}
private Object receiveMessage(String queueName) {
Message message = jmsTemplate.receive(queueName);
TextMessage textMessage = (TextMessage) message;
try {
return new GenericMessage<>(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
return null;
}
}
}
Currently i need them in both test folders (producer and consumer).
Normally i would expect that the JmsMessageVerifier in my Producer would be packaged inside the generated stubs JAR so that the consumer contract tests use this JmsMessageVerifier instead of implementing their own.
What are your thoughts in this Marcin Grzejszczak ?
I would create an Issue for that if its a useful feature.
Here is the repository with both services:
but I was not able to follow it with the setting I have ( Spring Services and ActiveMQ ).
ActiveMQ is not supported out of the box, you would have to provide your own bean of MessageVerifier type, where you would teach the framework on how to send and receive messages
Is it a good idea to use consumer-driven contract testing for messaging ?
Absolutely! You can follow the same flow as with HTTP but for messaging
What are the best practices ?
It depends ;) You can follow the standard practices of doing cdc as if it was an http based communication. If you want to abstract the producer and the consumer of such messages in such a way that you care more about the topic / queue as such, you can follow this guideline https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_how_can_i_define_messaging_contracts_per_topic_not_per_producer where we describe how to define messaging contracts per topic and not per producer
If its a good idea, do you have any good tutorials on this for consumer-driven contract testing spring services that communicate via JMS and ActiveMQ ?
As I said earlier we don't have such support out of the box. You can however use Spring Integration or Apache Camel and communicate with ActiveMQ via those.
I'm new to JAX-RS and trying to figure out what is happening here:
I have a simple Hello World Jersey REST service running on Glassfish (Eclipse plugin). I can access it successfully from a browser.
Now, I'd like to call it from a Java class (so I can build JUnit tests around it) but I get this error on buildGet() method:
java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
Unless some magic I'm not aware of happens, I'm not packaging my service and/or client in any jar so it's not related to my application jar signature.
Anyone could explain what I'm doing wrong?
Why is the exception triggered on buildGet() metod and not on any method called before?
My main:
package com.test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
public class HelloTest {
public static void main(String[] args)
{
Client client = ClientBuilder.newClient();
Response response = null;
try {
WebTarget webTarget = client.target("http://localhost:9595/Hello/api/ping");
Invocation helloInvocation = webTarget.request().buildGet();
response = helloInvocation.invoke();
}
catch (Throwable ex) {
System.out.println(ex.getMessage());
}
finally {
response.close();
}
}
}
My service:
package com.api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("ping")
public class Hello
{
#GET
#Produces(MediaType.TEXT_HTML)
public String sayHtmlHello()
{
return "<html>" + "<title>" + "Hello" + "</title>"
+ "<body><h1>" + "Hello!!!" + "</body></h1>" + "</html>";
}
}
After struggling on this for a while, it seems that my Maven configuration had issues and some dependencies were not downloaded/built correctly. I restarted a new project, copied my source files and everything started to work as expected.
I am new to JEE7 and have been working on some quick exercises but I've bumped into a problem. I have a sample Java SE application that sends a message to an ActiveMQ queue and I have an MDB deployed on Wildfly 8 that reads the messages as they come in. This all works fine and I can receive the messages using getText. However, when I use getBody to get the message body, I get an "Unknown Error". Can anyone let me know what I'm doing wrong?
Here's my code below;
/***CLIENT CODE****/
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class SimpleMessageClient {
// URL of the JMS server. DEFAULT_BROKER_URL will just mean
// that JMS server is on localhost
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
// Name of the queue we will be sending messages to
private static String subject = "MyQueue";
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server and starting it
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
// JMS messages are sent and received using a Session. We will
// create here a non-transactional session object. If you want
// to use transactions you should set the first parameter to 'true'
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Destination represents here our queue 'TESTQUEUE' on the
// JMS server. You don't have to do anything special on the
// server to create it, it will be created automatically.
Destination destination = session.createQueue(subject);
// MessageProducer is used for sending messages (as opposed
// to MessageConsumer which is used for receiving them)
MessageProducer producer = session.createProducer(destination);
// We will send a small text message saying 'Hello' in Japanese
TextMessage message = session.createTextMessage("Jai Hind");
//Message someMsg=session.createMessage();
// someMsg.
// Here we are sending the message!
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
connection.close();
}
}
And the consumer;
package javaeetutorial.simplemessage.ejb;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "MyQueue")
})
public class SimpleMessageBean implements MessageListener {
#Resource
private MessageDrivenContext mdc;
static final Logger logger = Logger.getLogger("SimpleMessageBean");
public SimpleMessageBean() {
}
#Override
public void onMessage(Message inMessage) {
try {
if (inMessage instanceof TextMessage) {
logger.log(Level.INFO,
"MESSAGE BEAN: Message received: {0}",
inMessage.getBody(String.class));
} else {
logger.log(Level.WARNING,
"Message of wrong type: {0}",
inMessage.getClass().getName());
}
} catch (JMSException e) {
e.printStackTrace();
logger.log(Level.SEVERE,
"SimpleMessageBean.onMessage: JMSException: {0}",
e.toString());
mdc.setRollbackOnly();
}
}
}
Part of the error I get is;
16:47:48,510 ERROR [org.jboss.as.ejb3] (default-threads - 32) javax.ejb.EJBTransactionRolledbackException: Unexpected Error
16:47:48,511 ERROR [org.jboss.as.ejb3.invocation] (default-threads - 32) JBAS014134: EJB Invocation failed on component SimpleMessageBean for method public void javaeetutorial.simplemessage.ejb.SimpleMessageBean.onMessage(javax.jms.Message): javax.ejb.EJBTransactionRolledbackException: Unexpected Error
at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleInCallerTx(CMTTxInterceptor.java:157) [wildfly-ejb3-8.2.0.Final.jar:8.2.0.Final]
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:253) [wildfly-ejb3-8.2.0.Final.jar:8.2.0.Final]
at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:342) [wildfly-ejb3-8.2.0.Final.jar:8.2.0.Final]
The method
<T> T Message.getBody(Class<T> c)
you refer to was an addition to JMS 2.0 (see also: http://www.oracle.com/technetwork/articles/java/jms20-1947669.html).
While WildFly 8 is fully compliant to Java EE 7 and therefore JMS 2.1, the current ActiveMQ (5.12.0) is still restricted to JMS 1.1.
Since you presumably import the JMS 2.1 API in your SimpleMessageBean, you reference a method simply not present in the ActiveMQ message.
When you try to call the getBody()-method on the message, it cannot be resolved in the message implementation and hence an AbstractMethodError is thrown. This results in the rollback of the transaction which gives you the EJBTransactionRolledbackException.
I see two immediate solutions for your problem:
If you want to keep using ActiveMQ, confine yourself to the JMS 1.1 API. The getText()-method you mentioned is part of JMS 1.1 and therefore works flawlessly. See here for the JMS 1.1 API (https://docs.oracle.com/javaee/6/api/javax/jms/package-summary.html) and here for the current ActiveMQ API documentation (http://activemq.apache.org/maven/5.12.0/apidocs/index.html).
Switch to a JMS 2.x compliant message broker. Since you are using WildFly, I recommend taking a look at HornetQ (http://hornetq.jboss.org/).
I have been trying to implement this design for couple of days ago and had no success. (before continuing I tell that I have read this article "How can I inject an EJB from an other application on the same GlassFish Server?" and other than glassfish-ejb-jar.xml I tried everything in it )
I have 2 ear application. test.ear is my main app which I want to test it, but I don't want to add any remote interfaces in this project, so I created another .ear(automation.ear) which contains remote interfaces and inject EJBs from test.ear . so my standalone application calls automation app and can not access test application directly.
each application contains these modules: entities, ejb , ear (for example automation application contains :automation-entities, automation-ejb, automation-ear)
here is my journey :
(I deployed test.ear before all these attempts)
-first try: I added test.ejb with ejb type as a maven dependency to automation-ejb pom.xml and injected EJB of test.ear in one of the beans of automation project and gave it a mappedName; it worked however I realized that it is a false positive and it actually doesn't go to the implementation of test.ear. it uses the copy it has (because of dependency)
- second try: I added a plugin in pom.xml of test.ear to create ejb-client for me (which only contains the interfaces and not the implementation) and added this .jar to automation pom.xml as a dependency, then injected the ejb gave it mappedName and try to deply ; well it complained about not finding the class!--> can't deploy
- third try: I added a copy of the interface I wanted to use(from test.ear) in automation.ear and removed the dependencies in pom.xml of automation. injected EJB and gave it the mappedName; tried to deploy and no luck ! still can't find the class--> can't deploy
Here are my classes:
test.ear application
package com.ericsson.test.service;
import javax.ejb.Local;
#Local
public interface TestService1 {
public String TestMethod1();
}
//////////////////////////////////////////////////////
package com.ericsson.test.service;
import javax.ejb.Local;
import javax.ejb.Stateless;
#Local(TestService1.class)
#Stateless(name = "TestService1")
public class TestServiceBean1 implements TestService1{
#Override
public String TestMethod1() {
return "Test Method1";
}
}
automation.ear application
#Remote
public interface RemoteInterfaceTest{
public void doSomething();
}
////////////////////////////////////////////////////////////////////////
package com.ericsson.test.service;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.naming.InitialContext;
#Singleton
#Startup
public class RemoteInterfaceTestBean implements RemoteInterfaceTest {
#EJB(mappedName = "Test-Server-Local/test-ejb-0.0.1-SNAPSHOT/TestService1/local")
private TestService1 testService1EJB;
#PostConstruct
public void doSomething() {
try {
InitialContext ic = new InitialContext();
TestService1 service1 = (TestService1) ic
.lookup("java:global/Test-Server-Local/test-ejb-0.0.1-SNAPSHOT/TestService1");
System.out.println(service1.TestMethod1());
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("OOOOOOPPPPPPSSSSSSSSS ! First try");
}
try {
testService1EJB.TestMethod1();
} catch (Exception e) {
System.out.println("OOOOOOPPPPPPSSSSSSSSS ! Second try");
}
}
}
and this is the exception I get:
SEVERE: Exception while deploying the app [automation-csdp-remote] : Cannot resolve reference Local ejb-ref name=com.ericsson.test.service.RemoteInterfaceTestBean/testService1EJB,Local 3.x interface =com.ericsson.test.service.TestService1,ejb-link=null,lookup=,mappedName=Test-Server-Local/test-ejb-0.0.1-SNAPSHOT/TestService1/local,jndi-name=,refType=Session
I have tried played with it like 1000 times, I checked the glass fish and the class is there, why it can not find it ? any idea would be greatly appreciated.
I am using EJB 3.x , glassfish 3.1.2 , maven 3.0.5, eclipse kepler
I am writing integration tests using Arquillian with embedded glassfish 3.1.2.2 using TestNG. I want to be able to run those tests in parallel, and for this case i need to dynamically configure glassfish ports and database name (we already have this set-up, and I want to reuse it of arquillian tests). What I am missing is a 'before container start' hook, where I could prepare the database, lookup free ports and update my glassfish configuration (domain.xml, could also be glassfish-resources.xml). Is there a 'clean' solution for this, or my usecase was not foreseen by Arquillian developers?
The hacky way I solved it currently is to override arquillian's beforeSuite method but this one gets called twice - at test startup and then in the container (therefore my pathetic static flag). Secondly, this solution would not work for JUnit based tests as there's no way to intercept arquillian's before suite:
public class FullContainerIT extends Arquillian {
private static boolean dbInitialized;
//#RunAsClient <-supported by #Test only
#Override
#BeforeSuite(groups = "arquillian", inheritGroups = true)
public void arquillianBeforeSuite() throws Exception {
if (dbInitialized == false) {
initializeDb();
dbInitialized = true;
}
super.arquillianBeforeSuite();
}
}
Some ideas I had:
+ having #BeforeSuite #RunAsClient seems to be what I need, but #RunAsClient is supported for #Test only;
+ I have seen org.jboss.arquillian.container.spi.event.container.BeforeStart event in Arquillian JavaDocs, but I have no clue how to listen to Arquillian events;
+ I have seen there is a possibility to have #Deployment creating a ShrinkWrap Descriptor, but these do not support Glassfish resources.
I found a clean solution for my problem on JBoss forum. You can register a LoadableExtension SPI and modify the arquillian config (loaded from xml). This is where I can create a database and filter glassfish-resources.xml with proper values. The setup looks like this:
package com.example.extenstion;
public class AutoDiscoverInstanceExtension
implements org.jboss.arquillian.core.spi.LoadableExtension {
#Override
public void register(ExtensionBuilder builder) {
builder.observer(LoadContainerConfiguration.class);
}
}
package com.example.extenstion;
public class LoadContainerConfiguration {
public void registerInstance(#Observes ContainerRegistry, ServiceLoader serviceLoader) {
//Do the necessary setup here
String filteredFilename = doTheFiltering();
//Get the container defined in arquillian.xml and modify it
//"default" is the container's qualifier
Container definition = registry.getContainer("default");
definition.getContainerConfiguration()
.property("resourcesXml", filteredFilename);
}
}
You also need to configure the SPI Extension by creating a file
META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
with this contents:
com.example.extenstion.AutoDiscoverInstanceExtension