How to transfer files (SIMON 1.0.0)¶
This tutorial describes how to transfer files from A to B with the help of SIMON.
Introduction¶
Normally SIMON is used for calling methods on remote objects. Such a remote method call includes a lot of reflection, proxying and serialization which is normally not noticed by the user regarding the time that is used to do all that magic internally. The calls seem to be very fast transferred to server and return value back to client. The time that is used to transfer the method call to server and back isn't critical at all.
But if you transfer files, this would have an impact on transfer-speed. So it's not an good idea to transfer the byte packages of a file via a normal remote method call. Say we have an 100MBit LAN and want to use the complete bandwidth (about 10MBytes/sec), then it would be not acceptable to have only 9, 8 or ever less MB/sec because of such internal magic ...
To overcome this, SIMON has a special feature called RawChannel. This transfers your data without involving reflection or proxys.
But before we start, you should know how the things work...
We assume you want to transfer file "MyGreatHolidayMovie.mpg" from your client to your server. The server then needs to be aware of your whish to transfer the file. So he has to be prepared. At a first step, you tell the server: I want to transfer a file. Maybe the name is also important and so you attach the filename to your transfer-request.
Then you transfer the file block by block (it's always a good idea not to load the complete file in memory...) and finally you tel the server: All data transfered, all done.
So in short:
- Request the receiving of a file
- Transfer data
- close the transfer
This can be mapped to SIMONs API like this:
Shared Code¶
First we setup the server's interface
1 package de.root1.simon.samples.rawchannel.shared;
2
3 import de.root1.simon.SimonRemote;
4 import de.root1.simon.exceptions.SimonRemoteException;
5
6 public interface RawChannelServer extends SimonRemote {
7
8 public static final String BIND_NAME = "RawChannelFileTransfer";
9
10 public int openFileChannel(String filename) throws SimonRemoteException;
11
12 public byte[] getFileBytes(String filename) throws SimonRemoteException;
13
14 }
Server Code¶
Then we setup the server's implementation:
1 package de.root1.simon.samples.rawchannel.server;
2
3 import de.root1.simon.Simon;
4 import de.root1.simon.exceptions.SimonRemoteException;
5 import java.io.DataInputStream;
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.FileNotFoundException;
9 import java.io.IOException;
10 import de.root1.simon.samples.rawchannel.shared.RawChannelServer;
11
12 public class RawChannelServerImpl implements RawChannelServer {
13
14 public int openFileChannel(String filename) throws SimonRemoteException {
15 int token = Simon.prepareRawChannel(new FileReceiver(filename),this);
16 return token;
17 }
18
19 public byte[] getFileBytes(String filename) throws SimonRemoteException {
20
21 File f = new File(filename);
22
23 byte[] data = new byte[(int)f.length()];
24
25 DataInputStream dis;
26 try {
27 dis = new DataInputStream(new FileInputStream(f));
28 dis.readFully(data);
29 } catch (FileNotFoundException ex) {
30 ex.printStackTrace();
31 } catch (IOException ex) {
32 ex.printStackTrace();
33 }
34
35 return data;
36 }
37
38 }
As you can see, we need a method on the server which returns a token, provided by the SIMON class. This token is needed by the client to open the transfer channel.
Also also we need to setup a listener - here called FileReceiver - that receives all the data:
1 package de.root1.simon.samples.rawchannel.server;
2
3 import de.root1.simon.RawChannelDataListener;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.nio.ByteBuffer;
9 import java.nio.channels.FileChannel;
10
11 public class FileReceiver implements RawChannelDataListener {
12
13 private FileChannel fc;
14
15 public FileReceiver(String filename) {
16 try {
17 fc = new FileOutputStream(new File(filename)).getChannel();
18 } catch (FileNotFoundException ex) {
19 // cannot really occur, because we wanto CREATE the file.
20 ex.printStackTrace();
21 }
22 }
23
24 public void write(ByteBuffer data) {
25 try {
26 fc.write(data);
27 } catch (IOException ex) {
28 ex.printStackTrace();
29 }
30 }
31
32 public void close() {
33 try {
34 fc.close();
35 } catch (IOException ex) {
36 ex.printStackTrace();
37 }
38 }
39
40 }
Okay, now the only missing server part is the SIMON Registry to which we bind the server:
1 package de.root1.simon.samples.rawchannel.server;
2
3 import de.root1.simon.Registry;
4 import de.root1.simon.Simon;
5 import de.root1.simon.exceptions.NameBindingException;
6 import de.root1.simon.samples.rawchannel.shared.RawChannelServer;
7 import java.io.IOException;
8 import java.net.InetAddress;
9 import java.net.UnknownHostException;
10
11 public class Server {
12
13 public static void main(String[] args) {
14
15 try {
16
17 Registry registry = Simon.createRegistry(InetAddress.getByName("0.0.0.0"), 2000);
18
19 RawChannelServerImpl rcsi = new RawChannelServerImpl() ;
20 registry.bind( RawChannelServer.BIND_NAME, rcsi);
21
22 // Server is now running. If you whish to shutdown, call this lines:
23 //registry.stop();
24 //registry.unbind(RawChannelServer.BIND_NAME);
25
26 } catch (UnknownHostException ex) {
27 ex.printStackTrace();
28 } catch (IOException ex) {
29 ex.printStackTrace();
30 } catch (NameBindingException ex) {
31 ex.printStackTrace();
32 }
33
34 }
35
36 }
Client Code¶
There's less code for the client... Let's have a look:
1 package de.root1.simon.samples.rawchannel.client;
2
3 import de.root1.simon.RawChannel;
4 import de.root1.simon.Simon;
5 import de.root1.simon.exceptions.LookupFailedException;
6 import de.root1.simon.exceptions.SimonRemoteException;
7 import de.root1.simon.samples.rawchannel.shared.RawChannelServer;
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.FileNotFoundException;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.nio.ByteBuffer;
16 import java.nio.channels.FileChannel;
17 import java.util.Random;
18
19 public class Client {
20
21 private static final String TEMPDIR = System.getProperty("java.io.tmpdir") + System.getProperty("file.separator");
22 private static final String TESTFILE_RECEIVER = TEMPDIR + "TestFileForReceiver.dat";
23 private static final String TESTFILE_SENDER = TEMPDIR + "TestFile.dat";
24
25 public static void main(String[] args) {
26
27 // create some testfiles
28 createTestFile();
29
30 try {
31
32 // Get connection to server
33 RawChannelServer rcs = (RawChannelServer) Simon.lookup(InetAddress.getLocalHost(), 2000, RawChannelServer.BIND_NAME);
34
35 // get a RawChannel Token from server. This is needed to open the
36 // RawChannel
37 int token = rcs.openFileChannel(TESTFILE_RECEIVER);
38
39 // with the remote object and token, tell SIMON that you need a
40 // RawChannel
41 RawChannel rawChannel = Simon.openRawChannel(token, rcs);
42
43 // first, we open a FileChannel. This is thanks to Java NIO faster
44 // than normal file operation
45 File file = new File(TESTFILE_SENDER);
46 FileChannel fileChannel = new FileInputStream(file).getChannel();
47
48 // we send the file in 512byte packages through the RawChannel
49 ByteBuffer data = ByteBuffer.allocate(512);
50 while (fileChannel.read(data) != -1) {
51 rawChannel.write(data);
52 data.clear();
53 }
54
55 // all data written. Now we can close the FileChannel
56 fileChannel.close();
57
58 // ... and also th RawChannel
59 rawChannel.close();
60
61 // Another way to send/get files is to pass them as return-values of
62 // method arguments. But if transfering files is the main purpose of
63 // your program, I really recommend to use the RawChannel approach
64 byte[] fileBytesReceived = rcs.getFileBytes(TESTFILE_RECEIVER);
65 FileOutputStream fileOutputStream = new FileOutputStream(new File(TESTFILE_RECEIVER));
66 fileOutputStream.write(fileBytesReceived);
67 fileOutputStream.close();
68
69 Simon.release(rcs);
70
71 } catch (UnknownHostException ex) {
72 ex.printStackTrace();
73 } catch (LookupFailedException ex) {
74 ex.printStackTrace();
75 } catch (SimonRemoteException ex) {
76 ex.printStackTrace();
77 } catch (IOException ex) {
78 ex.printStackTrace();
79 }
80
81 // finally we clean up our temporary testfiles
82 cleanUpFiles();
83 }
84
85 /**
86 * Generates a test file
87 */
88 private static void createTestFile() {
89 try {
90 FileChannel fc = new FileOutputStream(new File(TESTFILE_SENDER)).getChannel();
91 Random r = new Random(System.currentTimeMillis());
92 byte[] data = new byte[1024];
93 for (int i = 0; i < 10; i++) {
94 r.nextBytes(data);
95 fc.write(ByteBuffer.wrap(data));
96 }
97 fc.close();
98 } catch (FileNotFoundException ex) {
99 ex.printStackTrace();
100 } catch (IOException ex) {
101 ex.printStackTrace();
102 }
103 }
104
105 /**
106 * Cleans up testfiles
107 */
108 private static void cleanUpFiles() {
109 File f1 = new File(TESTFILE_SENDER);
110 f1.delete();
111 File f2 = new File(TESTFILE_RECEIVER);
112 f2.delete();
113 }
114 }