DistObsx

skillfulwolverineSoftware and s/w Development

Dec 2, 2013 (3 years and 10 months ago)

96 views

Advanced Java


Distributed Objects

1


Distributed Objects

Serialization

Here is our friend, the Point:

public

class

Point {




int

x
;


int

y
;




@Override


public

String toString() {



return

"x="
+
x
+
", y="
+
y
;


}


}

Serialization means converting to and from a serial stream of bytes. The
conversion is built into the JVM,
but you have to grant the JVM permission to handle it:

import

java.io.Serializable;


public

class

Point
implements

Serializable {




// Number to represent the version of this class (Eclipse can
gen
)


private

static

final

long

serialVersionUID

= 0x43_21_67_89;




int

x
;


int

y
;




public

Point(
int

x,
int

y) {



this
.
x

= x;



this
.
y

= y;


}




transient

int

stuff
;




@Override


public

String toString() {



return

"x="
+
x
+
", y="
+
y
;


}


}





Advanced Java


Distributed Objects

2


import

java.io.FileOutputStream;

import

java.io.ObjectOutputStream;

import

java.io.OutputStream;


public

class

DisTinker {




public

static

void

main(String [] args)
throws

Exception {






Point a =
new

Point(1,2);



//Point b = new Point(3,4);






OutputStream os =
new

FileOutputStream
(
"test.ser"
);



ObjectOutputStream oos =
new

ObjectOutputStream(os);






oos.writeObject(a);






oos.flush();



oos.close();







}


}



InputStream is =
new

FileInputStream(
"test.ser"
);



ObjectInputStream ois =
new

ObjectInputStream(is);






Point c

= (Point)ois.readObject();






ois.close();






System.
out
.println(c);

The serialization ID keeps code with two different versions from getting mixed up:

private

static

final

long

serialVersionUID

= 0x43_21_67_90;

Exception in thread "main"
java.io.InvalidClassException
: Point; local class
incompatible: stream classdesc serialVersionUID = 1126262665, local class
serialVersionUID = 1126262672


at java.io.ObjectStreamClass.initNonProxy(Unknown Source)


Advanced Java


Distributed Objects

3


Over a Socket

public

class

DisClient {




public

static

void

main(String [] args)
throws

Exception {






Socket cs =
new

Socket(
"localhost"
,1234);






ObjectOutputStream oos =
new

ObjectOutputStream(cs.getOutputStream());






Point p =
new

Point(5,6);








oos.writeObject(p);






oos.flush(
);






cs.close();







}


}

Call the toString on whatever object is shipped over. Create a separate project for the server:

public

class

DisServer {




public

static

void

main(String [] args)
throws

Exception {






ServerSocket
ss

=
new

ServerSocket(
1234);






Socket cs = ss.accept();






ObjectInputStream ois =
new

ObjectInputStream(cs.getInputStream());






Object o = ois.readObject();






cs.close();






System.
out
.println(o);





}


}






Advanced Java


Distributed Objects

4


The server project has no “Point” class in it.

Exception in thread "main" java.lang.ClassNotFoundException: Point


at java.net.URLClassLoader$1.run(Unknown Source)


at java.net.URLClassLoader$1.run(Unknown Source)


at java.security.AccessController.doPrivileged(Native Method)



at java.net.URLClassLoader.findClass(Unknown Source)


at java.lang.ClassLoader.loadClass(Unknown Source)


You have to share the class files between the runtimes.


Fields marked “transient”

are NOT serialized
.

Object Graphs

The serialization proce
ss is complex. It recurses the object graph and gets all objects.

public

class

Point
implements

Serializable {




// Number to represent the version of this class (Eclipse can
gen
)


private

static

final

long

serialVersionUID

= 0x43_21_67_89;




int

x
;


int

y
;




String
name
;




public

Point(
int

x,
int

y, String
name
) {



this
.
x

= x;



this
.
y

= y;



this
.
name

=
name
;


}





@Override


public

String toString() {



return

"x="
+
x
+
", y="
+
y

+
"("
+
name
+
")"
;


}


}

Line contains two points … they get serialized too. Square contains 4 lines. All of the tree gets serialized
correctly.




Advanced Java


Distributed Objects

5


The seralizer handles loops in the graph.

public

class

Point
implements

Serializable {




int

x
;


int

y
;




Point
other
;





@Override


public

String toString() {



return

"x="
+
x
+
", y="
+
y

+
"(other "
+
other
.
x
+
" "
+
other
.
y
+
")"
;


}


}


Point

a =
new

Point
(1,2);



Point

b =
new

Point
(3,4);






a.
other

= b;



b.
other

= a;






OutputStream os =
new

FileOutputStream(
"test.ser"
);



ObjectOutputStream oos =
new

ObjectOutputStream(os);






oos.writeObject(a);

The serializer writes “a” and all of its objects. The “a” points to “b” so it gets serialized next. But “b”
points back to “a”. The serializer knows that “a” has already been wri
tten and just writes a reference. In
the other direction, the reference becomes a pointer again.


You can also serialize objects in and out of XML. The JDK ships with JAXB. We’ll look at it with
annotations.

http://www.vogella.com/articles/JAXB/article.html








Advanced Java


Distributed Objects

6


RMI

From:

http://en.wikipedia.org/wiki/Java_remote_method_invocation


In this picture, the “client” is
the object doing the method call and the “server” is the object responding
to the request.

RMI allows you to insert proxies into the call and handle the transaction over the network.

For instance, a simple client with some data and methods:

public

class

StringUtil {




String
name
;




public

void

setName(String name) {



this
.
name

= name;




}




public

String getName() {



return

name
;


}




public

String toUp(String m) {



return

m.toUpperCase();


}


}



public

static

void

main(String [] args)
throws

Exception {






// Somehow get a reference to the object you want to use



StringUtil u =
new

StringUtil();






// Make calls on the object



u.setName(
"My Name"
);



System.
out
.println(u.getName());






String a = u.toUp(
"Hello World"
);



System.
out
.println(a);





}


Advanced Java


Distributed Objects

7


With RMI the caller and target are in separate processes (maybe separate machines). The server creates
the StringUtil object and creates a skeleton object that calls it. The client creates a stub object that has
the same interface as St
ringUtil.

Other objects on the client talk to the stub not knowing that aren’t talking to the real thing. The stub
sends the request to the skeleton, which calls the real object. The real object doesn’t know a skeleton
called it. The return goes back to t
he client.

The caller and target do not

realize that two other objects were used in between. The networking layer
is completely separated from the business logic.

This network layer can be automated for any object.


There are some OO mechanics. First you d
efine an interface that the client and server will share.

public

interface

StringUtilInterface {




public

void

setName(String name);




public

String getName();




public

String toUp(String m);


}


public

class

StringUtil
implements

StringUtilInterface {



The new interface must extend the “Remote” interface:

import

java.rmi.Remote;

import

java.rmi.RemoteException;


public

interface

StringUtilInterface
extends

Remote {




public

void

setName(String name)
throws

RemoteException;




public

String getName()
throws

RemoteException;




public

String toUp(String m)
throws

RemoteException;


}

You need to throw RemoteException

so the client can catch network troubles. You can throw any of
your own exceptions too. YOUR code will never throw one of these things. This is for the skeleton/stub
to report failures back to the calling client.


Advanced Java


Distributed Objects

8


In order for your object to be used exter
nally, it should extend the UnicastRemoteObject. This makes
your object “exportable” by adding several overloade “export” methods.

The “UnicastRemoteObject” default constructor can throw a RemoteException. Your constructor must
be prepared to handle it (an
d should let it bubble out).

Your object should implement the interface, but note that they don’t have to declare that they throw
the RemoteException.

import

java.rmi.RemoteException;

import

java.rmi.server.UnicastRemoteObject;


public

class

StringUtil
ext
ends

UnicastRemoteObject
implements

StringUtilInterface {



private

static

final

long

serialVersionUID

= 1L;



protected

StringUtil()
throws

RemoteException {



super
();




}



String
name
;




@Override


public

void

setName(String name) {



this
.
name

= name;




}




@Override


public

String getName() {



return

name
;


}




@Override


public

String toUp(String m) {



return

m.toUpperCase();


}


}






Advanced Java


Distributed Objects

9


RMI Registry

The RMI registry allows you to make a remote
-
able object available to the outside world.
Server register
objects with the registry (giving them a String name) and clients look up the objects by name.

The registry runs as a separate process and uses port 1099 by default.

The remote
-
able classes must be in the classpath for the registry to find.

You can set a global variable or
you can run the registry in your bin directory since “.” will be in the path.


import

java.rmi.Naming;


public

class

StringUtilServer {




public

static

void

main(String [] args)
throws

Exception


{






StringUtil so =
new

StringUtil();






Naming.
bind
(
"StringUtility"
, so);






// Main ends here, but the "bind" starts a listening thread for



// the skeleton. The JVM continues to run.









}


}




Advanced Java


Distributed Objects

10


The Client Stub

Earlier versions of java required an explicit “stub”
class for the client to use. You used to have to run the
“rmic” on the server
-
object to generate the stub:


With Java’s new Dynamic Proxies you can skip this step. The code will build a proxy stub for you when
you need it. You need only use the

registry t
o find the object:


Start the registry and server:





Advanced Java


Distributed Objects

11


public

class

StringUtilUser {




public

static

void

main(String [] args)
throws

Exception {






// Somehow get a reference to the object you want to use



//StringUtilInterface u = new StringUtil();






StringUtilInterface u =
(StringUtilInterface)Naming.
lookup
(
"rmi://localhost/StringUtility"
);






// Make calls on the object



u.setName(
"My Name"
);



System.
out
.println(u.getName());






String a = u.toUp(
"Hello World"
);



System.
out
.println(a);





}


}

These methods can pass complex parameters … as long as they are serializable.