Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Java.JDK.5.Edition (Wrox)

.pdf
Скачиваний:
39
Добавлен:
29.02.2016
Размер:
12.07 Mб
Скачать

Chapter 10

you to export the object and bind it to a port. In order to do this you can use the method UnicastRemoteObject.exportObject(this, 3432) in your default constructor or you can simply extend UnicastRemoteObject as demonstrated in this example and allow it to do the export for you.

In the preceding code, there are also four variables that are explicitly used by this class for tracking user, server, and message information. They are described in the following table.

Variable

Description

 

 

String m_sServerName

Allows you to associate a name with the server. Not a critical

 

variable, but it can be useful if you were to set up multiple

 

servers.

String m_sLastMsg

This variable holds the last chat message that was submitted

 

by a client to the server.

int nGUID

This variable is called the Global Unique Identifier. It is

 

used to assign a unique ID to each client that is using the

 

chat server.

ArrayList m_alUsers

This array list holds ChatUser objects, each which repre-

 

sents an individual chat user or client of the server.

 

 

The main variables that are constantly changing throughout the life of the server are the m_alUsers, nGUID, and the m_sLastMsg:

//Default Constructor

public RMIChatImpl() throws RemoteException { super();

nGUID = 0;

m_sLastMsg = “”;

m_alUsers = new ArrayList();

}

// Constructor which excepts a server name

public RMIChatImpl(String sServerName) throws RemoteException {

super(); nGUID = 0;

m_sLastMsg = “”; m_sServerName = sServerName; m_alUsers = new ArrayList();

}

The contructors for the RMIChatImpl class reset all the variables and call their respective super constructors. One thing to note is that the nGUID and the array list of users are always reinitialized when the RMIChatImpl server is created:

456

Communicating between Java Components with RMI and EJB

public ChatUser logIn(String sNickName) throws RemoteException {

ChatUser cu = this.findUser(sNickName);

if (cu != null) { return cu;

}

cu = new ChatUser(sNickName, nGUID);

m_alUsers.add(cu);

nGUID++;

return cu;

}

The first real remote method is shown above and it is called the logIn method. Its main purpose is to allow chat users to log in to the server. When users log in, they must supply the nickname that they wish to use. When the server receives a login request, it will first check to see if the user already exists on the server. If the user doesn’t exist, a new ChatUser object is associated with the user and the new user is added to the array list of users:

public boolean logOut(ChatUser cuUser) throws RemoteException {

if (cuUser == null) { return false;

}

return m_alUsers.remove(cuUser);

}

The logOut remote method is just the opposite of the logIn remote method. It allows users to disconnect from the chat server. When users invoke the logOut method, they must supply their credentials in the form of a ChatUser object. Once this is received, the server will then attempt to log the user out and perform any necessary cleanup operations:

public void sendMessage(String sMessage, ChatUser cuUser) throws RemoteException {

if (cuUser != null) {

m_sLastMsg = “<” + cuUser.getUserName() + “> “ + sMessage;

}

}

The sendMessage remote method is a bit deceiving. It requires a user to submit their credentials and a message to be displayed to all the other chat clients. However, if you look closely, the code doesn’t physically send the message anywhere; it simply stores the message in the m_sLastMsg variable. The reason for this is that the chat clients (users) are constantly pinging the server for the last message that was sent. If the last message has changed, they will display the new message. This was an easy approach to take to make this sample code smaller in size and more understandable:

457

Chapter 10

public String getMessage() throws RemoteException { return m_sLastMsg;

}

The getMessage remote method is the method that the clients continuously poll for new messages:

public ChatUser findUser(String sNickName) throws RemoteException { if (m_alUsers != null && this.getUserCount() > 0) {

int alSize = m_alUsers.size(); ChatUser cuTemp;

for (int i = 0; i < alSize; i++) { cuTemp = (ChatUser) m_alUsers.get(i);

if (cuTemp != null) {

String sTmp = cuTemp.getUserName();

if (sTmp.equalsIgnoreCase(sNickName)) { return cuTemp;

}

}

}

}

return null;

}

The findUser remote method allows clients or servers to retrieve a user’s credentials or ChatUser object based on the user’s nickname. You may have noticed that this is the first RMI remote method that returns a custom object. RMI is so powerful that it allows you to utilize your own custom objects with one main constraint — the custom object you are returning must be serializable. ChatUser is the object being returned and it is serializable, so it is perfectly valid:

public ArrayList getUsers() throws RemoteException { return m_alUsers;

}

public int getUserCount() throws RemoteException { if (m_alUsers == null) {

return 0;

}

return m_alUsers.size();

}

public String getServerName() throws RemoteException { return m_sServerName;

}

The above code is used to retrieve the values of different variables. You can retrieve a list of users on the server that could possibly be used by the clients to show a list of users in a listbox. I did not add this to the present example. However, it would be a good exercise for you to try:

458

Communicating between Java Components with RMI and EJB

public static void main(String[] args) { // Setup a security manager

if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager());

}

try {

RMIChatImpl rmiObj = new RMIChatImpl();

// Bind this object to “RMIChatServer” Naming.rebind(“RMIChatServer”, rmiObj);

System.out.println(“RMIChatServer registered with the RMI registry”); } catch (Exception ex) {

System.out.println(“RMIChatImpl error: “ + ex.getMessage()); ex.printStackTrace();

}

}

}

The main method of the class is important because it is used when the RMIChatImpl class is registered with the RMI registry. Following is a list of necessary steps that it performs:

1.First it sets up a security manager for the server. You can tailor the security manager to fit your individual architectural needs. This simple example just uses the default system security manager.

2.It then constructs an RMIChatImpl object that will be registered with the RMI registry.

3.Finally it uses the Naming.bind method to bind the RMIChatImpl object with the name “RMIChatServer” in the RMI registry. Clients can search for “RMIChatServer” to obtain a remote RMIChatImpl object.

The RMI registry must be started before the steps above are executed to ensure proper registration with the RMI registry.

ChatUser Class

The ChatUser class is used to store specific information about users of the RMIChatImpl server. The server uses this class extensively to track, search, and accept messages, and authenticate users. While there isn’t much to this class, there is something important that must be noted. Since this class is being returned through a remote object via one of the RMIChatImpl server’s methods, it must be serializable. If it were not serializable, exceptions would be thrown and the server would not work:

public class ChatUser implements java.io.Serializable { private String m_sUserName;

private int m_nUserID;

protected Object clone() throws CloneNotSupportedException { return super.clone();

}

protected void finalize() throws Throwable {

459

Chapter 10

super.finalize();

}

public boolean equals(Object arg0) { return super.equals(arg0);

}

public int hashCode() { return super.hashCode();

}

The preceding code is mainly used for serialization purposes to make sure that the class is utilizing serialization as specified by the interface java.io.Serializable. The code shown below is simply used to track user information such as the user’s name (or nickname) and the user’s unique ID:

public ChatUser(String sUserName, int nUserID) { m_sUserName = sUserName;

m_nUserID = nUserID;

}

public int getUserID() { return m_nUserID;

}

public void setUserID(int userID) { m_nUserID = userID;

}

public String getUserName() { return m_sUserName;

}

public void setUserName(String userName) { m_sUserName = userName;

}

}

ChatApplet Class

The ChatApplet class is the main class for the client. It contains the Swing code for the GUI and it also contains the client code for sending and receiving messages. Since the Swing code can be very large, I will eliminate most of it from the chapter discussion. However you can find all of the working code on http://www.wrox.com:

import javax.swing.*; import java.rmi.Naming;

public class ChatApplet extends JApplet implements Runnable { private JPanel jContentPane = null;

private JButton jButton = null; private JTextField jTextField = null; private JButton jButton1 = null;

460

Communicating between Java Components with RMI and EJB

private JScrollPane jScrollPane = null; private JList jList = null;

private JLabel jLabel = null;

private DefaultListModel listModel = null;

private RMIChat m_rmiChat = null; private ChatUser m_ChatUser = null;

private boolean m_bIsConnected = false;

The ChatApplet class does not extend or implement any specific RMI interfaces or classes, nor is it required to. The code for accessing the remote objects of the RMIChatImpl object will be shown shortly. Throughout the explanation of this class there are three variables that are considered global. They are described in the following table.

Variable

Description

 

 

RMIChat m_rmiChat

This represents an RMIChat object that will be used later to

 

communicate with the remote objects on the server.

ChatUser m_ChatUser

This variable contains specific user information that per-

 

tains to the client.

Boolean m_bIsConnected

This is a boolean flag that lets you constantly know what

 

your status is with the server. So if the value is set to true,

 

you are connected to the server, otherwise you are not.

 

 

public void start() { super.start();

Thread t = new Thread(this); t.start();

}

public void stop() { super.stop();

}

The start and stop methods can be used to determine when an applet has been started or when it has been stopped. In the start method, you are spawning off a thread which will be used to poll the server for new chat messages:

public void run() { String sOldMsg = “”; String sNewMsg = “”;

while (true) {

if (this.m_bIsConnected) { try {

if (this.m_rmiChat != null) {

461

Chapter 10

sNewMsg = this.m_rmiChat.getMessage(); if (!sNewMsg.equalsIgnoreCase(sOldMsg)) {

listModel.addElement(sNewMsg); sOldMsg = sNewMsg;

}

}

} catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace();

}

}

try {

Thread.currentThread().sleep(500);

}catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace();

}

}

}

The run method is the body of the thread that was spawned in the start method. This method checks to see if you are connected and that you have a valid remote RMIChat object. If you do, it polls the chat server every 500 milliseconds using the RMIChat object, m_rmiChat, for new messages with the rmiChat.getMessage method:

public ChatApplet() { super();

init();

}

public void init() {

listModel = new DefaultListModel(); listModel.addElement(“Chat Client loaded successfully.”); listModel.addElement(“Chat messages will appear below.”);

jList.setModel(listModel);

try {

m_rmiChat = (RMIChat) Naming.lookup(“RMIChatServer”);

} catch (Exception ex) {

System.out.println(“ChatApplet error: “ + ex.getMessage()); ex.printStackTrace();

}

}

The init method contains your core RMI code. It looks up the RMIChatServer remote object using the Naming.lookup method and returns an RMIChat object upon success. If the operation was successful, you can immediately begin using the RMIChat object to call remote methods on the server. It is that easy!

462

Communicating between Java Components with RMI and EJB

private javax.swing.JButton getJButton() { if (jButton == null) {

jButton = new javax.swing.JButton(); jButton.setText(“Send”); jButton.setName(“btSend”);

jButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) {

if (m_bIsConnected) {

if (m_rmiChat != null && m_ChatUser != null) { try {

m_rmiChat.sendMessage(jTextField.getText(), m_ChatUser);

}catch (Exception ex) { System.out.println(ex.getMessage()); ex.printStackTrace();

}

}

}

}

});

}

return jButton;

}

The getJButton method is where you will perform your send message code. When the send button is pressed, you should grab the text from the jTextField control and then send a message to the server using the m_rmiChat.sendMessage method. This method simply takes as parameters the message you want to send in String form and your credentials in the form of a ChatUser object:

private javax.swing.JButton getJButton1() { if (jButton1 == null) {

jButton1 = new javax.swing.JButton(); jButton1.setText(“Connect”); jButton1.setName(“btConnect”);

jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) {

if (jButton1.getText().equalsIgnoreCase(“Connect”)) { // Create user here

m_bIsConnected = true;

if (m_rmiChat != null) {

String sUserName = jTextField.getText();

if (sUserName.equalsIgnoreCase(“”)) { sUserName = “noname”;

}

try {

m_ChatUser = m_rmiChat.logIn(sUserName);

}catch (Exception ex) { System.out.println(ex.getMessage()); ex.printStackTrace();

}

}

463

Chapter 10

When the user clicks the Connect or Disconnect button, the above ActionEvent is fired. What occurs is the applet will send a message to the server requesting to log in with a particular username using the m_rmiChat.logIn method. If the network layer is present and the method succeeds, an m_ChatUser object is returned. If a connection already exists, then the logOut code below will be executed. The applet will attempt to disconnect the client from the server once the m_rmiChat.logOut method is called:

jButton1.setText(“Disconnect”);

}else {

m_bIsConnected = false; jButton1.setText(“Connect”);

if (m_rmiChat != null && m_ChatUser != null) { try {

m_rmiChat.logOut(m_ChatUser);

}catch (Exception ex) {

System.out.println(ex.getMessage());

ex.printStackTrace();

}

}

}

}

});

}

return jButton1;

}

}

The final piece of code in this example is the HTML code for loading the applet. This code will embed the applet in your Web browser of choice:

<html>

<body>

<applet code=ChatApplet.class width=”300” height=”200” >

</applet>

</body>

</html>

Compiling the RMIChat Application

So far, this chapter has briefly touched on the subject of compiling RMI applications and using the rmic tool to generate stubs, but it has not yet shown you an example of how to do so. Below are the necessary steps to compile an RMI application:

1.The first step is to compile all the source files as you normally would do with any Java application.

2.You will need to then run the rmic tool on the RMIChatImpl class to generate the appropriate stub. From a command prompt in the directory where your compiled source files are located, type the following:

rmic RMIChatImpl

464

Communicating between Java Components with RMI and EJB

3.Once you have compiled the source files and generated the appropriate stubs, you will then need to start the RMI registry using the command stated below:

start rmiregistry

4.The final step is to start the RMIChatImpl server. This will register the server with the RMI register and cause it to await client connections.

java -Djava.rmi.server.codebase=http://host/username/dir/ -Djava.security.policy=policy RMIChatImpl

And that is all there is to creating a very significant RMI application. This example has covered the most important functionalities of RMI, and this knowledge hopefully will provide you with a solid foundation for building future RMI applications. Now turn your attention to Enterprise JavaBeans.

Enterprise JavaBeans

Enterprise JavaBeans, or EJBs, are a server-side component-based architecture that is used to build applications that are scalable, transactional, distributable, portable, and secure. If you think in terms of reusable code, then you will see why EJBs are so vital. EJBs are only concerned with the business logic of the application; the system logic is the requirement of the EJB container. EJB containers are basically application servers that provide you with a server platform to deploy your EJBs on. They handle all the scalability, transactional, security, connection pools, and other system logic components, thus making your job as a developer much easier. Because EJBs are a standard, and application servers must implement the EJB specification, EJBs can be deployed on different application servers with very little configuration changes and almost no code changes. Therefore EJBs are very portable.

RMI, on the other hand, is great if you never really plan to have a very robust enterprise application. In order to make a robust RMI enterprise application, it would require much more work to write all the transaction, security, and connection pools that EJB containers provide. So, depending on your needs, you can decide which technology is best for your architectural requirements.

RMI is also involved in EJB development. EJBs are accessed via RMI, so all that you learned about RMI will apply nicely to EJB development. Several of the components you used in RMI you will also use in EJBs, like remote interfaces and remote exceptions, but you won’t have to deal with complex system logic components like transaction support.

EJB Basics

Just like RMI, EJB clients interact with interfaces that expose methods that they can use to communicate with the server. Therefore, clients never need access to the implementation code on the server in order to communicate and use its methods. EJBs also do not need to manage resources; they simply interact with their container in order to obtain connections to external resources like databases. Developers can leverage these resources quickly and easily and do not need to worry about setting up connection pools, transaction support, or security restrictions. Those tasks fall on the administrator of the container and keep the EJB code itself portable.

465

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]