Download presentation
Presentation is loading. Please wait.
1
Command Invocation Protocol
Lecture 14 Command Invocation Protocol
2
Generic Command Invocation Protocol
We will describe a generic protocol Allows clients to execute remote commands on the server Many complex applications can be described using this generic protocol Proposed Interface: public interface Command<T> implements Serializable { execute(T arg); }
3
Generic Command Invocation: Life Cycle
At Client: Command object is instantiated Command object is serialized into a byte[] int length of byte[] is sent to server (4 bytes) byte[] is sent to server At Server: Data is received at server, byte by byte First 4 bytes are converted to int int size is read and stored in byte[] byte[] is deserialized back into Command object Command object is executed – function returns object Object is serialized into byte[] – this is the response byte[] sent back to client
4
Java Serialization/De-Serialization
5
Java Serialization Object serialization is a process that converts the object to a sequence of bytes containing: Object data and their types Object type A serialized object (byte[]) can be deserialized into an instance of the original object The type information and bytes that represent the object and its data are used to recreate the object in memory Serialization API: ObjectInputStream - deserializes Java objects previously written using an ObjectOutputStream ObjectOutputStream – writes Java objects to an OutputStream A class is Serializable if it: Implements the Serializable interface or it super class is Serializable Its first non-serializable super class has a no-arguments (default) constructor All its non-transient fields are Serializable The transient keyword in Java is used to indicate that a field should not be part of the serialization process
6
Java Serialization: Example
class Person implements java.io.Serializable { public transient int age; public String name; public Example(int age, String name){ this.age = age; this.name = name; } } To make a class serializable, all needs to be done is to implement: Class <ClassName> implements Serializable{ … } Java will handle the rest! Note: transient will exclude age from the serialization process!
7
ObjectOutputStream: Example
import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.util.Date; public class Main { public static void main(String[] args) throws Exception{ FileOutputStream fos = new FileOutputStream(“file_name"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeInt(12345); oos.writeObject("today"); oos.writeObject(new Date()); oos.close(); } }
8
ObjectInputStream: Example
import java.io.FileInputStream; import java.io.ObjectInputStream; import java.util.Date; public class Main { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("file_name"); ObjectInputStream ois = new ObjectInputStream(fis); int i = ois.readInt(); String today = (String) ois.readObject(); Date date = (Date) ois.readObject(); ois.close(); } }
9
Generic Command Invocation: Life Cycle
Client: Command object is instantiated Command object is serialized into byte[] int length of the byte[] is sent to server (4 bytes) byte[]is sent to server Server: Data is received at server, byte by byte First 4 bytes are converted to int int size is read and stored in byte[] byte[] is deserialized back into Command object Command object is executed – function returns object Object is serialized into byte[] – this is the response byte[] sent back to client
10
ObjectEncoderDecoder: Class &API
public class ObjectEncoderDecoder<> implements MessageEncoderDecoder<Serializable> { private final byte[] lengthBytes = new byte[4]; private int lengthBytesIndex = 0; private byte[] objectBytes = null; private int objectBytesIndex = 0; API: public Serializable decodeNextByte(byte nextByte); private static void intToBytes(int i, byte[] b); private static int bytesToInt(byte[] b); public byte[] encode(Serializable message); private Serializable deserializeObject(); private byte[] serializeObject(Serializable message);
11
ObjectEncoderDecoder: decodeNextByte
@Override public Serializable decodeNextByte(byte nextByte) { if (objectBytes == null) { lengthBytes[lengthBytesIndex++] = nextByte; if (lengthBytesIndex == lengthBytes.length) { int len = bytesToInt(lengthBytes); objectBytes = new byte[len]; objectBytesIndex = 0; lengthBytesIndex = 0; } } else { objectBytes[objectBytesIndex++] = nextByte; if (objectBytesIndex == objectBytes.length) { Serializable result = deserializeObject(); objectBytes = null; return result; } return null; }
12
ObjectEncoderDecoder: bytesToInt &intToBytes
private static int bytesToInt(byte[] b) { return ((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) | ((b[2] & 0xff) << 8) | (b[3] & 0xff); } bytesToInt – converts an integer into byte[] intToBytes – converts byte[] into an integer Both functions used to convert the length of the serialized object to send it as byte[] or convert it back to int once received private static void intToBytes(int i, byte[] b) { b[0] = (byte) (i >> 24); b[1] = (byte) (i >> 16); b[2] = (byte) (i >> 8); b[3] = (byte) i; }
13
ObjectEncoderDecoder: encode
@Override public byte[] encode(Serializable message) { return serializeObject(message); }
14
ObjectEncoderDecoder: serializeObject
private byte[] serializeObject(Serializable message) { try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); //initialize object size for (int i = 0; i < 4; i++) bytes.write(0); ObjectOutput out = new ObjectOutputStream(bytes); out.writeObject(message); out.flush(); byte[] result = bytes.toByteArray(); //now write the object size intToBytes(result.length - 4, result); return result; } catch (Exception ex) { throw new IllegalArgumentException("cannot serialize object", ex); } }
15
ObjectEncoderDecoder: deserializeObject
private Serializable deserializeObject() { try { ObjectInput in = new ObjectInputStream(new ByteArrayInputStream(objectBytes)); return (Serializable) in.readObject(); } catch (Exception ex) { throw new IllegalArgumentException("cannot desrialize object", ex); } }
16
RemoteCommandInvocationProtocol: process
public class RemoteCommandInvocationProtocol<T> implements MessagingProtocol<Serializable> { private T arg; public RemoteCommandInvocationProtocol(T arg) { this.arg = arg; } public Serializable process(Serializable msg) { return ((Command) msg).execute(arg); public boolean shouldTerminate() { return false; }
17
RemoteCommandInvocation: Client Implementation
public class RCIClient implements Closeable{ private final ObjectEncoderDecoder encdec; private final Socket sock; private final BufferedInputStream in; private final BufferedOutputStream out; public RCIClient(String host, int port) throws IOException { sock = new Socket(host, port); encdec = new ObjectEncoderDecoder(); in = new BufferedInputStream(sock.getInputStream()); out = new BufferedOutputStream(sock.getOutputStream()); } API: public void send(Command<?> cmd) throws IOException; public Serializable receive() throws IOException; public void close() throws IOException;
18
Usage Example: News Feed Server
This server will allow clients to execute two commands: Publish news to a channel by its name Fetch all the news which were published to a specific channel Implementation Steps: Defining NewsFeed API and implementation: Command execute uses this API! Implementing Commands PublishNewsCommand FetchNewsCommand Implement NewsFeedServer The server uses defined API, and defined commands to work public interface NewsFeed { void clear(); ArrayList<String> fetchNews(String channel); void publishNews(String channel, String news); }
19
ConcurrentNewsFeed: Class Definition &API
public class ConcurrentNewsFeed implements NewsFeed { private ConcurrentHashMap<String, ConcurrentLinkedQueue<String>> channels = new ConcurrentHashMap<>(); public ArrayList<String> fetchNews(String channel); void publishNews(String channel, String news); void clear(); @Override public void clear() { channels.clear(); }
20
NewsFeed Fetch News: fetchNews
@Override public ArrayList<String> fetchNews(String channel) { ConcurrentLinkedQueue<String> queue = channels.get(channel); if (queue == null) { return new ArrayList<>(0); } else { return new ArrayList<>(queue); } }
21
Implementing Commands: FetchNewsCommand
public class FetchNewsCommand implements Command<NewsFeed> { private String channel; public FetchNewsCommand(String channel) { this.channel = channel; } public Serializable execute(NewsFeed feed) { return feed.fetch(channel); } }
22
NewsFeed Publish News: PublishNews
@Override public void publishNews(String channel, String news) { ConcurrentLinkedQueue<String> queue = channels.computeIfAbsent(channel, k -> new ConcurrentLinkedQueue<>()); queue.add(news); } computeIfAbsent: A method of ConcurrentHashMap If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null! This is done once per news channel. After its invocation, the specific channel will have an entry in the hashmap, so the consequent executions will have no effect – since not absent! computeIfAbsent implementation: if (map.get(key) == null) { V newValue = mappingFunction.apply(key); if (newValue != null) map.put(key, newValue); }
23
Implementing Commands: PublishNewsCommand
public class PublishNewsCommand implements Command<NewsFeed> { private String channel; private String news; public PublishNewsCommand(String channel, String news) { this.channel = channel; this.news = news; } public Serializable execute(NewsFeed feed) { feed.publish(channel, news); return "OK"; } }
24
NewsFeedServer: Implementation
public class NewsFeedServer{ public static void main(String[] args) { ConcurrentNewsFeed feed = new ConcurrentNewsFeed(); new ThreadPerClientServer( 8080, () -> new RemoteCommandInvocationProtocol<>(feed), () -> new ObjectEncoderDecoder<>() ).serve(); } }
25
ThreadPerClientServer:Reminder!
public class ThreadPerClientServer extends BaseServer { public ThreadPerClientServer( int port, Supplier<MessagingProtocol> protocolFactory, Supplier<MessageEncoderDecoder> encoderDecoderFactory) { super(port, protocolFactory, encoderDecoderFactory); } protected void execute(ConnectionHandler handler) { new Thread(handler).start(); } }
26
RemoteCommandInvocationClient: Implementation
public void send(Command<?> command) throws IOException { out.write(encdec.encode(command)); out.flush(); } public Serializable receive() throws IOException { int read; while ((read = in.read()) >= 0) { Serializable message = encdec.decodeNextByte((byte) read); if (message != null) { return message; } } throw new IOException("error: disconnected!"); }
27
Usage Example: Three clients Example
public class NewsFeedClientMain { public static void main(String[] args) throws Exception { if (args.length == 0) { args = new String[]{"localhost"}; } System.out.println("running clients"); runFirstClient(args[0]); runSecondClient(args[0]); runThirdClient(args[0]); } Client 1: will publish three news articles under two categories: jobs, and headlines Client 2: will fetch news articles under jobs category Client 3: will fetch news articles under headlines category
28
Running first Client private static void runFirstClient(String host) throws Exception { try (RCIClient c = new RCIClient(host, 8080)) { c.send(new PublishNewsCommand( "jobs", "System Programmer, knowledge in C++, Java and Python required. call 0x134693F")); c.receive(); "headlines", "new SPL assignment is out soon!!")); "THE CAKE IS A LIE!")); c.receive(); } }
29
Running second Client private static void runSecondClient(String host) throws Exception { try (RCIClient c = new RCIClient(host, 8080)) { c.send(new FetchNewsCommand("jobs")); System.out.println("second client received: " + c.receive()); } }
30
Running third Client private static void runThirdClient(String host) throws Exception { try (RCIClient c = new RCIClient(host, 8080)) { c.send(new FetchNewsCommand("headlines")); System.out.println("third client received: " + c.receive()); } }
31
Example: Output second client received: [System Programmer, knowledge in C++, Java and Python required. call 0x134693F] third client received: [new SPL assignment is out soon!!, THE CAKE IS A LIE!]
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.