| ||||||||||||||||||||||||||||||||||||
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
IntroductionEnterprise Java Beans (EJB) [1] are an established technology for implementing software components on a Java platform; a lot of software exists that relies on them. Many IT key-players are providing application servers for hosting EBJs, in particular IBM's WebSphere, BEA's WebLogic, and the opensource JBoss. EBJs can be used to create distributed object systems, and rely on Java RMI/IIOP to exchange messages; EJBs can also be exposed as Web Services. Although using Web Services to interoperate is currently trendy, they also have their share of limitations. Web Services are great when it comes to integrate heterogeneous loosely coupled systems, but they have no support for remote object references. In practice, they are stateless and closer to a remote method call than to a distributed object system. Furthermore, SOAP and XML are by no means a compressed format and tend to be quite verbose. In a previous article [2], I did show how to access a .NET component from Java using IIOP.NET; this article presents the opposite direction: how to access a Java EJB service from a .NET client using the IIOP.NET remoting channel. No modification on the EJB site is required for this purpose. About IIOP.NETIIOP.NET [3] is a .NET remoting channel based on the IIOP protocol, the same used by Java's RMI/IIOP [4]. IIOP is part of the CORBA stardard [5]. IIOP.NET acts as an ORB (a CORBA object request broker): it makes objects defined in your .NET application accessible to other remote ORBs, and vice-versa. Java RMI/IIOP implements a subset of the CORBA type system (due to some limitations in Java's type system) and roughly provides the same features as IIOP.NET for the J2EE platform.
Using IIOP.NET is almost as simple as using .NET's built-in remoting. IIOP.NET is an open-source project hosted on sourceforge (http://iiop-net.sourceforge.net/). It was developed by Dominic Ullmann as part of his master thesis at ETH-Z; further work is now sponsored by his current employer ELCA Informatique SA [6], where IIOP.NET is used to let the Java and .NET flavours of its LEAF framework [7] interoperate. Not surprisingly, IIOP.NET is not the only software you can use for this purpose. The open-source project Remoting.Corba [8] is quite similar in its goals but doesn't support EJBs at the moment, and Janeva [9] from Borland promises to do the same, but is not free. The Example: an EJB-based ChatroomTo show you how to access an EJB from .NET, we will use a simple non-trivial example: a chat service. The service is an EJB, which allows users to register and unregister a listener for receiving the messages submitted to the chatroom; the EJB manages the list of clients and dispatches the messages to all registered clients. The followig Java interfaces and classes are used to communicate with the service; they will be converted to IDL files to allow access from other CORBA clients. A public class Message implements Serializable {
private String m_originator;
private String m_msg;
public Message() { ... }
public Message(String msg, String originator) { ... }
public String getMsg() { ... }
public String getOriginator() { ... }
}
The public interface MessageListener extends java.rmi.Remote {
/* notify the listener, that a new message has arrived. */
public void notifyMessage(Message msg) throws java.rmi.RemoteException;
}
Finally, the public interface Chatroom extends EJBObject {
/* post message in chat-room */
public void broadCast(Message msg)
throws java.rmi.RemoteException;
/* register a client, interested in chatroom messages */
public void registerMe(MessageListener listener, String forUser)
throws java.rmi.RemoteException, AlreadyRegisteredException;
/* unregister the client with the name userName. */
public void unregisterMe(String userName)
throws java.rmi.RemoteException, NotRegisteredException;
}
The chatroom works according to the following UML sequence diagram:
In the attached example, the EJB is implemented and configured for WebSphere 5.0 [10]; the IIOP.NET release also contains a version for WebLogic 6.1 [11] , and for JBoss 3.2.1 [12] . Although written for WebSphere, we will try to keep the code in this article as generic as possible, assuming you know your application server well enough to configure the EJB for it. We not start the realization of the EJB chatroom server and of the .NET client. This requires 6 steps. Step 1: Install IIOP.NETTo build and execute this example, you need at least a Java SDK 1.3, an application server (we assume IBM WebSphere 5.0), the Microsoft .NET Framework SDK 1.0 or 1.1 [13], the Microsoft J# SDK version 1.0 or 1.1, and IIOP.NET (at least 1.3.1). Installing the first four is out of this article's scope. To install IIOP.NET, download the latest version from sourceforge. The IIOP.NET project contains a few directories:
Before building IIOP.NET, copy the files lib\ir.idl and
lib\orb.idl from your Java SDK directory into IIOP.NET's
Step 2: Implementing the EJBGiven the previous definitions, the implementation of the EJB is quite
straightforward. We create the class For the bean implementation, three files are provided: the bean home
interface is in the interface public void registerMe(MessageListener listener,
String forUser) throws AlreadyRegisteredException {
ChatroomServer server = ChatroomServer.getSingleton();
server.addClient(listener, forUser);
}
public void unregisterMe(String userName) throws NotRegisteredException {
ChatroomServer server = ChatroomServer.getSingleton();
server.removeClient(userName);
}
public void broadCast(Message msg) {
ChatroomServer server = ChatroomServer.getSingleton();
MessageListener[] listeners = server.getClients();
for (int i = 0; i < listeners.length; i++) {
try {
listeners[i].notifyMessage(msg);
} catch (Exception e) {
System.err.println("error sending msg: " + e);
System.err.println("--> removing listener");
server.removeListener(listeners[i]);
}
}
}
Broadcasting the message is done in the Java bean itself to minimize the time spent in the chatroom (during which the chatroom is locked). Sending the messages can take a long time, in particular when a client is not available. The ideal solution here would be a CORBA one-way asynchronous call, but this is not possible in EJB; the proposed way of implementing this requires using the Java Message Service (JMS) [14], which is out of this article's scope; thus, for the sake of simplicity, we will let each bean send out the messages. The implementation of public class ChatroomServer {
private static ChatroomServer s_chatroomServer = new ChatroomServer();
private Hashtable m_clients = new Hashtable();
private ChatroomServer() { super(); }
public static ChatroomServer getSingleton() { return s_chatroomServer; }
public synchronized void addClient(MessageListener ml, String forUser)
throws AlreadyRegisteredException {
if (!m_clients.containsKey(forUser)) {
m_clients.put(forUser, ml);
} else {
throw new AlreadyRegisteredException(
"a message listener is already registered for user: "
+ forUser);
}
}
public synchronized void removeClient(String forUser)
throws NotRegisteredException {
if (m_clients.containsKey(forUser)) {
m_clients.remove(forUser);
} else {
throw new NotRegisteredException(
"no message listener registered for the user: " + forUser);
}
}
public synchronized void removeListener(MessageListener listener) {
m_clients.values().remove(listener);
}
public synchronized MessageListener[] getClients() {
MessageListener[] result = new MessageListener[m_clients.size()];
result = (MessageListener[])(m_clients.values().toArray(result));
return result;
}
}
Note the Step 3: Generating the IDL filesOnce the service is implemented, the next step is the generation of the IDL files, which describe the service interface using the CORBA model. Each application server provides its own way to generate the IDL, because each application server works with different versions of the EJB specifications, which in turn have different interfaces.
Refer to your application server documentation for the exact procedure to generate the IDL files. Step 4: Generating the CLS modules from the IDL filesFrom the IDL files, the Why does the generator create a netmodule instead of a C# stub? Well, there are a few reasons, but the most important ones are simplicity and portability. First, netmodules are quite easy to generate using the reflection-emit interface of .NET; generating source code would require some pretty-printing algorithm. Second, the netmodules contain the definitions in the CLS form, which is understood by all .NET-compliant languages, thus it doesn't matter whether you code is in C# or Visual Basic (or even Oberon). Invoke the generator specifying the output directory (-o) and the idl files to use. IDLToCLSCompiler.exe -o ..\bin chatroom
ch\elca\iiop\demo\ejbChatroom\Chatroom.idl
ch\elca\iiop\demo\ejbChatroom\ChatroomHome.idl
The generator will remind you that you must provide the implementation for
some classes: these are the classes implementing the CORBA valuetypes; in .NET
these classes are defined with the In our example, the classes to be implemented are the
Our implementation of those classes is in using System;
namespace ch.elca.iiop.demo.ejbChatroom {
///<SUMMARY>
/// Implementation of the CORBA value type Message
/// </SUMMARY>
[Serializable] public class MessageImpl : Message {
public MessageImpl() { }
public MessageImpl(string originator, string msg) {
m_originator = originator;
m_msg = msg;
}
public override string fromUser {
get { return m_originator; }
}
public override string msg {
get { return m_msg; }
}
}
}
Keep in mind that IIOP.NET will search for class Step 5: Implementing the C# clientThe client provides a user interface to collect the user's messages, invoke the service, and display the information sent by the service. A simple GUI is used in the example; independently of the user interface, there are a few important things to do. First, register the IIOP.NET channel, connect to the EJB, and get an instance of the service. // register IIOP.NET channel
IiopChannel channel = new IiopChannel(callbackPort);
ChannelServices.RegisterChannel(channel);
// get the naming service
RmiIiopInit init = new RmiIiopInit(ejbNameServiceHost,
ejbNameServicePort);
NamingContext nameService = (NamingContext)init.GetService(
"NameServiceServerRoot");
NameComponent[] name = new NameComponent[] {
new NameComponent("demo", ""),
new NameComponent("chatroomHome", "") };
// get the chatroom home interface
ChatroomHome home = (ChatroomHome) nameService.resolve(name);
Chatroom chatroom = home.create();
The To be able to receive messages, the client must register a listener, i.e. a
remotable object implementing the m_listener = new MessageListenerImpl(m_usernameTextbox.Text, this);
m_chatroom.registerMe(m_listener, m_listener.userName);
Now the chatroom is ready to be used. Sending a message to the room is simple: MessageImpl msg = new MessageImpl(m_listener.userName,
m_messageTextbox.Text);
try {
m_chatroom.broadCast(msg);
} catch (Exception ex) {
Console.WriteLine("exception encountered, while broadcasting: " + ex);
MessageBox.Show("an exception occured, while broadcasting!");
}
This code performs a synchronous method invocation, i.e. it waits for the server to complete the broadcast and return. However, this is not mandatory, as the call returns no result and the client has no need to synchronize with the server. Thus, an asynchronous invocation is also possible: delegate void BroadCastDelegate(Message msg);
MessageImpl msg = new MessageImpl(m_listener.userName,
m_messageTextbox.Text);
try {
BroadCastDelegate bcd = new BroadCastDelegate(m_chatroom.broadCast);
// async call to broadcast
bcd.BeginInvoke(msg, null, null);
// do not wait for response
} catch (Exception ex) {
Console.WriteLine("exception encountered, while broadcasting: " + ex);
MessageBox.Show("an exception occured, while broadcasting!");
}
The listener's Step 6: Run the exampleRunning the example is the last step. On the server side, the
On the client side, launch the application; then set your name, connect to the server, and you will be able to send and receive messages.
Problems and PitfallsThe presented example is conceptually simple. Nevertheless, a few pitfalls may ruin your day and prevent the code from running. Most problems are related to the various application servers and their cumbersome configuration (many errors show only at execution-time, causing a tedious and time-consuming try-and-correct loop). In IIOP.NET, CORBA valuetypes prevent the fully automated generation of proxies. When invoking a method on a remote object, the call is forwarded to the machine holding the object instance by the remoting infrastructure. Valuetypes are cloned and all invocations are processed locally, thus their implementation must also be available locally. Because the IDL contains solely the interface definitions, the implementation must be provided by hand. This is not needed when remoting from .NET to .NET: the DLL containing the definitions also contains the implementation of the classes. Synchronization is another problem specific to this example. When using the graphical user interface, each operation (i.e. clicking on a button) locks the whole frame during the execution of the associated code. When sending a message to the server, the server will obviously forward the same message to the client, whereas the incoming message arrives in a different thread, and the frame lock prevents it from being delivered (causing a classical deadlock). Various solutions are possible: special handling of own messages is one approach, asynchronous calls are the other one: asynchronous calls can be made on the client side while sending or receiving the message, and on the server side while dispatching the message. In this examples we implemented asynchronous calls on message reception; furthermore, the client allows choosing between synchronous and asynchrounous message sending. ConclusionsThis article presents an example in which the IIOP.NET channel is used to provide access to a Java EJB from a .NET-based client. The various steps involved are presented and detailed. The IIOP protocol used to access the object is well established and quite efficient compared to the fancier and less powerful Web Services. The example itself is quite elaborate: it includes the .NET and J2EE platforms, and the CORBA technology to allow them to interoperate. The development of such an application includes some pitfalls, often caused by the complex and non-trivial configuration and use of the various application servers hosting the EJB services.In particular, the service naming and location may heavily differ among the various servers. Interacting with the various application servers was quite an eye-opener: we discovered many different interpretations of the same IIOP specifications, far more than we could imagine in our wildest dreams! A big fat thank goes here to the fast-growing IIOP.NET community, which helped us testing the channel on the many different servers in just a few weeks time. Nevertheless, IIOP.NET has been tested with the most common application servers; code and configuration examples are available for WebSphere 5, WebLogic 6.1, and JBoss 3.2.1. Links and References
About Patrik Reali
Other popular C# Programming articles:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||
|
All Topics, C#, .NET >> C# Programming >> General
Updated: 27 Aug 2003 Editor: Nishant S |
Article content copyright Patrik Reali, 2003 everything else Copyright © CodeProject, 1999-2004. Advertise on The Code Project | Privacy |
| MSDN Communities | ASPAlliance • Developer Fusion • DevelopersDex • DevGuru • Programmers Heaven • SitePoint • Tek-Tips Forums • TopXML • VisualBuilder • ZVON • Search Us! |