/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Exoffice Technologies. For written permission,
* please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Exoffice Technologies. Exolab is a registered
* trademark of Exoffice Technologies.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved.
*
* $Id: SimpleConsumer.java,v 1.40 2002/03/12 11:57:31 jima Exp $
*
* Date Author Changes
* 04/25/2000 jima Created
*/
package openjms.examples.client.console;
import java.io.PrintStream;
import java.util.Hashtable;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.jms.DeliveryMode;
import javax.jms.Session;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.jms.MessageListener;
import javax.jms.Message;
import javax.jms.Topic;
import javax.jms.TextMessage;
import javax.jms.TopicConnectionFactory;
import javax.jms.JMSException;
import javax.jms.ExceptionListener;
import org.exolab.jms.util.CommandLine;
import org.exolab.core.logger.LoggerFactory;
import org.exolab.core.logger.LoggerIfc;
import org.exolab.core.logger.LogEventType;
import org.exolab.jms.client.JmsTopicConnectionFactory;
import org.exolab.jms.jndi.JndiConstants;
import org.exolab.jms.jndi.rmi.RmiJndiInitialContextFactory;
/**
* The simple consumer is used to subscribe to a particular topic and receive
* events. It will set a message listener so that events can be pushed down
* to it when they become available.
*/
public class SimpleConsumer
implements MessageListener, TimerListener, ExceptionListener {
public SimpleConsumer(String name, TopicConnection connection,
TopicSubscriber subscriber, int ackMode, int seconds, boolean summary) {
this(name, connection, subscriber, ackMode, false, 0, seconds, summary);
}
public SimpleConsumer(String name, TopicConnection connection,
TopicSubscriber subscriber, int ackMode, int count, int seconds, boolean summary) {
this(name, connection, subscriber, ackMode, true, count, seconds, summary);
}
private SimpleConsumer(String name, TopicConnection connection,
TopicSubscriber subscriber, int ackMode,
boolean counting, int count, int seconds, boolean summary) {
name_ = name;
connection_ = connection;
subscriber_ = subscriber;
ackMode_ = ackMode;
counting_ = counting;
count_ = count;
seconds_ = seconds;
summary_ = summary;
timer_ = new Timer(this, seconds_ * 1000);
timer_.start();
received_ = 0;
}
static public void main(String[] args) {
try {
CommandLine cmdline = new CommandLine(args);
if (cmdline.exists("help")) {
// the help option has been specified, print the usage
// information
usage();
} else if (cmdline.exists("topic")) {
// see if an ack mode has been specified. If it hasn't
// then assume CLIENT_ACKNOWLEDGE mode.
int ackMode = Session.CLIENT_ACKNOWLEDGE;
if (cmdline.exists("ackmode")) {
String amode = cmdline.value("ackmode");
if (amode.equals("auto")) {
ackMode = Session.AUTO_ACKNOWLEDGE;
} else if (amode.equals("dups")) {
ackMode = Session.DUPS_OK_ACKNOWLEDGE;
} else if (!amode.equals("client")) {
// ignore all ack modes, to test no acking
ackMode = -1;
}
}
// enable debugging - undocumented option
if (cmdline.exists("debug")) {
LoggerIfc logger = LoggerFactory.getLogger();
logger.setLogLevel(LogEventType.Debug);
}
String topic_name = cmdline.value("topic");
if (topic_name != null) {
// connect to the JNDI server and get a reference to
// root context
Hashtable props = new Hashtable();
String host = "localhost";
String port = null;
String jndiname = "JndiServer";
String mode = "rmi";
if (cmdline.exists("mode")) {
mode = cmdline.value("mode");
}
String modeType =
RmiJndiInitialContextFactory.class.getName();
if (cmdline.exists("jndiport")) {
port = cmdline.value("jndiport");
}
if (cmdline.exists("jndihost")) {
host = cmdline.value("jndihost");
}
// override the default server name if specified on the
// command line.
if (cmdline.exists("jndiname")) {
jndiname = cmdline.value("jndiname");
}
if (mode.equals("ipc") || mode.equals("tcp")) {
if (port == null) {
port = "3035";
}
props.put(Context.PROVIDER_URL,
"tcp://" + host + ":" + port + "/");
modeType =
"org.exolab.jms.jndi.mipc.IpcJndiInitialContextFactory";
} else if (mode.equals("http")) {
System.out.println("Using HTTP");
/* CONNECTING BY WAY OF PROXY
Client needs to set these system properties if
it requires to go through a proxy for HTTP.
System.setProperty("http.proxyHost", host);
System.setProperty("http.proxyPort", port);
*/
/* CONNECTING FOR SSL TRANSACTIONS.
Client needs to set these system properties
if it requires to go through a proxy for
HTTPS.
System.setProperty("https.proxyHost", "host");
System.setProperty("https.proxyPort", "port");
Client needs to pass in "-mode http -secure"
to use https,
Client needs -Djavax.net.ssl.trustStore=cacerts
passed in. JSSE is required to make this work.
*/
String type = "http://";
if (cmdline.exists("secure")) {
if (port == null) {
port = "8443";
}
type = "https://";
modeType =
"org.exolab.jms.jndi.http.SslHttpJndiInitialContextFactory";
} else {
if (port == null) {
port = "8080";
}
modeType =
"org.exolab.jms.jndi.http.HttpJndiInitialContextFactory";
}
props.put(Context.PROVIDER_URL,
type + host + ":" + port + "/" +
"openjms/servlet/OpenJMSJndi");
String url = cmdline.value("url");
if (url == null) {
if (cmdline.exists("secure")) {
url = type + "localhost:8443";
} else {
url = type + "localhost:8080";
}
}
// Client URL to allow server to send messages
// to registered consumers/receivers
System.getProperties().setProperty
(JndiConstants.HTTP_CLIENT_URL_PROPERTY, url);
} else {
if (port == null) {
port = "1099";
}
props.put(Context.PROVIDER_URL,
"rmi://" + host + ":" + port + "/" + jndiname);
}
System.err.println("Using provider url " + props.get(Context.PROVIDER_URL));
props.put(Context.INITIAL_CONTEXT_FACTORY, modeType);
Context context = new InitialContext(props);
// lookup the connection factory from the context
TopicConnectionFactory factory = (TopicConnectionFactory)
context.lookup("JmsTopicConnectionFactory");
// if we can't find the factory then throw an exception
if (factory == null) {
throw new RuntimeException(
"Failed to locate connection factory");
}
LoggerFactory.getLogger().logDebug("Have the connection factory " + factory);
TopicConnection connection =
factory.createTopicConnection();
TopicSession session =
connection.createTopicSession(false, ackMode);
Topic topic = null;
if (cmdline.exists("persistent")) {
topic = (Topic)context.lookup(topic_name);
} else {
topic = session.createTopic(topic_name);
}
if (topic == null) {
System.err.println("Failed to get administered object"
+ " exiting.....");
System.exit(-1);
}
String selector = null;
if (cmdline.exists("selector")) {
selector = "JMSPriority=1";
}
// determine if the summary flag has been specified. By
// default the message details are printed out. If the
// summary flag is specified then the count of the messages
// received is displayed.
boolean summary = false;
if (cmdline.exists("summary")) {
summary = true;
}
// if the 'name' option has been specified then assume a
// durable subscriber otherwise transient
TopicSubscriber subscriber = null;
String name = cmdline.value("name");
if (name != null) {
subscriber = session.createDurableSubscriber
(topic, name, selector, false);
} else {
subscriber = session.createSubscriber(topic, selector, false);
}
int secs = 60;
boolean counted = false;
int count = 1;
if (cmdline.exists("count")) {
counted = true;
try {
String value = cmdline.value("count");
count = Integer.parseInt(value);
} catch (Exception exception) {
System.err.println("Illegal count value");
System.exit(-1);
}
}
// the timeout is used to terminate the consumer if not
// messags are received within the specified timeframe.
// The default value is 5 minutes.
secs = 5 * 60;
if (cmdline.exists("timeout")) {
secs = Integer.parseInt(cmdline.value("timeout"));
}
SimpleConsumer consumer = (counted)
? new SimpleConsumer(name, connection, subscriber, ackMode, count, secs,summary)
: new SimpleConsumer(name, connection, subscriber, ackMode, secs, summary);
subscriber.setMessageListener(consumer);
connection.setExceptionListener(consumer);
// start the connection unless the noStart option is specified
if (!cmdline.exists("noStart")) {
connection.start();
}
} else {
System.err.println("Cannot subscribe to messages under "
+ "null topic");
}
} else {
// anything else print the usage message
usage();
}
} catch (Exception exception) {
exception.printStackTrace();
System.err.println("Fatal error: " + exception + "\nExiting.....");
System.exit(-1);
}
}
/**
* All messages that for this consumer are pumped down to this method
*
* @param message message to retrieve
*/
public void onMessage(Message message) {
String mode = "unknown";
String id = "unset";
int priority = -1;
if (!summary_) {
System.err.print(message + ", ");
}
try {
if (message.getJMSDeliveryMode() == DeliveryMode.NON_PERSISTENT) {
mode = "NON_PERSISTENT";
} else if (message.getJMSDeliveryMode() == DeliveryMode.PERSISTENT) {
mode = "PERSISTENT";
}
} catch (JMSException ignore) {
}
try {
id = message.getJMSMessageID();
priority = message.getJMSPriority();
} catch (JMSException ignore) {
// ignore the exception.
}
// only print the details if the summary flag is not specified
if (!summary_) {
System.err.println("JMSDeliveryMode=" + mode + ", Priority=" +
priority + ", JMSMessageID=" + id);
}
// timer_.reset();
try {
if (ackMode_ == Session.CLIENT_ACKNOWLEDGE) {
message.acknowledge();
}
} catch (JMSException exception) {
System.err.println("Failed in ack message " + exception);
}
if (counting_ && ++received_ >= count_) {
try {
// can't do the close in the same thread as the callback.
// therefore reset the message listener and set the timer
// to do the exist and clean up in another thread. Doing
// a close in the same thread can cause a lockup
subscriber_.setMessageListener(null);
// display a summary of the messages received
if (summary_) {
System.err.println("The consumer " + name_ + " received " +
received_ + " messages from topic " + subscriber_.getTopic());
}
// kill the timer
timer_.stop();
exit("");
} catch (JMSException exception) {
System.err.println("Could not unset the listener " + exception);
}
}
}
public void onTimeout() {
exit("No message received in the last " + seconds_ +
" seconds, exiting");
}
// implementation of ExceptionListener.onException
public void onException(JMSException exception) {
exit("Received onException notification");
}
private void exit(String message) {
if (!summary_) {
System.err.println(message);
}
try {
connection_.close();
timer_.stop();
} catch (Exception error) {
System.exit(0);
error.printStackTrace();
}
}
/**
* Print out information on running this sevice
*/
static protected void usage() {
PrintStream out = System.out;
out.println("usage: java " + SimpleConsumer.class.getName() +
" [options]\n");
out.println("options:");
out.println(" -topic <name> topic to subscribe to.\n");
out.println(" -name <consumer> durable consumer name.\n");
out.println(" -mode <ipc | rmi | http> " +
"connect using ipc, http or rmi mode. " +
"Defaults to 'rmi'.\n");
out.println(" -url <url> only used for http mode, the client passes\n"
+ " this url to the server, to allow the server\n"
+ " to invoke the clients servlet at the\n"
+ " specfified url. Defaults to "
+ "'http://localhost:8080'.\n");
out.println(" -secure only used for http mode. Connect using https.\n"
+ " requires JSSE jars, https enabled Web server etc.\n");
out.println(" -ackmode <auto | client | dups>\n" +
" message acknowledgement mode. " +
"Defaults to 'client'.\n");
out.println(" -persistent " +
"specifies persistent delivery mode.\n");
out.println(" -jndiport <num> port where the jndi server runs.\n");
out.println(" -jndihost <host> host where jndi server runs.\n");
out.println(" -jndiname <name> name of the jndi server\n");
out.println(" -count <num> number of messages to wait for.\n");
out.println(" -timeout <seconds> seconds to wait before exiting.\n");
out.println(" -help displays this screen.\n");
}
private String name_;
private TopicSubscriber subscriber_;
private TopicConnection connection_;
private int ackMode_;
private Timer timer_;
private int seconds_;
private boolean counting_;
private int count_;
private int received_;
private boolean summary_;
}