I am trying to establish a connection with twitter client and getting the following exception:
This exception appears while establishing the base connection whereas zookeepers and Kafka server are running.
Exception in thread "main" java.lang.IllegalStateException: There is already a connection thread running for Hosebird-Client-01, endpoint: /1.1/statuses/filter.json?delimited=length&stall_warnings=true
at com.twitter.hbc.httpclient.BasicClient.connect(BasicClient.java:92)
at com.github.simpleProject.kafka.simpleProject.twitter.TwitterProducer.run(TwitterProducer.java:37)
at com.github.simpleProject.kafka.simpleProject.twitter.TwitterProducer.main(TwitterProducer.java:28)
Here is my code
public class TwitterProducer {
Logger logger = LoggerFactory.getLogger(TwitterProducer.class.getName());
String consumerKey ="sbkOd*********tMJQUpU4Iz4j";
String consumerSecret="eZZPa788***********TR6hx39MlkvWylO6rF";
String token = "758284333996277763-*************D6f5CanCsre2qPgviv";
String secret="jJRbC9cMOaacHEHEd7y6po********1VwsS5x0ZmDG";
public TwitterProducer(){}
public static void main(String[] args){
new TwitterProducer().run();
}
public void run(){
/** Set up your blocking queues: Be sure to size these properly based on expected TPS of your stream */
BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>(1000);
// create a twitter client
Client client = createTwitterClient(msgQueue);
// Attempts to establish a connection.
client.connect();
// create a kafka producer
// loops to send tweets to kafka
// on a different thread, or multiple different threads....
while (!client.isDone()) {
String msg =null;
try{
msg = msgQueue.poll(5, TimeUnit.SECONDS);
} catch (InterruptedException e){
e.printStackTrace();
client.stop();
}
if(msg!=null){
logger.info(msg);
}
logger.info("End of application");
}
}
public Client createTwitterClient(BlockingQueue<String> msgQueue){
/** Declare the host you want to connect to, the endpoint, and authentication (basic auth or oauth) */
Hosts hosebirdHosts = new HttpHosts(Constants.STREAM_HOST);
StatusesFilterEndpoint hosebirdEndpoint = new StatusesFilterEndpoint();
List<String> terms = Lists.newArrayList("bitcoin");
hosebirdEndpoint.trackTerms(terms);
// These secrets should be read from a config file
Authentication hosebirdAuth = new OAuth1(consumerKey, consumerSecret, token, secret);
ClientBuilder builder = new ClientBuilder()
.name("Hosebird-Client-01") // optional: mainly for the log
.hosts(hosebirdHosts)
.authentication(hosebirdAuth)
.endpoint(hosebirdEndpoint)
.processor(new StringDelimitedProcessor(msgQueue));
Client hosebirdClient = builder.build();
// Attempts to establish a connection.
hosebirdClient.connect();
return hosebirdClient;
}
}
Related
I'm working on a POC using LaunchDarkly's Java + Redis SDK and one of my requirements is initializing a 2nd LaunchDarkly client in "offline" mode. Due to my existing architecture one application will connect to LaunchDarkly and hydrate a Redis instance. The 2nd application will connect to the same data store, but the client will initialize as "offline" -- is there currently a way for me to read stored events from the offline client and flush them to the LaunchDarkly servers?
In the code snippet below I am initializing the first client + redis store, then initializing a 2nd client in a background thread that connects to the same local redis instance. I can confirm that when I run this snippet I do not see events populate in the LaunchDarkly UI.
NOTE: this is POC to determine whether LaunchDarkly will work for my use case. It is not a Production-grade implementation.
public static void main(String[] args) throws IOException {
LDConfig config = new LDConfig.Builder().dataStore(Components
.persistentDataStore(
Redis.dataStore().uri(URI.create("redis://127.0.0.1:6379")).prefix("my-key-prefix"))
.cacheSeconds(30)).build();
LDClient ldClient = new LDClient("SDK-KEY", config);
Runnable r = new Runnable() {
#Override
public void run() {
LDConfig offlineConfig = new LDConfig.Builder().dataStore(Components
.persistentDataStore(
Redis.dataStore().uri(URI.create("redis://127.0.0.1:6379")).prefix("my-key-prefix"))
.cacheSeconds(30)).offline(true).build();
LDClient offlineClient = new LDClient("SDK-KEY", offlineConfig);
String uniqueId = "abcde";
LDUser user = new LDUser.Builder(uniqueId).custom("customField", "customValue").build();
boolean showFeature = offlineClient.boolVariation("test-feature-flag", user, false);
if (showFeature) {
System.out.println("Showing your feature");
} else {
System.out.println("Not showing your feature");
}
try {
offlineClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(r);
executor.shutdown();
ldClient.close();
}
Problem I'm facing is the something like this :
in Main class, I'll try to connect to a server and attach Channel Listener for future actions.
If Connection establishes successfully, SSL Handshake is done without any problem.
But if Connection in Step 1 fails, I'll try to connect to same or different server and again attach same channel listener same as point.
But expectation is it should SSL handshake as before in point 2 if connection is established. But it's not. Even if I forcefully call renegotiate metthod in SslHandler.
Expected behavior
If any connection exception using bootstrap object to connect to the server, expectation is it should SSL handshake.
Actual behavior
It's skipping the SSL handshake while retrying and failing with UnknownMessage type expected(ByteBuf)
Steps to reproduce
While Main Connection
public class Main {
private final static Logger LOGGER = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
ClientConfig clientConfig = null;
LOGGER.info("initializing Agent Stats uploader");
// Set up.
InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE);
Bootstrap clientBootstrap = getBootstrap();
clientConfig = ClientConfig.getInstance();
InetSocketAddress server = clientConfig.getPrimaryScnInetAddrs();
Objects.nonNull(server.getHostName());
Objects.nonNull(server.getPort());
// Make a new connection.
LOGGER.info("Initialization complete, ready to connect to the host and port {}:{}", server.getHostName(),
server.getPort());
ServerChannelFutureListener serverChannelFutureListener = ServerChannelFutureListener.getInstance();
serverChannelFutureListener.setClientBootStrap(clientBootstrap);
ChannelPromise channelPromise =
(ChannelPromise) clientBootstrap.connect(server).addListener(serverChannelFutureListener);
EventLoopGroup eventGroupExecutor = clientBootstrap.config().group();
AgentStatsProcess agentStatsThread = AgentStatsProcess.getInstance();
agentStatsThread.setParentChannelFuture(channelPromise);
eventGroupExecutor.scheduleAtFixedRate(agentStatsThread, clientConfig.getInitialDelay(),
clientConfig.getScheduleInterval(), TimeUnit.SECONDS);
LOGGER.info("Scheduled Agent Stats uploading, should start in 30 secs");
LOGGER.info("Connection complete");
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.info("Killing AgentStatUploader Thread");
eventGroupExecutor.shutdownGracefully();
}));
}
public static final Bootstrap getBootstrap() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(NioSocketChannel.class);
b.handler(new AgentStatsChannelInitializationHandler());
b.option(ChannelOption.SO_KEEPALIVE, true);
b.option(ChannelOption.TCP_NODELAY, true);
return b;
}
}
Having Channel Future handler for implementing re-try logic in step 1
public final class ServerChannelFutureListener implements GenericFutureListener {
private static final Logger logger = LoggerFactory.getLogger(ServerChannelFutureListener.class.getName());
private static ServerChannelFutureListener instance;
private AtomicInteger count = new AtomicInteger(1);
private ClientConfig clientConfig = ClientConfig.getInstance();
private boolean isPrimary=true;
private ChannelFuture channelFuture;
private Bootstrap clientBootStrap;
private long timeout;
private ServerChannelFutureListener(){
this.timeout = clientConfig.getRetryAfter();
}
#override
public void operationComplete(ChannelFuture future) throws Exception {
channelFuture = future;
int maxretries = clientConfig.getMaxRetries();
if (!future.isSuccess()) {
logger.info("Connection to {} scn is not successful, retrying ({}/{})", getServerType(), count.get(),maxretries);
logger.debug("Connection to server is failed with error: ",future.cause());
if ( count.incrementAndGet() > maxretries) {
// fails to connect even after max-retries, try to connect to next server.
logger.info("Failed to connect to {} server, will try to connect to {} now.",
getServerType(),
isPrimary() ? "SECONDARY":"PRIMARY");
count.getAndSet(1);
isPrimary = !isPrimary();
this.timeout = clientConfig.getRetryAfter();
logger.info("Connecting Server type changed, so resetting timeout: {}", this.timeout);
}else{
// retry
logger.info("Exponential Back-off set to: {} secs, waiting for next server connection", this.timeout);
//TimeUnit.SECONDS.sleep(this.timeout);
this.timeout = ExpontentialBackOff.getNextBackOff(this.timeout);
}
InetSocketAddress server = getServer();
logger.info("Initialization complete, ready to connect to the host and port {}:{}", server.getHostName(),
server.getPort());
channelFuture = clientBootStrap.connect(server).addListener(this);
}else {
logger.info("Using Connection with config: {}, to Server {} ", future.channel().config(),
future.channel().localAddress());
this.timeout = clientConfig.getRetryAfter();
logger.info("Time out Back-off reset to: {} for next server connection", this.timeout);
}
AgentStatsProcess.getInstance().setParentChannelFuture(channelFuture);
}
private String getServerType() {
return isPrimary() ? "PRIMARY" : "SECONDARY";
}
private InetSocketAddress getServer(){
return isPrimary()?clientConfig.getPrimaryScnInetAddrs():clientConfig.getSecondaryScnInetAddrs();
}
public static ServerChannelFutureListener getInstance(){
if(null == instance){
instance = new ServerChannelFutureListener();
}
return instance;
}
public boolean isPrimary() {
return isPrimary;
}
public ChannelFuture getChannelFuture() {
return channelFuture;
}
public void setClientBootStrap(Bootstrap cb) {
this.clientBootStrap = cb;
}
}
Expectation is SSL Handshake should happen after trying to reconnect but its failing.
Netty version: 4.1.12.Final
Fixed this issue, Culprit here is "ProtobufVarint32FrameDecoder " and it's parent Class "ByteToMessageDecoder". "ByteToMessageDecoder" make sure it's child classes are not shareable.
Because above classes are not shareable, every time code try to reconnect using boostrap, initializer class fails to add handlers in pipeline results in "ctx.close()" and no handlers.
I've did work-around of adding those two classes into my project and raised #10371 bug to address this issue.
I have a small spring-boot app set up that connects to one or more Topics on ActiveMQ, which are set in the application's application.properties file on startup - and then sends these messages on to a database.
This is all working fine, but I am having some problems when trying to implement a failover - basically, the app will try to reconnect, but after a certain number of retries, the application process will just automatically exit, preventing the retry (ideally, I would like the app to just retry forever until killed manually or ActiveMQ becomes available again). I have tried explicitly setting the connection options (such as maxReconnectAttempts) in the connection URL (using url.options in application.properties) to -1/0/99999 but none of these seem to be right as the behavior is the same each time. From looking at the advice on Apache's own reference page I would also expect this behavior to be working as default too.
If anyone has any advice to force the app not to quit, I would be very grateful! The bits of my code that I think will matter is below:
#Configuration
public class AmqConfig {
private static final Logger LOG = LogManager.getLogger(AmqConfig.class);
private static final String LOG_PREFIX = "[AmqConfig] ";
private String clientId;
private static ArrayList<String> amqUrls = new ArrayList<>();
private static String amqConnectionUrl;
private static Integer numSubs;
private static ArrayList<String> destinations = new ArrayList<>();
#Autowired
DatabaseService databaseService;
public AmqConfig (#Value("${amq.urls}") String[] amqUrl,
#Value("${amq.options}") String amqOptions,
#Value("${tocCodes}") String[] tocCodes,
#Value("${amq.numSubscribers}") Integer numSubs,
#Value("${clientId}") String clientId) throws UnknownHostException {
Arrays.asList(amqUrl).forEach(url -> {
amqUrls.add("tcp://" + url);
});
String amqServerAddress = "failover:(" + String.join(",", amqUrls) + ")";
String options = Strings.isNullOrEmpty(amqOptions) ? "" : "?" + amqOptions;
this.amqConnectionUrl = amqServerAddress + options;
this.numSubs = Optional.ofNullable(numSubs).orElse(4);
this.clientId = Strings.isNullOrEmpty(clientId) ? InetAddress.getLocalHost().getHostName() : clientId;
String topic = "Consumer." + this.clientId + ".VirtualTopic.Feed";
if (tocCodes.length > 0){
Arrays.asList(tocCodes).forEach(s -> destinations.add(topic + "_" + s));
} else { // no TOC codes = connecting to default feed
destinations.add(topic);
}
}
#Bean
public ActiveMQConnectionFactory connectionFactory() throws JMSException {
LOG.info("{}Connecting to AMQ at {}", LOG_PREFIX, amqConnectionUrl);
LOG.info("{}Using client id {}", LOG_PREFIX, clientId);
ActiveMQConnectionFactory connectionFactory =
new ActiveMQConnectionFactory(amqConnectionUrl);
Connection conn = connectionFactory.createConnection();
conn.setClientID(clientId);
conn.setExceptionListener(new AmqExceptionListener());
conn.start();
destinations.forEach(destinationName -> {
try {
for (int i = 0; i < numSubs; i++) {
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(destinationName);
MessageConsumer messageConsumer = session.createConsumer(destination);
messageConsumer.setMessageListener(new MessageReceiver(databaseService, destinationName));
}
} catch (JMSException e) {
LOG.error("{}Error setting up queue # {}", LOG_PREFIX, destinationName);
LOG.error(e.getMessage());
}
});
return connectionFactory;
}
}
public class MessageReceiver implements MessageListener, ExceptionListener {
public static final Logger LOG = LogManager.getLogger(MessageReceiver.class);
private static final String LOG_PREFIX = "[Message Receiver] ";
private DatabaseService databaseService;
public MessageReceiver(DatabaseService databaseService, String destinationName){
this.databaseService = databaseService;
LOG.info("{}Creating MessageReceiver for queue with destination: {}", LOG_PREFIX, destinationName);
}
#Override
public void onMessage(Message message) {
String messageText = null;
if (message instanceof TextMessage) {
TextMessage tm = (TextMessage) message;
try {
messageText = tm.getText();
} catch (JMSException e) {
LOG.error("{} Error getting message from AMQ", e);
}
} else if (message instanceof ActiveMQMessage) {
messageText = message.toString();
} else {
LOG.warn("{}Unrecognised message type, cannot process", LOG_PREFIX);
LOG.warn(message.toString());
}
try {
databaseService.sendMessageNoResponse(messageText);
} catch (Exception e) {
LOG.error("{}Unable to acknowledge message from AMQ. Message: {}", LOG_PREFIX, messageText, e);
}
}
}
public class AmqExceptionListener implements ExceptionListener {
public static final Logger LOG = LogManager.getLogger(AmqExceptionListener.class);
private static final String LOG_PREFIX = "[AmqExceptionListener ] ";
#Override
public void onException(JMSException e){
LOG.error("{}Exception thrown by ActiveMQ", LOG_PREFIX, e);
}
}
The console output I get from my application is just the below (apologies, as it is not much to go off)
[2019-12-12 14:43:30.292] [WARN ] Transport (tcp://[address]:61616) failed , attempting to automatically reconnect: java.io.EOFException
[2019-12-12 14:43:51.098] [WARN ] Failed to connect to [tcp://[address]:61616] after: 10 attempt(s) continuing to retry.
Process finished with exit code 0
Very interesting Question!
Configuring the maxReconnectAttempts=-1 will cause the connection attempts to be retried forever, but what I feel the problem here are as follows:
You are trying to connect to ActiveMQ while creating the Bean at App
startup, If ActiveMQ is not running when APP is starting up, the
Bean creation would retry the connection attempts forever causing a
timeout and not letting the APP to start.
Also when the ActiveMQ stops running midway you are not reattempting the connection as it is done inside #Bean and will only happen on APP startup
Hence the Connection shouldn't happen at Bean creation time, but maybe it can be done after the APP is up (maybe inside a #PostConstruct block)
These are just the pointers, You need to take it forward
Hope this helps!
Good luck!
Very little documentation, above what exists in the sample code is currently available for Grizzly 2.2 and I have found this to be most difficult to navigate as it relates to SSL implementation. I am desperately in need of some guidance in this area. After reviewing my code to determine what I need to post in order to pose a complete question, I realized it might be most beneficial to first cement the basics.
Below are three classes provided by the Grizzly project in order to demonstrate a sample implementation of Grizzly's SSL capabilities. Besides the removal of comments, the code is identical to the 2.2.19 released code base as maintained in git at git://java.net/grizzly~git and also available here.
The git repository also provides the referenced truststore and keystore.
EchoFilter:
public class EchoFilter extends BaseFilter{
#Override
public NextAction handleRead(FilterChainContext ctx)throws IOException {
//|Peer address is used for non-connected UDP Connection
final Object peerAddress = ctx.getAddress();
final Object message = ctx.getMessage();
ctx.write(peerAddress, message, null);
return ctx.getStopAction();
}
}
SSLEchoServer:
public class SSLEchoServer{
public static final String HOST = "localhost";
public static final int PORT = 7777;
public static void main(String[] args) throws IOException{
//|Create a FilterChain using FilterChainBuilder
FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
//|Add TransportFilter, which is responsible for reading and writing data to the connection
filterChainBuilder.add(new TransportFilter());
//|Initialize and add SSLFilter
final SSLEngineConfigurator serverConfig = initializeSSL();
final SSLEngineConfigurator clientConfig = serverConfig.copy().setClientMode(true);
filterChainBuilder.add(new SSLFilter(serverConfig, clientConfig));
//|Add StringFilter, which will be responsible for Buffer <-> String transformation
filterChainBuilder.add(new StringFilter(Charset.forName("UTF-8")));
//|Use the plain EchoFilter
filterChainBuilder.add(new EchoFilter());
//|Create TCP transport
final TCPNIOTransport transport = TCPNIOTransportBuilder.newInstance().build();
//|Set filterchain as a Transport Processor
transport.setProcessor(filterChainBuilder.build());
try{
//|Binding transport to start listen on certain host and port
transport.bind(HOST, PORT);
//|Start the transport
transport.start();
System.out.println("Press any key to stop the server...");
System.in.read();
}finally{
System.out.println("Stopping transport...");
//|Stop the transport
transport.stop();
System.out.println("Stopped transport...");
}
}
private static SSLEngineConfigurator initializeSSL(){
//|Initialize SSLContext configuration
SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
//|Set key store
ClassLoader cl = SSLEchoServer.class.getClassLoader();
URL cacertsUrl = cl.getResource("ssltest-cacerts.jks");
if(cacertsUrl != null){
sslContextConfig.setTrustStoreFile(cacertsUrl.getFile());
sslContextConfig.setTrustStorePass("changeit");
}
//|Set trust store
URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
if(keystoreUrl != null){
sslContextConfig.setKeyStoreFile(keystoreUrl.getFile());
sslContextConfig.setKeyStorePass("changeit");
}
//|Create SSLEngine configurator
return new SSLEngineConfigurator(sslContextConfig.createSSLContext(), false, false, false);
}
}
SSLEchoClient:
public class SSLEchoClient{
private static final String MESSAGE = "Hello World!";
public static void main(String[] args) throws IOException{
//|Create a FilterChain using FilterChainBuilder
FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
//|Add TransportFilter, which is responsible for reading and writing data to the connection
filterChainBuilder.add(new TransportFilter());
//|Initialize and add SSLFilter
final SSLEngineConfigurator serverConfig = initializeSSL();
final SSLEngineConfigurator clientConfig = serverConfig.copy().setClientMode(true);
final SSLFilter sslFilter = new SSLFilter(serverConfig, clientConfig);
filterChainBuilder.add(sslFilter);
//|Add StringFilter, which will be responsible for Buffer <-> String transformation
filterChainBuilder.add(new StringFilter(Charset.forName("UTF-8")));
//|Add Filter, which will send a greeting message and check the result
filterChainBuilder.add(new SendMessageFilter(sslFilter));
//|Create TCP transport
final TCPNIOTransport transport = TCPNIOTransportBuilder.newInstance().build();
//|Set filterchain as a Transport Processor
transport.setProcessor(filterChainBuilder.build());
try{
//|Start the transport
transport.start();
//|Perform async. connect to the server
transport.connect(SSLEchoServer.HOST, SSLEchoServer.PORT);
System.out.println("Press any key to stop the client...");
System.in.read();
}finally{
System.out.println("Stopping transport...");
//|Stop the transport
transport.stop();
System.out.println("Stopped transport...");
}
}
private static class SendMessageFilter extends BaseFilter{
private final SSLFilter sslFilter;
public SendMessageFilter(SSLFilter sslFilter){
this.sslFilter = sslFilter;
}
#Override
#SuppressWarnings("unchecked")
public NextAction handleConnect(FilterChainContext ctx) throws IOException{
final Connection connection = ctx.getConnection();
//|Execute async SSL handshake
sslFilter.handshake(connection, new EmptyCompletionHandler<SSLEngine>(){
//|Once SSL handshake will be completed - send greeting message
#Override
public void completed(SSLEngine result){
//|Here we send String directly
connection.write(MESSAGE);
}
});
return ctx.getInvokeAction();
}
#Override
public NextAction handleRead(FilterChainContext ctx) throws IOException{
//|The received message is String
final String message = (String) ctx.getMessage();
//|Check the message
if(MESSAGE.equals(message)){
System.out.println("Got echo message: \"" + message + "\"");
}else{
System.out.println("Got unexpected echo message: \"" + message + "\"");
}
return ctx.getStopAction();
}
}
private static SSLEngineConfigurator initializeSSL(){
//|Initialize SSLContext configuration
SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
//|Set key store
ClassLoader cl = SSLEchoClient.class.getClassLoader();
URL cacertsUrl = cl.getResource("ssltest-cacerts.jks");
if(cacertsUrl != null){
sslContextConfig.setTrustStoreFile(cacertsUrl.getFile());
sslContextConfig.setTrustStorePass("changeit");
}
//|Set trust store
URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
if(keystoreUrl != null){
sslContextConfig.setKeyStoreFile(keystoreUrl.getFile());
sslContextConfig.setKeyStorePass("changeit");
}
//|Create SSLEngine configurator
return new SSLEngineConfigurator(sslContextConfig.createSSLContext(), false, false, false);
}
}
When executing:
Run SSLEchoServer:
Press any key to stop the server...
Run SSLEchoClient:
Press any key to stop the client...
Question:
What is this code supposed to accomplish or demonstrate? Beyond the console output you see above, this code does nothing on my end.
In reviewing the code, my expectation was that the client was supposed start its TCP transport and connect it to the server. In the process of that connection being made, the SendMessageFilter previously added to the filter stream will execute its handleConnect() method, which I confirmed does execute. But, this code never executes the connection.write(MESSAGE) statement.
It's clear the intention here is to execute the write() method after the handshake thread completes, but it doesn't appear to do so, and in examining the handshake() method in SSLFilter class in grizzly-framework-2.2.19, I was also unable to determine where the overridden parent completed() method is even defined.
Can anyone lend some insight to whether this disconnect is due to my lack of understanding something here or if it is potentially a bug in the sample implementation provided by Grizzly? I believe clearing this up will go a long way to further my understanding here. Thank you in advance!
I successfully managed to send the message to queue name ReceiverQueue on my localhost Jboss server, how can I retrieve message I sent to it or how do I check if there is any messages in the queue if any retrieve them. or can I get an explanation of some sort what is the best way to do this. Thank you
A working send/receive tutorial would be accept as well. Anything that will get me to just send to the queue and receive message from that queue will get accepted answer.
I'm using Spring.
I want a solution that does it using application context with bean injection ..
Standard JMS API steps:
1. Create a javax.naming.Context with the access details of the server
context = new InitialContext(environment)
2. Look up javax.jms.QueueConnectionFactory in the context. Factory name is specific to the JMS server
factory = (QueueConnectionFactory)context.lookup(factoryName)
3. Create a javax.jms.QueueConnection
connection = factory.createQueueConnection(...)
4. Create a javax.jms.QueueSession
session = connection.createQueueSession(...)
5. Look up your javax.jms.Queue in the context
queue = (Queue) context.lookup(qJndiName)
Till now it is the same as sending....
6. Create a javax.jms.QueueReceiver with the session
receiver = session.createReceiver(queue)
7. JMS API provides 2 ways to retrieve a message:
7.a Wait for a message with one of the receiver.receive() methods
7.b Implement javax.jms.MessageListener in your class and register it as the listener
receiver.setMessageListener(this)
JMS API will call your onMessage() method whenever a new message arrives
8. Don't forget to start the listener:
connection.start()
9. Close the context (very important, when you access multiple JMS servers from the same program):
context.close()
The above is a typical solution from a stand-alone application. In EJB environment you should use message driven beans. You can find ino on them on http://java.sun.com/javaee/6/docs/tutorial/doc/gipko.html and a tutorial on http://schuchert.wikispaces.com/EJB3+Tutorial+5+-+Message+Driven+Beans
Here is the working example you've asked for:
import java.util.Hashtable;
import javax.naming.*;
import javax.jms.*;
public class JMSJNDISample implements MessageListener {
public static final String JNDI_URL = "jnp://localhost:1099";
public static final String JNDI_CONTEXT_FACTORY = "org.jnp.interfaces.NamingContextFactory";
public static final String JMS_USER = null;
public static final String JMS_PASSWORD = null;
public static final String JMS_CONNECTION_FACTORY = "MyConnectionFactory";
public static final String QUEUE_JNDI_NAME = "ReceiverQueue";
QueueConnection qConn = null;
QueueSession qSession = null;
QueueSender qSender = null;
QueueReceiver qReceiver = null;
public JMSJNDISample () {
}
public void init() throws JMSException, NamingException {
// Set up JNDI Context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_CONTEXT_FACTORY);
env.put(Context.PROVIDER_URL, JNDI_URL);
if (JMS_USER != null)
env.put(Context.SECURITY_PRINCIPAL, JMS_USER);
if (JMS_PASSWORD != null)
env.put(Context.SECURITY_CREDENTIALS, JMS_PASSWORD);
Context jndiContext = new InitialContext(env);
// Lookup queue connection factory
QueueConnectionFactory cFactory = (QueueConnectionFactory)jndiContext.lookup(JMS_CONNECTION_FACTORY);
// Create Connection
if (JMS_USER == null || JMS_PASSWORD == null)
qConn = cFactory.createQueueConnection();
else {
qConn = cFactory.createQueueConnection(JMS_USER, JMS_PASSWORD);
}
// Create Session
qSession = qConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
// Lookup Queue
Queue queue = (Queue) jndiContext.lookup(QUEUE_JNDI_NAME);
// Create Queue Sender
qSender = qSession.createSender(queue);
// Create Queue Receiver
qReceiver = qSession.createReceiver(queue);
qReceiver.setMessageListener(this);
// Start receiving messages
qConn.start();
// Close JNDI context
jndiContext.close();
}
public void sendMessage (String str) throws JMSException {
TextMessage msg = qSession.createTextMessage(str);
qSender.send(msg);
}
public void onMessage (Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage)message;
System.out.println("Text Message Received: "+textMessage.getText());
} else {
System.out.println(message.getJMSType()+" Message Received");
}
} catch (JMSException je) {
je.printStackTrace();
}
}
public void destroy() throws JMSException {
if (qSender != null) qSender.close();
if (qReceiver != null) qReceiver.close();
if (qSession != null) qSession.close();
if (qConn != null) qConn.close();
}
public static void main(String args[]) {
try {
JMSJNDISample sample = new JMSJNDISample();
// Initialize connetion
sample.init();
// Send Message
sample.sendMessage("Hello World");
// Wait 2 sec for answer
Thread.sleep(2000);
// Disconnect
sample.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Other than having a MessageDrivenBean listening to that queue?
EDIT:
You are using spring just to create the payload, right? JMS is a JavaEE spec. You don't need to use Spring for actually sending/receiving messages. You don't have to manually check whether there are messages in the queue etc., either. All you need to do is have an MDB(MessageDrivenBean) set up like this,
#MessageDriven(activationConfig = {
#ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(
propertyName = "destination", propertyValue = "queue/myqueue")
})
public class MyMessageDrivenBean implements MessageListener {
public void onMessage(Message message) {
ObjectMessage objMsg = (ObjectMessage) message;
Payload payload = (Payload)objMsg.getObject();
//do stuff
}
}
And then send some JMS messages.
#Stateless
public class QueuerBean implements QueuerLocal {
#Resource(mappedName = "java:/JmsXA")
private ConnectionFactory jmsConnectionFactory;
#Resource(mappedName = "queue/myqueue")
private Queue queue;
private void queue(MyPayload payload) {
try {
Connection connect = jmsConnectionFactory.createConnection();
Session session = connect.createSession(false,
Session.DUPS_OK_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
// create a JMS message and send it
ObjectMessage objMsg = session.createObjectMessage(payload);
producer.send(objMsg);
producer.close();
session.close();
connect.close();
} catch (JMSException e) {
log.error("Bad thing happened", e);
}
}
}
The queue is configured by the annotation. When a message is sent, JBoss will automatically trigger the MDB.
Here's an example showing how to set up a message-driven POJO in Spring. I'd recommend following this idiom if you're already using Spring.
As for the part about seeing how many messages are on the queue, I'd say you should be using the admin console for JBOSS, not your code.
I would recommend also using a tool like HermesJMS (http://www.hermesjms.com/confluence/display/HJMS/Home) to inspect the queue manager and queues. It's a great debugging tool.