This chapter explains how to use hints to control java2cs code generation for valuetypes in VisiBroker for .NET.
In order to fully understand the use of hints and how they affect java2cs code generation, the following example shows a simple Java type called User.
We are generating a C# class corresponding to the Java class. The methods in Java classes are irrelevant because porting the methods would involve essentially reverse engineering the Java class, and so the porting of methods is not supported. If you would like to have the same or similar methods in your C# class corresponding to the Java version of your valuetype, you will have to implement the C# equivalent yourself. Later sections in this document will explain how that is done.
The C# type User represents the Java class
User. As is apparent, this is incorrect in a few ways.
Now let us look at the generated ValueFactory class for User. This class is responsible for creating and initializing an instance of the C# type
User when it reads an instance of the Java class
User from the network. it is also responsible for writing the correct data to the network when you pass an instance of the C# class
User to a remote server. It is important to note that the ValueFactory is associated with the corresponding Java type. That is, each distinct Java type will have a distinct factory. This allows more than one Java type to map to a given C# type.
Note that UserValueData is a class that contains as public data members every instance member of the
User class as shown in the following example.
Based on the above table, you see that the ValueData class represents the data that is marshaled on the wire, irrespective of how the data is stored or maintained in the C# type.
Notice that the ValueFactory created the object in one step (CreateObject) and read the data in another step (
InitObject). There is a good reason for this. When unmarshaling or marshaling a type that is inherited from other stateful types, each type's factory is normally responsible for marshaling and unmarshaling only the state at its level in the hierarchy. To achieve this, the infrastructure will first instantiate an instance of the type that is being unmarshaled, but will pass it to the factory corresponding to each type in the hierarchy, starting from the base, to unmarshal the relevant state and work its way up the hierarchy. When writing, the same process is repeated, this time using the
InitData methods.
The hints file is an XML file that provides hints to the java2cs compiler allowing the user to customize the code that is generated.
To run the java2cs compiler with the above hints file, enter the following at the command line:
You can now write your implementation of the User class as desired and compile it with the generated code.
Using the above hint, the compiler no longer generates the User class or the
UserData class. However, all of the other classes are generated with the assumption that you will implement the
UserData class.
You could now write a UserData class (as shown in the following example) and use it with the generated code.
The only difference between this hint file and the previous one is that the mode is set to custom. The generated code changes very little. In fact the only difference is in the
InitObject and
InitData methods. They are generated as follows:
This ValueFactory will automatically be registered as the ValueFactory for the User Java class as long as one of the Helper classes in the generated code is used. To explicitly register a ValueFactory you can either call
ORB.RegisterValueFactory(), or you can call
ORB.RegisterAssembly() which will register all of the factories in the provided assembly.
This warning indicates that the interface (which is not a remote interface) has methods that are ignored by the java2cs compiler. The compiler ignores these methods as it is not possible for the compiler to map methods that are not designed to be invoked remotely. This is due to the fact that the parameters that such methods take may be valid only in the local contexts. If you look at the generated code, the compiler will generate the following code for
Principal:
The compiler ignored the generating the code for the getUserName method. The compiler warnings suggest that this is most likely not what is expected, and therefore you must use a hint to map this to an appropriate .NET interface.
This maps the interface Principle to the C# interface
IPrinciple (which the compiler will not generate). Let us say we also add the
IAuthenticatable to our .NET code as follows (note that you could use an existing interface, such as
System.Security.Principals.IPrincipal):
Now it is obvious why this warning is generated. The User class that is generated cannot possibly know that the
IPrincipal has a method called
GetName that needs to be implemented. And even if it did, it could not possibly know how the method was implemented.
In the above case the User type implemented an interface. There are many cases where we develop classes that implement interfaces but our classes are private implementations that are never exposed to the user. For example, consider an Iterator of any collection. While the
Iterator interface is public, all implementations of it are typically hidden and are never exposed to the user.
For example, if User were one such type, you do not want your ValueFactories actually exposing the type in its signatures because ValueFactories are public classes. To avoid this you can use the signature type in the hint to control what is exposed by the ValueFactory.
The only changes from the previously generated code are the GetValueType and
CreateObject methods which are also abstract now.
The key here is that cs-sig-type element is used in the hint rather than
cs-impl-type. This instructs the compiler to exclude all references to the implementation class.
still results in the InitObject and
InitData methods being generated as shown below:
Consider the earlier example of the UserData class with one slight modification. In the following example we removed the
init method and the default void constructor:
However, our ValueFactory creates the object in the CreateObject method and initializes it in another step (
InitObject). This obviously will not work for us. To support this case, we provide the
immutable mode in the hint.
Also, the CreateObject call is no longer generated (abstract or otherwise).
Notice here how the InitObject returns an
IPrincipal rather than receiving one as argument. This allows you to write a ValueFactory that creates a
UserData with the value data that has already been unmarshaled and return it.
Be aware that with the immutable mode you are responsible for using all the state in the data object (which will include all the data for all of the base classes as well) to initialize your immutable object as appropriate.
This is a custom marshaled Java Serializable class. The default code generation for this class (with no hints) shows some changes. The value class is no longer generated. This is because the compiler knows that your class is custom marshaled, so it cannot possibly generate the appropriate fields in your class. However, it does know to generate the ValueData class, as that represents the fields (the non-transient fields) that would be marshaled if the class used default marshaling. As show in the code sample above, the class also marshals some additional data.
Notice that the GetValueType and
CreateObject methods are now abstract. The compiler requires you to provide the implementation for these as it does not know the name of your C# class. Second, note that you no longer have the
InitObject and
InitData methods. Instead, you have two new methods:
ReadObject and
WriteObject. You will have to implement these methods to provide the appropriate custom marshaling logic. As you can see, the ValueData object and the value class are still passed to the method, but in addition a Stream is also passed. This allows the custom marshaling logic to be written. And finally some additional methods (
DefaultReadValueData and
WriteValueData) are generated to allow the user to read or write default marshaled data.
The ORB.Init is setting up all the default ORB behavior, including doing the ValueFactory registration shown above. This default has the
HashMap ValueFactory taking precedence over the
Hashtable ValueFactory. But then after initializing the ORB, we explicitly register the
Hashtable ValueFactory, which will cause this to take precedence over all the previous ValueFactory registrations.