The client-half object pattern has been given as a means of abstracting away from particular middleware solutions, so that code not directly involved in networking is not affected by changes of middleware.
In this section, we look at the "simple ftp" problem considered earlier, and give a variety of solutions using different middleware systems
We specify the service using a Java interface.
package common;
import java.rmi.RemoteException;
import java.io.IOException;
public interface FileTransfer extends java.rmi.Remote {
public boolean chdir(String dir) throws RemoteException;
public String[] dir() throws RemoteException;
public String getdir() throws RemoteException;
public String get(String filename) throws RemoteException, IOException;
public void quit() throws RemoteException;
}
There is a concession to lessons from RMI being adopted by a number of middleware systems:
in recognition that network errors can occur in calling remote services, the interface
extends Remote
and all methods throw RemoteException
.
This exception should not be visible "on the wire" since it is not a cross-platform
solution - for example, .NET remoting has its different equivalent
System.Runtime.Remoting.RemotingException
However, these exceptions are normally generated in the client HOPP and do not
appear on the wire. Web Services don't conform to this and the WSDL lists
remote exceptions even though they are not generated on the service side.
package server;
import java.io.File;
import java.io.IOException;
import common.FileTransfer;
public class FileTransferServerHOPP implements FileTransfer {
File clientDir = new File(".");
public boolean chdir(String dir) {
File newDir = new File(clientDir, dir);
if (newDir.isDirectory()) {
clientDir = newDir;
return true;
} else {
return false;
}
}
public String[] dir() {
String[] fileNames = clientDir.list();
// list() doesn't show current and parent dirs, so add them
String[] allFileNames = new String[fileNames.length + 2];
allFileNames[0] = ".";
allFileNames[1] = "..";
for (int n = 0; n < fileNames.length; n++) {
allFileNames[n+2] = fileNames[n];
}
return allFileNames;
}
public String getdir() {
try {
return clientDir.getCanonicalPath();
} catch(IOException e) {
return "";
}
}
public String get(String filename) {
// stub
return "";
}
public void quit() {
}
}
The client UI is independent of the network code. It just needs to know that the client
half-object implements the common interface common.FileTransfer
.
To avoid hard-coding the actual half-object
into the UI, a call is made to a factory object.
package client;
import java.io.*;
import java.net.*;
import java.rmi.RemoteException;
import common.FileTransfer;
import common.FileTransferFactory;
public class FileTransferClientUI {
private static String DIR = "dir";
private static String QUIT = "quit";
private static String CD = "cd";
private static String GETDIR ="getdir";
private static String GET ="get";
private BufferedReader console;
private FileTransfer clientHOPP;
public static void main(String[] args){
if (args.length < 1) {
System.err.println("Usage: Client args");
System.exit(1);
}
FileTransfer clientHOPP = null;
try {
clientHOPP = FileTransferFactory.newClient(args);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
if (clientHOPP == null) {
System.out.println("Unknown client type");
System.exit(2);
}
FileTransferClientUI ui = new FileTransferClientUI(clientHOPP);
ui.loop();
}
public FileTransferClientUI(FileTransfer clientHOPP) {
this.clientHOPP = clientHOPP;
console = new BufferedReader(new InputStreamReader(System.in));
}
public void loop() {
while (true) {
String line = null;
try {
System.out.print("Enter request: ");
line = console.readLine();
if (line == null) {
break;
}
System.out.println("Request was " + line);
} catch(IOException e) {
e.printStackTrace();
quit();
}
if (line.equals(DIR)) {
dir();
} else if (line.startsWith(CD)) {
chdir(losePrefix(line, CD));
} else if (line.startsWith(GETDIR)) {
getdir();
} else if (line.startsWith(GET)) {
get(losePrefix(line, GET));
} else if (line.equals(QUIT)) {
quit();
} else {
System.out.println("Unrecognised command");
}
}
}
/**
* Given that the string starts with the prefix,
* get rid of the prefix and any whitespace
*/
public String losePrefix(String str, String prefix) {
int index = prefix.length();
String ret = str.substring(index).trim();
return ret;
}
protected void dir() {
String[] dirList = null;
try {
dirList = clientHOPP.dir();
} catch (RemoteException e) {
System.out.println("Error in remote call " + e.toString());
}
if (dirList == null) {
System.out.println("No dir list available");
} else {
for (int n = 0; n < dirList.length; n++) {
System.out.println(dirList[n]);
}
}
}
public void chdir(String dir) {
try {
if (clientHOPP.chdir(dir)) {
System.out.println("Chdir okay");
} else {
System.out.println("Chdir failed");
}
} catch (RemoteException e) {
System.out.println("Error in remote call " + e.toString());
}
}
public void getdir() {
try {
String dir = clientHOPP.getdir();
System.out.println("Current dir: " + dir);
} catch (RemoteException e) {
System.out.println("Error in remote call " + e.toString());
}
}
public void get(String filename) {
// omitted
}
private void quit() {
try {
clientHOPP.quit();
} catch (RemoteException e) {
// nothing
}
System.exit(0);
}
} // FileTransferClientUI
A factory object is used to generate specific client half-objects. It uses command
line parameters passed from the UI object to determine which half-object to return.
All half-objects implement the FileTransfer
interface
package common;
import java.util.ArrayList;
public class FileTransferFactory {
public static FileTransfer newClient(String[] args) throws Exception {
String type = args[0];
if (type.equals("socket")) {
return new socket.FileTransferClientHOPP(args[1]);
}
if (type.equals("webservice")) {
return new webservice.FileTransferClientHOPP(args[1]);
}
if (type.equals("corba")) {
// lose 1st element
String[] shortArgs = new String[args.length - 1];
for (int n = 0; n < args.length-1; n++) {
shortArgs[n] = args[n + 1];
}
return new corba.FileTransferClientHOPP(shortArgs);
}
if (type.equals("rmi")) {
return new rmi.FileTransferClientHOPP(args[1]);
}
return null;
}
}
The socket-based solution requires a client half-object and a server talking across an
ordinary socket
This just defines strings that are common to both client and server
package socket;
/**
* FileTransferConstants.java
*
*
* Created: Sun Jul 29 21:36:11 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
public class FileTransferConstants {
public static final String CD = "CD";
public static final String DIR = "DIR";
public static final String GET = "GET";
public static final String GETDIR = "GETDIR";
public static final String ERROR = "ERROR";
public static final String SUCCEEDED = "SUCCEEDED";
public static final String QUIT = "QUIT";
public static final int PORT = 18889;
public static final String CR_LF = "\r\n";
}// FileTransferConstants
package socket;
/**
* FileTransferClientHOPP.java
*
*
* Created: Fri Jul 20 12:54:51 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
import java.io.*;
import java.net.*;
import common.FileTransfer;
import java.rmi.RemoteException;
public class FileTransferClientHOPP implements FileTransfer {
protected Socket sock;
protected BufferedReader reader;
protected PrintStream writer;
public FileTransferClientHOPP(String server)
throws UnknownHostException, IOException {
InetAddress address = InetAddress.getByName(server);
sock = null;
InputStream in = null;
OutputStream out = null;
sock = new Socket(address, FileTransferConstants.PORT);
in = sock.getInputStream();
out = sock.getOutputStream();
reader = new BufferedReader(new InputStreamReader(in));
writer = new PrintStream(out);
}
public void quit() throws RemoteException {
try {
writer.print(FileTransferConstants.QUIT +
FileTransferConstants.CR_LF);
reader.close();
writer.close();
sock.close();
} catch(Exception e) {
throw new RemoteException("Quit failed", e);
}
}
public String[] dir() {
// stub
return null;
}
public boolean chdir(String dir) throws RemoteException {
writer.print(FileTransferConstants.CD + " " + dir +
FileTransferConstants.CR_LF);
String response = null;
try {
response = reader.readLine();
} catch(IOException e) {
throw new RemoteException("Chdir failed", e);
}
if (response.startsWith(FileTransferConstants.SUCCEEDED)) {
return true;
} else {
return false;
}
}
public String getdir() throws RemoteException {
writer.print(FileTransferConstants.GETDIR + FileTransferConstants.CR_LF);
String response = null;
try {
response = reader.readLine();
} catch(IOException e) {
throw new RemoteException("Chdir failed", e);
}
return response;
}
public String get(String filename) throws RemoteException {
// stub
return "";
}
} // FileTransferClientHOPP
package socket;
import java.io.*;
import java.net.*;
import server.FileTransferServerHOPP;
public class FileTransferServer {
public static void main(String argv[]) {
ServerSocket s = null;
try {
s = new ServerSocket(FileTransferConstants.PORT);
} catch(IOException e) {
System.out.println(e);
System.exit(1);
}
while (true) {
Socket incoming = null;
try {
incoming = s.accept();
} catch(IOException e) {
System.out.println(e);
continue;
}
new SocketHandler(incoming).start();
}
}
}
class SocketHandler extends Thread {
Socket incoming;
FileTransferServerHOPP fileServer = new FileTransferServerHOPP();
BufferedReader reader;
PrintStream out;
SocketHandler(Socket incoming) {
this.incoming = incoming;
}
public void run() {
try {
reader = new BufferedReader(new InputStreamReader(
incoming.getInputStream()));
out = new PrintStream(incoming.getOutputStream());
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
System.out.println("Received request: " + line);
if (line.startsWith(FileTransferConstants.CD)) {
changeDirRequest(losePrefix(line,
FileTransferConstants.CD));
} else if (line.startsWith(FileTransferConstants.DIR)) {
directoryRequest();
} else if (line.startsWith(FileTransferConstants.GETDIR)) {
getdirRequest();
} else if (line.startsWith(FileTransferConstants.GET)) {
// code omitted
} else if (line.startsWith(FileTransferConstants.QUIT)) {
break;
} else {
out.print(FileTransferConstants.ERROR +
FileTransferConstants.CR_LF);
}
}
incoming.close();
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* Given that the string starts with the prefix,
* get rid of the prefix and any following whitespace
*/
public String losePrefix(String str, String prefix) {
int index = prefix.length();
String ret = str.substring(index).trim();
return ret;
}
public void changeDirRequest(String dir) {
if (fileServer.chdir(dir)) {
out.print(FileTransferConstants.SUCCEEDED + " " +
fileServer.getdir() +
FileTransferConstants.CR_LF);
} else {
out.print(FileTransferConstants.ERROR +
FileTransferConstants.CR_LF);
}
}
public void getdirRequest() {
out.print(fileServer.getdir() +
FileTransferConstants.CR_LF);
}
public void directoryRequest() {
String[] fileNames = fileServer.dir();
if (fileNames == null) {
out.print(FileTransferConstants.ERROR +
FileTransferConstants.CR_LF);
}
for (int n = 0; n < fileNames.length; n++) {
out.print(fileNames[n] +
FileTransferConstants.CR_LF);
}
out.print(FileTransferConstants.CR_LF);
}
}
To run the server,
java socket.FileTransferServer
To run the client
java client.FileTransferClientUI socket localhost
Steps:
FileTransferServerHOPP.java
/home/local/jakarta-tomcat/webapps/axis
and change the extension to .jws
cp FileTransferServerHOPP.java \
/home/local/jakarta-tomcat/webapps/axis/FileTransferServerHOPP.jws
http://host:8088/axis/FileTransferServerHOPP
import java.io.File;
import java.io.IOException;
public class FileTransferServerHOPP {
File clientDir = new File(".");
public boolean chdir(String dir) {
File newDir = new File(clientDir, dir);
if (newDir.isDirectory()) {
clientDir = newDir;
return true;
} else {
return false;
}
}
public String[] dir() {
String[] fileNames = clientDir.list();
return fileNames;
}
public String getdir() {
try {
return clientDir.getCanonicalPath();
} catch(IOException e) {
return "";
}
}
public void quit() {
// do nothing
}
}
There are several ways of building a client HOPP. One way is to generate the WSDL
and from there generate a set of client-side classes. This is the technically
simpler but more laborious way based on constructing each function call by hand.
Note that call.invoke()
will throw a RemoteException
if an error occurs.
package webservice;
import java.io.IOException;
import java.rmi.RemoteException;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
import common.FileTransfer;
public class FileTransferClientHOPP implements FileTransfer {
private Call call;
public FileTransferClientHOPP(String host) throws Exception {
String endpoint =
"http://" + host + ":8088/axis/FileTransferServerHOPP.jws";
Service service = new Service();
call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
}
public void quit() throws RemoteException {
call.setOperationName(new QName("http://soapinterop.org/", "quit"));
call.invoke( new Object[] {} );
}
public String[] dir() throws RemoteException {
call.setOperationName(new QName("http://soapinterop.org/", "dir"));
String[] ret = (String []) call.invoke( new Object[] {} );
return ret;
}
public boolean chdir(String dir) throws RemoteException {
call.setOperationName(new QName("http://soapinterop.org/", "chdir"));
Boolean ret = (Boolean) call.invoke( new Object[] { dir } );
return ret.booleanValue();
}
public String getdir() throws RemoteException {
call.setOperationName(new QName("http://soapinterop.org/", "getdir"));
String ret = (String) call.invoke( new Object[] {} );
return ret;
}
public String get(String filename) throws RemoteException, IOException {
call.setOperationName(new QName("http://soapinterop.org/", "get"));
String ret = (String) call.invoke( new Object[] { filename } );
return ret;
}
}
Download Axis from Apache.org. Copy the FileTransferClientHOPP.java
to the axis subdirectory with the .jws
extension.
Start Axis
To run the client, the classpath needs to include the Axis libraries and also the Xerces XML parser library. Then
java -classpath ... client.FileTransferClientUI webservice localhost
The file ftp.idl
contains
module corba {
typedef sequence<string> string_seq;
interface FileTransfer {
boolean chdir(in string dir);
string_seq dir();
string getdir();
string get(in string filename);
void quit();
};
};
Generate files by
idlj -fall ftp.idl
See
http://java.sun.com/j2se/1.5.0/docs/guide/rmi-iiop/toJavaPortableUG.html
Generated files are
CORBA generates its own interfaces and classes. By specifying the module as corba
,
it generates the corba.FileTransfer
, not common.FileTransfer
.
It also introduces classes such as FileTransferPOA
which have to be used.
So introduce a "wrapper" class to translate between CORBA and our implementation.
package corba;
/**
* FileTransferServer.java
*
*
* Created: Wed Sep 21 11:22:44 2005
*
* @author <a href="mailto:jan.newmarch@infotech.monash.edu.au">Jan Newmarch</a>
* @version 1.0
*/
import java.io.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
import java.util.Properties;
public class FileTransferServer {
static public void main(String[] args) {
try{
// create and initialize the ORB
ORB orb = ORB.init(args, null);
// get reference to rootpoa & activate the POAManager
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
rootpoa.the_POAManager().activate();
// create servant and register it with the ORB
FileTransferImpl impl = new FileTransferImpl();
// impl.setORB(orb);
// get object reference from the servant
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(impl);
FileTransfer href = FileTransferHelper.narrow(ref);
// get the root naming context
// NameService invokes the name service
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt which is part of the Interoperable
// Naming Service (INS) specification.
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
// bind the Object Reference in Naming
String name = "FileTransfer";
NameComponent path[] = ncRef.to_name( name );
ncRef.rebind(path, href);
System.out.println("FileTransferServer ready and waiting ...");
// wait for invocations from clients
orb.run();
}
catch (org.omg.CORBA.SystemException e) {
System.err.println("CORBA ERROR: " + e);
e.printStackTrace(System.out);
}
catch (org.omg.CORBA.UserException e) {
System.err.println("CORBA User ERROR: " + e);
e.printStackTrace(System.out);
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
System.out.println("FileTransferServer Exiting ...");
}
} // FileTransferServer
package corba;
/**
* FileTransferImpl.java
*
*
* Created: Wed Sep 21 11:34:08 2005
*
* @author <a href="mailto:jan.newmarch@infotech.monash.edu.au">Jan Newmarch</a>
* @version 1.0
*/
import server.FileTransferServerHOPP;
public class FileTransferImpl extends FileTransferPOA {
private FileTransferServerHOPP serverHOPP = new FileTransferServerHOPP();
public FileTransferImpl() {
} // FileTransferImpl constructor
public boolean chdir(String dir) {
return serverHOPP.chdir(dir);
}
public String[] dir() {
return serverHOPP.dir();
}
public String getdir() {
return serverHOPP.getdir();
}
public String get(String filename) {
return serverHOPP.get(filename);
}
public void quit() {
serverHOPP.quit();
}
} // FileTransferImpl
CORBA IDL allows the application-defined exceptions. However, any remote method
call can also throw an exception. These exceptions are unchecked,
that is, they do not form part of the method signature and do not need to be caught.
However, if an unchecked exception is thrown and not caught, then it will
terminate the application. So it is better to catch them. They are all
subclasses of org.omg.CORBA.SystemException
.
package corba;
/**
* FileTransferClientHOPP.java
*
*
* Created: Wed Sep 21 12:19:55 2005
*
* @author <a href="mailto:jan.newmarch@infotech.monash.edu.au">Jan Newmarch</a>
* @version 1.0
*/
// import common.FileTransfer;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import java.rmi.RemoteException;
import java.io.IOException;
import org.omg.CORBA.SystemException;
public class FileTransferClientHOPP implements common.FileTransfer {
private corba.FileTransfer impl;
public FileTransferClientHOPP(String[] args) {
try{
// create and initialize the ORB
ORB orb = ORB.init(args, null);
// get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt instead of NamingContext. This is
// part of the Interoperable naming Service.
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
// resolve the Object Reference in Naming
String name = "FileTransfer";
impl = FileTransferHelper.narrow(ncRef.resolve_str(name));
System.out.println("Obtained a handle on server object: " + impl);
} catch (Exception e) {
System.out.println("ERROR : " + e) ;
e.printStackTrace(System.out);
}
} // FileTransferClientHOPP constructor
public boolean chdir(String dir) throws RemoteException{
try {
return impl.chdir(dir);
} catch (SystemException e) {
throw new RemoteException("CORBA exception", e);
}
}
public String[] dir() throws RemoteException {
try {
return impl.dir();
} catch (SystemException e) {
throw new RemoteException("CORBA exception", e);
}
}
public String getdir() throws RemoteException {
try {
return impl.getdir();
} catch (SystemException e) {
throw new RemoteException("CORBA exception", e);
}
}
public String get(String filename) throws RemoteException, IOException {
try {
return impl.get(filename);
} catch (SystemException e) {
throw new RemoteException("CORBA exception", e);
}
}
public void quit() throws RemoteException {
try {
impl.quit();
} catch (SystemException e) {
throw new RemoteException("CORBA exception", e);
}
}
} // FileTransferClientHOPP
A name server needs to run
tnameserv -ORBInitialPort 9876
Then you can run the server
java corba.FileTransferServer \
-ORBInitialHost localhost \
-ORBInitialPort 9876
The client is
java client.FileTransferClientU corba \
-ORBInitialHost localhost \
-ORBInitialPort 9876
package rmi;
/**
* FileTransferClientHOPP.java
*
*
* Created: Thu Sep 22 10:58:07 2005
*
* @author <a href="mailto:jan.newmarch@infotech.monash.edu.au">Jan Newmarch</a>
* @version 1.0
*/
import common.FileTransfer;
import java.rmi.RemoteException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.Naming;
public class FileTransferClientHOPP implements FileTransfer {
private FileTransfer proxy;
public FileTransferClientHOPP(String server)
throws MalformedURLException, NotBoundException, RemoteException {
// find the object
System.setSecurityManager(new java.rmi.RMISecurityManager());
proxy = (FileTransfer) Naming.lookup("rmi://" + server + "/FileTransfer");
} // FileTransferClientHOPP constructor
public boolean chdir(String dir) throws RemoteException {
return proxy.chdir(dir);
}
public String[] dir() throws RemoteException {
return proxy.dir();
}
public String getdir() throws RemoteException {
return proxy.getdir();
}
public String get(String filename) throws RemoteException, IOException {
return proxy.get(filename);
}
public void quit() throws RemoteException {
proxy.quit();
}
} // FileTransferClientHOPP
package rmi;
/**
* FileTransferServer.java
*
*
* Created: Thu Sep 22 10:52:48 2005
*
* @author <a href="mailto:jan.newmarch@infotech.monash.edu.au">Jan Newmarch</a>
* @version 1.0
*/
import java.rmi.RemoteException;
import java.rmi.Naming;
import common.FileTransfer;
import server.FileTransferServerHOPP;
import java.rmi.server.RemoteStub;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RMISecurityManager;
public class FileTransferServer{
public static void main(String[] args) {
FileTransfer fileServer = null;
System.setSecurityManager(new RMISecurityManager());
fileServer = new FileTransferServerHOPP();
RemoteStub stub = null;
try {
stub = UnicastRemoteObject.exportObject(fileServer);
} catch (RemoteException e) {
e.printStackTrace();
System.exit(1);
}
System.out.println("Running");
// make it public - advertise it
try {
Naming.rebind("rmi://localhost/FileTransfer", fileServer);
} catch(RemoteException e) {
e.printStackTrace();
System.exit(1);
} catch(java.net.MalformedURLException e) {
System.out.println(e);
System.exit(1);
}
// main can now finish. An RMI thread keeps the server alive
}
} // FileTransferServer
Run an RMI name server
rmiregistry
Run the server
java -classpath classes \
-Djava.rmi.server.codebase=http://localhost/classes/ \
-Djava.security.policy=policy.all \
rmi.FileTransferServer
Run the client
java -classpath classes \
-Djava.security.policy=policy.all \
client.FileTransferClientUI \
rmi \
localhost
The HOPP model shows that we can change the middleware without too many problems for a decent middleware system. But we can also change the UI for any given system in the same way!
Here is a GUI based client that uses Swing and exactly the same HOPP objects
package client;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import common.FileTransfer;
import common.FileTransferFactory;
import java.rmi.RemoteException;
public class FileTransferClientGUI extends JFrame implements ActionListener {
private JLabel dirLabel;
private JList dirList;
private JButton getBtn;
private JButton chdirBtn;
private JButton quitBtn;
private FileTransfer clientHOPP;
public static void main(String args[]) {
if (args.length < 1) {
System.err.println("Usage: Client args");
System.exit(1);
}
FileTransfer clientHOPP = null;
try {
clientHOPP = FileTransferFactory.newClient(args);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
if (clientHOPP == null) {
System.out.println("Unknown client type");
System.exit(2);
}
new FileTransferClientGUI(clientHOPP);
}
FileTransferClientGUI(FileTransfer clientHOPP) {
this.clientHOPP = clientHOPP;
Container pane = getContentPane();
pane.setLayout(new BorderLayout());
// directory label in the top
dirLabel = new JLabel("Current dir:");
pane.add(BorderLayout.NORTH, dirLabel);
// directory listing in the middle
dirList = new JList();
dirList.setModel(new DefaultListModel());
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setView(dirList);
pane.add(BorderLayout.CENTER, scrollPane);
// pane full of buttons in the bottom
JPanel buttons = new JPanel();
pane.add(BorderLayout.SOUTH, buttons);
makeButtons(buttons);
populateGUI();
setSize(400, 600);
setVisible(true);
}
private void makeButtons(JPanel buttons) {
buttons.setLayout(new GridLayout(1, 3));
getBtn = new JButton("Get");
chdirBtn = new JButton("Chdir");
quitBtn = new JButton("Quit");
buttons.add(getBtn);
buttons.add(chdirBtn);
buttons.add(quitBtn);
getBtn.addActionListener(this);
chdirBtn.addActionListener(this);
quitBtn.addActionListener(this);
}
public void populateGUI() {
try {
String dir = clientHOPP.getdir();
dirLabel.setText("Current dir: " + dir);
} catch (RemoteException e) {
// display warning dialog?
}
try {
String[] list = clientHOPP.dir();
setDirList(list);
} catch (RemoteException e) {
// display warning dialog?
}
}
public void setDirList(String[] list) {
DefaultListModel model = (DefaultListModel) dirList.getModel();
System.out.println("Class "+model.getClass().toString());
model.clear();
if (list == null) {
return;
}
for (int n = 0; n < list.length; n++) {
model.addElement(list[n]);
}
}
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == getBtn) {
try {
String[] list = clientHOPP.dir();
setDirList(list);
} catch (RemoteException e) {
// display warning dialog
}
return;
}
if (source == chdirBtn) {
String dir = (String) dirList.getSelectedValue();
if (dir == null) {
return;
}
try {
if (clientHOPP.chdir(dir)) {
dirLabel.setText("Current dir: " + clientHOPP.getdir());
String[] list = clientHOPP.dir();
setDirList(list);
} else {
// display warning dialog
}
} catch (RemoteException e) {
// display warning dialog
}
return;
}
if (source == quitBtn) {
try {
clientHOPP.quit();
} catch (RemoteException e) {
// display warning dialog?
}
System.exit(0);
}
}
}
chdir
, the next time
a getdir
is requested it is to a new copy of the
service, which remembers no state changes
chdir
then the value
of the state changes for every client
chdir
is unique for each client connection