: jdbc courier code (v3)

Pitfalls While Coding With the C++ v3 Proxy Types

There are some mistakes a lot of JDBC Courier developers make, particularly when they are new to the product. We have put together a list of the most common ones to help you when you are looking for a solution to a nagging problem related to C++ proxy types.

Please feel free to send us problems you ran into that you think deserve to be on this list.

Click on a pitfall to see its explanation.

I have a valid proxy instance but I keep getting a NullPointerException thrown from Java.

We'll take a guess at what your code looks like. It's either:

Foo *pFoo = new Foo();
if( pFoo != NULL ) {
    // this is where the exception gets thrown
    pFoo->doSomething();
}
        

or:

Foo foo = Foo();
// this is where the exception gets thrown
pFoo.doSomething();
        

In both cases you have a valid proxy object instance that references Java null. Just because you have a proxy object does not mean that it actually references a corresponding Java object. Much like a Java variable is initialized to null when you declare it without an explicit value, proxy objects are initially set to be "empty." Explicitly invoking the C++ default constructor only results in explicitly setting them to "empty."

Please see the coding tutorial lessons on Constructors and null for more details.

The no-argument constructor of my Java type is not working.

Please see the coding tutorial lessons on Constructors for a detailed explanation, but to cut a long story short, the Java no-argument constructor and the Java one-argument-of-declaring-type constructor need to be invoked with a special argument called _use_java_ctor.

The reason is that their natural proxy type signatures clash with two special C++ constructors, the so-called default constructor and the copy constructor.

To construct a java.lang.Object using its no-argument constructor from C++, you have to write:

java::lang::Object   obj( _use_java_ctor );

To construct a java.lang.String using its String(String) constructor from C++, you have to write:

java::lang::String   s1 = "test";
java::lang::String   s2( s1, _use_java_ctor );

If you forget the _use_java_ctor argument you will still have a valid String reference in s2 but it will reference the same instance that is referenced by s1 because you invoked the C++ copy constructor rather than the Java constructor that you intended to invoke.

I need to make sure my Java object is not NULL, but my code does not compile.

Taking a wild guess, you have something like:

Foo foo( 42 );
// this is where I get the compilation error
if( foo != NULL ) {
    ...
}
        

NULL or, in more modern C++, nullptr is what C++ developers compare their pointers to when they check them for validity. Your proxy objects are not pointers and they can never be NULL.

We provide you with a global variable called null that you should use when checking whether your proxies represent a valid Java object. So your code should look like this:

Foo foo( 42 );
// this now compiles
if( foo != null ) {
    ...
}
        

In Java I write com.myproduct.Foo.class. How do I do that in C++?

class is a reserved word in both Java and C++, but Java allows .class after a type name to refer to the Class instance representing the named type. C++ has no corresponding feature and the keyword class is only allowed in declarations and some template contexts.

To gain access to a Class instance for a given type name you need to use Class::forName(), for example:

Class fooClass = Class::forName("com.myproduct.Foo");
        

I know I'm getting back a Foo. I tried casting the returned Object to Foo but it does not compile.

You probably have some code like this:

// the factory method is declared to return its results as Objects
Object result = MyFactory::getInstance();
// but we know that it really is a Foo instance, yet this
// does not compile
Foo    foo = (Foo)result;
        

You know that your Java function is returning a Foo, even though it's declared as returning an Object. This does not cause problems because in Java all objects are passed by reference, so you can return a Foo from a method that only promises to return an Object.

The C++ proxy method's type is based on the Java method's declared type, so it returns an instance of C++ proxy type Object. The big difference to the Java side is that it actually returns an object of type Object by value, not by reference. The Java object that it references will be a Foo, but on the C++ side it is most definitely an Object, never anything else.

You cannot just cast an Object to a Foo when it is not a Foo and hope that things work out. Instead, you need to use the JDBC Courier method of giving you a Foo proxy object for an existing proxy object of a different type. That method is called dyna_cast().

When you have a proxy object of one type and you want to say:

"I know you think you represent just an Object, but I know better, so please present yourself as a proxy object for type Foo instead!"

Simply write:

// the factory method is declared to return its results as Objects
Object result = MyFactory::getInstance();
// but we know that it really is a Foo instance
Foo    foo = Foo::dyna_cast( result );
        

This is the JDBC Courier way of performing a class cast. You can read more about this in the coding tutorial lessons on Casting.

I'm getting garbage or a crash in printf().

You are probably using a function from the printf() family that has a format string and an ellipsis for the additional arguments. You cannot use a proxy String instance directly in a place where a "%s" string argument is expected. You also cannot use a primitive proxy element with its format code, e.g. "%d", and expect it to work without a cast.

Instead, you have to ask the String instance for a pointer to its characters or make sure that the field is cast to the proper primitive type. The following snippet illustrates:

java::lang::String  foo = "I am a Java string";
Bar  bar( _use_java_ctor );

printf( "--> %s <--\n", foo.to_chars() );
printf( "--> %d <--\n", (int)bar.intField );
        

My application just crashes when I try to run it.

There could be many reasons for that, starting with a misconfiguration of your Java environment. We assume that you have all the required pieces (runtime library and other shared libraries) in place or you would probably say that your application does not start.

The first candidate for debugging is always the configured JVM. Very often you will get a crash when the configured JVM cannot be found or when it is incompatible, for example your application is built in 64-bit mode and the JVM is 32-bit, or vice versa.

Then we would look at the runtime configuration. Are you setting JVM options that could cause problems, for example heap sizes that cannot be satisfied or paths with typos?

Finally, are you catching exceptions? We always recommend that you initialize your Java environment in your main() method and surround it with a try/catch block, like this:

try {
    // use verbose tracing if you're debugging a crash
    xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader( true, true, TraceAll, TraceVerbose );

    ... // configure your JVM

    xmog_jvm * pJvm = loader.load();
    if( pJvm == NULL ) {
        // handle load error that did not throw exception
        ...
    }
}
catch( xmog_exception & xe ) {
    // handle load error that threw framework exception
    const char * message = xe.get_message_chars();
    std::cerr << message << std::endl;
    xmog_java_string::free( message );
}
        

My application fails with an IncompatibleClassChangeError.

The most likely cause is that your proxy types were generated for a slightly incompatible version of the Java API in question. We ran into this issue the first time when a customer tried running a C++ application that was using JMS v2 proxy types with a JBoss MQ implementation that was still using JMS v1.1. At runtime all calls worked, except for the Connection.close() call at the very end.

We finally figured out that in JMS v1.1 the close() method is declared by the Connection type and not inherited from any super interfaces. In JMS v2 on the other hand, the close() method is inherited from the java.lang.AutoClosable interface.

Our C++ proxy types that were generated against the JMS v2 API mirrored this new inheritance. When running with a JMS 1.1 implementation, they attempted to resolve the connect() method against the AutoClosable type and the result was an IncompatibleClassChangeError because while Java found the close() method it did not find the inheritance.

Within pure Java this API change was non-breaking for callers. When using JNI, the declaring type of the method is used when resolving the method and the change is breaking.

This JMS API version issue is just one concrete example of why your proxy types should always be generated against the version of your Java API that you intend to run with. The only Java changes you are safe to ignore are implementation changes. Changes to inheritance or the public APIs should always cause you to regenerate your C++ bindings.