How to write a Target in C/C++/C#

Table of Contents

  1. Introduction
  2. Implementing the Native Methods
  3. Target Initialization
  4. Binding to a TUN

1. Introduction

This tutorial was written with the assumption that the reader is already familiar with the basics of writing a target. If this is not true, please read How to Write a Target in Java first. This tutorial is an introduction to the TargetFacadeForJni, which takes care of implementing the required interfaces, and exposes the necessary functions to a C/C++/C# target using the Java Native Interface.

The example target used in this tutorial is an elevator written in C. The elevator contains commands similar to the buttons found on the outside and inside of an elevator (up/down call buttons on the outside and floor select buttons on the inside). It operates by keeping track of each user (identifiable by their session id) along with each user's current location and desired location. While there are commands in progress, the elevator moves up and down, picking up and dropping off users at their desired floor.

2. Implementing the Native Methods

Included in the edu.wisc.trace.urcsdk.target.facade package is a header file containing C method declarations for the native methods referenced in the TargetFacadeForJni. These must be implemented by your target. Also included in the package is template called TargetTemplate.c that contains stubs for these methods, along with helper methods for making callbacks to the facade.

The C methods will have headers similar to:

JNIEXPORT void JNICALL
Java_edu_wisc_trace_urcsdk_target_facade_TargetFacadeForJni_nativeSessionOpened(JNIEnv *env, jobject obj, jstring _sessionId);

Each method will at least contain the arguments JNIEnv *env and jobject obj. An understanding of these is not necessary, but can be found in any JNI tutorial. All of the additional arguments will be of the type jstring. Also, the return type may be of type jstring. The jstring type is a JNI type used to represent Java strings. In order to implement these methods, you will need to convert between char arrays and jstrings.

To convert a jstring argument to a char array, you will need to use the JNI method GetStringUTFChars. You also should check the return value from this method, because memory allocation for the chars may fail.

const char * ptr;
ptr = (*env)->GetStringUTFChars(env, _sessionId, NULL);
if(ptr == NULL) {
    // If can't read sessionId, behavior undefined, so quit
    printf("Couldn't allocate memory for chars\n");
    fflush(stdout);
    exit(EXIT_FAILURE);
}

Also, to free the memory allocated by this method, call ReleaseStringUTFChars.

(*env)->ReleaseStringUTFChars(env, _sessionId, ptr);

To create a new jstring (for returning from a method), use the JNI method NewStringUTF.

jstring val = (*env)->NewStringUTF(env, "InProgress");

3. Target Initialization

In order for the TargetFacadeForJni to parse the target and socket descriptions, you must make a callback to the initialize(String codeBase, String tdLocation) method, passing it the code and target description locations. To do this, use the following JNI calls.

In the elevator target:

jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "initialize", "(Ljava/lang/String;Ljava/lang/String;)V");
if(mid==NULL) {
 printf("MethodID initialize not found");
 return;
}
jstring codelocation = (*env)->NewStringUTF(env, bufCodeLocation);
jstring tdlocation = (*env)->NewStringUTF(env, "docs/elevator.td.xml");
(*env)->CallVoidMethod(env, obj, mid, codelocation, tdlocation);

4. Binding to a TUN

The callback methods addTunParameter(String name, String value) and bindToClientTun(String tunClassName) are provided in the TargetFacadeForJni. To bind to a TUN, use addTunParameter(String name, String value) to add each necessary TUN parameter to the TUN parameter map, and when all parameters are added call bindToClientTun(String tunClassName).

The elevator binds to the UPnP 2-Service TUN:

// Add necessary parameters to the Tun map and bind to the Tun
jstring name = (*env)->NewStringUTF(env, "UDN");
jstring value = (*env)->NewStringUTF(env, "www.myurc.com_UrcSimEnv_elevator_socket"); // concat date later
callAddTunParameter(env, obj, name, value);

name = (*env)->NewStringUTF(env, "friendlyName");
value = (*env)->NewStringUTF(env, "Elevator");
callAddTunParameter(env, obj, name, value);

name = (*env)->NewStringUTF(env, "presentationURL");
value = (*env)->NewStringUTF(env, "file:/C:/Projects/UrcSimEnv/dev/src/edu/wisc/trace/targetsamples/elevator/docs/elevator.td.xml");
callAddTunParameter(env, obj, name, value);

name = (*env)->NewStringUTF(env, "targetName");
value = (*env)->NewStringUTF(env, "http://resources.myurc.org/UrcSimEnv/elevator");
callAddTunParameter(env, obj, name, value);

callBindToTargetTun(env, obj, (*env)->NewStringUTF(env, "edu.wisc.trace.urcsdk.target.upnp2s.Upnp2sTargetTun"));

More on the TUN parameter map can be found in How to Write a Target in Java.

Back to the URC SDK 1.0 Tutorial


Last updated: Jamie Tabaka, 2006-10-18