Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: nectar/reda/client/base/ClientComThread.java


1   /*
2       Copyright (C) 2003  Kai Schutte
3    
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8    
9       This program is distributed in the hope that it will be useful,
10      but WITHOUT ANY WARRANTY; without even the implied warranty of
11      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12      GNU General Public License for more details.
13   
14      You should have received a copy of the GNU General Public License
15      along with this program; if not, write to the Free Software
16      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   
18   * ClientComThread.java
19   *
20   * Created on April 7, 2003, 4:57 PM
21   */
22  
23  package nectar.reda.client.base;
24  
25  import nectar.reda.io.*;
26  
27  import nectar.reda.client.action.*;
28  import java.beans.PropertyChangeSupport;
29  import java.beans.PropertyChangeListener;
30  /** Client Communication Thread -- handles all the data communication with the server.
31   * @author Kai Schutte skander@skander.com
32   */
33  public class ClientComThread extends ControllerThread {
34      
35      private RedaClientConnection client = new RedaClientConnection();
36      private String login = "login";
37      private String password = "";
38      private Long myUserId = null;
39      private Long myProject = null;
40      private boolean authenticated = false;
41      private ClientJFrame mainWindow = null;
42      private ConnectionStats connStats = null;
43      private ActionPump pump = null;
44      private java.io.ObjectInputStream in = null;
45      private java.io.ObjectOutputStream out = null;
46      
47      private ConnectionStatusJDialog statusDialog = null;
48      
49      protected PropertyChangeSupport statusChangeListeners;
50      /** Property Change Event description, indicating the percentage progress of the connection establishement.
51       */    
52      public static final String CONNECTION_PROGRESS_PERCENT = RedaClientConnection.CONNECTION_PROGRESS_PERCENT;
53      /** Property Change Event description, indicating the currently running operation in the connection establishement process.
54       */    
55      public static final String CONNECTION_PROGRESS_STRING = RedaClientConnection.CONNECTION_PROGRESS_STRING;
56      /** Property Change Event description, indicating whether the connection is established or not.
57       */    
58      public static final String CONNECTION_STATUS = "CONNECTION_STATUS";
59      
60      protected String progressString = null;
61      protected int progressPercent = 0;
62      
63      /** Creates a new instance of ClientComThread
64       * @param mainWindow The ClientJFrame instance.
65       * @param pump The ActionPump instance.
66       */
67      public ClientComThread(ClientJFrame mainWindow, ActionPump pump) {
68          super(new ThreadGroup("ClientComThread"), "ClientComThread");
69          this.mainWindow = mainWindow;
70          this.pump = pump;
71          connStats = new ConnectionStats(pump, this);
72          statusChangeListeners = new PropertyChangeSupport(this);
73      }
74      
75      /** Reloads configuration variables from the Configuration object. This operation can *ONLY* be performed when the communication is inactive -- when the client is disconnected.
76       * @param config The Configuration instance to copy variables from.
77       * @throws IOException thrown when the connection is active.
78       */    
79      public void reloadConfig(Configuration config) throws java.io.IOException {
80          client.setServerHostname(config.get("serverHostname"));
81          client.setServerPort(config.get("serverPort"));
82          client.setSoTimeout(config.get("soTimeout"));
83          login = config.get("login");
84          password = config.get("password");
85      }
86      
87      /** Attempts to disconnect from the server.
88       * @throws IOException Any IOException, including when this method is called an a disconnected connection.
89       */    
90      public void disconnect() throws java.io.IOException {
91          this.statusChangeListeners.firePropertyChange(CONNECTION_STATUS, new Boolean(true), new Boolean(false));
92          authenticated = false;
93          client.disconnect();
94      }
95      
96      public void setStatus(String status, int progress) {
97          String oldStatus = this.progressString;
98          int oldPercent = this.progressPercent;
99          this.progressString = status;
100         this.progressPercent = progress;
101         statusChangeListeners.firePropertyChange(CONNECTION_PROGRESS_PERCENT, new Integer(oldPercent), new Integer(progress));
102         statusChangeListeners.firePropertyChange(CONNECTION_PROGRESS_STRING, oldStatus, status);
103     }
104     
105     /** The main running loop for the communication, called when the Thread is started.
106      */    
107     public void runThread() {
108         Exception dataExchangeException = null;
109         authenticated = false;
110         try {
111             client.connect();
112             setStatus("Reda: Initializing Object I/O Streams", 65);
113             in = new java.io.ObjectInputStream(client.getInputStream()) {
114                 protected Class resolveClass(java.io.ObjectStreamClass desc) throws java.io.IOException, ClassNotFoundException {
115                     return ClientJFrame.getClassLoader().loadClass(desc.getName().trim());
116                 }
117             };
118             setStatus("Reda: Initializing Object I/O Streams", 70);
119             out = new java.io.ObjectOutputStream(client.getOutputStream());
120             out.flush();
121         } catch (java.io.IOException e) {
122             e.printStackTrace();
123             statusDialog.hide();
124             statusDialog.dispose();
125             mainWindow.displayException(e);
126             this.statusChangeListeners.firePropertyChange(CONNECTION_STATUS, new Boolean(true), new Boolean(false));
127             return;
128         }
129         
130         setStatus("Logging in...", 100);
131         // do authentication
132         boolean authResult = false;
133         while (!authResult) {
134             if (this.login != null && this.login.length() > 0 && this.password != null && this.password.length() > 0) {
135                 try {
136                     authResult = authenticate(in, out);
137                 } catch (Exception e) {
138                     e.printStackTrace();
139                     mainWindow.displayException(e);
140                     this.statusChangeListeners.firePropertyChange(CONNECTION_STATUS, new Boolean(true), new Boolean(false));
141                     return;
142                 }
143                 if (authResult) {
144                     authenticated = true;
145                     setStatus("Connection established", 100);
146                 } // else loop
147             }
148             if (!authResult) {
149                 LoginJDialog loginDialog = new LoginJDialog(statusDialog, true, this.login);
150                 loginDialog.show();
151                 if (loginDialog.hasBeenCancelled()) {
152                     try {
153                         disconnect();
154                     } catch (java.io.IOException e) {
155                     }
156                     return;
157                 } else {
158                     this.login = loginDialog.getLogin();
159                     this.password = loginDialog.getPassword();
160                 }
161             }
162         }
163         statusChangeListeners.firePropertyChange(CONNECTION_STATUS, new Boolean(false), new Boolean(true));
164         
165         connStats.start();
166         boolean loop = true;
167         if (dataExchangeException == null) {
168             while (keepRunning() && client.isConnected() && loop) {
169                 try {
170                     Object actionObj = in.readObject();
171                     this.pump.returnAction((ClientAction)actionObj); // this can throw a ClassCastException if it wants...
172                 } catch (ClassNotFoundException e) {
173                     e.printStackTrace();
174                     // should log this -- might point to a bug in the updater if we're
175                     // getting Object's that we should have a class for...
176                     dataExchangeException = e;
177                     loop = false; // kill this connection;
178                 } catch (java.io.InvalidClassException e) {
179                     e.printStackTrace();
180                     // ??
181                     dataExchangeException = e;
182                     loop = false; // kill this connection;
183                 } catch (java.io.StreamCorruptedException e) {
184                     e.printStackTrace();
185                     // this is bad... perhaps the connection got reset, in that case we
186                     // should disconnect and display a warning...
187                     // else, we probably have a communication bug in the underlying StreamLayers
188                     // that corrupted our data, since it's improbable that it's due to the actual
189                     // TCP transmission...
190                     dataExchangeException = e;
191                     loop = false; // kill this connection;
192                 } catch (java.io.OptionalDataException e) {
193                     e.printStackTrace();
194                     // probably a bug in the StreamLayers... there shouldn't be any raw data in
195                     // this stream, all lower levels commands should have been recuperated by the
196                     // layers...
197                     dataExchangeException = e;
198                     loop = false; // kill this connection;
199                 } catch (java.io.EOFException e) {
200                     // abrupt disconnect
201                     loop = false; // kill this connection;
202                 } catch (java.io.InterruptedIOException e) {
203                     // ControllerThread interrupted this thread, perhaps wanting us to shutdown?
204                     loop = false; // kill this connection;
205                 } catch (java.net.SocketException e) {
206                     loop = false;
207                 } catch (java.io.IOException e) {
208                     // an unknown Exception;
209                     dataExchangeException = e;
210                     e.printStackTrace();
211                     loop = false; // kill this connection;
212                 }
213             }
214         }
215         
216         try {
217             System.err.println("disconnecting...");
218             System.err.flush();
219             disconnect();
220         } catch (java.io.IOException e) {
221             e.printStackTrace();
222         }
223         if (dataExchangeException != null) {
224             dataExchangeException.printStackTrace();
225             statusDialog.hide();
226             statusDialog.dispose();
227             mainWindow.displayException(dataExchangeException);
228             this.statusChangeListeners.firePropertyChange(CONNECTION_STATUS, new Boolean(true), new Boolean(false));
229         }
230     }
231     
232     public void addPropertyChangeListener(PropertyChangeListener listener) {
233         client.addPropertyChangeListener(listener);
234         this.statusChangeListeners.addPropertyChangeListener(listener);
235     }
236     
237     public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
238         client.addPropertyChangeListener(property, listener);
239         this.statusChangeListeners.addPropertyChangeListener(property, listener);
240     }
241     
242     public void removePropertyChangeListener(PropertyChangeListener listener) {
243         client.removePropertyChangeListener(listener);
244         this.statusChangeListeners.removePropertyChangeListener(listener);
245     }
246     
247     /** check whether the connection is established.
248      * @return true if connected, false otherwise.
249      */    
250     public boolean isConnected() {
251         return client.isConnected();
252     }
253     /** Check whether authentication has been successful on the current connection.
254      * @return true if authenticated, false otherwise.
255      */    
256     public boolean isAuthenticated() {
257         return authenticated;
258     }
259     
260     /** Sends a ClientAction to the Server -- Note that you should use the ActionPump mechanism to send commands and receive responses.
261      * @param action The ClientAction to send.
262      * @throws ClientActionException If the action execution, or it's transport through the connection cause any trouble.
263      * @see nectar.reda.client.action.ActionPump
264      * @see nectar.reda.client.action.ClientAction
265      * @see nectar.reda.client.action.ActionPump#doAction
266      */    
267     public synchronized void sendAction(ClientAction action) throws nectar.reda.client.action.ClientActionException {
268         if (!this.isConnected() || !this.isAuthenticated())
269             throw new nectar.reda.client.action.ClientActionNotConnectedException();
270         try {
271             this.out.writeObject(action);
272             this.out.flush();
273         } catch (java.io.IOException e) {
274             e.printStackTrace();
275             throw new nectar.reda.client.action.ClientActionException();
276         }
277     }
278     
279     public ConnectionStats getConnectionStats() {
280         return this.connStats;
281     }
282     
283     public void setStatusDialog(ConnectionStatusJDialog dialog) {
284         this.statusDialog = dialog;
285     }
286     
287     public boolean authenticate(java.io.ObjectInputStream in, java.io.ObjectOutputStream out) throws Exception {
288         LoginAction loginAction = new LoginAction();
289         loginAction.setLogin(this.login);
290         loginAction.setPassword(this.password);
291         out.writeObject(loginAction);
292         out.flush();
293         Object obj = in.readObject();
294         if (!(obj instanceof LoginAction))
295             throw new java.io.IOException("Invalid Authentication Response.");
296         loginAction = (LoginAction)obj;
297         if (loginAction.isLoggedIn()) {
298             setMyUserId(loginAction.getUserId());
299             setMyProject(loginAction.getProjectId());
300             return true;
301         }
302         return false;
303     }
304     
305     private void setMyUserId(Long id) {
306         this.myUserId = id;
307     }
308     
309     public Long getMyUserId() {
310         return myUserId;
311     }
312     
313     private void setMyProject(Long project) {
314         this.myProject = project;
315     }
316     
317     public Long getMyProject() {
318         return myProject;
319     }
320 }