GSAMS Java Java Networking Networking Topics • Overview: A Short History of Java Networking – Where from; where to; “Are We There Yet?” • Competing Networking Models: OSI v. Internet – Whose Kung Fu is Better? • Network Protocols in Java: – – – – TCP UDP Multicast Threaded and Non-Threaded examples • Remote Method Invocation (RMI) A Short History of Java Networking • The Dark Ages: C/C++ Networking and the Need for Change – Before Java, simple network connectivity required lots of code. – C/C++ provided many, many choices/options for networking • Good for C programmers. • Most programmers did not use all these options. • Java 1.0 Reflects Java’s non-academic motivations – Basic networking capability with high level abstraction of URL handlers. – Low level choices were missing (e.g., connection timeouts.) – Raw socket classes were final--java.net.Socket and java.net.ServerSocket could not be extended. – “One shot” implementations with java.net.SocketImpl allowed only one implementation in each VM. A Short History of Java Networking • JDK 1.1 (the big leap) – Added four common socket options. – Allowed raw data manipulations within high-level abstractions. – Removed final designations. – Added multicast connections (previously limited to LAN and/or external native calls). • JDK 1.2 (a second big leap) – Addition of fine-grained Java Security Model. • ability to control processes/connections/ports with very fine granular control over details. – Added functionality to APIs. Competing Networking Models: OSI vs. Internet Whose Kung Fu is Better? • Two competing models are used to describe networking – Open Systems Interconnection (OSI) architecture – Internet Architecture • Open Systems Interconnection Standard – partitions network connectivity into seven layers. – published by OSI and International Telecommunications Union (ITU) in series of “X dot” standards: X.25, X.400, etc. • Internet Architecture Standard – also known as “TCP/IP architecture” after its two key protocols – evolved from packet-switched ARPANET of DoD – Internet Engineering Task Force (IETF) maintenance. A Comparison of the OSI Seven Layer Model and the Taco Bell Seven Layer Burrito Application Presentation Session Transport Network Data in use Syntax Packet recovery; sessions between applications Message quality; end-to-end error correction Packet routing; network connections Sour Cream Cheese Guacamole Tomatoes Lettuce Data Link Bit level organization; reliability Seasoned Rice Physical Media, electrical properties Refried Beans End Host Application OSI in Action End Host Network traffic in the OSI model must always travel up and down the protocol stack to be routed. Application Presentation Presentation Session Session Transport Nodes in Network Transport Network Network Network Network Data Link Data Link Data Link Data Link Physical Physical Physical Physical Internet Architecture Model Design theory: “rough consensus and running code” Application Protocols Transport Protocols (TCP/UDP) Internet Protocol (IP) Network Protocols The IETF culture requires that new layers provide a protocol specification and at least two or three running implementations. Likely, the four layer model will not grow in complexity (unless the internet does first.) Internet Architecture Model The Internet Architecture is also flexible; applications can reach the network layer directly, or work through protocols such as TCP/UDP and IP APPLICATION TCP UDP IP NETWORK Internet Architecture Model Transport through each layer requires the use of additional headers. Application Layer DATA Transport Layer Internet Layer Network Layer HEADER HEADER DATA HEADER HEADER DATA HEADER HEADER DATA Network Protocols in Java In terms of protocols and services, Java offers API support for three basic types of connectivity: TCP -- Transport Control Protocol UDP -- User Datagram Protocol java.net package Mcast -- Multicast Protocol. -- Basic Terminology -A Socket is an abstraction of a "communications link" between machines over some network. Socket communication is the same regardless of whether the network connection is via a phone line, cable modem, ethernet, or fiber-optic line. A packet is a discrete quantity of information suitable for routed transport over a shared network. Packet sizes are limited, so a packet may be a fragment of a large file or message. Basic Terminology (cont’d) Naming and Addressing The “IPv4” addressing scheme uses 32-bit addresses, often presented as a set of four octets. Read from left to right, we progressively define a particular machine: 192.168.1.200 The “dotted quad” or “dotted decimal” format is quite common. Numbering is controlled by the Internet Assigned Numbers Authority (IANA), and later the Internet Corporation for Assigned Names and Numbers (ICANN). IP Address Classes A 127.0.0.0 and below First bit == 0 B 127.0.1.0 to 191.255.255.255 First bits == 1 0 C 192.0.1.0 to 223.255.255.255 First bits == 1 1 0 D Not allocated to networks -- mcast First bits == 1 1 1 0 E 240.0.0.0 and above; not assigned First bits == 1 1 1 1 Special numbers: 127.0.0.1 -- used for loopback addresses 192.168.x.x -- class B private networks 10.x.x.x. -- class A private networks IP Address Classes The IPv4 address scheme is limited by potentially wasteful address allocation. For example, the jump from class C (one octet, or ~254 addresses) to class B (two octets, or ~65334 addresses) is quite large. So, a company with more than 250 computers might request a class B address, but use far less than the ~65,000 possible combinations. This inefficiency consumes the address space quickly. Solutions include more efficient number allocations, ip chaining (firewalls), and migration to IPv6 (128 bit address), . . . even IPv8. Java’s underlying native code has support for IPv6, so it will be ready if/when companies (notably Cisco) decide to embrace IPv6. IP Addresses: Names To make IP addresses readable, a table of names is used to represent IP numbers. The names are in a hierarchical order; however, there is no one-to-one mapping between name hierarchies and dotted quad number hierarchies. DNS, or Domain Name Service, provides a name to IP addressing service. . net gov mil (Root) org com edu On a Unix machine, one often uses “nslookup” or some similar program to covert between names and numbers. Ports Most computers have a single network connection used for all data. Most operating systems allow multi-tasking, so several network applications can be running at once. How does the computer know which application gets the data? Ports allow us to specify specific applications on the host. The TCP and UDP protocols attach port information (16 bit number) to each packet. Machine2 Machine1 Machine3 Thus, when creating connections between computers, we need an IP address as well as the port to specify the machine and client process The java.net.* Package Key Classes: java.net.InetAddress IP Address structures, and services java.net.ServerSocket This class implements server sockets. java.net.Socket This class implements client sockets (also called just "sockets"). java.net.URL Class URL represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web. Address representations java.net.InetAddress: -- A class representing an IP address -- Performs validity checking on IP names -- Thus, there are no public constructors! -- Instead, just call a static class method to obtain an InetAddress: InetAddress has no public constructors since it performs validity checking of all IP names import java.net.*; public class MyMachineName { public static void main (String arg[]){ InetAddress local = null; try { local = InetAddress.getLocalHost(); } catch (UnknownHostException e){ System.err.println ("Identity Crisis!"); System.exit(0); } String strAddress = local.getHostName(); System.out.println ("Local Host = " + strAddress); } } Converting IP Numbers to Strings import java.net.*; This does things public class MyMachineName { the hard way! public static void main (String arg[]){ InetAddress local = null; Just call try { getHostAddress() local = InetAddress.getLocalHost(); instead! } catch (UnknownHostException e){ System.err.println Lesson: Java’s ("Identity Crisis!"); net package has most System.exit(0); every method you } will need! byte[] b = local.getAddress(); String strAddress=“”; for (int i = 0; i < b.length; i++) strAddress += ((int)255 & b[i]) + “.”; System.out.println (“Local = “ + strAddress); } } Other InetAddress Services public boolean isMulticastAddress(); static InetAddress[] getAllByName(String host); Determines all the IP addresses of a host, given the host's name. static InetAddress getByName(String host); Determines the IP address of a host, given the host's name. Let’s duplicate the basic service of the Unix command “nslookup” in Java. This will allow us to convert any IP name into its valid IP number. (We won’t support the -opt switches, to keep things simple.) From the Unix “nslookup man page(1M)”: “nslookup is an interactive program to query ARPA Internet domain name servers. The user can contact servers to request information about a specific host, or print a list of hosts in the domain.” import java.net.*; public class nslookup { public static void main(String arg[]){ if (arg.length == 0) showUsage(); InetAddress[] names = null; try{ names = InetAddress.getAllByName(arg[0]); } catch (UnknownHostException e){ System.err.println("Error: Unknown Host: " + arg[0]); System.exit(1); } for (int i=0; i< names.length; i++) System.out.println ("\nName: " + names[i].getHostName() + "\nAddress: " + names[i].getHostAddress()); }//main public static void showUsage(){ System.out.println ("Usage:\n\tnslookup <ip name>"); System.exit(0); }//showUsage }//nslookup Sockets . . . and Threads Sockets -- The Basics: TCP -- The Not-So-Basics -- The Basics: UDP Quick Notes on Threads -- A Threaded Example Sockets A java.net.Socket provides an easy interface to a TCP connection. Of the eight Socket constructors, there are generally four commonly used: public Socket(InetAddress address, int port); Creates a stream socket and connects it to the specified port number at the specified IP address. public Socket(InetAddress address, int port, InetAddress localAddr, int localPort); Creates a socket and connects it to the specified remote address on the specified remote port. public Socket(String host, int port); Creates a stream socket and connects it to the specified port number on the named host. public Socket(String host, int port, InetAddress localAddr, int localPort); Creates a socket and connects it to the specified remote host on the specified remote port. Sockets Additionally, there are several commonly used Socket methods: public InputStream getInputStream(); Returns an input stream for this socket. public OutputStream getOutputStream(); Returns an output stream for this socket. The streams returned from these accessors rely on TCP’s error correction and flow control. public void close(); Closes this socket. public int getPort(); Returns the remote port to which this socket is connected. public InetAddress getInetAddress(); Returns the address to which the socket is connected. public int getLocalPort(); Returns the local port to which this socket is bound. Sockets: Simple Usage We can therefore create a simple Socket with: Socket s = new Socket (“acme.gatech.edu”, 13); We can obtain a Stream for receiving information with: InputStream in = s.getInputStream(); We can obtain a stream for sending information with: OutputStream out = s.getOutputStream(); Socket Options in Java Often, it is necessary to change basic features in Sockets. Berkeleyderived sockets allow one to set various standard options Some options are available only for ServerSockets (explained later). Standard socket options available in Java include: SO_LINGER -- The “Socket Option Linger”; for when a close call catches material still in the send buffer SO_TIMEOUT -- The “Socket Option Timeout”; determines how long a connection may be idle before timeout. TCP_NODELAY -- The so-called “Nagle’s Algorithm” option; useful for WAN-based applications where small packets are likely (rlogin, ssh, telnet, etc.), and limited window sizes will lead to delay. Socket Options in Java public void setSoLinger(boolean on, int linger); Enable/disable SO_LINGER with the specified linger time in seconds. public void setSoTimeout(int timeout); Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. public void setTcpNoDelay(boolean on); Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). public int getSoLinger(); Returns setting for SO_LINGER. public int getSoTimeout(); Returns setting for SO_TIMEOUT. public boolean getTcpNoDelay(); Tests if TCP_NODELAY is enabled. public void setSendBufferSize(int size); Sets the SO_SNDBUF option to the specified value for this DatagramSocket. TCP/UDP Connections Distinguished The foregoing examples used TCP connections: session oriented connections with a high degree of congestion control and error correction. The Socket abstraction hides the SocketImpl class, which provides these services. To that extent, TCP is said to provide “reliable” services. unreliable but fast Datagram TCP error correction; reliable TCP There may be circumstances where we don’t want the overhead associated with reliability. UDP, user datagram protocol, allows us to use so-called ‘unreliable’ networking services. UDP is a datagram-oriented protocol, meaning there is no session. A Day Time Client From RFC 867 (J. Postel, 1983): “One daytime service is defined as a connection based application on TCP. A server listens for TCP connections on TCP port 13. Once a connection is established the current date and time is sent out the connection as a ascii character string (and any data received is thrown away). The service closes the connection after sending the quote.” Thus, we simply write a program to connect to a server’s port 13, and output the message. import java.net.*; import java.io.*; public class DayTimeClient { public static final int iDAY_TIME_PORT = 13; // RFC 867 public static final int BUFF = 256; public static void main (String arg[]) throws Exception { if (arg.length == 0) showUsage(); Socket sock = new Socket(arg[0], iDAY_TIME_PORT); InputStream is = sock.getInputStream(); int i; byte[] b = new byte[BUFF]; while ( (i=is.read(b)) != -1) System.out.println(new String(b, 0, i)); } public static void showUsage(){ System.err.println ("Usage:\n\tjava DayTimeClient <IP Address>"); System.exit(0); }//showUsage }//class DayTimeClient Reading and Writing From Sockets Sockets in Java are full duplex--meaning that they can both send and receive information. A good example of this feature is an echo client. The echo service listens to Port 7, and simply returns or ‘echoes back’ all information received from the client. An echo client, therefore, has to be able to send and receive over the same socket. Let’s look at an echo client written by Sun. . . . public class EchoClient { public static void main(String[] args) throws IOException { Socket echoSocket = null; PrintWriter out = null; BufferedReader in = null; try { echoSocket = new Socket("taranis", 7); out = new PrintWriter(echoSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader( echoSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println ("Don't know about host: taranis."); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for " + "the connection to: taranis."); System.exit(1); } Source: http://www.javasoft.com BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println ("echo: " + in.readLine()); } out.close(); in.close(); stdIn.close(); echoSocket.close(); } } Analysis and An Idea COMMENTS: The entire program is a single static method (This is fine for an example). The program seems to repeat much of the code from our example. In fact, at some level this resembles most IO stream handlers. IDEA: Let’s abstract out the common stream handling features, and make a client that reads from/writes to a stream. We can then use this client for a variety of purposes. NOTION: We should thread the class we create. The need for threading requires more explanation. ServerSockets We may also wish to have a process listening on a port for incoming calls. Perhaps we’ve written a web browser that stands ready to send HTML content when a connection is made. The ServerSocket class allows us to capture incoming calls. ServerSocket outSock = new ServerSocket (6000); while (true) { Socket inSock = outSock.accept(); handleConnection(inSock); inSock.close(); } ServerSockets public ServerSocket(int port); Creates a server socket on a specified port. public ServerSocket(int port, int backlog); Creates a server socket and binds it to the specified local port number. public ServerSocket (int port, int backlog, InetAddress bindAddr); Create a server with the specified port, listen backlog, and local IP address to bind to. public Socket accept(); Listens for a connection to be made to this socket and accepts it. public void close(); Closes this socket. public InetAddress getInetAddress(); Returns the local address of this server socket. Blocking Network Calls When using ServerSockets, be aware: the call to “accept()” causes the program to wait until the method returns. This “blocking call” can cause a program to hang. ServerSocket servSock = new ServerSocket (6000); while (true) { Socket inSock = servSock.accept(); handleConnection(inSock); inSock.close(); } If other operations must take place, we need some way of placing the “accept()” call in its own thread. Threads Quickly, Incompletely Defined: A Thread is a lightweight process (chunk of code) that can act independently of another any other code in an application. They allow us to give the illusion that more than one process is running at the same time. How-To in Java: Subclass java.lang.Thread, or implement the interface Runnable and provide a method: public void run() { // threaded code goes here } The method run() is started via a call to “start()”. (One can also override start(), but this is not always necessary.) Simple Threads Example public class Clock extends Thread { String strSound; public Clock(String strSound){ Note use of infinite loop this.strSound = strSound; in the run() method } public void run(){ while (true){ System.out.println (strSound); try{ this.sleep(1000);}// DOES NOT BLOCK catch (InterruptedException e){} } } This code can run while other threads are being run! public static void main (String arg[]){ Clock c1 = new Clock("Tick"); Clock c2 = new Clock("Tock"); c1.start(); c2.start(); } }// class Clock Order of execution between c1 and c2 is not guaranteed! Thread Issues Race Conditions: Access to shared objects may result in race conditions. One can guard against this mutexing or synchronizing blocks of code: public synchronized void increaseAccount(){ // mutexed operations } Deadlocks: Pervasive use of semaphores can, if improperly designed, result in deadlock conditions. Threaded Socket Clients Using these principles, we can design a simple client that opens a TCP socket, and has facilities for reading/writing to the socket. SocketClient Interface SocketClientConstants Interface DefaultSocketClient Class The key is that the client class Threads itself--preventing it from blocking other code. Threaded Socket Clients: Interfaces public interface SocketClientInterface { boolean openConnection(); void handleSession(); void closeSession(); } public interface SocketClientConstants { int iECHO_PORT = 7; int iDAYTIME_PORT = 13; int iSMTP_PORT = 25; boolean DEBUG = true; } import java.net.*; import java.io.*; public class DefaultSocketClient extends Thread implements SocketClientInterface, SocketClientConstants { private private private private private BufferedReader reader; BufferedWriter writer; Socket sock; String strHost; int iPort; public DefaultSocketClient(String strHost, int iPort) { setPort (iPort); setHost (strHost); }//constructor public void run(){ if (openConnection()){ handleSession(); closeSession(); } }//run public boolean openConnection(){ try { sock = new Socket(strHost, iPort); } catch(IOException socketError){ if (DEBUG) System.err.println ("Unable to connect to " + strHost); return false; } try { reader = new BufferedReader (new InputStreamReader(sock.getInputStream())); writer = new BufferedWriter (new OutputStreamWriter (sock.getOutputStream())); } catch (Exception e){ if (DEBUG) System.err.println ("Unable to obtain stream to/from " + strHost); return false; } return true; } public void handleSession(){ String strInput = ""; if (DEBUG) System.out.println ("Handling session with " + strHost + ":" + iPort); try { while ( (strInput = reader.readLine()) != null) handleInput (strInput); } catch (IOException e){ if (DEBUG) System.out.println ("Handling session with " + strHost + ":" + iPort); } } public void sendOutput(String strOutput){ try { writer.write(strOutput, 0, strOutput.length()); } catch (IOException e){ if (DEBUG) System.out.println ("Error writing to " + strHost); } } public void handleInput(String strInput){ System.out.println(strInput); } public void closeSession(){ try { writer = null; reader = null; sock.close(); } catch (IOException e){ if (DEBUG) System.err.println ("Error closing socket to " + strHost); } } public void setHost(String strHost){ this.strHost = strHost; } public void setPort(int iPort){ this.iPort = iPort; } public static void main (String arg[]){ /* debug main; does daytime on local host */ String strLocalHost = ""; try{ strLocalHost = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e){ System.err.println ("Unable to find local host"); } DefaultSocketClient d = new DefaultSocketClient (strLocalHost, iDAYTIME_PORT); d.start(); } }// class DefaultSocketClient Threaded Client Sockets To use this threaded client, we merely subclass it, override the handleConnection() method, and spawn off new instances. DefaultSocketClient start() Networked GUI Our Specific Socket Client (e.g., web browser) View (Buttons, etc.) Sending Mail We can use our Threaded Client to write a simple e-mail client. We won’t implement the user interface beyond a debug test main. (We could design a complex GUI, but won’t right now.) DefaultSocketClient MailSocket -- Changes handleSession() to match RFC mail requirements QuickMail (Sends a test message) Sending Mail public class MailSocket extends DefaultSocketClient { String strMessage; String strRecipient; String strSender; public MailSocket(String strServer, String strSender, String strRecipient, String strMessage){ super (strServer, iSMTP_PORT); this.strMessage = strMessage; this.strRecipient = strRecipient; this.strSender = strSender; } /* cont’d . . . Note that we could also have more constructor variables-subject line, etc. etc. */ Sending Mail public void handleSession(){ if (DEBUG) System.out.println("Sending mail"); sendOutput("HELO fromQuickMail\n"); /* Some servers verify this!!*/ sendOutput("MAIL FROM: <" + strSender + ">\n"); sendOutput("RCPT TO: " + strRecipient + "\n"); sendOutput("DATA\n"); sendOutput("Subject: Here's some mail\n"); sendOutput("From: Your Name <" + strSender + " >\n"); sendOutput("\n\n"); /* send the message */ sendOutput(strMessage); sendOutput("\n.\n\n"); sendOutput("QUIT\n"); if (DEBUG) System.out.println("Sent mail"); } }// class MailSocket Sending Mail public class QuickMail { public static void main (String arg[]){ MailSocket mSock = new MailSocket ("mail.mindspring.com", "david.dagon@mindspring.com", "david.dagon@mindspring.com", "Hi Dave,\n Here’s some mail from GSMAS.\n"); mSock.start(); } } // class QuickMail Note: I don’t mind getting e-mail from GSAMS participants; you might want to use your own e-mail address when testing. URLs A URL specifies the location of networked resource--a file, for example. A common URL is one for a web resource: http://www.javasoft.com Host Name -- the name of the machine ‘hosting’ the resource Other protocols are possible: Filename -- the pathname to the file on the host ftp:// file:// jdbc:// Port Number -- the port number to which to connect (optional). Reference -- a reference to a named anchor within a resource that usually identifies a specific location within a file (optional). URLs The easiest way to create a URL in Java is to start with a String: try{ URL myURL = new URL (“http://www.cc.gatech.edu”); } catch (MalformedURLException e){ System.err.println (“This method: “ + e.toString()); } URLs can also be created relative to an existing URL: try{ URL firstURL = new URL(“http://www.foo.com/top_level”); URL secondURL = new URL (firstURL, “lower_level”); } catch (Exception e){;} Using URLs URLs contain many useful methods, including: getProtocol(); returns the protocol identifier component of the URL (ftp/http, e.g.). getHost(); returns the host name component of the URL. getPort(); returns the port number component of the URL (or -1 if not set). getFile(); returns the filename component of the URL. getRef(); returns the reference component of the URL. import java.net.*; import java.io.*; public class URLReaderExample { public static void main (String[] args) throws Exception { if (args.length == 0) showUsage(); URL myURL = new URL("http://” + args[0]); BufferedReader in = new BufferedReader(new InputStreamReader (myURL.openStream())); String strInput; while ((strInput = in.readLine()) != null) System.out.println(strInput); in.close(); } public static void showUsage(){ System.err.println ("Usage:\n\tjava URLReaderExample <IP Address>"); System.exit(0); }//showUsage }//URLReaderExample Connecting to a URL One can also use “openConnection()” to connect to a URL. This creates a communication link between your Java program and the URL over the network. For example: try { URL myURL = new URL("http://www.blarg.foo.org/"); myURL.openConnection(); } catch (MalformedURLException e) { ; } catch (IOException e) { ; } One can then interact with the remote URL. (E.g., POST information to web pages Extracting the Stream from a URL import import public public java.net.*; java.io.*; class URLConnectionReader { static void main (String[] args) throws Exception { URL myURL = new URL("http://www.cc.gatech.edu/"); URLConnection uc = myURL .openConnection(); BufferedReader in = new BufferedReader (new InputStreamReader (uc.getInputStream())); /* see also getOutputStream() */ String inputLine; while ((inputLine = in.readLine()) != null) System.out.println(inputLine); in.close(); } } RMI: “Distributing the Object” Being a Analysis of Java’s Remote Method Invocation Capabilities 1. Motivation CORBA & RMI: Two Flavors of Alphabet Soup? Maybe you’ve heard of CORBA and/or RMI: the “new hot thing” in computing. GUI CORBA HTML OMG IDL Thin Clients (CGI) URL Java ORB (UDP) Foo (TCP/IP) Null Clients HORB (Non-type-safe Interaction) It’s hard to understand CORBA and RMI, in part because of all the confusing terms... What is CORBA? DEFINED The Common Object Request Broker Architecture defines a model of distributed computing. PURPOSE Allows local invocation of operations on objects located anywhere on a network. What is RMI? DEFINED Java’s Remote Method Invocation defines a model of distributed computing. PURPOSE Allows local invocations of operations on objects located anywhere on a network. What’s the Difference? • • • • • CORBA supports multiple languages RMI is (relatively) easier to implement RMI allows optimization of protocols for communication CORBA separates interface definitions from implementation HORB, a variant of RMI, actually works in JDK 1.02 (and therefore in most browsers without error). Here, we only add to the confusion with more terms. It might help to see an example of RMI. 2. “Hello RMI” A Contrived Experiment With RMI For this experiment, you will need: Two Java-ready Computers Two Fake Passports Three round trip tickets to Moscow 3,500 Miles of CAT-5 wiring John Doe BOARDING PASS An MI5 License to Thread Experiment With RMI (Cont’d) 1. Hook one end of the CAT 5 cable to a plane, and the other end to the first computer. 2. Fly to Moscow. 3. Blend in with the natives, and connect the cable to the second computer. Experiment With RMI (Cont’d) 4. Return home. You now have a network.* RMI lets us invoke methods that are found on the remote machine. So let’s write a little program . . . *(You may use a different networking configuration, if you’re short on cable.) Program: Matryoshka Doll Server We will use this network to smuggle Matroyska dolls into the U.S. Our Moscow server will take in a doll, and return a new instance, encapsulating our original reference. Stubs DollClient public MatroyskaDoll getDoll (MatroyskaDoll d); Moscovite Implementation DollServer public MatroyskaDoll getDoll (MatroyskaDoll d) { // working code } RMI Design Cycle Start Client Data Types policy Define Interface Implement Interface javac Implement Client Client Stub CLIENT Compile javac rmic Bind Objects Start Server policy Start RMI Registry Server class Server skeleton SERVER 1. Common Data Type public class Matryoska implements java.io.Serializable { private Matryoska child; private String strMicrofilm; public Matryoska(String strMicrofilm, Matryoska child){ setChild(child); setMicrofilm(strMicrofilm); This is merely } public void setMicrofilm (String strMicrofilm){ a linked list this.strMicrofilm = strMicrofilm; structure, but } it must support public void setChild(Matryoska child){ serialization this.child = child; } public Matryoska getChild(){ return child; } public String getMicrofilm(){ return strMicrofilm; } }// class Matryoska TOP SECRET: 2. Define The Interface import java.rmi.Remote; import java.rmi.RemoteException; public interface DollDesign extends Remote { Matryoska getDoll(Matryoska doll) throws RemoteException; } This means our object is Callable from any VM Communication failure or protocol problem occurs The java.rmi package: java.rmi java.rmi.activation java.rmi.dgc java.rmi.registry java.rmi.server 3. Implement the Interface Or extend java.rmi.activation. Activatable (JDK 1.2) import java.rmi.*; import java.rmi.server.*; import java.net.*; public class DollImplementation extends UnicastRemoteObject implements DollDesign { public DollImplementation() throws RemoteException { super(); } public Matryoska getDoll(Matryoska doll) throws RemoteException { return new Matryoska (strMessage, doll); } public static String strMessage=”From Moscow With Stubs"; Causes object to be exported Convenience class; defines java.lang.Object methods to work with RMI (exports bytes, etc.) calls UnicastRemoteObject. exportObject() 3. Implement Interface (cont’d) public static void main (String arg[]) { if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager()); try { String name = "secretAgent"; /* or “//host/secretAgent” */ strMessage = InetAddress.getLocalHost().getHostName(); DollDesign secretAgent = new DollImplementation(); System.out.println ("Attempting to bind name:" + name); Naming.rebind(name, secretAgent); System.out.println ("Mission accomplished." + "\n\tOperative: " + name + "bound on " + strMessage); } catch (Exception e) { System.err.println ("Nyet."); System.err.println (e.getMessage()); Note the upcasting to e.printStackTrace(); the interface from the } }//main implementation; this }//DollImplemenation exposes the stubs and not the object! 4. Compile (javac & rmic) Bash Bash Bash Bash % % % % javac Matryoska.java javac DollImplementation.java rmic DollImplementation ls -l -rw-rw-r--rw-rw-r--rw-rw-r--rw-rw-r--rw-rw-r--rw-rw-r--rw-rw-r--rw-rw-r-- 1 1 1 1 1 1 1 1 boris boris boris boris boris boris boris boris 247 181 1757 1293 1729 3225 530 393 Apr Apr Apr Apr Apr Apr Apr Apr 12 12 12 12 12 12 12 12 14:36 14:20 14:58 14:58 15:28 15:28 14:36 14:36 Bash% Skel == Server Stubs == Client Rmic is an RMI compiler for java. It’s in the soup. DollDesign.class DollDesign.java DollImplementation.class DollImplementation.java DollImplementation_Skel.class DollImplementation_Stub.class Matryoska.class Matryoska.java 6. From Moscow With Stubs Note use of debugging listing of available objects import java.rmi.*; import java.net.*; public class DollClient{ public static void main (String arg[]) { if (System.getSecurityManager()==null) System.setSecurityManager (new RMISecurityManager()); try { String name = "//" + arg[0] + "/secretAgent"; String[] agents = Naming.list(name); for (int i=0; i<agents.length; i++) System.out.println ("\t-->" + agents[i] + " found"); String localHost = InetAddress. getLocalHost().getHostName(); DollDesign secretAgent = (DollDesign) Naming.lookup(name); Matryoska doll = new Matryoska (localHost, null); doll = secretAgent.getDoll(doll); System.out.println ("Done”); while (doll!=null){ System.out.println ("Doll-->" + doll.getMicrofilm()); doll=doll.getChild(); } }//try 6. From Moscow With Stubs (cont’d) catch (Exception e){ System.err.println ("Error: " + e.getMessage()); e.printStackTrace(); } }//Main }//DollClient NOTE: NOTE: NOTE: We only create an instance of the interface, not the remote implementation Client must set a security manager as well; a similar policy must be invoked Remote resolution of stubs requires use of security manager Smuggling Matryoska Dolls grant { permission java.net.SocketPermission "*:1024-65535", “connect,accept"; permission java.net.SocketPermission "*:80", "connect"; }; 7. OK. Fly back to Moscow and make a file called “java.policy” Bash % rmiregistry & [5] Pid 4523 Bash % unset CLASSPATH Bash % java Djava.security.policy=java.policy DollImplementation & [6] Pid 4524 Set the policy Bash % file on startup with the -D option, or else! 8. Start The Client Bash % java -Djava.security.policy=java.policy DollClient 8. Fly home and create a similar policy file for the client. Start the service. Failure to start with the policy option will throw a security exception. This is new to JDK 1.2 3. RMI Redux RMI Summary: Value RMI passes local objects by value--a copy gets sent. RMI passes remote objects by reference--not by copying. RMI stubs are proxies for the client to remote object (just like CORBA). RMI Summary: Errors & GC Additional errors can be encountered; new exceptions must be addressed. RMI uses a reference counting gc; a zero count marks the distributed object as a weak reference. A “lease” is made on remote objects with a “dirty” call. Leases must be renewed or will expire. The java.rmi.server.Unreferenced interface allows objects to receive notification just prior to finalization. (Single method: unreferenced() ). RMI Summary: LocateRegistry Default port: 1099 Class java.rmi.registry.LocateRegistry helps locate remote registries: public static Registry getRegistry(); /* local */ public static Registry getRegistry (String host, int port); public static Registry getRegistry (String host); public static Registry createRegistry (int port); java.rmi.Naming Class void bind(String name, Remote obj) throws RemoteException, AlreadyBoundException, AccessException; Binds the specified name to a remote object. String[] list() throws RemoteException, AccessException; Returns an array of the names bound in the registry. Remote lookup(String name) throws RemoteException, NotBoundException, AccessException; Returns a reference, a stub, for the remote object associated with the specified name. void rebind(String name, Remote obj) AccessException; throws RemoteException, Rebinds the specified name to a new remote object. void unbind(String name) throws NotBoundException, AccessException; RemoteException, Destroys the binding for the specified name that is associated with a remote object. RMI: Dynamic Stub Loading Dynamic stub loading is used where the client does not have local copies of the remote object. The stubs can even be generated on the fly. Lookup: 1) Local resolution attempted via CLASSPATH -- no security manager required 2) Server tells client the stub’s URL java.rmi.server.codebase -- can even be a third party Registry ! -- requires security manager Java Native Invocation “Return of the Native Method” Native Methods--What? • Through Java Native Interface (JNI), Java supports the ability to call native methods--shared object files written in languages such as C/C++ • The native keyword is used to identify methods that have implementation defined in such shared object files: public void native sayHello(); JNI Java program Share Native object Native Methods -- Why? • Before writing JNI, consider: – Could the program be rewritten in/ported to Java? – It’s possible to write programs quickly in Java – What happens to portability? • JNI provides a means of providing cross-platform native code, but only if appropriate libraries are generated for each machine. (Code is portable, but not WORA--write once, run anywhere.) JNI Generation--Overview Java Source with native Shared Object File (observe naming conventions) javac Java Class File (byte code) Java Source File loading library javah -jni C/C++ Header #include JNI Implementation (C/C++ Code) Execution Step 1: Organizing JNI vs. Java Functionality First, decide what code belongs with native methods, and what is better left to Java. Consider: 1) JNI means loss of visibility modifiers (even private members can be discovered!) 2) JNI means manual garbage collection-difficult at times 3) JNI can imply a loss of OO control, unless native implementation is in C++, or a very OO-oriented set of C libraries. public class HelloWorld { public native void sayHello(String strMessage); public void speakUp() { System.out.println ("Java Says Hello, C"); sayHello("C Says Hello, Java"); }// speakUp() }// class HelloWorld Step 2: Generate Header File 1) Use javah tool from JDK to create header file. 2) Target your compiled class file. 3) Be sure to use the -jni switch: %javah -jni HelloWorld 4) For older, non-JNI header files, use the -stub option with javah. (Ask: why are you using the old native invocation anyway? Consider upgrading to 1.1) 5) Don’t edit the source Java file (e.g., even adding a package statements invalidates the header file.) This should create a header file essential for your implementation . . . /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloWorld */ #ifndef _Included_HelloWorld #define _Included_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: HelloWorld * Method: sayHello * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif See the “Do Not Edit” Warning? Obey! Step 3: Implement Your JNI Method 1) #include the header from javah 2) Use the function prototypes generated by javah 3) Remember that Java Strings are objects, not char arrays. 4) Consult the JNI API for a list of helpful functions and methods 5) Note that ALL functions have two parameters, at least: JNIEnv * env, jobject thisObj These are references to the VM and “this” object, respectively. They are the window into the process running in the VM. #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv * env, jobject thisObj, jstring strMessage){ const char *str = (*env)->GetStringUTFChars(env, strMessage, 0); printf("%s\n", str); (*env)->ReleaseStringUTFChars(env, strMessage, str); }// JNI method /*Note need to convert chars from Unicode to UTF, and then free created char buffer from the VM */ 4. Compile the Object Observe the naming convention: lib<ShareObject>.so LINUX: %cc -I$JDK_HOME/include -I$JDK_HOME/include/genunix \ -shared -o libHello.so Hello.c SOLARIS: %cc -I$JDK_HOME/include -I$JDK_HOME/include/solaris \ -G -o libHello.so Hello.c WINDOWS: Consider using a GNU port, or the upcoming batch file ... ftp://go.cygnus.com/pub/ftp.cygnus.com/ gnu-win32/gnu-win32-b18/cdk.exe # # Makefile for Linux JNI Compilation # UPATH = /usr/bin/ JDK_PATH = /usr/local/jdk117_v1a/ # define utility programs and options CC = $(UPATH)cc CFLAGS = -I$(JDK_PATH)include -I$(JDK_PATH)include/genunix -shared MAKE = $(UPATH)make CTAGS = $(UPATH)ctags For Solaris, INDENT = $(UPATH)indent -bl -c41 -i4 -l72 -pcs change to # default target - builds Hello executable # /solaris Hello: Hello.c and -G $(CC) $(CFLAGS) -o libHello.so Hello.c # "debug" target - builds executable with debug code # debug: Hello.c @CFLAGS="$(CFLAGS) -DDEBUG";export CFLAGS;$(MAKE) -e # "pretty" target - beautify source files pretty: Hello.c ls $? | xargs -p -n1 $(INDENT) @touch pretty # "clean" target - remove unwanted object files and executables clean: rm -f Hello Hello.o pretty tags lint nohup.out a.out core Windows Batch File @echo Batch File for JNI W95 DevStudio 5 @echo Making clean @del *.obj @del *.lib @del *.dll @del *.pch @del *.pdb @del *.exp @del *.idb @echo Compiling all C files from this directory... @cl -I%JDKPATH%\include -I%JDKPATH%\include\win32 /nologo /MTd /W4 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c /LD *.c @echo linking ... @link /nologo /subsystem:windows /dll /incremental:no /machine:I386 /out:%1.dll *.obj 5. Load the Library public class Test{ static { /* * Our library is in a file called "libHello.so", but * just pass in "Hello" since Java will prepend "lib" * and append the ".so" extension. Windows users * should omit the “.dll” extension as well. */ System.loadLibrary("Hello"); }// static load public static void main (String arg[]) { HelloWorld hw = new HelloWorld(); hw.speakUp(); } // main }// class Test 6. Execute 1) Make sure the library is in the path of the environment variable: LD_LIBRARY_PATH 2) For your shell, you can set: LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH 3) Windows users need to set the PATH appropriately: PATH=%PATH%;c:\MyLibrary 7. DEBUG (and repeat . . .) Numerous problems may come up during compilation. Resources for debugging JNI problems: http://www.codeguru.com/java/JNI/index.shtml http://www.mindspring.com/~david.dagon/jni/Native.txt Concluding Thoughts Thank you for your patience, and very constructive suggestions! I’ve enjoyed working on this lecture series. If you’re ever called upon to deliver a satellite lecture, may I offer some of my experiences? Lessons learned: -- GSAMS lectures are a different beast. -- Two-way connectivity is present, but needs to be facilitated. -- Lecture materials need can develop contextual dependencies that make them unsuitable/irrelevant for wider audiences. -- Provide supplemental feedback mechanisms: the “television” effect seems to limit some types of discussion, and encourage others. -- The lecture ends when the network goes down.