KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > quickserver > net > server > impl > BasicClientHandler


1 /*
2  * This file is part of the QuickServer library
3  * Copyright (C) 2003-2005 QuickServer.org
4  *
5  * Use, modification, copying and distribution of this software is subject to
6  * the terms and conditions of the GNU Lesser General Public License.
7  * You should have received a copy of the GNU LGP License along with this
8  * library; if not, you can download a copy from <http://www.quickserver.org/>.
9  *
10  * For questions, suggestions, bug-reports, enhancement-requests etc.
11  * visit http://www.quickserver.org
12  *
13  */

14
15 package org.quickserver.net.server.impl;
16
17 import java.io.*;
18 import java.net.Socket JavaDoc;
19 import java.net.SocketException JavaDoc;
20 import java.net.SocketTimeoutException JavaDoc;
21 import java.net.InetAddress JavaDoc;
22 import java.util.*;
23 import java.util.logging.*;
24 import javax.net.ssl.*;
25 import java.security.*;
26 import java.nio.*;
27 import java.nio.channels.*;
28
29 import org.quickserver.net.*;
30 import org.quickserver.util.*;
31 import org.quickserver.net.server.*;
32
33 /**
34  * Basic implementation of ClientHandler that handles clients for QuickServer.
35  * <p> This class is used by {@link QuickServer} to handle each new client
36  * connected. This class is responsible to handle client sockets. It can operate
37  * in both blocking mode and non-blocking mode (java nio).</p>
38  * <p>
39  * Contributions By:
40  * Martin Benns : BYTE Mode
41  * </p>
42  * @author Akshathkumar Shetty
43  * @author Martin Benns : Added BYTE mode
44  */

45 public abstract class BasicClientHandler implements ClientHandler {
46     private static final Logger logger = Logger.getLogger(BasicClientHandler.class.getName());
47
48     protected static final String JavaDoc NEW_LINE = QuickServer.getNewLine();
49     protected static final byte NEW_LINE_BYTES[] = NEW_LINE.getBytes();
50
51     //Some variable are not initialised to any value because the
52
//default java value was desired initial value.
53

54     /** Client socket */
55     protected Socket JavaDoc socket;
56     /** Client authorisation status */
57     protected volatile boolean authorised;
58     /** Count of client login attempts */
59     protected int counAuthTry;
60     /** max allowed login attempts */
61     protected int maxAuthTry = 5;
62     /** timeout message */
63     protected String JavaDoc timeoutMsg;
64     /** Message to be displayed when max login attempt reaches.*/
65     protected String JavaDoc maxAuthTryMsg;
66
67     protected int socketTimeout;
68     protected volatile boolean connection; //false
69
protected boolean lost; //false
70

71     protected QuickServer quickServer;
72     protected Authenticator authenticator; //v1.3
73
protected ClientAuthenticationHandler clientAuthenticationHandler; //v1.4.6
74
protected ClientEventHandler clientEventHandler; //v1.4.6
75
protected ClientExtendedEventHandler clientExtendedEventHandler; //v1.4.6
76
protected ClientCommandHandler clientCommandHandler;
77     protected ClientObjectHandler clientObjectHandler; //v1.2
78
protected ClientBinaryHandler clientBinaryHandler; //1.4
79
protected ClientData clientData;
80
81     protected InputStream in;
82     protected OutputStream out;
83     protected BufferedReader bufferedReader;
84     //if DataMode.OBJECT
85
protected ObjectOutputStream o_out; //v1.2
86
protected ObjectInputStream o_in; //v1.2
87
//added for BYTE mode and BINARY mode
88
protected BufferedInputStream b_in;
89     protected BufferedOutputStream b_out;
90
91     //logger for the application using this QuickServer
92
protected Logger appLogger;
93     protected DataMode dataModeIN = null;
94     protected DataMode dataModeOUT = null;
95
96     protected boolean communicationLogging = true;
97     protected Date clientConnectedTime = null;
98     protected Date lastCommunicationTime = null;
99     protected boolean secure = false;
100
101     //--v1.4.5
102
protected static final ThreadLocal JavaDoc threadEvent = new ThreadLocal JavaDoc();
103
104     protected String JavaDoc maxConnectionMsg;
105     protected Set clientEvents = new HashSet();
106     protected List unprocessedClientEvents = Collections.synchronizedList(new ArrayList());
107     
108     protected volatile boolean closeOrLostNotified;
109     protected Object JavaDoc lockObj = new Object JavaDoc();
110     protected volatile boolean willClean;
111     protected String JavaDoc charset;
112
113     private static Map idMap = new HashMap();
114     private int instanceCount;
115     private int id;
116     private String JavaDoc name;
117     private String JavaDoc hostAddress;
118     private int port;
119
120     static class InstanceId {
121         private int id = 0;
122         public int getNextId() {
123             return ++id;
124         }
125     };
126
127     private static int getNewId(int instanceCount) {
128         InstanceId instanceId = (InstanceId) idMap.get(""+instanceCount);
129         if(instanceId==null) {
130             instanceId = new InstanceId();
131             idMap.put(""+instanceCount, instanceId);
132         }
133         return instanceId.getNextId();
134     }
135
136     public BasicClientHandler(int instanceCount) {
137         this.instanceCount = instanceCount;
138         id = getNewId(instanceCount);
139
140         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
141         sb.append("<ClientHandler-Pool#");
142         sb.append(instanceCount);
143         sb.append("-ID:");
144         sb.append(id);
145         sb.append(">");
146         name = sb.toString();
147     }
148
149     public int getInstanceCount() {
150         return instanceCount;
151     }
152
153
154     public BasicClientHandler() {
155         this(-1);
156     }
157     
158     public void clean() {
159         counAuthTry = 0;
160         authorised = false;
161         in = null;
162         out = null;
163         bufferedReader = null;
164         o_out = null; o_in = null;
165         b_in = null; b_out = null;
166
167         dataModeIN = null;
168         dataModeOUT = null;
169
170         lost = false;
171         clientData = null;
172         clientConnectedTime = null;
173         lastCommunicationTime = null;
174         communicationLogging = true;
175         socketTimeout = 0;
176         secure = false;
177         
178         authenticator = null;
179         clientAuthenticationHandler = null;//1.4.6
180
clientCommandHandler = null;
181         clientObjectHandler = null;
182         clientBinaryHandler = null;//1.4
183
clientData = null;
184
185         maxConnectionMsg = null;
186         synchronized(clientEvents) {
187             clientEvents.clear();
188             unprocessedClientEvents.clear();
189         }
190
191         closeOrLostNotified = false;
192
193         if(socket!=null) {
194             try {
195                 socket.close();
196             } catch(Exception JavaDoc er) {
197                 appLogger.warning("Error in closing socket: "+er);
198             }
199             socket = null;
200         }
201
202         hostAddress = null;
203         port = 0;
204
205         quickServer = null;
206         willClean = false;
207         charset = null;
208     }
209
210     protected void finalize() throws Throwable JavaDoc {
211         super.finalize();
212     }
213
214     /**
215      * Associates the ClientHanlder with the client encapsulated by
216      * <code>theClient</code>.
217      * @param theClient object that encapsulates client socket
218      * and its configuration details.
219      */

220     public void handleClient(TheClient theClient) {
221         setServer(theClient.getServer());
222
223         if(getServer().isRunningSecure()==true) {
224             setSecure(true);
225         }
226         setSocket(theClient.getSocket());
227
228         if(theClient.getTrusted()==false) {
229             setAuthenticator(theClient.getAuthenticator());
230             setClientAuthenticationHandler(theClient.getClientAuthenticationHandler());
231         }
232         setClientEventHandler(theClient.getClientEventHandler());
233         setClientExtendedEventHandler(theClient.getClientExtendedEventHandler());
234         setClientCommandHandler(theClient.getClientCommandHandler());
235         setClientObjectHandler(theClient.getClientObjectHandler());
236         setClientBinaryHandler(theClient.getClientBinaryHandler()); //v1.4
237

238         setClientData(theClient.getClientData());
239         if(theClient.getTrusted()==false) {
240             socketTimeout = theClient.getTimeout();
241         }
242         timeoutMsg = theClient.getTimeoutMsg();
243         maxAuthTryMsg = theClient.getMaxAuthTryMsg();
244         maxAuthTry = theClient.getMaxAuthTry(); //v1.2
245
appLogger = quickServer.getAppLogger(); //v1.2
246

247         setCommunicationLogging(theClient.getCommunicationLogging()); //v1.3.2
248

249         maxConnectionMsg = theClient.getMaxConnectionMsg();//1.4.5
250
addEvent(theClient.getClientEvent());//1.4.5
251
}
252
253     /**
254      * Returns the QuickServer object that created it.
255      * @see #setServer
256      */

257     public QuickServer getServer() {
258         return quickServer;
259     }
260     /**
261      * Sets the QuickServer object associated with this ClientHandler.
262      * @see #getServer
263      */

264     protected void setServer(QuickServer server) {
265         Assertion.affirm(server!=null, "QuickServer can't be null!");
266         quickServer = server;
267     }
268     
269     /**
270      * Sets the ClientData object associated with this ClientHandler
271      * @see ClientData
272      * @see #getClientData
273      */

274     protected void setClientData(ClientData data) {
275         this.clientData = data;
276     }
277     /**
278      * Returns the ClientData object associated with this ClientHandler,
279      * if not set will return <code>null</code>
280      * @see ClientData
281      * @see #setClientData
282      */

283     public ClientData getClientData() {
284         return clientData;
285     }
286
287     /**
288      * Sets the ClientAuthenticationHandler class that handles the
289      * authentication of a client.
290      * @param clientAuthenticationHandler fully qualified name of the class that
291      * implements {@link ClientAuthenticationHandler}.
292      * @since 1.4.6
293      */

294     protected void setClientAuthenticationHandler(ClientAuthenticationHandler clientAuthenticationHandler) {
295         this.clientAuthenticationHandler = clientAuthenticationHandler;
296     }
297
298     /**
299      * Sets the Authenticator class that handles the
300      * authentication of a client.
301      * @param authenticator fully qualified name of the class that
302      * implements {@link Authenticator}.
303      * @since 1.3
304      */

305     protected void setAuthenticator(Authenticator authenticator) {
306         this.authenticator = authenticator;
307     }
308
309     /**
310      * Returns the {@link java.io.InputStream} associated with
311      * the Client being handled.
312      * @see #setInputStream
313      */

314     public InputStream getInputStream() {
315         return in;
316     }
317     /**
318      * Sets the {@link java.io.InputStream} associated with
319      * the Client being handled.
320      * @since 1.1
321      * @see #getInputStream
322      */

323     protected abstract void setInputStream(InputStream in) throws IOException;
324
325     /**
326      * Returns the {@link java.io.OutputStream} associated with
327      * the Client being handled.
328      * @see #setOutputStream
329      */

330     public OutputStream getOutputStream() {
331         return out;
332     }
333     /**
334      * Set the {@link java.io.OutputStream} associated with
335      * the Client being handled.
336      * @since 1.1
337      * @see #getOutputStream
338      * @exception IOException if ObjectOutputStream could not be created.
339      */

340     public void setOutputStream(OutputStream out) throws IOException {
341         this.out = out;
342         if(getDataMode(DataType.OUT) == DataMode.STRING ||
343                 getDataMode(DataType.OUT) == DataMode.BYTE ||
344                 getDataMode(DataType.OUT) == DataMode.BINARY) {
345             o_out = null;
346             b_out = new BufferedOutputStream(out);
347         } else if(getDataMode(DataType.OUT) == DataMode.OBJECT) {
348             b_out = null;
349             o_out = new ObjectOutputStream(out);
350             o_out.flush();
351         } else {
352             throw new IllegalStateException JavaDoc("Unknown DataMode " +getDataMode(DataType.OUT));
353         }
354     }
355     
356     /**
357      * Returns the {@link java.io.BufferedReader} associated with
358      * the Client being handled. Note that this is only available under blocking mode.
359      * @see #getBufferedWriter
360      */

361     public abstract BufferedReader getBufferedReader();
362
363     /**
364      * Returns the {@link java.io.BufferedWriter} associated with
365      * the Client being handled.
366      * @deprecated since 1.4.5 use getOutputStream()
367      */

368     public BufferedWriter getBufferedWriter() {
369         return new BufferedWriter(new OutputStreamWriter(b_out));
370     }
371
372     /**
373      * Returns the {@link java.io.ObjectOutputStream} associated with
374      * the Client being handled.
375      * It will be <code>null</code> if no {@link ClientObjectHandler}
376      * was set in {@link QuickServer}.
377      * @see #getObjectInputStream
378      * @since 1.2
379      */

380     public ObjectOutputStream getObjectOutputStream() {
381         return o_out;
382     }
383     /**
384      * Returns the {@link java.io.ObjectInputStream} associated with
385      * the Client being handled.
386      * It will be <code>null</code> if no {@link ClientObjectHandler}
387      * was set in {@link QuickServer}.
388      * @see #getObjectOutputStream
389      * @since 1.2
390      */

391     public ObjectInputStream getObjectInputStream() {
392         return o_in;
393     }
394
395     /**
396      * Sets the ClientEventHandler class that gets notified of client events.
397      * @since 1.4.6
398      */

399     protected void setClientEventHandler(ClientEventHandler handler) {
400         clientEventHandler=handler;
401     }
402
403     /**
404      * Sets the ClientExtendedEventHandler class that gets notified of extended client events.
405      * @since 1.4.6
406      */

407     protected void setClientExtendedEventHandler(ClientExtendedEventHandler handler) {
408         clientExtendedEventHandler=handler;
409     }
410
411     /**
412      * Sets the ClientCommandHandler class that interacts with
413      * client sockets.
414      */

415     protected void setClientCommandHandler(ClientCommandHandler handler) {
416         clientCommandHandler=handler;
417     }
418
419     /**
420      * Sets the ClientObjectHandler class that interacts with
421      * client sockets.
422      * @param handler fully qualified name of the class that
423      * implements {@link ClientObjectHandler}
424      * @since 1.2
425      */

426     protected void setClientObjectHandler(ClientObjectHandler handler) {
427         clientObjectHandler = handler;
428     }
429
430     /** Closes client socket associated. */
431     public abstract void closeConnection();
432
433     /** Returns client socket associated. */
434     public Socket JavaDoc getSocket() {
435         return socket;
436     }
437
438     /**
439      * Returns client socket associated.
440      * @since 1.4.0
441      * @see #updateInputOutputStreams
442      */

443     public void setSocket(Socket JavaDoc socket) {
444         this.socket = socket;
445     }
446
447     /**
448      * Checks if the client is still connected.
449      * @exception SocketException if Socket is not open.
450      * @deprecated since 1.4.5 Use {@link #isConnected}
451      */

452     public boolean isConected() throws SocketException JavaDoc {
453         return isConnected();
454     }
455
456     /**
457      * Checks if the client is still connected.
458      * @exception SocketException if Socket is not open.
459      * @since 1.4.5
460      */

461     public boolean isConnected() throws SocketException JavaDoc {
462         if(isOpen()==false)
463             throw new SocketException JavaDoc("Connection is no more open!");
464         else
465             return true;
466     }
467
468     /**
469      * Checks if the client is still connected and if socket is open. This is same as isConnected()
470      * but does not throw SocketException.
471      * @since 1.4.6
472      */

473     public boolean isOpen() {
474         if(lost==true || socket==null || socket.isConnected()==false || socket.isClosed()==true)
475             return false;
476         else
477             return true;
478     }
479
480     /**
481      * Checks if the client is closed.
482      * @since 1.4.1
483      */

484     public boolean isClosed() {
485         if(socket==null || socket.isClosed()==true)
486             return true;
487         else
488             return false;
489     }
490
491     /**
492      * Send a String message to the connected client
493      * it adds a new line{\r\n} to the end of the string.
494      * If client is not connected it will just return.
495      * @exception IOException
496      * if Socket IO Error or Socket was closed by the client.
497      */

498     public void sendClientMsg(String JavaDoc msg) throws IOException {
499         isConnected();
500
501         if(dataModeOUT != DataMode.STRING)
502             throw new IllegalStateException JavaDoc("Can't send String :" +
503                 "DataType.OUT is not in DataMode.STRING");
504         if(getCommunicationLogging()) {
505             appLogger.fine("Sending ["+getHostAddress()+"] : "+msg);
506         }
507         byte data[] = msg.getBytes(charset);
508         b_out.write(data, 0, data.length);
509         b_out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
510         b_out.flush();
511
512         updateLastCommunicationTime();
513     }
514
515     /**
516      * Send a String message to the connected client as a string of bytes.
517      * If client is not connected it will just return.
518      * @since 1.3.1
519      * @exception IOException
520      * if Socket IO Error or Socket was closed by the client.
521      */

522     public void sendClientBytes(String JavaDoc msg) throws IOException {
523         isConnected();
524
525         if (dataModeOUT != DataMode.BYTE)
526             throw new IllegalStateException JavaDoc("Can't send String :" +
527                 "DataType.OUT is not in DataMode.BYTE");
528         if(getCommunicationLogging()) {
529             appLogger.fine("Sending ["+getHostAddress()+"] : "+msg);
530         }
531         byte data[] = msg.getBytes(charset);
532         b_out.write(data,0,data.length);
533         b_out.flush();
534
535         updateLastCommunicationTime();
536     }
537
538
539     /**
540      * Send a Object message to the connected client. The message Object
541      * passed must be serializable. If client is not connected it
542      * will just return.
543      * @exception IOException if Socket IO Error or Socket was closed
544      * by the client.
545      * @exception IllegalStateException if DataType.OUT is not in
546      * DataMode.OBJECT
547      * @see #setDataMode
548      * @since 1.2
549      */

550     public void sendClientObject(Object JavaDoc msg) throws IOException {
551         isConnected();
552
553         if(dataModeOUT != DataMode.OBJECT)
554             throw new IllegalStateException JavaDoc("Can't send Object : DataType.OUT is not in DataMode.OBJECT");
555         if(getCommunicationLogging()) {
556             appLogger.fine("Sending ["+getHostAddress()+"] : "+msg.toString());
557         }
558         o_out.writeObject(msg);
559         o_out.flush();
560
561         updateLastCommunicationTime();
562     }
563
564     /**
565      * Send a String message to the logger associated with
566      * {@link QuickServer#getAppLogger} with Level.INFO as its level.
567      */

568     public void sendSystemMsg(String JavaDoc msg) {
569         sendSystemMsg(msg, Level.INFO);
570     }
571
572     /**
573      * Send a String message to the logger associated with
574      * {@link QuickServer#getAppLogger}.
575      * @since 1.2
576      */

577     public void sendSystemMsg(String JavaDoc msg, Level level) {
578         appLogger.log(level, msg);
579     }
580
581     /**
582      * Send a String message to the system output stream.
583      * @param newline indicates if new line required at the end.
584      * @deprecated Use {@link #sendSystemMsg(java.lang.String)},
585      * since it uses Logging.
586      */

587     public void sendSystemMsg(String JavaDoc msg, boolean newline) {
588         if(newline)
589             System.out.println(msg);
590         else
591             System.out.print(msg);
592     }
593
594     public abstract void run();
595
596     protected void prepareForRun() throws SocketException JavaDoc, IOException {
597         clientConnectedTime = new java.util.Date JavaDoc(); //v1.3.2
598
lastCommunicationTime = clientConnectedTime;//v1.3.3
599

600         setCharset(getServer().getBasicConfig().getAdvancedSettings().getCharset());//1.4.5
601
hostAddress = getSocket().getInetAddress().getHostAddress();//1.4.5
602
port = getSocket().getPort();
603
604         if(logger.isLoggable(Level.FINEST)) {
605             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
606             sb.append(getName());
607             sb.append(" -> ");
608             sb.append(hostAddress);
609             sb.append(':');
610             sb.append(port);
611             logger.finest(sb.toString());
612         }
613
614         socket.setSoTimeout(socketTimeout);
615         connection = true;
616
617         dataModeIN = getServer().getDefaultDataMode(DataType.IN);
618         dataModeOUT = getServer().getDefaultDataMode(DataType.OUT);
619
620         updateInputOutputStreams();
621     }
622
623     protected void processMaxConnection(ClientEvent currentEvent) throws IOException {
624         if(clientExtendedEventHandler!=null) {
625             if(clientExtendedEventHandler.handleMaxConnection(this)) {
626                 removeEvent(getThreadEvent());
627                 if(getThreadEvent()==ClientEvent.MAX_CON) {
628                     currentEvent = ClientEvent.ACCEPT;
629                 } else if(getThreadEvent()==ClientEvent.MAX_CON_BLOCKING) {
630                     currentEvent = ClientEvent.RUN_BLOCKING;
631                 } else {
632                     throw new IllegalArgumentException JavaDoc("Unknown ClientEvent: "+getThreadEvent());
633                 }
634                 synchronized(clientEvents) {
635                     clientEvents.add(currentEvent);
636                 }
637                 threadEvent.set(currentEvent);
638             }
639         } else if(maxConnectionMsg.length()!=0) {
640             out.write(maxConnectionMsg.getBytes(charset), 0, maxConnectionMsg.length());
641             out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
642             out.flush();
643         }
644     }
645
646     protected AuthStatus processAuthorisation() throws SocketException JavaDoc,
647             IOException, AppException {
648         logger.finest("INSIDE");
649         while(authorised==false && connection==true) {
650             isConnected();
651
652             counAuthTry++;
653
654             if(authorised == false) {
655                 if(counAuthTry > maxAuthTry) {
656                     processMaxAuthTry();
657                 }
658             }
659
660             try {
661                 if(clientAuthenticationHandler!=null) {
662                     return clientAuthenticationHandler.askAuthentication(this);
663                 } else if(authenticator!=null) {
664                     authorised = authenticator.askAuthorisation(this);
665                 }
666             } catch(NullPointerException JavaDoc e) {
667                 logger.severe("Authenticator implementation has not handled null properly."+
668                     " Input from client should be checked for null!");
669                 throw e;
670             } catch(SocketTimeoutException JavaDoc e) {
671                 handleTimeout(e);
672             }
673
674             updateLastCommunicationTime();
675         } //end of auth while
676
return AuthStatus.SUCCESS;
677     }
678
679     private void processMaxAuthTry() throws SocketException JavaDoc, IOException, AppException {
680         if(clientExtendedEventHandler!=null) {
681             clientExtendedEventHandler.handleMaxAuthTry(this);
682         } else {
683             String JavaDoc temp = maxAuthTryMsg;
684             if(dataModeOUT == DataMode.STRING)
685                 temp = temp + NEW_LINE;
686             if(dataModeOUT != DataMode.OBJECT) {
687                 out.write(temp.getBytes(charset));
688                 out.flush();
689             }
690         }
691         appLogger.warning("Max Auth Try Reached - Client : "+getHostAddress());
692         if(true) throw new AppException(maxAuthTryMsg);
693     }
694
695
696     protected void notifyCloseOrLost() throws IOException {
697         synchronized(this) {
698             if(closeOrLostNotified==false) {
699                 if(lost==true) {
700                     clientEventHandler.lostConnection(this);
701                 } else {
702                     clientEventHandler.closingConnection(this);
703                 }
704                 closeOrLostNotified = true;
705             }
706         }
707     }
708
709     protected synchronized void returnClientData() {
710         if(clientData==null || getServer().getClientDataPool()==null)
711             return;
712         logger.finest("Returning ClientData to pool");
713         try {
714             getServer().getClientDataPool().returnObject(clientData);
715             clientData = null;
716         } catch(Exception JavaDoc e) {
717             logger.warning("IGNORED: Could not return ClientData to pool: "+e);
718         }
719     }
720
721     protected void returnClientHandler() {
722         try {
723             synchronized(lockObj) {
724                 logger.finest(Thread.currentThread().getName()+" returning "+getName());
725                 getServer().getClientHandlerPool().returnObject(this);
726             }
727         } catch(Exception JavaDoc e) {
728             logger.warning("IGNORED: Could not return ClientHandler to pool: "+e);
729         }
730     }
731
732     /**
733      * Returns the ClientHandler name
734      * @since 1.4.6
735      */

736     public String JavaDoc getName() {
737         return name;
738     }
739
740     /**
741      * Returns the ClientHandler detailed information.
742      * If ClientData is present and is ClientIdentifiable will return ClientInfo else
743      * it will return Clients InetAddress and port information.
744      */

745     public String JavaDoc info() {
746         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
747         sb.append("{");
748         sb.append(name);
749         sb.append(" - ");
750         String JavaDoc info = getClientIdentifiable(this);
751         if(info!=null) {
752             sb.append("[ClientInfo: ");
753             sb.append(info);
754             sb.append(']');
755         }
756
757         if(getSocket()==null || getSocket().isClosed()==true) {
758             sb.append("[non-connected]");
759         } else if(info==null) {
760             sb.append('[');
761             sb.append(hostAddress);
762             sb.append(':');
763             sb.append(port);
764             sb.append(']');
765         }
766         sb.append('}');
767         return sb.toString();
768     }
769
770     /**
771      * Returns the ClientHandler information.
772      * If ClientData is present and is ClientIdentifiable will return ClientInfo else
773      * it will return Clients InetAddress and port information.
774      */

775     public String JavaDoc toString() {
776         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
777         sb.append("{");
778         sb.append(name);
779         sb.append(" - ");
780         if(getSocket()==null || getSocket().isClosed()==true) {
781             sb.append("[non-connected]");
782         } else if(hostAddress!=null) {
783             sb.append('[');
784             sb.append(hostAddress);
785             sb.append(':');
786             sb.append(port);
787             sb.append(']');
788         }
789         synchronized(clientEvents) {
790             if(clientEvents.size()!=0) {
791                 sb.append(' ');
792                 sb.append(clientEvents);
793             }
794         }
795         sb.append('}');
796         return sb.toString();
797     }
798
799     protected static String JavaDoc getClientIdentifiable(ClientHandler foundClientHandler) {
800         if(foundClientHandler==null) return null;
801         ClientData foundClientData = null;
802         foundClientData = foundClientHandler.getClientData();
803         if(foundClientData==null)
804             return null;
805         else if(ClientIdentifiable.class.isInstance(foundClientData)==false)
806             return null;
807         else
808             return ((ClientIdentifiable)foundClientData).getClientInfo();
809     }
810
811     /**
812      * Sets the {@link DataMode} for the ClientHandler
813      *
814      * Note: When mode is DataMode.OBJECT and type is DataType.IN
815      * this call will block until the client ObjectOutputStream has
816      * written and flushes the header.
817      * @since 1.2
818      * @exception IOException if mode could not be changed.
819      * @param dataMode mode of data exchange - String or Object.
820      * @param dataType type of data for which mode has to be set.
821      */

822     public abstract void setDataMode(DataMode dataMode, DataType dataType) throws IOException;
823
824     protected void checkDataModeSet(DataMode dataMode, DataType dataType) {
825         if(dataMode==DataMode.STRING && dataType==DataType.IN && clientCommandHandler==null) {
826             throw new IllegalArgumentException JavaDoc("Can't set DataType.IN mode to STRING when ClientCommandHandler is not set!");
827         }
828
829         if(dataMode==DataMode.BYTE && dataType==DataType.IN && clientCommandHandler==null) {
830             throw new IllegalArgumentException JavaDoc("Can't set DataType.IN mode to BYTE when ClientCommandHandler is not set!");
831         }
832
833         if(dataMode==DataMode.OBJECT && dataType==DataType.IN && clientObjectHandler==null) {
834             throw new IllegalArgumentException JavaDoc("Can't set DataType.IN mode to OBJECT when ClientObjectHandler is not set!");
835         }
836
837         if(dataMode==DataMode.BINARY && dataType==DataType.IN && clientBinaryHandler==null) {
838             throw new IllegalArgumentException JavaDoc("Can't set DataType.IN mode to BINARY when ClientBinaryHandler is not set!");
839         }
840     }
841
842     /**
843      * Returns the {@link DataMode} of the ClientHandler for the
844      * DataType.
845      * @since 1.2
846      */

847     public DataMode getDataMode(DataType dataType) {
848         if(dataType == DataType.IN)
849             return dataModeIN;
850         else if(dataType == DataType.OUT)
851             return dataModeOUT;
852         else
853             throw new IllegalArgumentException JavaDoc("Unknown DataType : " +
854                 dataType);
855     }
856
857     /**
858      * Returns the {@link java.sql.Connection} object for the
859      * DatabaseConnection that is identified by id passed. If id passed
860      * does not match with any connection loaded by this class it will
861      * return <code>null</code>.
862      * This just calls <code>getServer().getDBPoolUtil().getConnection(id)</code>
863      * @since 1.3
864      * @deprecated as of v1.4.5 use <code>getServer().getDBPoolUtil().getConnection(id)</code>
865      */

866     public java.sql.Connection JavaDoc getConnection(String JavaDoc id) throws Exception JavaDoc {
867         if(getServer()==null)
868             throw new Exception JavaDoc("ClientHandler no longer is associated with any client! Try to use quickserver.getDBPoolUtil().getConnection("+id+")");
869         return getServer().getDBPoolUtil().getConnection(id);
870     }
871
872     /**
873      * Returns the date/time when the client socket was assigned to this
874      * ClientHanlder. If no client is currently connected it will return
875      * <code>null</code>
876      * @since 1.3.1
877      */

878     public Date getClientConnectedTime() {
879         return clientConnectedTime;
880     }
881
882     /**
883      * Read the byte input. This will block till some data is
884      * received from the stream.
885      * @return The data as a String
886      * @since 1.3.1
887      */

888     protected abstract byte[] readInputStream() throws IOException;
889
890     protected static byte[] readInputStream(InputStream _in) throws IOException {
891         byte data[] = null;
892         if(_in==null)
893             throw new IOException("InputStream can't be null!");
894         
895         int s = _in.read();
896         if(s==-1) {
897             return null; //Connection lost
898
}
899         int alength = _in.available();
900         if(alength > 0) {
901             data = new byte[alength+1];
902             _in.read(data, 1, alength);
903         } else {
904             data = new byte[1];
905         }
906         data[0] = (byte)s;
907         return data;
908     }
909
910     /**
911      * Read the byte input. This will block till some data is
912      * received from the stream. Allowed only when
913      * <code>DataType.IN</code> is in <code>DataMode.BYTE</code> mode.
914      * @return The data as a String
915      * @since 1.3.2
916      */

917     public String JavaDoc readBytes() throws IOException {
918         if(dataModeIN != DataMode.BYTE)
919                 throw new IllegalStateException JavaDoc("Can't read Byte: " +
920                     "DataType.IN is not in DataMode.BYTE");
921         byte data[] = readInputStream();
922         if(data!=null)
923             return new String JavaDoc(data, charset);
924         else
925             return null;
926     }
927
928     /**
929      * Sets the communication logging flag.
930      * @see #getCommunicationLogging
931      * @since 1.3.2
932      */

933     public void setCommunicationLogging(boolean communicationLogging) {
934         this.communicationLogging = communicationLogging;
935     }
936     /**
937      * Returns the communication logging flag.
938      * @see #setCommunicationLogging
939      * @since 1.3.2
940      */

941     public boolean getCommunicationLogging() {
942         return communicationLogging;
943     }
944
945     /**
946      * Returns the date/time when the client socket last sent a data to this
947      * ClientHanlder. If no client is currently connected it will return
948      * <code>null</code>
949      * @since 1.3.3
950      */

951     public Date getLastCommunicationTime() {
952         return lastCommunicationTime;
953     }
954
955     /**
956      * Updates the last communication time for this client
957      * @since 1.3.3
958      */

959     public void updateLastCommunicationTime() {
960         lastCommunicationTime = new Date();
961     }
962
963     /**
964      * Force the closing of the client by closing the associated socket.
965      * @since 1.3.3
966      */

967     public synchronized void forceClose() throws IOException {
968         if(getSelectionKey()!=null) getSelectionKey().cancel();
969         if(getSocketChannel()!=null) {
970             getSocketChannel().close();
971             setSocketChannel(null);
972         }
973         if(getSocket()!=null) {
974             getSocket().close();
975             setSocket(null);
976         }
977     }
978
979     /**
980      * Returns flag indicating if the client is connected in secure mode
981      * (SSL or TLS).
982      * @return secure flag
983      * @since 1.4.0
984      */

985     public boolean isSecure() {
986         return secure;
987     }
988
989     /**
990      * Sets flag indicating if the client is connected in secure mode
991      * (SSL or TLS).
992      * @param secure
993      * @since 1.4.0
994      */

995     public void setSecure(boolean secure) {
996         this.secure = secure;
997     }
998
999     /**
1000     * Updates the InputStream and OutputStream for the ClientHandler for the
1001     * set Socket.
1002     * @since 1.4.0
1003     * @see #setSocket
1004     */

1005    public abstract void updateInputOutputStreams() throws IOException;
1006
1007    /**
1008     * Makes current Client connection to secure protocol based on the
1009     * secure configuration set to the server. This method will just call
1010     * <code>makeSecure(false, false, true, null)</code>.
1011     * @throws IOException
1012     * @throws NoSuchAlgorithmException
1013     * @throws KeyManagementException
1014     * @since 1.4.0
1015     */

1016    public void makeSecure() throws IOException, NoSuchAlgorithmException,
1017            KeyManagementException {
1018        makeSecure(false, false, true, null);
1019    }
1020
1021    /**
1022     * Makes current Client connection to secure protocol.
1023     * This method will just call <code>makeSecure(false, false, true, protocol)</code>.
1024     * @throws IOException
1025     * @throws NoSuchAlgorithmException
1026     * @throws KeyManagementException
1027     * @since 1.4.0
1028     */

1029    public void makeSecure(String JavaDoc protocol) throws IOException,
1030            NoSuchAlgorithmException, KeyManagementException {
1031        makeSecure(false, false, true, protocol);
1032    }
1033
1034    /**
1035     * Makes current Client connection to secure protocol.
1036     * @param useClientMode falg if the socket should start its first handshake in "client" mode.
1037     * @param needClientAuth flag if the clients must authenticate themselves.
1038     * @param autoClose close the underlying socket when this socket is closed
1039     * @param protocol the standard name of the requested protocol. If <code>null</code> will use the protocol set in secure configuration of the server.
1040     * @throws IOException
1041     * @throws NoSuchAlgorithmException
1042     * @throws KeyManagementException
1043     * @since 1.4.0
1044     */

1045    public void makeSecure(boolean useClientMode, boolean needClientAuth,
1046            boolean autoClose, String JavaDoc protocol) throws IOException,
1047            NoSuchAlgorithmException, KeyManagementException {
1048        if(isSecure()==true) {
1049            throw new IllegalStateException JavaDoc("Client is already in secure mode!");
1050        }
1051        
1052        appLogger.fine("Making secure - Protocol: "+protocol+
1053            ", Client: ["+getHostAddress()+"]");
1054
1055        javax.net.ssl.SSLSocketFactory sslSf = getServer().getSSLSocketFactory(protocol);
1056        String JavaDoc host = getServer().getBindAddr().getHostAddress();
1057        if(host.equals("0.0.0.0")) host = InetAddress.getLocalHost().getHostAddress();
1058        SSLSocket newSocket = (SSLSocket) sslSf.createSocket(
1059            getSocket(), host, getServer().getPort(), autoClose);
1060        newSocket.setNeedClientAuth(needClientAuth);
1061        newSocket.setUseClientMode(useClientMode);
1062        setSocket(newSocket);
1063        setSecure(true);
1064        updateInputOutputStreams();
1065    }
1066
1067    /**
1068     * Send a binary data to the connected client.
1069     * If client is not connected it will just return.
1070     * @since 1.4
1071     * @exception IOException
1072     * if Socket IO Error or Socket was closed by the client.
1073     */

1074    public void sendClientBinary(byte data[]) throws IOException {
1075        sendClientBinary(data, 0, data.length);
1076    }
1077
1078    /**
1079     * Send a binary data to the connected client.
1080     * If client is not connected it will just return.
1081     * @since 1.4.5
1082     * @exception IOException
1083     * if Socket IO Error or Socket was closed by the client.
1084     */

1085    public void sendClientBinary(byte data[], int off, int len) throws IOException {
1086        if(isConnected()) {
1087            if(dataModeOUT != DataMode.BINARY)
1088                throw new IllegalStateException JavaDoc("Can't send Binary :" +
1089                    "DataType.OUT is not in DataMode.BINARY");
1090            if(getCommunicationLogging()) {
1091                appLogger.fine("Sending ["+getHostAddress()+"] : "+MyString.getMemInfo(len));
1092            }
1093            b_out.write(data, off, len);
1094            b_out.flush();
1095        } else {
1096            logger.warning("Client not connected.");
1097        }
1098    }
1099
1100    /**
1101     * Read the binary input. This will block till some data is
1102     * received from the stream. Allowed only when
1103     * <code>DataType.IN</code> is in <code>DataMode.BINARY</code> mode.
1104     * @return The data as a String
1105     * @since 1.4
1106     */

1107    public byte[] readBinary() throws IOException {
1108        if(dataModeIN != DataMode.BINARY)
1109                throw new IllegalStateException JavaDoc("Can't read Binary :" +
1110                    "DataType.IN is not in DataMode.BINARY");
1111        byte data[] = readInputStream();
1112        return data;
1113    }
1114
1115    /**
1116     * Sets the ClientBinaryHandler class that interacts with
1117     * client sockets.
1118     * @param handler fully qualified name of the class that
1119     * implements {@link ClientBinaryHandler}
1120     * @since 1.4
1121     */

1122    protected void setClientBinaryHandler(ClientBinaryHandler handler) {
1123        clientBinaryHandler=handler;
1124    }
1125
1126    /**
1127     * Returns client SelectionKey associated, if any.
1128     * @since 1.4.5
1129     */

1130    public Logger getAppLogger() {
1131        return appLogger;
1132    }
1133
1134    /**
1135     * Sets the client socket's timeout.
1136     * @param time client socket timeout in milliseconds.
1137     * @see #getTimeout
1138     * @since 1.4.5
1139     */

1140    public void setTimeout(int time) {
1141        socketTimeout = time;
1142    }
1143    /**
1144     * Returns the Client socket timeout in milliseconds.
1145     * @see #setTimeout
1146     * @since 1.4.5
1147     */

1148    public int getTimeout() {
1149        return socketTimeout;
1150    }
1151
1152    /**
1153     * Checks if this client has the event.
1154     * @since 1.4.5
1155     */

1156    public boolean hasEvent(ClientEvent event) {
1157        synchronized(clientEvents) {
1158            return clientEvents.contains(event);
1159        }
1160    }
1161
1162    /**
1163     * Adds the ClientEvent.
1164     * @since 1.4.5
1165     */

1166    public void addEvent(ClientEvent event) {
1167        synchronized(clientEvents) {
1168            unprocessedClientEvents.add(event);
1169            clientEvents.add(event);
1170        }
1171    }
1172
1173    /**
1174     * Removes the ClientEvent.
1175     * @since 1.4.5
1176     */

1177    public void removeEvent(ClientEvent event) {
1178        if(event==null) return;
1179
1180        synchronized(clientEvents) {
1181            clientEvents.remove(event);
1182        }
1183
1184        ClientEvent _clientEvent = (ClientEvent)threadEvent.get();
1185        if(_clientEvent!=null && _clientEvent==event) {
1186            threadEvent.set(null);
1187        }
1188        
1189    }
1190
1191    /**
1192     * Returns threads current event for this client.
1193     * @since 1.4.5
1194     */

1195    protected ClientEvent getThreadEvent() {
1196        return (ClientEvent)threadEvent.get();
1197    }
1198
1199    /**
1200     * Sets message to be displayed when maximum connection reaches.
1201     * @since 1.4.5
1202     */

1203    public void setMaxConnectionMsg(String JavaDoc msg) {
1204        maxConnectionMsg = msg;
1205    }
1206    /**
1207     * Returns message to be displayed to the client when maximum
1208     * connection reaches.
1209     * @since 1.4.5
1210     */

1211    public String JavaDoc getMaxConnectionMsg() {
1212        return maxConnectionMsg;
1213    }
1214
1215    /**
1216     * Sets client socket channel associated, if any.
1217     * @since 1.4.5
1218     */

1219    public abstract void setSocketChannel(SocketChannel socketChannel);
1220    /**
1221     * Returns client socket channel associated, if any.
1222     * @since 1.4.5
1223     */

1224    public abstract SocketChannel getSocketChannel();
1225
1226    /**
1227     * Sets client SelectionKey associated, if any.
1228     * @since 1.4.5
1229     */

1230    public abstract void setSelectionKey(SelectionKey selectionKey);
1231    /**
1232     * Returns client SelectionKey associated, if any.
1233     * @since 1.4.5
1234     */

1235    public abstract SelectionKey getSelectionKey();
1236
1237    public boolean getWillClean() {
1238        return willClean;
1239    }
1240
1241    /**
1242     * Register OP_READ with the SelectionKey associated with the channel. If SelectionKey is
1243     * not set then it registers the channel with the Selector.
1244     * @since 1.4.5
1245     */

1246    public abstract void registerForRead() throws IOException,
1247        ClosedChannelException;
1248    
1249    /**
1250     * Register OP_WRITE with the SelectionKey associated with the channel.
1251     * @since 1.4.5
1252     */

1253    public abstract void registerForWrite() throws IOException,
1254        ClosedChannelException;
1255
1256    /**
1257     * Sets the ClientWriteHandler class that interacts with
1258     * client sockets.
1259     * @param handler fully qualified name of the class that
1260     * implements {@link ClientWriteHandler}
1261     * @since 1.4.5
1262     */

1263    protected abstract void setClientWriteHandler(ClientWriteHandler handler);
1264
1265    /**
1266     * Sets the Charset to be used for String decoding and encoding.
1267     * @param charset to be used for String decoding and encoding
1268     * @see #getCharset
1269     * @since 1.4.5
1270     */

1271    public void setCharset(String JavaDoc charset) {
1272        if(charset==null || charset.trim().length()==0)
1273            return;
1274        this.charset = charset;
1275    }
1276    /**
1277     * Returns Charset to be used for String decoding and encoding..
1278     * @see #setCharset
1279     * @since 1.4.5
1280     */

1281    public String JavaDoc getCharset() {
1282        return charset;
1283    }
1284
1285    /**
1286     * Returns cached socket host ip address.
1287     * @since 1.4.5
1288     */

1289    public String JavaDoc getHostAddress() {
1290        return hostAddress;
1291    }
1292
1293    protected void assertionSystemExit() {
1294        logger.warning("[Assertions Was Enabled] Forcing program exit to help developer.");
1295        org.quickserver.net.qsadmin.QSAdminShell.tryFullThreadDump();//it can help debug.
1296
try {
1297            Thread.sleep(100);
1298        } catch(InterruptedException JavaDoc e) {
1299            logger.fine("Interrupted: "+e);
1300        }
1301        System.exit(-1);
1302    }
1303
1304    /**
1305     * Checks if the passed ClientEvent is the one next for
1306     * processing if a thread is allowed through this object.
1307     * @since 1.4.6
1308     */

1309    public boolean isClientEventNext(ClientEvent clientEvent) {
1310        ClientEvent ce = null;
1311        synchronized(clientEvents) {
1312            if(unprocessedClientEvents.size()>0)
1313                ce = (ClientEvent) unprocessedClientEvents.get(0);
1314        }
1315        return clientEvent == ce;
1316    }
1317
1318    /**
1319     *Returns the {@link java.io.BufferedInputStream} associated with
1320     * the Client being handled. Can be null if not available at the time of method call.
1321     * @see #getBufferedOutputStream
1322     * @since 1.4.6
1323     */

1324    public BufferedInputStream getBufferedInputStream() {
1325        return b_in;
1326    }
1327
1328    /**
1329     * Returns the {@link java.io.BufferedOutputStream} associated with
1330     * the Client being handled. Can be null if not available at the time of method call.
1331     * @see #getBufferedInputStream
1332     * @since 1.4.6
1333     */

1334    public BufferedOutputStream getBufferedOutputStream() {
1335        return b_out;
1336    }
1337
1338    protected void handleTimeout(SocketTimeoutException JavaDoc e) throws SocketException JavaDoc, IOException {
1339        appLogger.fine("Timeout - Client [" + getHostAddress() +"]");
1340        appLogger.finest("SocketTimeoutException : " + e.getMessage());
1341
1342        String JavaDoc temp = null;
1343        if(clientExtendedEventHandler!=null) {
1344            clientExtendedEventHandler.handleTimeout(this);
1345        } else {
1346            temp = timeoutMsg;
1347            if(dataModeOUT == DataMode.STRING)
1348                temp = temp + NEW_LINE;
1349            if(dataModeOUT != DataMode.OBJECT) {
1350                out.write(temp.getBytes(charset));
1351                out.flush();
1352            }
1353            if(true) throw new SocketException JavaDoc("Timeout");
1354        }
1355    }
1356}
1357
Popular Tags