Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 47 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
47
Dung lượng
473,57 KB
Nội dung
354 Chapter 9 • Programming LEGO MINDSTORMS with Jini 5. However, the stub object does not implement the code within those method calls; it knows how to communicate back to the original service object, which may well be running on another machine, and that orig- inal object does all of the processing. This flow is shown in Figure 9.8. Now that would make sense; if the service object was expecting to commu- nicate with an RCX via a serial port, then it still could because the object would be invoked in the original JVM that created it.And if that object held references to other objects, these references would still be valid for the same reasons. The only problem is that we have to write an absolutely enormous amount of code to achieve this complex interaction between objects across the network, right? Wrong! Fortunately, Jini’s use of RMI means that this can be achieved with essentially no coding whatsoever! There are several ways to implement this, but the one we will use in the examples is by extending a class known as java.rmi.server.UnicastRemoteObject. Basically, the steps required are as follows: 1. Make sure that the class of the object that is to be registered as a service extends UnicastRemoteObject (and also ensure that it still implements Serializable). 2. When you compile your service’s class (for example, MyCoolService.class), run the RMI compiler against it to produce a stub class. www.syngress.com Figure 9.8 The Use of RMI Stubs in a Jini Architecture Client JVM Service JVM Lookup service JVM Service object 1. Service object created 2. Stub object registered with lookup service Stub object Stub object (serialized) 3. Stub object reconstructed in client JVM 5. Stub object communicates back to service object 4. Methods called against stub object locally 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 354 Programming LEGO MINDSTORMS with Jini • Chapter 9 355 3. Make sure that the stub class file is accessible to the client via the HTTP server. That’s it! That’s all you have to do; the RMI compiler will generate all of the code required for the stub so that the stub and the original object know how to communicate across JVMs and even across a network.You still specify the original class name in your code; Jini knows to instantiate the stub and register that with reggie instead of your local object. Generating the stub class using the RMI compiler is no trouble at all.The compiler executable is rmic, and the argument is the class file that the stub is being produced for.The output is the class name followed by an underscore, fol- lowed by Stub, followed by the .class extension. So, if the original class was called MyCoolServiceClass.class, the generated stub would automatically be called MyCoolServiceClass_Stub.class. The most commonly used options for rmic in this context are: ■ -keep Suppresses the deletion of intermediate files.The RMI compiler actually generates a .java source file on its way to generating the .class file, and this option stops the source file from being deleted; handy if you want to understand a little more about what’s going on behind the scenes. ■ -v1.2 Under Java 1.1, the standard was to produce stub and skeleton files, with “_stub” and “_skel” suffixes.The skeleton files are no longer required, so this option suppresses their creation. So, running the command: rmic -v1.2 mindstorm.MindstormProxy would create a single stub file called MindstormProxy_Stub.class in the mindstorm directory. Proxies and Service Architectures Having now covered some of the basics of registering and using a Jini service, we also need to consider how to decide on the correct architecture for a specific sit- uation, and most appropriately in this context, how to select the right architec- ture for a situation involving LEGO MINDSTORMS and Jini. www.syngress.com 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 355 356 Chapter 9 • Programming LEGO MINDSTORMS with Jini Selecting the Right Architecture As with most things, there are good and bad ways to approach the architecture of a Jini federation, but often no “right” or “wrong” way.The following sections will serve as a guide to how your particular solutions may be designed. Using Proxies As we have already covered, Jini is often considered appropriate as a technology for allowing the services of a wide range of devices to be readily discovered and utilized in a distributed environment. If you think about it, it makes perfect sense that a large device such as a household air conditioning unit may contain enough computing power to run Java, (including RMI), a TCP/IP stack, and Jini, so that it can register its own services.What then if the device contained a much less powerful embedded system (perhaps an 8- or 16-bit microcontroller), with only a primitive communications facility (perhaps RS-232) and limited RAM (measured in Kbytes rather than Mbytes or Gbytes)? That’s when you would need a proxy. A proxy in the Jini context is a service that runs on a more powerful com- puting platform (such as a PC or perhaps a home control system) and which stands between the client and something like an embedded device.The thing about the proxy is that to the client, the proxy appears to actually be the service itself. However, the reality is that the proxy is a layer of abstraction; it handles all of the communication with the device that is physically performing the service (over RS-232 in our previous example), and it handles such tasks as service regis- tration and lease renewal. Figure 9.9 shows the basic architecture of a system where a single object acts as a proxy, and the physical service is carried out by a LEGO MINDSTORMS device. A RCX Jini Proxy Service In this section, we will delve into building an example Jini service and client that will allow multiple LEGO MINDSTORMS to interact with each other.This will follow a design similar to that shown in Figure 9.9, where a proxy architec- ture is used. Why a Proxy? If you were thinking that our discussions about embedded devices with limited computing powers and primitive communications abilities could apply to the www.syngress.com 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 356 Programming LEGO MINDSTORMS with Jini • Chapter 9 357 RCX, you’re absolutely right. Despite the fact that people have been able to implement a form of Java Virtual Machine within the RCX, it really doesn’t come close to having the computing power required to run Jini. That said, the Jini proxy architecture makes perfect sense in the case of the RCX. Since the only way it has of connecting to a network is via an infrared link connected to some form of more powerful computer, that computer is the ideal candidate to act as proxy. So, a Jini federation of MINDSTORMS is a very real possibility; one or many machines connected to infrared towers register proxy services.To the client or clients, these proxy services appear as the services them- selves—the client calls a method DoWhateverRobotsDo(), and the robot responds by doing it.What has really taken place though is that the proxy has interpreted the method call and executed it by sending and receiving a set of RCX opcodes over the infrared connection. www.syngress.com Figure 9.9 The Use of Proxies for Embedded Devices Client JVM Service JVM Lookup service JVM Proxy object 1. Proxy object created 2. Stub object registered with lookup service Stub object Stub object (serialized) 3. Stub object reconstructed in client JVM 5. Stub object communicates back to proxy object 4. Methods called against stub object locally Embedded device (e.g., LEGO MINDSTORMS) Proprietary protocol (e.g., RCX opcodes over infrared link) 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 357 358 Chapter 9 • Programming LEGO MINDSTORMS with Jini Interfacing with the RCX Java API There are multiple mechanisms by which the Jini proxy could interface with the RCX—in our example, we shall use the RCX Java API.We will create a service program that will instantiate a proxy object (and its corresponding stub), and reg- ister this with the lookup service, and we will run more than one instance of this program (using more than one RCX, each connected to its own machine).When a client invokes methods against these proxy objects, the proxies will actually make use of the RCX Java API to send and receive the RCX opcodes to com- mand the RCX to behave a certain way and to receive feedback from the RCX. Using the RCX Jini Service: Example Server and Client Now we will cover a more complex example scenario that involves one or more LEGO MINDSTORMS. In this example, the MINDSTORMS will effectively communicate with each other (via a central client program) to perform a kind of synchronized dance. The desired outcome is this: for a pair of MINDSTORMS, the first will per- form a certain “dance step” (actually one of eight predefined movements); the second will imitate the exact movement, wait several seconds, and then perform its own randomly chosen movement.This will then be imitated by the first, and so on until the program is terminated or the batteries run out! If there is only one MINDSTORMS robot in the Jini Federation, it will just have to be content with imitating its own movements. Likewise, if there are more than two robots, they will form a “daisy chain,” with each one copying the moves of the previous one in the chain before deciding on its own original step. Figure 9.10 shows the basic scenario for this example, assuming two robots. As you can see in this diagram, the PCs which are connected to the infrared towers will act as proxies for RCX units themselves.Also, because some of the components of this system are likely to reside on different machines (or at least in different JVMs in the case where one machine may control multiple RCXs via multiple serial ports), the proxy objects must be executed in the JVM of the ser- vice that registers them.Therefore, it will actually be stub objects that will be reg- istered with reggie and these stub objects will communicate via RMI with the proxies. www.syngress.com 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 358 Programming LEGO MINDSTORMS with Jini • Chapter 9 359 A RCX Jini Server The first step is to create an interface that can be known to both client and ser- vice.Again, as in the simple Jini example shown previously, we will create this interface in a package called shared (see Figure 9.11).The interface defines some methods to allow certain attributes to be retrieved, as well as the method imitateThis(), which is a command to a MINDSTORMS robot to imitate a cer- tain dance step. Figure 9.11 Mindstorm.java /** * Mindstorm.java * */ www.syngress.com Figure 9.10 Example Scenario for Jini and the RCX Infrared PC and Infrared Tower MINDSTORMS robot 2 Infrared PC and Infrared Tower MINDSTORMS robot 1 Locator Service (reggie) Client Register service proxy Register service proxy Lookup both service proxies Send commands Receive messages Send commands Receive messages Continued 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 359 360 Chapter 9 • Programming LEGO MINDSTORMS with Jini package shared; import java.rmi.Remote; import java.rmi.RemoteException; import net.jini.core.event.RemoteEventListener; public interface Mindstorm extends Remote { // Define the two possible message types: // EVENT_RCX_MSG is a message from a remote RCX. // EVENT_DANCE_STEP is a notification that a Mindstorm // has completed a dance step. public final int EVENT_RCX_MSG = 1; public final int EVENT_DANCE_STEP = 2; // Method to retrieve the ID of a Mindstorm. public String getRcxId() throws RemoteException; // Tell a Mindstorm to imitate a dance step. public void imitateThis(int danceStep) throws RemoteException; // Retrieve the most recent RCX Message received. Should // be called in response to receiving an EVENT_RCX_MSG. public byte[] GetLastRCXMessage() throws RemoteException; // Retrieve the most recent dance step performed // by an RCX. Should be called in response to // receiving an EVENT_DANCE_STEP. public int GetLastStepPerformed() throws RemoteException; // Allow the client to register a listener // to receive events from the service. public void RegisterRemoteListener(RemoteEventListener listener) www.syngress.com Figure 9.11 Continued Continued 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 360 Programming LEGO MINDSTORMS with Jini • Chapter 9 361 throws RemoteException; } The proxy class will be called mindstorm.MindstormProxy. It will make extensive use of the RCX Java classes, so make sure you are familiar with the RCX Java mechanisms before you embark on this one.This proxy will also make use of mul- tithreading; it will spawn a new thread of execution and return, before proceeding through the cycle of imitating a dance step, pausing for 10 seconds, and then deciding on another dance step to perform.This is so that the client program is not held up for the entire 10-plus seconds, and it is a very common technique to use for this kind of scenario in Java. In this case, the class that will process the additional thread (ImitationThread) has been implemented as an inner class. It is a class declared within another class, which has implicit access to the members of the enclosing class and is not visible outside of that class. Just remember that when MindstormProxy.java is compiled, two .class files will be produced—MindstormProxy .class and MindstormProxy$ImitationThread.class—and that both these files must be in the Java runtime’s CLASSPATH for the example to work. The other thing to note about this class is that instances of it will be required to execute on the machine that they were created on (because they will be com- municating out a physical serial port to an RCX device), so we will be required to register an instance of a proxy that will be capable of communicating back with the equivalent object of this class from a remote client.As already discussed, we are very fortunate that RMI can provide this functionality with very little effort on our part.As can be seen from the following code, the proxy class will extend UnicastRemoteObject.The only other thing you must remember to do is run the RMI compiler against the compiled class file to produce a stub class.You can invoke the compiler with a command such as: rmic -v1.2 mindstorm.MindstormProxy After running rmic, you should now have produced a file called MindstormProxy_Stub.class in the /mindstorm subdirectory; this file is the proxy code (see Figure 9.12).That certainly beats coding it by hand! www.syngress.com Figure 9.11 Continued 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 361 362 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9.12 MindstormProxy.java /** * MindstormProxy.java * */ package mindstorm; import java.rmi.server.UnicastRemoteObject; import net.jini.core.event.UnknownEventException; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import shared.Mindstorm; import java.io.Serializable; import java.rmi.RemoteException; import rcx.RCXListener; import rcx.RCXPort; public class MindstormProxy extends UnicastRemoteObject implements Mindstorm, Serializable, RCXListener { // Make sure to generate a stub class using the // RMI compiler since this class extends // UnicastRemoteObject. protected static final byte FORWARDS = (byte)0x80; protected static final byte BACKWARDS = (byte)0x00; protected static final byte ON = (byte)0x80; protected static final byte OFF = (byte)0x40; protected static final byte MOTOR_A = (byte)0x01; protected static final byte MOTOR_C = (byte)0x04; protected static final byte MOTOR_DIR = (byte)0xe1; protected static final byte MOTOR_ON_OFF = (byte)0x21; // Store our unique ID so that the client www.syngress.com Continued 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 362 Programming LEGO MINDSTORMS with Jini • Chapter 9 363 // can differentiate between robots. protected String rcxId = null; protected RCXPort rcxPort = null; protected RemoteEventListener remoteListener = null; // Store any message received from the RCX // as a raw byte array for retrieval by the client. // Note that any subsequent message will overwrite // the existing one and if messages are retireved // in quick succession, data may be lost. A more // robust implementation could be employed for a // production system protected byte[] lastRCXEvent = null; // Similarly for the most recent dance step performed protected int lastDanceStep = 0; protected long seqNo = 0; public MindstormProxy() throws RemoteException { } public String getRcxId() { return rcxId; } protected void setRcxId(String id) { rcxId = id; } protected void openRcxPort(String port) { // Open a specific serial port rcxPort = new RCXPort(port); www.syngress.com Figure 9.12 Continued Continued 177_LEGO_Java_09.eps 4/3/02 1:11 PM Page 363 [...]... exceptions and no responses from the robot The code for the service is as shown in Figure 9. 13 Figure 9. 13 MindstormService.java /** * MindstormService.java * */ package mindstorm; Continued www.syngress.com 371 177 _LEGO_ Java_ 09. eps 372 4/3/02 1:11 PM Page 372 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9. 13 Continued import net.jini.discovery.LookupDiscovery; import net.jini.discovery.DiscoveryListener;... { // Firstly execute the move in // imitation of the other robot executeMovement(step); // Then wait 10 seconds try { Continued www.syngress.com 3 69 177 _LEGO_ Java_ 09. eps 370 4/3/02 1:11 PM Page 370 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9. 12 Continued Thread.sleep(10000); } catch(InterruptedException e) { // Do nothing } // Now randomly pick a new movement // (between 0 and 7 inclusive)... be called by the client // to retrieve details of the last step // after we have sent a notification return lastDanceStep; Continued www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 3 69 Programming LEGO MINDSTORMS with Jini • Chapter 9 Figure 9. 12 Continued } public void RegisterRemoteListener( RemoteEventListener listener) { // Called by the client to register its // listener object (which should... leaseManager = new LeaseRenewalManager(); // Store our own ID so we can be differentiated from // any other MINDSTORMS in the federation static protected String rcxId = null; Continued www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 373 Programming LEGO MINDSTORMS with Jini • Chapter 9 Figure 9. 13 Continued // The name of the serial port static protected String portId = null; static public void... "c:\\jini1_2\\policy\\policy.all"); System.setSecurityManager(new SecurityManager()); new Client(); // Ensure client runs indefinitely Continued www.syngress.com 3 79 177 _LEGO_ Java_ 09. eps 380 4/3/02 1:11 PM Page 380 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9. 14 Continued Object keepAlive = new Object(); synchronized(keepAlive) { try { keepAlive.wait(); } catch(java.lang.InterruptedException e) { // Do... collection Continued www.syngress.com 381 177 _LEGO_ Java_ 09. eps 382 4/3/02 1:11 PM Page 382 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9. 14 Continued // or ignore it try { ProcessMatched(matched); } catch (RemoteException e) { // This will likely be old // services that are no longer // available continue; } } } } System.out.println("Finished list"); if (mindstorms. isEmpty()) { // Client cannot... // next from the list or the first Continued www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 385 Programming LEGO MINDSTORMS with Jini • Chapter 9 Figure 9. 14 Continued // in the list // If there's only one, it will just // notify itself if (iter.hasNext()) { ((Mindstorm)iter.next()) imitateThis(step); } else { ((Mindstorm )mindstorms firstElement()) imitateThis(step); } } } catch(RemoteException... Turn one or both motors on byte[] msg = new byte[] {}; sendToRcx( new byte[] { MOTOR_ON_OFF, (byte)(ON | motors)}); Continued www.syngress.com 365 177 _LEGO_ Java_ 09. eps 366 4/3/02 1:11 PM Page 366 Chapter 9 • Programming LEGO MINDSTORMS with Jini Figure 9. 12 Continued } protected void motorsOff() { // Turn both motors off sendToRcx( new byte[] { MOTOR_ON_OFF, (byte)(OFF | MOTOR_A | MOTOR_C)}); } protected... just performed We will attempt to // imitate it Handle this from a new thread new ImitationThread(danceStep, this).start(); Continued www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 367 Programming LEGO MINDSTORMS with Jini • Chapter 9 Figure 9. 12 Continued } public void receivedMessage(byte[] msg) { // Receive messages from the RCX if (null != msg) { System.out.println( "RCX message: " + byteArrayToString(msg));... event) { //try { // Must be implemented from the // DiscoveryListener interface // This method will be called with a list Continued www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 375 Programming LEGO MINDSTORMS with Jini • Chapter 9 Figure 9. 13 Continued // of all lookup services found // (actually their registrar proxies) ServiceRegistrar[] regArray = event.getRegistrars(); try { proxy = . architec- ture for a situation involving LEGO MINDSTORMS and Jini. www.syngress.com 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 355 356 Chapter 9 • Programming LEGO MINDSTORMS with Jini Selecting the. locally Embedded device (e.g., LEGO MINDSTORMS) Proprietary protocol (e.g., RCX opcodes over infrared link) 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 357 358 Chapter 9 • Programming LEGO MINDSTORMS with Jini Interfacing. code (see Figure 9. 12).That certainly beats coding it by hand! www.syngress.com Figure 9. 11 Continued 177 _LEGO_ Java_ 09. eps 4/3/02 1:11 PM Page 361 362 Chapter 9 • Programming LEGO MINDSTORMS with