#include <iostream>
#include <string>
#include <vector>
#include "shared.h"
#include "xmog_java_client.h"
#include "xmog_util_file.h"
// parse the commandline arguments and configure the JVM options
//
// You can edit this function to hard-code your JVM configuration or use any
// other configuration methods we support through the configuration API. This
// is just an example of how to do it.
//
bool init_java_runtime( int argc, char * argv[] ) {
xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader( true, true, TraceAll, TraceWarnings );
// calculate the source directory for relative file paths;
// in a product app this would be derived either from an environment
// variable or an OS API call to determine the location of the executable.
// For this example we assume that jar files are in a lib directory in the
// source code folder.
const char * source = __FILE__ ;
const char * last_sep = strrchr( source, XMOG_FILE_SEPARATOR );
std::string dir( source, last_sep );
std::string lib( dir + XMOG_FILE_SEPARATOR_STR + "lib" );
// this allows us to pick up the jndi.properties file that contains JMS configuration settings
loader.appendToClassPath( dir.c_str() );
std::vector<xmog::util::file> jars;
if( xmog::util::file( lib ).list( jars, "*.jar" ) > 0 ) {
for( std::vector<xmog::util::file>::const_iterator jar = jars.cbegin(); jar != jars.cend(); jar++ ) {
loader.appendToClassPath( jar->name() );
}
}
// this allows us to pick up any jar files in the example's lib directory
// that gives you an easy way to configure the classpath that works with your
// concrete JMS provider: simply copy its client jar file into the lib directory.
// In a real-life production application you would probably configure the
// classpath relative to your executable. Again, this is just example code.
loader.appendToClassPath( (dir + lib).c_str() );
loader.addReads( "java.base", "ALL-UNNAMED");
// parse the optional commandline arguments. You would probably want to do
// some validation in a production app
for( int i=1; i<argc; i++) {
char * arg = argv[ i ];
if( arg[ 0 ] == '-' || arg[ 0 ] == '/' ) {
if( !strcmp(arg+1, "cp") || !strcmp(arg+1, "classpath")) {
loader.appendToClassPath( argv[ ++i ] );
}
if( !strcmp(arg+1, "mp") || !strcmp(arg+1, "modulepath")) {
loader.appendToModulePath( argv[ ++i ] );
}
else if( !strcmp(arg+1, "jvm")) {
loader.setJvmPath( argv[ ++i ] );
}
else if( !strcmp(arg+1, "t") || !strcmp(arg+1, "trace")) {
loader.setTracing( argv[ ++i ] );
}
}
}
return true;
}
bool load_java_runtime() {
xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader();
try {
xmog_jvm * pJvm = loader.load();
if( pJvm ) {
loader.trace( TraceJvm, TraceInfo, "Successfully loaded JVM." );
return true;
}
else
loader.trace( TraceJvm, TraceError, "*** Failed to load JVM!" );
}
catch( xmog_exception xe ) {
loader.trace( TraceJvm, TraceError, "*** Failed to load JVM!" );
}
const char * jvm = loader.getJvmPath();
if( jvm == NULL || *jvm == 0 ) {
loader.trace( TraceJvm, TraceError, "*** The default JVM did not work. Please configure a JVM, for example via the -jvm option." );
}
else {
loader.trace( TraceJvm, TraceError, "*** The configured JVM '%s' did not work. Please configure a different JVM.", jvm );
}
return false;
}
Subscribe to a Topic from C++
This is one of the examples that we use to demonstrate the ease with which you can integrate JMS into a C++ application, or, said differently, integrate a C++ application into a JMS message bus.
The two most interesting source files are shown below.
The sub.cpp
file contains the meat of the blocking message subscriber, but even in this file the bulk of the code
deals with application set up and tear down. The code that subscribes to a topic consists of a mere two lines
in the middle.
The shared.cpp
file contains application
initialization logic that would be common to all of your application. Its purpose is to configure and load
a Java Virtual Machine for use by your C++ application.
We hope that this gives you an idea of how easy it can be for your C++ developers to participate in a Java Message Service-based, distributed application.
#include "java_lang_pkg.h"
#include "java_util_pkg.h"
#include "javax_jms_pkg.h"
#include "javax_naming_pkg.h"
#include "shared.h"
#include <iostream>
extern "C" int main( int argc, char * argv[] ) {
std::cerr << "Running JMS synchronous subscriber example" << std::endl;
// parse the commandline arguments to take optional configuration settings into account
if( !init_java_runtime(argc, argv)) {
return 1;
}
// try to load the Java runtime into the process
if( !load_java_runtime()) {
return 1;
}
// we execute our entire Java code in a try block and catch any exceptions
// that might be thrown at this level. In a more sophisticated application
// you might have local try/catch clauses in other places.
try {
// this relies on the jndi.properties file in the example directory
// being configured properly. We have to use _use_java_ctor to invoke the
// Java constructor because the C++ default constructor just initializes
// proxy objects to null (as it should). See the CODE tutorial for more
// info on this.
InitialContext ictx( _use_java_ctor );
// create a connection factory. We have to use TopicConnectionFactory::dyna_cast()
// because the lookup() method returns an Object instance by value (not by reference
// like in Java) and this is the way to "convert" the Object to the proper proxy type.
TopicConnectionFactory tcf = TopicConnectionFactory::dyna_cast( ictx.lookup( "topic/connectionFactory" ) );
// no dyna_cast() necessary here because the return type works for us
TopicConnection tc = tcf.createTopicConnection();
// create a session. Note how we access the AUTO_ACKNOWLEDGE flag with ::
TopicSession ts = tc.createTopicSession( false, Session::AUTO_ACKNOWLEDGE );
// lookup the topic we want to publish to
Topic topic = Topic::dyna_cast( ictx.lookup( "topic/topic0" ) );
// create a subscriber...
TopicSubscriber tsub = ts.createSubscriber( topic );
// ... and start the connection
tc.start();
// ------------------------------------------------------------
// up to this point we have boilerplate setup code that you
// usually do once in your application. The next steps are
// done per message.
// You would probably do this in a separate thread that blocks
// while it's waiting for messages and that terminates when it
// receives a "kill" message or when it is stopped during a
// timeout.
// ------------------------------------------------------------
// receive a text message
TextMessage message = TextMessage::dyna_cast( tsub.receive() );
// print what we got
std::cerr << "Message received: " << message.getText().to_chars() << std::endl;
// ------------------------------------------------------------
// from this point on we're doing boilerplate teardown that you
// usually only do once in your application.
// ------------------------------------------------------------
// close the topic connection
tc.close();
}
catch( Throwable t ) {
std::cerr << "*** Caught Java exception through Throwable: " << t.toString().to_chars() << std::endl;
}
catch( xmog_exception xe ) {
char * message = xe.get_message_chars();
std::cerr << "*** Caught framework exception: " << message << std::endl;
xmog_java_string::free( message );
}
std::cerr << "Done with JMS subscriber example!" << std::endl;
return 0;
}