CE303 Lecture 3
Lecture 4.
Copying Objects;
I/O and Serialisation;
Sockets and Client/Server
1
Copying Objects;
shallow vs deep cloning
Aliasing: Copying References
Assignment of an object reference to a variable creates an alias: the variable refers to an existing object
Exercise:
Employee employee1 = new Employee(“Pete”);
Employee employee2 = employee1;
employee1.setSalary(“5000”);
employee2.setSalary(“7000”);
Passing an object to a method means passing a reference
It does not make a copy of the object!
Copying references is much faster and requires less memory compared to copying the actual object with its various fields
But sometimes you may want to make a copy of an object
3
See this code example in IntelliJ to demonstrate that employee1 and employee2 are *exactly* the same object!
Cloning in Java
Class Object:
protected Object clone()
Method creates and returns a shallow copy of an object
Same class as the original
Primitive type fields: values are copied
Reference (non-primitive) type fields: aliasing, e.g. copying of the reference only
If you want to invoke Object.clone() on an object, then the class of that object should implement the interface Cloneable
If it does not, then Object.clone() will throw a CloneNotSupportedException
4
Cloning in C#
Cloning is not supported by Object
A class that supports cloning will usually implement interface ICloneable that defines method Clone()
Method MemberwiseClone() implements shallow copying
The MemberwiseClone() method is protected; it can only be accessed from within the class
More Considerations
If you want to copy objects of a class, then you should usually write a custom method for that
It is normally expected that
The cloned object is equal to the original object
(e.g. x.clone().equals(x))
The cloned object is of the same type as the original object (e.g. x.clone().getClass() == x.getClass())
There is no need to clone immutable objects – you can just copy references to such objects
6
Java Example (slide 1)
public class Author {
public String id;
public String name;
public Author(String authorId, String name) {
this.id = authorId;
this.name = name;
}
…
}
public class Book implements Cloneable {
public String isbn;
public double price;
public String title;
public List
public Book(String title, String isbn, double price,
List
this.title = title;
this.isbn = isbn == null ? “” : isbn;
this.price = price;
this.authors = new ArrayList<>();
if (authors != null)
this.authors.addAll(authors);
}
…
}
‘Shallow’ Book Cloning
public Book clone() {
try {
return (Book) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace(); return null;
}
}
In the clone, the fields isbn, title and authors refer to the same objects as the original
This is not a problem for isbn and title because strings are immutable in Java
But it means that the mutable authors list will be shared by the original Book object and the clone
They are not independent objects!
9
Show this example in IntelliJ
‘Shallow’ Book Cloning
isbn
price=22.00
title
authors
“978-0007120765”
id
name
Original
Clone
isbn
price=22.00
title
authors
“The Mystery of the Blue Train”
“Christie, Agatha”
List
“AGAT”
10
‘Deep’ Cloning
public Book clone() {
return new Book(
this.title, this.isbn, this.price,
this.authors);
}
Changes to this clone will not affect the original object and vice versa
Note that the Book constructor creates a new ArrayList object and assigns it to the authors field
11
How to create an immutable class?
1. The class must be declared as final (So that child classes can’t be created)
2. Data members in the class must be declared as final (So that we can’t change the value of it after object creation)
3. A parameterized constructor
4. Getter method for all the variables in it
5. No setters(To not have the option to change the value of the instance variable)
Note the last bullet point in the slides! Book constructor creates a NEW arraylist every time! So that’s why it’s ok to simply return new instances of Book(). Otherwise we’d have to copy all a
‘Deep’ Book Cloning
isbn
price=22.00
title
authors
“978-0007120765”
id
name
Original
Clone
isbn
price=22.00
title
authors
“The Mystery of the Blue Train”
“Christie, Agatha”
List
“AGAT”
List
12
Because the clone uses a new Book constructor, which creates a new Arraylist, we end up with 2 completely different copies of the same list. They both have the same values, but as they are separate objects, any updates won’t affect each other. This is because both id and name are Strings, such IMMUTABLE.
What if some objects are not immutable?
Assume that you wanted to clone the List authors on its own
How do we deep clone it?
We’d have to create a new instance and copy all of its elements
We’ll see such an example it the lab
Weaknesses of Cloning
Weaknesses of the unified cloning mechanism:
The compiler cannot guarantee that your implementation satisfies the expected conditions
There is nothing that indicates how deep your cloning is (you won’t know if it’s deep or shallow clone)
Microsoft advises against implementing ICloneable in public APIs
This doesn’t stop you from providing cloning functionality in other forms such as constructors or functions with self-explanatory names (both Java and C#)
As we’ve already said, an alternative is to create a user-defined copy() method, rather than call clone(), as it might be hard to implement it correctly and it might lead to errors
And then just create new instances of any classes that should be copied and copy all of their elements
From: https://javaconceptoftheday.com/difference-between-shallow-copy-vs-deep-copy-in-java/
15
Shallow cloning
https://dzone.com/articles/java-copy-shallow-vs-deep-in-which-you-will-swim
16
Deep cloning
https://dzone.com/articles/java-copy-shallow-vs-deep-in-which-you-will-swim
Input/Output Streams
This section is a refresher
18
I/O Streams
Both Java and C# have APIs for stream-based I/O
Input: reading from a source
Output: writing data to a destinations
Streams can be connected to many different kinds of sources and destinations including console, local files, remote files, sockets, devices, memory arrays, …
Data communicated in streams can be
low level (bytes) or
more high level (character streams, object streams)
Streams are often buffered so as to improve performance
But for output streams, this means you may have to explicitly flush the buffer on occasions
A Buffer is a portion in the memory that is used to store a stream of data from peripheral devices. Then from this buffer this stream of data is collected and stored in variables. A stream can be defined as a continuous flow of data. The buffer is quite useful as Java deals everything as a String.
19
Streams in Java
Java provides classes InputStream and OutputStream for reading from and writing to binary streams
Examples of streams:
System.in, System.out, System.err
FileReader, FileWriter
For textual data, one can use wrappers:
InputStreamReader can be used as a wrapper for InputStream
OutputStreamWriter can be used as a wrapper for OutputStream
Commonly used with BufferedReader / BufferedWriter for buffering the data and hence improving the efficiency
Streams in C#
The Stream class is used both for reading and writing
Examples of streams are FileStream and MemoryStream
BufferedStream class is a wrapper that implements buffering
StreamReader and StreamWriter classes are wrappers for reading and writing text data (both are buffered by default)
Similar to try-with-resources, C# has a using block:
using (StreamReader reader = File.OpenText(filename))
firstLine = reader.ReadLine();
(Static class File provides many convenient functions to work with files, similar to Files in Java.)
Serialisation and deserialisation
Serialisation
Serialisation is the process of saving Java/C# objects into a stream
Java example: objects are automatically serialised when you write them into an ObjectOutputStream:
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream(“bank.dat”));
out.writeObject(b);
Objects can be composed from other objects
includes reading/writing of all instance fields,
and all instance fields of those fields, etc.
A single writeObject() invocation can lead to a large number of objects being written!
23
De-Serialisation
ObjectInputStream is for reading objects from a stream
readObject() returns an Object reference
You need to know the types of the objects to cast them
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream(“bank.dat”));
BankAccount b = (BankAccount) in.readObject();
readObject() can throw ClassNotFoundException and IOException
these are checked exceptions, so you must either catch or declare them
24
(De)Serialising a Book Object
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Author kenGrahame = new Author(“GRAH”, “Grahame, Ken”);
Book willows = new Book(“The Wind in the Willows”,
“0140621229”, 1406, kenGrahame);
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream(“book.dat”))) {
out.writeObject(willows);
}
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream(“book.dat”))) {
Book willowsLoaded = (Book) in.readObject();
if (willowsLoaded.equals(willows))
System.out.println(“(De)Serialization successful”);
}
}
25
Serialisation API
Enabled by implementing interface Serializable
Many standard classes implement this interface
When defining a new class, you can declare fields as transient – these will not be serialised
Default implementation of Serializable interface:
all non-transient fields must be serialisable
if the parent class it not serialisable, then it must have an accessible no-arg constructor to initialise the state
26
Workings of Serialisation
Writing objects to an ObjectOutputStream
each object is assigned a serial number in the stream
if the same object is saved twice, only the serial number is written out the second time
writes whole object
transient and static (“class”) fields are ignored
if a non-transient instance field cannot be serialised (e.g. Sockets cannot be serialised), the process will fail with a NotSerializableException
Objects are saved in a binary format
including class name and serialVersionUID
27
Workings of De-Serialization
JVM reads object and determines its class
JVM attempts to find and load object class
Creates a new object
object instance variables are given values
transient fields get a null value for reference (non-primitive) types and defaults (0, false, …) for primitive types
Duplicate serial numbers are restored as references to the same object
Objects are read from a stream in the same order in which they were written
28
Serialization: The Version Problem
Problem with different versions of a class
suppose attributes are added, deleted, or modified?
how to read in previously saved objects in this case?
Serious problem for long-term persistence!
De-serialisation algorithm can deal with some changes:
adding new instance variables, changing instance variable access level, change of inheritance tree
typically get null/default values for new fields
If you want the JVM to de-serialise objects belonging to an older version of a class, set serialVersionUID for the class
Otherwise this identifier will be computed automatically, so it will change whenever the class is changed
The JVM will try de-serialisation of an object if the UID of the serialised and the current class are the same
29
UID Unique Identifier
For more info, see: https://www.javaworld.com/article/2071731/ensure-proper-version-control-for-serialized-objects.html?page=2
Serialisation/De-Serialisation in C#
The concept is similar, however C# supports multiple serialisation formats
BinaryFormatter for a .NET-specific binary format, similar to the Java object streams
SoapFormatter for an XML-based format; SOAP (Simple Object Access Protocol) was an attempt to create a unified format for objects however it is not popular now
One can implement custom formatters, however this is rarely practical
To serialise/deserialise, you first create a formatter (e.g., BinaryFormatter) and then call its functions Serialize or Deserialize
Further considerations
Very easy to store / load objects
Can be used for deep cloning, although that tends to be slower than field-by-field cloning
Major scalability problems, as it reads / writes large objects in one go
difficult to pick out particular parts
infeasible for very large objects
Potential version problems if the Java/C# class has changed since the object was stored
Potential problems with serialising objects from third-party libraries if they were not tagged as serialisable
31
Client-Server Architecture;
Sockets
PART I: INTRODUCTION
Internet Basics Reminder
TCP
Reliable due to inbuilt error-checking causing potential retransmission requests
Connections
UDP: User Datagram Protocol
connectionless
supports multicast (send to all subscribers) and broadcast (send to all on local network)
less overhead – and less reliable – compared to TCP
We will not make use of UDP in module CE303
34
Ports
One computer can offer multiple services over the Internet
For example, both a web server and an email server
When data are sent to that computer, they need to indicate which program is to receive the data
IP uses port numbers for this
A port number is an integer between 0 and 65535
The sending program must know the port number of the receiving program
This number is included in the transmitted data
Port numbers 0-1023 are reserved for standard protocols (“well-known ports”)
Firewalls tend to block many ports
35
Client-Server Architecture
A server waits for clients to connect
The client takes the initiative
Client-Server Example: WWW (HTTP)
Client: browser program provides user interface, simple validation, JavaScript engine, …
Server: remote HTTP server
provides static and dynamic web content, access control, concurrent access
36
Sockets
A socket is a bi-directional connection between two programs,
on top of a TCP connection (usually)
Supported across different programming languages and on different platforms
Java and C#: socket is associated with input / output streams
The different sockets connected to one server all have the same server address and port number
but different client IP addresses / port numbers
server can distinguish different connections from one client by different client port numbers
37
Client
Server
Input / Output Streams
associated with a socket
38
END OF PART I
PART II: Bank Server Example
Bank Server Example
Sample banking application
One server, multiple concurrent clients (each client is a separate process, possibly running on a different machine)
Domain classes: Bank, Account
When you develop a server application, you need to design a protocol for the client-server interaction
Recommendation:
base protocols on methods of accessed objects
make sure there is a clean separation of functionality between the server and the clients
The full source codes in C# and Java are available online
The two implementations work with each other
41
Show Bank Source code first
DEMO
END OF PART II
PART III: Bank Server
Bank Server: Protocol Outline
It is a client-server application, i.e. the client initiates a connection
The protocol is text-based, i.e. not binary
The server never initiates an exchange of data; every exchange is initiated by the client, and the server immediately responds to each client request
The protocol does not include any authentication
Obviously this would be unacceptable in real world!
Each client request consists of a single text line
Server response can be a single text line or multiple lines, depending on the request (see the table on the next slide)
In case of an error, the server responds with a line “ERROR” followed by an error message
45
Bank Server: Protocol Outline
Client request Server response (if success)
(On connection)
ACCOUNTS
…
BALANCE
TRANSFER
46
Processes and threads
The server and each client are separate processes
In C#, the client and the server are two separate projects; each project in C# corresponds to one executable (.exe file) or library (.dll file)
In Java, one could have both programs in one package, however we’ve split them into two completely independent projects so that you do not confuse the server and the client processes
Each client runs a single thread
The server has its main thread for listening for new connections and one additional thread for each connected client – to process the requests from that specific client
Processes and threads diagram
Server process
Main thread – listens to new connections
Client 1 process
Main thread – UI and communications
Establishing
TCP connection
Note that there is a “dialogue” happening here! The client sends a request, the server listens, acts on the request and responds. So when coding we need to be coding these commands in a sequential order.
48
Processes and threads diagram
Server process
Main thread – listens to new connections
Handling client 1
Client 1 process
Main thread – UI and communications
Processes and threads diagram
Server process
Main thread – listens to new connections
Handling client 1
Handling client 2
Client 1 process
Main thread – UI and communications
Client 2 process
Main thread – UI and communications
Processes and threads diagram
Server process
Main thread – listens to new connections
Handling client 1
Handling client 2
Client 1 process
Main thread – UI and communications
Client 3 process
Main thread – UI and communications
Client 2 process
Main thread – UI and communications
Handling client 3
Server – Main Thread (Java, slide 1)
public class ServerProgram
{
private final static int port = 8888;
private static final Bank bank = new Bank();
public static void main(String[] args)
{
bank.createAccount(1, 1001, 100);
bank.createAccount(1, 1002, 200);
bank.createAccount(2, 1003, 150);
bank.createAccount(3, 1004, 50);
RunServer();
}
…
Server – Main Thread (Java, slide 2)
…
private static void RunServer() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println(
“Waiting for incoming connections…”);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new ClientHandler(socket, bank))
.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
53
Server Main Thread
The server waits for clients to connect on a certain port
This port number is 8888 in case of our bank server
To listen for incoming connections, create a server socket for that port and call accept()
This will block until a client connects
When a client connects, it will return a Socket object
ServerSocket is just for setting up the connection
Application data is exchanged in Socket objects
The ClientHandler class handles the interaction
54
Note: Problem with sockets: unresponsive sockets block
java.nio.SocketChannel has advantage that it does not block
END OF PART III
PART IV
BANK SERVER: CLIENT HANDLING
ClientHandler class (Java, slide 1)
public class ClientHandler implements Runnable {
private final Socket socket;
private final Bank bank;
public ClientHandler(Socket socket, Bank bank) {
this.socket = socket;
this.bank = bank;
}
@Override
public void run() {
…
}
}
ClientHandler class (Java, slide 2)
public void run() {
int customerId = 0;
try (
Scanner scanner = new Scanner(socket.getInputStream());
PrintWriter writer = new PrintWriter(
socket.getOutputStream(), true)) {
try {
customerId = Integer.parseInt(scanner.nextLine());
System.out.println(“New connection; customer ID ”
+ customerId);
if (bank.getListOfAccounts(customerId).size() == 0)
throw new Exception(“Unknown customer.”);
writer.println(“SUCCESS”);
while (true) {
String line = scanner.nextLine();
String[] substrings = line.split(” “);
switch (substrings[0].toLowerCase()) {
…
The Boolean in the PrintWriter class constructor is for the autoFlush, which is set to “true”, ie the methods println, printf, or format, will flush the output buffer.
The buffering is mainly done to improve the I/O performance.
When we give any command, the streams of that command are stored in the memory location called buffer(a temporary memory location) in our computer. When all the temporary memory location is full then we use flush(), which flushes all the streams of data and executes them completely and gives a new space to new streams in buffer temporary location.
58
ClientHandler class (Java, slide 3)
…
switch (substrings[0].toLowerCase()) {
case “balance”:
int account = Integer.parseInt(substrings[1]);
writer.println(bank.getAccountBalance(
customerId, account));
break;
…
}
}
} catch (Exception e) {
writer.println(“ERROR ” + e.getMessage());
socket.close();
}
} catch (Exception e) {
} finally {
System.out.println(“Customer ” + customerId
+ ” disconnected.”);
}
ClientHandler thread
The server runs a thread (the ClientHandler class) for each connected client
ClientHandler is created after a client application connects and runs until it disconnects
It handles all the communications with the client:
At the beginning, it receives the customer ID
Then, in an infinite loop, it waits for commands
It uses a switch statement to process commands
Every time the server receives a command, it responds according to the protocol
Telnet and Putty
Telnet is an ‘application layer’ internet protocol
Historically used to execute commands on a remote server
But Telnet has security problems, notably lack of encryption and proper authentication
hence is has been largely replaced by SSH
Telnet can be used as a basic client when developing applications that communicate text over sockets
Both Windows and Unix come with Telnet clients
May require some setup before you can use them
Putty is a popular SSH and Telnet client for Windows and Unix platforms
61
Bank Server – Putty Client
Server: localhost
Port: 8888
Connection Type: Raw
1
SUCCESS
accounts
2
1001
1002
balance 1002
200
transfer 1002 1001 40
SUCCESS
balance 1002
160
62
END OF PART IV
PART V: CLIENT
Client Programming
The main() method is responsible for the UI:
Prompts the customer ID before connecting to the server
Displays the list of accounts with balances
Allows transferring funds
All the communications with the server are delegated to the Client class
This is a convenient architecture that separates the UI from the communication layer
Sockets – Client Programming
Call Socket constructor to connect to server
Socket socket = new Socket(“localhost”, port);
throws UnknownHostException if the host is not found
Use the input and output streams attached to the socket to communicate with the other endpoint
reader = new Scanner(socket.getInputStream());
writer = new PrintWriter(socket.getOutputStream(),
true);
When you send data to writer, the socket forwards it to the server; you can read the server’s response (if any) through reader
When communication with the server ends, close the socket socket.close();
66
Client class (Java, slide 1)
public class Client implements AutoCloseable {
final int port = 8888;
private final Scanner reader;
private final PrintWriter writer;
public Client(int customerId) throws Exception {
Socket socket = new Socket(“localhost”, port);
reader = new Scanner(socket.getInputStream());
writer = new PrintWriter(socket.getOutputStream(), true);
writer.println(customerId);
String line = reader.nextLine();
if (line.trim().compareToIgnoreCase(“success”) != 0)
throw new Exception(line);
}
…
}
Client class (Java, slide 2)
…
public int getBalance(int accountNumber) {
writer.println(“BALANCE ” + accountNumber);
String line = reader.nextLine();
return Integer.parseInt(line);
}
public void transfer(int fromAccount, int toAccount,
int amount) throws Exception {
writer.println(“TRANSFER ” + fromAccount + ” ”
+ toAccount + ” ” + amount);
// Reading the response
String line = reader.nextLine();
if (line.trim().compareToIgnoreCase(“success”) != 0)
throw new Exception(line);
}
…
}
General Remarks about Text Formats
Strong points
Human readable and editable
Easily interchangeable with other platforms
Support for standardised / legacy text formats
Weak points
Usually less efficient than binary formats
Unless the description of the format is precise, there can be compatibility issues
Note
Text formats rely on a character encoding; the default encoding in Java and C# is UTF-8
You can specify other encodings
69
Socket Summary
Socket communication works across different computer architectures and different programming languages
Java/C# socket programming is fairly straightforward:
server socket listens for connection requests from clients
sockets have input/output streams for exchanging data
Unresponsive sockets can block a thread
use time-outs
Applications need to agree on protocols
Serialisation-based protocols will often have compatibility issues
Text stream handling can be awkward due to formatting, string composition and parsing, handling delimiters, new lines, flushing of output streams, etc.
More high level frameworks for distributed computing like web services or RMI are built on top of sockets
70
/docProps/thumbnail.jpeg