: jms courier use cases

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;
}
#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;
}