C:\Java WorkShop\Lexx\src\lexx\server\socket\Client.java

1    package lexx.server.socket; 
2     
3    import java.io.DataInputStream; 
4    import java.io.DataOutputStream; 
5    import java.io.File; 
6    import java.io.IOException; 
7    import java.net.InetAddress; 
8    import java.net.Socket; 
9    import java.util.Properties; 
10   import java.util.Vector; 
11    
12   import java.awt.BorderLayout; 
13   import java.awt.event.KeyEvent; 
14   import java.awt.event.KeyListener; 
15   import javax.swing.JLabel; 
16   import javax.swing.JOptionPane; 
17   import javax.swing.JPanel; 
18   import javax.swing.JScrollPane; 
19   import javax.swing.JTextArea; 
20   import javax.swing.JTextField; 
21    
22   import lexx.gui.EditorFrame; 
23   import lexx.server.Chat; 
24   import lexx.server.ClientBase; 
25   import lexx.server.HashProperties; 
26   import lexx.server.RequestedFile; 
27    
28   /** 
29    * <p>Client uses direct communication via a socket</p> 
30    * <p>Copyright (c) 2002-2003</p> 
31    * @since 20/03/2003 
32    * @author Mohammed Imran 
33    * @version 1.0 
34    */ 
35   public final class Client extends Thread implements ClientBase 
36   { 
37     private static final org.apache.log4j.Logger log = org.apache.log4j.Logger. 
38         getLogger(Client.class); 
39     private final JPanel panel = new JPanel(); 
40     private final JTextArea text = new JTextArea(); 
41     private final JTextField field = new JTextField(); 
42     private int clientNum = 1; 
43     private final JLabel usersNum = new JLabel("Number of users online = 1"); 
44     private JLabel status = null; 
45    
46     /** 
47      * Used if you would like to log when the client performs a task 
48      * @param l where the text will be logged 
49      */ 
50     public final void setStatus(JLabel l) 
51     { 
52       status = l; 
53     } 
54    
55     /** 
56      * lexx.server.socket.Client [directory to store project] [server address] 
57      * [server port]<br> 
58      * lexx.server.socket.Client [directory to store project] [server address] 
59      * [server port] [proxy address] [proxy port]<br> 
60      */ 
61     public static void main(String args[]) 
62     { 
63       System.out.println("Usuage:"); 
64       System.out.println(" lexx.server.socket.Client [directory to store project] [server address] [server port]"); 
65       System.out.println(" lexx.server.socket.Client [directory to store project] [server address] [server port] [proxy address] [proxy port]"); 
66    
67       if(args.length == 5) 
68       { 
69         Properties properties = System.getProperties(); 
70         properties.put("http.proxyHost", args[3]); 
71         properties.put("http.proxyPort", args[4]); 
72         properties.put("socksProxyHost", args[3]); 
73         properties.put("socksProxyPort", args[4]); 
74         properties.put("ftp.proxyHost", args[3]); 
75         properties.put("ftp.proxyPort", args[4]); 
76         System.setProperties(properties); 
77       } 
78    
79       String user = "lexx" + ( (int) (Math.random() * 100)); 
80    
81       lexx.utils.Config.setupConfig(args[0], null, null); 
82       Client c = new Client(args[1], Integer.parseInt(args[2]), user); 
83     } 
84    
85     /** 
86      * The GUI for instant messaging. It should contain inside it all the interactions 
87      * required for chatting on-line. 
88      * @return panel for instant messaging 
89      */ 
90     public final JPanel getPanel() 
91     { 
92       return panel; 
93     } 
94    
95     /** 
96      * Tells you the number of clients that are on-line 
97      * @return numner of clients that are on-line 
98      */ 
99     public final int getClientNum() 
100    { 
101      return clientNum; 
102    } 
103   
104    private String username = ""; 
105    private DataOutputStream remoteOut; 
106    private Socket sock = null; 
107   
108    public Client(String hostname, int port, String userName) 
109    { 
110      try 
111      { 
112        if(hostname.equals("local")) 
113        { 
114          hostname = null; 
115   
116        } 
117        InetAddress serverAddr = InetAddress.getByName(hostname); 
118   
119        sock = Chat.getSocket(serverAddr.getHostName(), port); 
120   
121        //NOTE don't use Buffer as it wont send/recieve message straight away 
122        remoteIn = new DataInputStream(sock.getInputStream()); 
123        remoteOut = new DataOutputStream(sock.getOutputStream()); 
124   
125        if(log.isDebugEnabled()) 
126        { 
127          log.debug("Connected to server " + serverAddr.getHostName() + 
128                    " on port " + sock.getPort()); 
129        } 
130        username = userName; 
131   
132        panel.setLayout(new BorderLayout()); 
133   
134        panel.add(new JScrollPane(text), BorderLayout.CENTER); 
135        panel.add(field, BorderLayout.SOUTH); 
136        panel.add(usersNum, BorderLayout.NORTH); 
137   
138        new Thread() 
139        { 
140          public void run() 
141          { 
142            this.setPriority(Thread.MIN_PRIORITY); 
143            while(true) 
144            { 
145              try 
146              { 
147                usersNum.setText("Number of users online = " + clientNum + 
148                                 ",         Sent " + 
149                                 ( (int) (Chat.sent / 1024)) + "Kb, Recieved " + 
150                                 ( (int) (Chat.receieved / 1024)) + "Kb"); 
151                this.sleep(2000); 
152              } 
153              catch(InterruptedException ex) 
154              { 
155                log.fatal(ex, ex); 
156              } 
157            } 
158          } 
159        }.start(); 
160   
161        field.addKeyListener(new KeyListener() 
162        { 
163          public void keyTyped(KeyEvent e) 
164          { 
165            if(e.getKeyChar() == '\n' || e.getKeyChar() == '\r') 
166            { 
167              if(field.getText() != null && !"".equals(field.getText())) 
168              { 
169                send(new Chat().sendMessage("[" + username + "] " + field.getText())); 
170                field.setText(""); 
171              } 
172            } 
173          } 
174   
175          public void keyPressed(KeyEvent e) 
176          {} 
177   
178          public void keyReleased(KeyEvent e) 
179          {} 
180        }); 
181   
182        this.start(); 
183        this.updateMyFiles(); 
184      } 
185      catch(IOException e) 
186      { 
187        log.fatal(e.getMessage() + " : Failed to connect to server." + e, e); 
188      } 
189    } 
190   
191    /** 
192     * Downloads all the changed files from the server 
193     */ 
194    public final void updateMyFiles() 
195    { 
196      this.send(c.sendMeHash()); 
197    } 
198   
199    /** 
200     * Uploads all the changed files to the server 
201     */ 
202    public final void updateServer() 
203    { 
204      this.send(c.sendHashProperties(HashProperties.hashCurrentProject(lexx.utils. 
205          Config.getProjectPath()))); 
206    } 
207   
208    private void send(String s) 
209    { 
210      try 
211      { 
212        if(log.isDebugEnabled()) 
213        { 
214          log.debug("sending " + s); 
215        } 
216        Chat.sendUTFMessage(s, remoteOut); 
217        if(log.isDebugEnabled()) 
218        { 
219          log.debug("sent " + s); 
220        } 
221      } 
222      catch(IOException ex) 
223      { 
224        log.fatal(ex, ex); 
225      } 
226    } 
227   
228    protected final void finalize() throws Throwable 
229    { 
230      try 
231      { 
232        sock.close(); 
233      } 
234      catch(IOException ex) 
235      { 
236        log.fatal(ex, ex); 
237      } 
238      super.finalize(); 
239    } 
240   
241    private final Chat c = new Chat(); 
242    private final Vector reqFileAns = new Vector(); 
243   
244    /** 
245     * Unlocks the file when you have finished with it, so other clients can use it 
246     * @param filename name of the file you have just unlocked 
247     */ 
248    public final void releaseFile(String filename) 
249    { 
250      if(filename.startsWith(lexx.utils.Config.getProjectPath())) 
251      { 
252        filename = filename.substring(lexx.utils.Config.getProjectPath().length() + 
253                                      1, filename.length()); 
254   
255      } 
256      this.send(c.sendRequestedFile(filename, false)); 
257    } 
258   
259    private HashProperties serverHash = null; 
260   
261    /** 
262     * Requset of file for editing 
263     * @param filename the name of the file you wish to edit 
264     * @return tells you if someone else has locked the file 
265     */ 
266    public final boolean requestFile(String filename) 
267    { 
268      if(filename.startsWith(lexx.utils.Config.getProjectPath())) 
269      { 
270        filename = filename.substring(lexx.utils.Config.getProjectPath().length() + 
271                                      1, filename.length()); 
272   
273      } 
274      this.send(c.sendRequestedFile(filename, true)); //want this file 
275   
276      while(true) //clientNum > 1) 
277      { 
278        try 
279        { 
280          Thread.sleep(1500); 
281          for(int i = 0; i < reqFileAns.size(); i++) 
282          { 
283            RequestedFile r = (RequestedFile) reqFileAns.get(i); 
284            if(r.getFilename().equals(filename)) 
285            { 
286              reqFileAns.remove(r); 
287              return r.canHaveFile(); 
288            } 
289          } 
290        } 
291        catch(InterruptedException ex) 
292        { 
293          log.fatal(ex, ex); 
294        } 
295      } 
296   
297  //    return true; 
298    } 
299   
300    private void handleMessage(String message) 
301    { 
302      status = lexx.utils.Config.getStatusLabel(); 
303   
304      if(message == null || "".equals(message.trim())) 
305      { 
306        return; 
307      } 
308   
309      if(message.startsWith(Server.MESSAGE)) //recieved a message 
310      { 
311        text.append(c.recievedMessage(message).trim() + '\n'); 
312        text.setCaretPosition(text.getText().length()); 
313   
314        if(lexx.utils.Config.getFrameForDialog()instanceof EditorFrame) 
315        { 
316          EditorFrame f = (EditorFrame) lexx.utils.Config.getFrameForDialog(); 
317          f.setLowerTabSelect(this.getPanel()); 
318        } 
319      } 
320   
321      if(message.startsWith(Server.REQUESTFILE)) //tells me if someone is using that file 
322      { 
323        //String fileName = c.getRequestedFile(message).getFilename(); 
324        reqFileAns.add(c.getRequestedFile(message)); 
325      } 
326   
327      if(message.startsWith(Server.FILENAME)) //wants send me a file 
328      { 
329        File f = c.createFile(message, lexx.utils.Config.getProjectPath(), false); 
330   
331        if(status != null) 
332        { 
333          status.setText("Recieved file from server - " + f.getName()); 
334        } 
335      } 
336   
337      if(message.startsWith(Server.HASHPROPERTIES)) //updating server files from clients 
338      { 
339        if(status != null) 
340        { 
341          status.setText("Recieved list of files contained on server"); 
342   
343        } 
344        HashProperties hash = HashProperties.hashCurrentProject(lexx.utils.Config. 
345            getProjectPath()); 
346        HashProperties newHash = c.getHashProperties(message); 
347        java.util.List diff = HashProperties.getDifferentFiles(hash, newHash); 
348   
349        if(waitCache) 
350        { 
351          serverHash = newHash; 
352   
353          if(waitCache2) 
354          { 
355            return; 
356          } 
357          else 
358          { 
359            waitCache2 = true; 
360          } 
361        } 
362   
363        for(int i = 0; i < diff.size(); i++) 
364        { //requesting different files 
365          if(status != null) 
366          { 
367            status.setText("Requesting " + diff.get(i).toString() + 
368                           " from server"); 
369          } 
370          this.send(c.sendMeFile(diff.get(i).toString())); 
371        } 
372        this.send(Server.END_OF_FILES); 
373      } 
374   
375      if(message.startsWith(Server.END_OF_FILES)) 
376      { //backup messure ensuring waitForCache never waits for ever 
377        waitCache = false; 
378        waitCache2 = false; 
379        serverHash = null; 
380      } 
381   
382      if(message.startsWith(Server.SENDMEHASH)) //send my hashproperties 
383      { 
384        if(status != null) 
385        { 
386          status.setText("Sending my list of files to server"); 
387        } 
388        this.send(c.sendHashProperties(HashProperties.hashCurrentProject(lexx. 
389            utils.Config.getProjectPath()))); 
390        if(status != null) 
391        { 
392          status.setText(""); 
393        } 
394      } 
395   
396      if(message.startsWith(Server.SENDMEFILE)) //send user a file 
397      { 
398        String filename = c.getSendFileName(message); 
399        if(status != null) 
400        { 
401          status.setText("Sending server " + filename + " file"); 
402        } 
403        this.send(c.sendFile(new File(lexx.utils.Config.getProjectPath() + File.separatorChar + 
404                                      filename), lexx.utils.Config.getProjectPath(), false)); 
405        if(status != null) 
406        { 
407          status.setText(""); 
408        } 
409      } 
410   
411      if(message.startsWith(Server.CLIENT_NUM)) //updating number of clients 
412      { 
413        clientNum = c.getClientNum(message); 
414  ////////////////      usersNum.setText("Number of users online = " + clientNum); 
415        if(lexx.utils.Config.getFrameForDialog()instanceof EditorFrame) 
416        { 
417          EditorFrame f = (EditorFrame) lexx.utils.Config.getFrameForDialog(); 
418          f.setLowerTabSelect(this.getPanel()); 
419        } 
420      } 
421    } 
422   
423    private boolean waitCache = false; 
424    private boolean waitCache2 = false; 
425   
426    /** 
427     * Sends the file to the server 
428     * @param f the file you wish to send 
429     */ 
430    public final void sendFile(File f) 
431    { 
432      String filename = f.toString(); 
433   
434      if(filename.startsWith(lexx.utils.Config.getProjectPath())) 
435      { 
436        filename = filename.substring(lexx.utils.Config.getProjectPath().length() + 
437                                      1, filename.length()); 
438   
439      } 
440      if(status != null) 
441      { 
442        status.setText("Sending server " + filename + " file"); 
443      } 
444      this.send(c.sendFile(f, lexx.utils.Config.getProjectPath(), false)); 
445      if(status != null) 
446      { 
447        status.setText(""); 
448      } 
449    } 
450   
451    /** 
452     * This is called when you call the updataMyFiles() method, as it waits until 
453     * all the files have been successfully updated 
454     */ 
455    public final void waitUtilCached() 
456    { 
457      waitCache = true; 
458      this.updateMyFiles(); 
459      long recieved = Chat.receieved; 
460   
461      while(waitCache) 
462      { 
463        if(serverHash != null) 
464        { 
465          boolean hasUpdated = HashProperties.getDifferentFiles( 
466              HashProperties.hashCurrentProject(lexx.utils.Config.getProjectPath()), 
467              serverHash).size() == 0; 
468   
469          if(hasUpdated) 
470          { 
471            waitCache = false; 
472            waitCache2 = false; 
473            serverHash = null; 
474            return; 
475          } 
476        } 
477        try 
478        { 
479          Thread.sleep(5000); 
480          if(recieved == Chat.receieved) 
481          { 
482            this.updateMyFiles(); 
483          } 
484   
485          recieved = Chat.receieved; 
486        } 
487        catch(InterruptedException ex) 
488        { 
489          log.fatal(ex, ex); 
490        } 
491      } 
492    } 
493   
494    private DataInputStream remoteIn = null; 
495   
496    public final /*synchronized*/ void run() 
497    { 
498      String s; 
499      try 
500      { 
501        while(true) 
502        { 
503          s = Chat.getUTFMessage(remoteIn); 
504   
505          if(!"".equals(s)) 
506          { 
507            if(log.isDebugEnabled()) 
508            { 
509              log.debug("recieved" + s); 
510            } 
511            this.handleMessage(s); 
512          } 
513        } 
514      } 
515      catch(IOException e) 
516      { 
517        lexx.utils.Config.beep(); 
518        JOptionPane.showMessageDialog(panel, 
519            "You have been disconnected from server, please reconnect", 
520            "Unable to connect to server", 
521            JOptionPane.ERROR_MESSAGE); 
522        log.fatal(e, e); 
523      } 
524      finally 
525      { 
526        try 
527        { 
528          remoteIn.close(); 
529        } 
530        catch(IOException ex) 
531        { 
532          log.fatal(ex, ex); 
533        } 
534      } 
535    } 
536  }