source: trunk/src/org/expeditee/network/FrameShare.java@ 919

Last change on this file since 919 was 919, checked in by jts21, 10 years ago

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

File size: 15.2 KB
Line 
1/**
2 * FrameShare.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.network;
20
21import java.io.BufferedReader;
22import java.io.File;
23import java.io.IOException;
24import java.io.InputStream;
25import java.io.OutputStream;
26import java.io.StringReader;
27import java.io.StringWriter;
28import java.net.DatagramPacket;
29import java.net.DatagramSocket;
30import java.net.InetAddress;
31import java.net.Socket;
32import java.net.SocketException;
33import java.net.UnknownHostException;
34import java.nio.charset.Charset;
35import java.util.Collection;
36import java.util.HashMap;
37import java.util.LinkedList;
38import java.util.List;
39import java.util.Map;
40
41import org.expeditee.gui.AttributeValuePair;
42import org.expeditee.gui.Frame;
43import org.expeditee.gui.FrameIO;
44import org.expeditee.gui.MessageBay;
45import org.expeditee.io.ExpReader;
46import org.expeditee.io.ExpWriter;
47import org.expeditee.io.FrameReader;
48import org.expeditee.io.FrameWriter;
49import org.expeditee.items.Item;
50import org.expeditee.items.Picture;
51import org.expeditee.items.Text;
52import org.expeditee.settings.network.NetworkSettings;
53
54public class FrameShare {
55
56 public final static Charset CHARSET = Charset.forName("UTF-8");
57
58 public static boolean disableNetworking = false;
59
60 private static Collection<DefaultServer> _servers = new LinkedList<DefaultServer>();
61
62 private static FrameShare _theSession;
63
64 private static boolean headless = false;
65
66 private Map<String, Peer> _peers;
67
68 private int _port = Peer.DEFAULT_PORT;
69
70
71 private void startServers() throws IOException
72 {
73 System.err.println("Starting Expeditee Server on port " + _port);
74 _servers.add(new FrameServer(_port));
75 _servers.add(new FrameSaver(_port));
76 _servers.add(new MessageReciever(_port));
77 _servers.add(new InfServer(_port));
78 _servers.add(new InfUpdate(_port));
79 _servers.add(new ImageServer(_port));
80 _servers.add(new ImageSaver(_port));
81
82 }
83
84 private FrameShare() {
85 _peers = new HashMap<String, Peer>();
86 }
87
88 private FrameShare(int port) {
89 this();
90 _port = port;
91
92 try {
93 startServers();
94
95 } catch (Exception e) {
96 e.printStackTrace();
97 System.err.println("Could not start servers [port: "
98 + port + "]");
99 }
100
101 try {
102 for (DefaultServer server : _servers) {
103 server.start();
104 }
105 } catch (Exception e) {
106 System.err.println("Error in server startup");
107 }
108 }
109
110
111 private FrameShare(Frame settingsFrame)
112 {
113 this();
114
115 // Set the settings
116 for (Text item : settingsFrame.getBodyTextItems(false)) {
117
118 AttributeValuePair avp = new AttributeValuePair(item.getText());
119 if (avp.isAnnotation())
120 continue;
121
122 String attribute = avp.getAttributeOrValue().toLowerCase();
123
124 if (attribute.equals("server")) {
125 try {
126 if (avp.hasPair()) {
127 _port = avp.getIntegerValue();
128 }
129 MessageBay.displayMessage("Starting Expeditee Server on port: " + _port);
130
131 startServers();
132 } catch (Exception e) {
133 e.printStackTrace();
134 MessageBay.errorMessage("Could not start servers ["
135 + avp.toString() + "]");
136 }
137 continue;
138 }
139
140 if (!avp.hasPair())
141 continue;
142
143 try {
144 Peer peer = new Peer(avp);
145 _peers.put(attribute, peer);
146 } catch (UnknownHostException e) {
147 MessageBay.errorMessage("Could not locate peer ["
148 + avp.toString() + "]");
149 }
150 }
151
152 try {
153 for (DefaultServer server : _servers) {
154 server.start();
155 }
156 } catch (Exception e) {
157 MessageBay.errorMessage("Error in PeerToPeer setup");
158 }
159 }
160
161 public void finalise() {
162 System.err.println("Closing servers");
163
164 for (DefaultServer server : _servers)
165 server.close();
166 }
167
168 public static FrameShare getInstance() {
169 return _theSession;
170 }
171
172 /**
173 * TODO check each peer on a different thread.
174 *
175 * @param frameName
176 * @return
177 */
178 public Frame loadFrame(String frameName, String peerName) {
179 String result = null;
180
181 try {
182 // get a datagram socket
183 DatagramSocket socket = new DatagramSocket();
184 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get() * 2);
185 if (peerName == null) {
186 for (Peer peer : _peers.values()) {
187 try {
188 result = getFrameContents(frameName, socket, peer);
189 if(result == null || result.length() == 0) {
190 continue;
191 }
192 peerName = peer.getName();
193 break;
194 } catch (Exception e) {
195 e.printStackTrace();
196 }
197 }
198 } else {
199 try {
200 Peer peer = _peers.get(peerName.toLowerCase());
201 if (peer != null) {
202 result = getFrameContents(frameName, socket, peer);
203 }
204 } catch (Exception e) {
205 e.printStackTrace();
206 }
207 }
208 socket.close();
209 } catch (Exception e) {
210 e.printStackTrace();
211 }
212
213 if (result == null || result.length() == 0)
214 return null;
215
216 // Now read the frame from the file contents
217 FrameReader reader = new ExpReader(frameName);
218 Frame frame = null;
219 try {
220 frame = reader.readFrame(new BufferedReader(
221 new StringReader(result)));
222 // Set the path for the frame to indicate it is NOT a local frame
223 // This allows the frame to be saved in the correct location
224 frame.setLocal(false);
225 frame.setPath(peerName);
226 } catch (IOException e) {
227 e.printStackTrace();
228 }
229
230 if (frame == null) {
231 MessageBay.errorMessage("Error: " + frameName
232 + " could not be successfully loaded.");
233 return null;
234 }
235
236 return frame;
237 }
238
239 /**
240 * Downloads an image from the server (if it exists),
241 * Saves it locally in the default image folder,
242 * Then returns the filename
243 *
244 * @param imageName
245 * @param peerName
246 * @return true if the image was successfully downloaded and saved in the default images folder
247 */
248 public boolean loadImage(String imageName, String peerName) {
249 boolean result = false;
250 try {
251 if (peerName == null) {
252 for (Peer peer : _peers.values()) {
253 try {
254 result = getImage(imageName, peer);
255 if(!result) {
256 continue;
257 }
258 peerName = peer.getName();
259 break;
260 } catch (Exception e) {
261 e.printStackTrace();
262 }
263 }
264 } else {
265 try {
266 Peer peer = _peers.get(peerName.toLowerCase());
267 if (peer != null) {
268 result = getImage(imageName, peer);
269 }
270 } catch (Exception e) {
271 e.printStackTrace();
272 }
273 }
274 } catch(Exception e) {
275 e.printStackTrace();
276 }
277 return result;
278 }
279
280 /**
281 * Send a list of images that may need to be uploaded
282 * Then wait for a response (in form of a list of shorts denoting which files to keep)
283 * Then send each file in sequence
284 * @author jts21
285 *
286 */
287 private class ImageSender extends Thread {
288
289 private List<File> imageFiles;
290 private Peer peer;
291
292 public ImageSender(List<File> imageFiles, Peer peer) {
293 super("ImageSender");
294 this.imageFiles = imageFiles;
295 this.peer = peer;
296 }
297
298 @Override
299 public void run() {
300 // System.out.println("Sending images to server");
301 try(Socket socket = new Socket(peer.getAddress(), peer.getPort() + ImageSaver.OFFSET)) {
302 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get() * 2);
303 OutputStream os = socket.getOutputStream();
304 // send the list of filenames to the server
305 int numFiles = imageFiles.size();
306 if(numFiles > 0xFFFF) {
307 throw new Exception("Too many images on the frame");
308 }
309 os.write((byte) ((numFiles >> 8) & 0xFF));
310 os.write((byte) (numFiles & 0xFF));
311 // send the list of different files
312 for(File f : imageFiles) {
313 byte[] fileName = f.getName().getBytes();
314 int fileNameLen = fileName.length;
315 if(fileNameLen > 255) {
316 throw new Exception("Filename too long");
317 }
318 os.write((byte) ((fileNameLen) & 0xFF));
319 os.write(fileName);
320 }
321 // the server sends indices of files it wants, send their data
322 InputStream is = socket.getInputStream();
323 int i = -1;
324 while((i = is.read()) != -1 && i < imageFiles.size()) {
325 File f = imageFiles.get(i);
326 // System.out.println("... sending " + f.getName());
327 ImageServer.sendImage(f, socket);
328 }
329 } catch(Exception e) {
330 e.printStackTrace();
331 }
332 }
333 }
334
335 private boolean getImage(String imageName, Peer peer) throws IOException {
336 try(Socket socket = new Socket(peer.getAddress(), peer.getPort() + ImageServer.OFFSET)) {
337 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get() * 2);
338 byte[] fileName = imageName.getBytes(FrameShare.CHARSET);
339 int fileNameLen = fileName.length;
340 OutputStream os = socket.getOutputStream();
341 os.write((byte) ((fileNameLen) & 0xFF));
342 os.write(fileName);
343 os.flush();
344 boolean ret = ImageSaver.recvImage(new File(FrameIO.IMAGES_PATH + imageName), socket);
345 socket.close();
346 return ret;
347 } catch(Exception e) {
348 e.printStackTrace();
349 return false;
350 }
351 }
352
353 /**
354 * @param frameName
355 * @param socket
356 * @param peer
357 * @return
358 * @throws IOException
359 */
360 private String getFrameContents(String frameName, DatagramSocket socket, Peer peer) throws IOException {
361 byte[] nameBuf = frameName.getBytes();
362 byte[] buf = new byte[FrameServer.MAX_PACKET_LENGTH];
363 // send request for a frame
364 DatagramPacket packet = new DatagramPacket(nameBuf,
365 nameBuf.length, peer.getAddress(), peer
366 .getPort());
367 socket.send(packet);
368
369 // get response
370 packet = new DatagramPacket(buf, buf.length);
371 socket.receive(packet);
372
373 // store frame contents
374 return new String(packet.getData(), 0, packet.getLength(), FrameShare.CHARSET);
375 }
376
377 public boolean sendMessage(String message, String peerName) {
378 Peer peer = _peers.get(peerName.toLowerCase());
379
380 if (peer == null) {
381 return false;
382 }
383
384 try {
385 // get a datagram socket
386 DatagramSocket socket = new DatagramSocket(_port - 1);
387 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get());
388
389 // message = peerName + " says " + message;
390 byte[] contentsBuf = message.getBytes();
391
392 try {
393 // send save request
394 DatagramPacket packet = new DatagramPacket(contentsBuf,
395 contentsBuf.length, peer.getAddress(), peer.getPort()
396 + MessageReciever.OFFSET);
397 socket.send(packet);
398 } catch (Exception e) {
399 e.printStackTrace();
400 }
401 socket.close();
402
403 } catch (IOException e) {
404 e.printStackTrace();
405 }
406 return true;
407 }
408
409 public String saveFrame(Frame toSave) {
410
411 FrameIO.setSavedProperties(toSave);
412
413 Peer peer = _peers.get(toSave.getPath().toLowerCase());
414
415 List<File> imageFiles = new LinkedList<File>();
416 for(Item i : toSave.getItems()) {
417 if(i instanceof Picture) {
418 ((Picture) i).moveToImagesFolder();
419 File f = new File(((Picture) i).getPath());
420 if(f != null && f.isFile() && !imageFiles.contains(f)) {
421 imageFiles.add(f);
422 }
423 }
424 }
425 new ImageSender(imageFiles, peer).start();
426
427 String fileContents = "";
428 // Now read the frame from the file contents
429 FrameWriter writer = new ExpWriter();
430 try {
431 // String tempName = new Date().toString() + ".exp";
432 // writer.setOutputLocation(tempName);
433 // Write out the file to a StringBuffer
434 StringWriter sw = new StringWriter();
435 // First write out the name of the frame
436 sw.write(toSave.getName() + "\n");
437 // Then the version
438 sw.write(toSave.getVersion() + "\n");
439 // Write out the rest of the frame
440 writer.writeFrame(toSave, sw);
441 // Now send the packet
442 fileContents = sw.getBuffer().toString();
443 byte[] contentsBuf = fileContents.getBytes(FrameShare.CHARSET);
444
445 // get a datagram socket
446 DatagramSocket socket = new DatagramSocket(_port - 2);
447 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get());
448
449 try {
450 // send save request
451 DatagramPacket packet = new DatagramPacket(contentsBuf,
452 contentsBuf.length, peer.getAddress(), peer.getPort()
453 + FrameSaver.OFFSET);
454 socket.send(packet);
455 } catch (Exception e) {
456 e.printStackTrace();
457 }
458 socket.close();
459
460 } catch (IOException e) {
461 e.printStackTrace();
462 }
463 toSave.setSaved();
464 return fileContents;
465 }
466
467 public String getPeerName(int port, InetAddress address) {
468 for (Peer p : _peers.values()) {
469 if (p.getPort() == port && p.getAddress().equals(address))
470 return p.getName();
471 }
472 return null;
473 }
474
475 public int getInfNumber(String peerName, String frameset, boolean update)
476 throws IOException {
477 int result = -1;
478 // get a datagram socket
479 DatagramSocket socket = null;
480 try {
481 socket = new DatagramSocket(_port - 3);
482 socket.setSoTimeout(NetworkSettings.FrameShareTimeout.get());
483
484 byte[] contentsBuf = frameset.getBytes();
485 Peer peer = _peers.get(peerName.toLowerCase());
486 if(peer != null) {
487 try {
488 // send inf request
489 DatagramPacket packet = new DatagramPacket(
490 contentsBuf,
491 contentsBuf.length,
492 peer.getAddress(),
493 peer.getPort()
494 + (update ? InfUpdate.OFFSET : InfServer.OFFSET));
495 socket.send(packet);
496
497 byte[] buf = new byte[100];
498 // get response
499 packet = new DatagramPacket(buf, buf.length);
500 socket.receive(packet);
501
502 // store frame contents
503 result = Integer.parseInt(new String(packet.getData(), 0,
504 packet.getLength()));
505 peerName = peer.getName();
506
507 } catch (Exception e) {
508 e.printStackTrace();
509 }
510 }
511 socket.close();
512 } catch (SocketException e1) {
513 e1.printStackTrace();
514 }
515
516 return result;
517 }
518
519 public static void init(Frame settingsFrame) {
520 if (disableNetworking || settingsFrame == null)
521 return;
522
523 if (_theSession == null)
524 _theSession = new FrameShare(settingsFrame);
525 }
526
527 public static void init(int port) {
528
529 if (_theSession == null) {
530 _theSession = new FrameShare(port);
531 }
532 }
533
534 public static void restart() {
535 if(_theSession != null) {
536 _theSession.finalise();
537 _theSession = new FrameShare(_theSession._port);
538 }
539 }
540
541 public static boolean isHeadless() {
542 return headless;
543 }
544
545
546 /**
547 * Start a server running on the port number supplied on the command line
548 *
549 * @param args
550 */
551 public static void main(String[] args) {
552
553 if (args.length != 1) {
554
555 // Work out the 'program name' (i.e. this class), but done in a general
556 // way in case class name is changed at a later date
557 StackTraceElement[] stack = Thread.currentThread ().getStackTrace ();
558 StackTraceElement main = stack[stack.length - 1];
559 String mainClass = main.getClassName ();
560
561 System.err.println("Usage: java " + mainClass + " port-number");
562 System.exit(1);
563 }
564
565 MessageBay.suppressMessages(true);
566
567 String port_str = args[0];
568
569 FrameShare.headless = true;
570
571 try {
572 int port = Integer.parseInt(port_str);
573
574 init(port);
575 }
576 catch (Exception e) {
577 e.printStackTrace();
578 System.exit(2);
579 }
580
581 }
582
583}
Note: See TracBrowser for help on using the repository browser.