EJB Clients and support for RMI/IIOP

Are CORBA clients supported?

Yes, very much. In addition to the use of RMI/IIOP since JNDI is implemented on CosNaming, and JTS is running on OTS, this is actually very straight-forward.

What about Visibroker 3.x clients ?

You will find writing CORBA 2.2 clients problematic. Although it is correct that there is a CORBA client in the bank example, this client will only run on top of VisiBroker4. It is difficult to support VisiBroker3 clients as they do not have OBV support necessary for handling many common Java data structures. The CorbaBankClient uses the CORBA naming service APIs to resolve the EJB home, and then uses CORBA style narrows to obtain a CORBA objref. However, you still have to use RMI style method signatures to access the home and remote interfaces.

How can I develop CORBA clients with ORBs that do not support OBV or in languages that do not have OBV mapped?

What's really lacking (or impossible, depending on how you prefer to see) in the EJB to CORBA mapping (rather Java 2 IDL reverse mapping) is a decent alternative to support pre-CORBA 2.3 clients in a uniform way across vendors (things like Java String maps to IDL 'string', serializable objects map to IDL 'struct' types where possible, etc.). Of course it is not trivial to come up with a Java to IDL mapping that doesn't require OBV

Now Java (including EJB programmers) tend to fully exploit the Java features. So expect lots of Java objects, collection classes etc in remote interfaces. For all these cases it's going to be hairy anyway. If you want to design for cross-language, you may start with IDL, generate Java from it and use the Java signatures in the remote interfaces.

One alternative is to implement a Corba to EJB adaptor. That is a middle layer that takes Corba calls, and delegates to respective EJB beans. Here is a customer story …

Our Corba IDL interface is a meta-data driven interface. That is, we use tags/value pairs to indicate the session bean to be called, the method on the session bean, and arguments. Really cool stuff, as we no longer have to change IDL when there is a change in a session bean interface, or even Corba client interface. The interface is data driven, just like XML. If you are coming in from a Corba client, then define a simple IDL with tag/value structures for commands and responses. And establish a contract between client and server on what is expected on those tags (maybe something like an XML dtd). Develop a parser on the server side to honor the contract, and have the parser delegate calls to respective beans. Some of your tags could be a hint to what bean and method is going to be called. We have made some of our tags to be the same as the fully qualified session bean class name so that we can use introspection in the corba-ejb adaptor.

What should I do if I have to support C++ clients?

If you are going to have Clients on VBC++ and they need to access EJBs, it is strongly suggested that you keep that requirement in mind at the time of designing your EJB components. Typically, one designs the server interfaces with the client type in mind. For example, an EJB which is to be access via a Servlet will probably be very different from an EJB which is to be access from another EJB, particularly if that other EJB is going to be colocated.

Thus, EJBs which are to be used by C++ clients will want to expose interfaces which are easily mapped to C++. This means avoiding "complex" data types (Hashtables, Vectors, etc) and sticking with the simpler, IDL/C++ friendly data types (primitives, strings, arrays, object references).

If one has a predefined EJB which is to C++-friendly, then one can simply write a thin layer which exposes a friendly interface, which calls in to the Java-heavy API. This is very similar to the standard technique of using a session bean to provide a remote-access friendly interface to entity beans, instead of accessing the entity bean directly from a remote client.

Note that with OBV it should definitely be possible to have java.util.Hashtable in C++. This is not a concern. The issue is that datatypes guaranteed to be available on the java platform (such as java.util.Hashtable) are not supplied (ie. automatically available for use by our users) with C++. The user would have to implement their C++ equivalent for java.util.Hashtable. It is just not available out of the box.

Does the CORBA mapping defined by the EJB1.1 Spec addendum differ from what the Inprise Container offers?

No. Both are based on standards from the OMG and the basic theme is the same – Java to IDL reverse mapping ensures interoperable distribution support. CosNaming and OTS ensures naming and transaction interoperability.

Is RMI server objects supported?

Our RMI/IIOP implementation does not support RMI servers. We are (at least for the time being) only interested in supporting RMI as a client-side interface to either EJB servers, or CORBA servers. The main problem is that RMI server-side objects require distributed garbage collection, which does not work in large-scale systems.We do not currently have plans to support "normal" RMI server-side objects. By "normal" RMI servers is meant classes extending UnicastRemoteObject/PortableRemoteObject, Note, however, that you can use RMI signatures in your server-side objects. That is, you can implement objects with the same method signatures as the RMI clients see. However, you will have to implement the object the CORBA way, which means using either the POA or the BOA, and explicitly controlling the lifetime of you objects (as opposed to using DGC).

What is RMI dynamic class downloading?

In RMI the server code base property allows classes to be downloaded from a remote site without the client having to have the actual implementation residing in its jar file. With this feature new implementations of dependent classes can be installed in the server and when streamed to the client have different behavior than previously.

Is dynamic class downloading supported?

There is some disagreement over whether or not an RMI-IIOP implementation is required to support class downloading as in "normal" RMI/JRMP. The actual section is 28.4.9 of OMG document ptc/99-03-09 (available from http://java.sun.com/products/rmi-iiop/). Some think the spec is clear that class downloading is required to be supported. Others disagree and think that the spec is deliberately worded to allow it to be optional. Inprise is hesitant to supporting it because if used carelessly it could create some real security problems. On the other hand it is possibly an important feature for long-term extensibility and maintenance of distributed systems.

So, this feature is not supported in VBJ 4.0 and hence not in IAS. We may be providing such support in a subsequent release.

Why do I need to have the implementation classes on the client side?

Scenario:

A remote interface has a business method like:
    public Employee getEmployee(long employeeID) throws RemoteException;
Employee is an interface. In the bean, I populate and return an object of class EmployeeState. EmployeeState implements Employee.

I separated the classes that I thought I would need on the client side, and I did not include the EmployeeState class. I thought that having the Employee interface on the client would be sufficient, as it is in the RMI world. I believe the class file of the return parameter would be marshaled to the client upon request... but that was not so.

Explanation:

The example illustrates the requirement of having to have all the concrete implementation types that might ever be returned from a remote method, available in advance and bundled with the client code. Code downloading can solve this situation.

How do I port existing RMI applications to be accessible from EJBs or CORBA Clients?

The main changes required are

(1) to extend all RMI server objects from PortableRemoteObject instead of UnicastRemoteObject,

(2) use the COSNaming implementation of JNDI, and of course

(3) recompile to get RMI-IIOP ties and stubs.

This gives you an "RMI" server object which you can invoke methods on from a CORBA object, a CORBA-based EJB, or (using the dual stub feature of the rmic compiler) even from a classic RMI/JRMP client. If the RMI-IIOP server gets an object reference to another RMI-IIOP server object, it must use PortableRemoteObject.narrow() instead of a Java cast to downcast the reference returned by a JNDI lookup.

The only problem areas are

(1) value types have to be serializable (this is the "OBV subset" problem),

(2) naming problems can occur with overloaded methods (name-mangling restrictions and the standard CORBA interface inheritance restriction),

(3) although type/variable names differing only in case are supported, some other names differing only in case may not be unique,

(4) there's no remote GC in RMI-IIOP, so any code using those interfaces will break, and

(5) code references to RMISocketFactory and Unreferenced will break.

Other than this (which is surprisingly little), porting a large RMI app to RMI-IIOP should be fairly easy. There's also a workaround, which is to use RMI-IIOP objects extending PortableRemoteObject as bridges between CORBA and classic RMI servers.

How can EJBs callback their Clients?

In terms of callbacks, you can use any CORBA object in the client as a callback. Note that the term "CORBA object" includes server-side objects with RMI signatures. You essentially pass the reference to the callback in your method invocation to the EJB.

When should I use PortableRemoteObject.narrow() and when does a simple cast suffice?

The EJB specification, at one time was not consistent WRT how one should convert the result of a JNDI lookup to an EJB interface instance and such issues. As a result you will see examples showing direct casting, and some examples show using the function: javax.rmi.PortableRemoteObject.narrow.

Furthermore, the spec. said that some implementations will require the narrow method, and some will not. It is considered mandatory for implementations based on IIOP.

So what is actually going on? CORBA based implementations, in general, do not return the "actual" server type from a method invocation. They return the "signature" type which can then be narrowed to a more specific type, if need be. In certain circumstances, determining if an object can be narrowed to the desired type requires an RPC to the server implementing the object. However, although our implementation is built on CORBA, we thought it was more user friendly to support the casting behavior, when possible. This was how our beta Container behaved. However, as stated above, sometimes this behavior requires an RPC to the server.

So, now we can start to answer the question.

Using javax.rmi.PortableRemoteObject.narrow will succeed in all cases, and is preferred. In fact the final EJB 1.1 spec pretty much mandates the use of PortableRemoteObject always.

The javax.rmi.PortableRemoteObject.narrow method takes two parameters: the instance to narrow, and the class to narrow to. So, in the examples that depend on the cast, you would change the following

from:

  AccountHome savingsHome = (AccountHome)   context.lookup("accounts/savings");
  AccountHome checkingHome = (AccountHome)    context.lookup("accounts/checking");
    
to:

  AccountHome savingsHome = (AccountHome)    javax.rmi.PortableRemoteObject.narrow
      (context.lookup("accounts/savings"), AccountHome.class);
  AccountHome checkingHome = (AccountHome)    javax.rmi.PortableRemoteObject.narrow
      (context.lookup("accounts/checking"), AccountHome.class);
On a related note … You should never have to use Helper.narrow(). That is a CORBA-ism which does not exist in the EJB world.

Weird behaviour on trying to verify that the orb correctly passes back the remote interface.

Scenario

I use different cases (same remote object though) to test this. The following cases are implemented in the bean:

1) passing an Account object back to the client when the signature of the method used is - "Account getAccount1()"

2) passing back the same object with the method signature - "Object getAccount2()"

3) using a Java object (say Wallet) to store the Account object in three ways (an Account, an EJBObject, and an Object) and passing this structure back to the client.

The client will print out the xxx.getClass().getName() for all the Account objects returned to the client. The output at the client looks like this:

"vbj Client

Class name for acnt1 (Account): _Account_Stub

Class name for acnt2 (Object): com.inprise.vbroker.orb.ObjectImpl

Wallet - Account: _Account_Stub

Wallet - EJBObject: javax.ejb._EJBObject_Stub

Wallet - Object: com.inprise.vbroker.orb.ObjectImpl

This cast ((Account)aw.acnt2) blows up:

Exception in thread "main" java.lang.ClassCastException:
javax.ejb._EJBObject_Stub
        at BankClient.main(BankClient.java:32)
"
Strangely, the result is not as expected (the same "Account" type for all cases).

Explanation

Basically, you are hitting a number of the corner cases for which the PortableRemoteObject was introduced. There are a number of cases in an RMI-over-IIOP implementation where our generated code cannot return a correct instance. What you need to do, in all cases where you don't already have an instance of Account, is to narrow the object, via the call:
        javax.rmi.PortableRemoteObject.narrow(object, Account.class)
This will return you an instance of Account, as you expect. This example provides an excellent motivation for forcing users to use the PRO.narrow.

Does this mean that the Helper classes won't be needed for deployment? Any other classes that become superfluous?

Actually, the class PortableRemoteObject uses the helper class to do its job. In fact, the PRO takes the name of the class passed in, appends "Helper" to it, looks a static "narrow" method, and calls it. The only difference is that this is the compliant way to write your code in EJB.

So no, the Helper class is still very much required.

Why is it that when I pass back a vector enumeration from a finder it works great but not when I return one from a business method?

Expanded question:

In a business method I collect data in a java.util.Vector and then return .elements() of this vector. I get the following message from Container:
    Attempting to return unmarshalable class: class
java.util.VectorEnumerator
    java.io.NotSerializableException: java.util.VectorEnumerator
That is understandable as VectorEnumerator isn't serializable and therefore it can't be returned to the client. Makes sense. But when I do _exactly_ the same thing in ejbFind<>(), everything works fine!

Explanation

There are two odd facts:

1) The Enumeration returned by java.util.Vector.elements() is not Serializable, and thus cannot be returned from a remote interface method (e.g. EJB business method)

2) You can return Vector.elements() from a finder method.

For (1), you can mimic the cart example, where a VectorEnumeration class is created to return a Serializable Enumeration computed from a Vector. This is the only correct work-around.

So then, why does (2) work? If you look closely, the Enumeration returned by a bean's implementation of a finder method contains primary keys as the elements. However, the Enumeration returned by the home's finder method contains EJBObject elements. Thus, the container is computing a new Enumeration (containing EJBObjects) based on the old Enumeration (containing primary keys). While the container does this, it also constructs a different Enumeration which happens to be Serializable.

Why do I get errors when trying to serialize and object that has a field of type java.lang.Class ?

Scenario:

in the datatype AccountWallet.java I have added the following:
public Class _class;
...
// and in constructor,

      _class = acnt.getClass();
So the _class object will be serialized to the client when AccountWallet is sent over. Now, running the client, I have two kind of errors:

1) a NullPointerException when at the client I do this:

    AccountWallet aw = teller.getAccountWallet();
    aw._class.toString();

2) the Client crashes with an Application Error, when at the client I do this:

    aw._class.getName().toString();

Explanation:

Instances of java.lang.Class are not supported currently in our RMI-over-IIOP implementation. Thus instances of this type cannot be sent over the wire.

The trick that we use, in our implementation of javax.ejb.EJBMetaData, for example, which "appears" to pass classes, is to have two fields in our objects:

        private transient Class _homeInterfaceClass;
        private String _homeInterfaceClassName;

Then, the getHomeInterfaceClass method would be:

        public Class getHomeInterfaceClass() {
          if(_homeInterfaceClass == null) {
            _homeInterfaceClass = Class.forName(_homeInterfaceClassName);
          }
          return _homeInterfaceClass;
        }
So, what is actually serialized is a string representing the class name. Again, the Class instance itself cannot be marshaled in our current product. We will look to support this data type in a subsequent release.

Problem with case insensitivity of CORBA IDL

You cannot have certain RMI definitions like a package named employee within which is an interface named Employee with only a case difference. This prevents java2iiop from mapping these classes into IDL. This is unfortunately an explicit restriction in the reverse mapping. IDL is case insensitive (due to a requirement to support target languages which are case insensitive). However, the Java-to-IDL reverse mapping does attempt to handle Java's case sensitivity. For example, the reverse mapping supports method names which differ only by case. However, the reverse mapping does not support "type" names which differ only by case, and peculiarly, package names map to an IDL construct (module) which is considered a type.

Note that all of this is as per the OMG specifications; these are not limitations in our product, per se. As such, these same limitations will exist in any compliant EJB product built using RMI-over-IIOP. Change either your package name or your interface name to not hit this restriction.

What are the types I can pass as parameters to EJBs?

The EJB spec is very clear about what types of parameters can be passed. A simple rule to keep in mind is that any parameter passed in any EJB call _might_ be marshaled. Thus, assume that it will be. If writing your object to a marshal buffer, and then reading it back in, somehow breaks the object, then this object cannot be passed as a parameter. With the Inprise Container for intra VM bean calls, we do not by default marshal parameters, for obvious performance reasons, but this is simply an optimization. If your EJBs would fail if the arguments were to be marshaled, then you have a bug in your EJBs.

Osfind shows these weird RMI things …

 
Here's a partial dump of OSFind running on my system:
>
> REPOSITORY ID: IDL:omg.org/CosTransactions/TransactionFactory:1.0
>         OBJECT NAME: EJB/JTS[test]
>         OBJECT NAME: EJB/JTS[test]
>
> REPOSITORY ID: RMI:us.oh.state.dot.pas.ProjectHome:0000000000000000
>         OBJECT NAME: project
>         OBJECT NAME: odot/pas/project
>
> Why the RMI tag on the object that I deployed? Is RMI being used as the
> protocol anywhere in this process?
The RMI repository ID is used to identify remote interfaces that are not "CORBA"(i.e. did not come from IDL) and is specified by the java to IDL reverse mapping in CORBA 2.3. We use IIOP as the communication protocol, and no RMI (protocol) anywhere.

If I name a bean AccountHolderBean, java2iiop fails. Anything with “Holder” (HolderBean as the bean name, Holder as the remote name and HolderHome as the home name) causes java2iiop to fail.

This is infact a problem with a bad interaction between the reverse and the forward mapping to and from Java to IDL. We are looking at ways to fix this. For now, any name containing a reserved IDL suffix will have the same problem, (eg. Package, Operations, POA, POATie etc).

What exception reaches the client when an unexpected server exception happens?

When a server throws an unexpected exception or an rmi.RemoteException, depending on the exception type, different exceptions get thrown on the client.
If the Original is It is mapped to
java.lang.Error java.rmi.ServerError
java.rmi.RemoteException java.rmi.ServerException
java.lang.RuntimeExcepition java.lang.RuntimeException

None of the IDL interfaces generated from java2idl for an EJB inherit from CosTransactions::TransactionalObject. Is that not required by the OTS 1.1 spec?

It is true that OTS 1.1 requires any object that needs to participate in a transaction to derive from CosTransactions::TransactionalObject. The latest revision of the OTS spec fixes this (bug). Now, all object receive the transactional context. We have adopted the latest behaviour in our runtime and hence do not need the inheritance requirement.