0001: /*
0002: * This file is part of the QuickServer library
0003: * Copyright (C) 2003-2005 QuickServer.org
0004: *
0005: * Use, modification, copying and distribution of this software is subject to
0006: * the terms and conditions of the GNU Lesser General Public License.
0007: * You should have received a copy of the GNU LGP License along with this
0008: * library; if not, you can download a copy from <http://www.quickserver.org/>.
0009: *
0010: * For questions, suggestions, bug-reports, enhancement-requests etc.
0011: * visit http://www.quickserver.org
0012: *
0013: */
0014:
0015: package org.quickserver.net.server.impl;
0016:
0017: import java.io.*;
0018: import java.net.Socket;
0019: import java.net.SocketException;
0020: import java.net.SocketTimeoutException;
0021: import java.net.InetAddress;
0022: import java.util.*;
0023: import java.util.logging.*;
0024: import javax.net.ssl.*;
0025: import java.security.*;
0026: import java.nio.*;
0027: import java.nio.channels.*;
0028:
0029: import org.quickserver.net.*;
0030: import org.quickserver.util.*;
0031: import org.quickserver.net.server.*;
0032:
0033: /**
0034: * Basic implementation of ClientHandler that handles clients for QuickServer.
0035: * <p> This class is used by {@link QuickServer} to handle each new client
0036: * connected. This class is responsible to handle client sockets. It can operate
0037: * in both blocking mode and non-blocking mode (java nio).</p>
0038: * <p>
0039: * Contributions By:
0040: * Martin Benns : BYTE Mode
0041: * </p>
0042: * @author Akshathkumar Shetty
0043: * @author Martin Benns : Added BYTE mode
0044: */
0045: public abstract class BasicClientHandler implements ClientHandler {
0046: private static final Logger logger = Logger
0047: .getLogger(BasicClientHandler.class.getName());
0048:
0049: protected static final String NEW_LINE = QuickServer.getNewLine();
0050: protected static final byte NEW_LINE_BYTES[] = NEW_LINE.getBytes();
0051:
0052: //Some variable are not initialised to any value because the
0053: //default java value was desired initial value.
0054:
0055: /** Client socket */
0056: protected Socket socket;
0057: /** Client authorisation status */
0058: protected volatile boolean authorised;
0059: /** Count of client login attempts */
0060: protected int counAuthTry;
0061: /** max allowed login attempts */
0062: protected int maxAuthTry = 5;
0063: /** timeout message */
0064: protected String timeoutMsg;
0065: /** Message to be displayed when max login attempt reaches.*/
0066: protected String maxAuthTryMsg;
0067:
0068: protected int socketTimeout;
0069: protected volatile boolean connection; //false
0070: protected boolean lost; //false
0071:
0072: protected QuickServer quickServer;
0073: protected Authenticator authenticator; //v1.3
0074: protected ClientAuthenticationHandler clientAuthenticationHandler; //v1.4.6
0075: protected ClientEventHandler clientEventHandler; //v1.4.6
0076: protected ClientExtendedEventHandler clientExtendedEventHandler; //v1.4.6
0077: protected ClientCommandHandler clientCommandHandler;
0078: protected ClientObjectHandler clientObjectHandler; //v1.2
0079: protected ClientBinaryHandler clientBinaryHandler; //1.4
0080: protected ClientData clientData;
0081:
0082: protected InputStream in;
0083: protected OutputStream out;
0084: protected BufferedReader bufferedReader;
0085: //if DataMode.OBJECT
0086: protected ObjectOutputStream o_out; //v1.2
0087: protected ObjectInputStream o_in; //v1.2
0088: //added for BYTE mode and BINARY mode
0089: protected BufferedInputStream b_in;
0090: protected BufferedOutputStream b_out;
0091:
0092: //logger for the application using this QuickServer
0093: protected Logger appLogger;
0094: protected DataMode dataModeIN = null;
0095: protected DataMode dataModeOUT = null;
0096:
0097: protected boolean communicationLogging = true;
0098: protected Date clientConnectedTime = null;
0099: protected Date lastCommunicationTime = null;
0100: protected boolean secure = false;
0101:
0102: //--v1.4.5
0103: protected static final ThreadLocal threadEvent = new ThreadLocal();
0104:
0105: protected String maxConnectionMsg;
0106: protected Set clientEvents = new HashSet();
0107: protected List unprocessedClientEvents = Collections
0108: .synchronizedList(new ArrayList());
0109:
0110: protected volatile boolean closeOrLostNotified;
0111: protected Object lockObj = new Object();
0112: protected volatile boolean willClean;
0113: protected String charset;
0114:
0115: private static Map idMap = new HashMap();
0116: private int instanceCount;
0117: private int id;
0118: private String name;
0119: private String hostAddress;
0120: private int port;
0121:
0122: static class InstanceId {
0123: private int id = 0;
0124:
0125: public int getNextId() {
0126: return ++id;
0127: }
0128: };
0129:
0130: private static int getNewId(int instanceCount) {
0131: InstanceId instanceId = (InstanceId) idMap.get(""
0132: + instanceCount);
0133: if (instanceId == null) {
0134: instanceId = new InstanceId();
0135: idMap.put("" + instanceCount, instanceId);
0136: }
0137: return instanceId.getNextId();
0138: }
0139:
0140: public BasicClientHandler(int instanceCount) {
0141: this .instanceCount = instanceCount;
0142: id = getNewId(instanceCount);
0143:
0144: StringBuffer sb = new StringBuffer();
0145: sb.append("<ClientHandler-Pool#");
0146: sb.append(instanceCount);
0147: sb.append("-ID:");
0148: sb.append(id);
0149: sb.append(">");
0150: name = sb.toString();
0151: }
0152:
0153: public int getInstanceCount() {
0154: return instanceCount;
0155: }
0156:
0157: public BasicClientHandler() {
0158: this (-1);
0159: }
0160:
0161: public void clean() {
0162: counAuthTry = 0;
0163: authorised = false;
0164: in = null;
0165: out = null;
0166: bufferedReader = null;
0167: o_out = null;
0168: o_in = null;
0169: b_in = null;
0170: b_out = null;
0171:
0172: dataModeIN = null;
0173: dataModeOUT = null;
0174:
0175: lost = false;
0176: clientData = null;
0177: clientConnectedTime = null;
0178: lastCommunicationTime = null;
0179: communicationLogging = true;
0180: socketTimeout = 0;
0181: secure = false;
0182:
0183: authenticator = null;
0184: clientAuthenticationHandler = null;//1.4.6
0185: clientCommandHandler = null;
0186: clientObjectHandler = null;
0187: clientBinaryHandler = null;//1.4
0188: clientData = null;
0189:
0190: maxConnectionMsg = null;
0191: synchronized (clientEvents) {
0192: clientEvents.clear();
0193: unprocessedClientEvents.clear();
0194: }
0195:
0196: closeOrLostNotified = false;
0197:
0198: if (socket != null) {
0199: try {
0200: socket.close();
0201: } catch (Exception er) {
0202: appLogger.warning("Error in closing socket: " + er);
0203: }
0204: socket = null;
0205: }
0206:
0207: hostAddress = null;
0208: port = 0;
0209:
0210: quickServer = null;
0211: willClean = false;
0212: charset = null;
0213: }
0214:
0215: protected void finalize() throws Throwable {
0216: super .finalize();
0217: }
0218:
0219: /**
0220: * Associates the ClientHanlder with the client encapsulated by
0221: * <code>theClient</code>.
0222: * @param theClient object that encapsulates client socket
0223: * and its configuration details.
0224: */
0225: public void handleClient(TheClient theClient) {
0226: setServer(theClient.getServer());
0227:
0228: if (getServer().isRunningSecure() == true) {
0229: setSecure(true);
0230: }
0231: setSocket(theClient.getSocket());
0232:
0233: if (theClient.getTrusted() == false) {
0234: setAuthenticator(theClient.getAuthenticator());
0235: setClientAuthenticationHandler(theClient
0236: .getClientAuthenticationHandler());
0237: }
0238: setClientEventHandler(theClient.getClientEventHandler());
0239: setClientExtendedEventHandler(theClient
0240: .getClientExtendedEventHandler());
0241: setClientCommandHandler(theClient.getClientCommandHandler());
0242: setClientObjectHandler(theClient.getClientObjectHandler());
0243: setClientBinaryHandler(theClient.getClientBinaryHandler()); //v1.4
0244:
0245: setClientData(theClient.getClientData());
0246: if (theClient.getTrusted() == false) {
0247: socketTimeout = theClient.getTimeout();
0248: }
0249: timeoutMsg = theClient.getTimeoutMsg();
0250: maxAuthTryMsg = theClient.getMaxAuthTryMsg();
0251: maxAuthTry = theClient.getMaxAuthTry(); //v1.2
0252: appLogger = quickServer.getAppLogger(); //v1.2
0253:
0254: setCommunicationLogging(theClient.getCommunicationLogging()); //v1.3.2
0255:
0256: maxConnectionMsg = theClient.getMaxConnectionMsg();//1.4.5
0257: addEvent(theClient.getClientEvent());//1.4.5
0258: }
0259:
0260: /**
0261: * Returns the QuickServer object that created it.
0262: * @see #setServer
0263: */
0264: public QuickServer getServer() {
0265: return quickServer;
0266: }
0267:
0268: /**
0269: * Sets the QuickServer object associated with this ClientHandler.
0270: * @see #getServer
0271: */
0272: protected void setServer(QuickServer server) {
0273: Assertion.affirm(server != null, "QuickServer can't be null!");
0274: quickServer = server;
0275: }
0276:
0277: /**
0278: * Sets the ClientData object associated with this ClientHandler
0279: * @see ClientData
0280: * @see #getClientData
0281: */
0282: protected void setClientData(ClientData data) {
0283: this .clientData = data;
0284: }
0285:
0286: /**
0287: * Returns the ClientData object associated with this ClientHandler,
0288: * if not set will return <code>null</code>
0289: * @see ClientData
0290: * @see #setClientData
0291: */
0292: public ClientData getClientData() {
0293: return clientData;
0294: }
0295:
0296: /**
0297: * Sets the ClientAuthenticationHandler class that handles the
0298: * authentication of a client.
0299: * @param clientAuthenticationHandler fully qualified name of the class that
0300: * implements {@link ClientAuthenticationHandler}.
0301: * @since 1.4.6
0302: */
0303: protected void setClientAuthenticationHandler(
0304: ClientAuthenticationHandler clientAuthenticationHandler) {
0305: this .clientAuthenticationHandler = clientAuthenticationHandler;
0306: }
0307:
0308: /**
0309: * Sets the Authenticator class that handles the
0310: * authentication of a client.
0311: * @param authenticator fully qualified name of the class that
0312: * implements {@link Authenticator}.
0313: * @since 1.3
0314: */
0315: protected void setAuthenticator(Authenticator authenticator) {
0316: this .authenticator = authenticator;
0317: }
0318:
0319: /**
0320: * Returns the {@link java.io.InputStream} associated with
0321: * the Client being handled.
0322: * @see #setInputStream
0323: */
0324: public InputStream getInputStream() {
0325: return in;
0326: }
0327:
0328: /**
0329: * Sets the {@link java.io.InputStream} associated with
0330: * the Client being handled.
0331: * @since 1.1
0332: * @see #getInputStream
0333: */
0334: protected abstract void setInputStream(InputStream in)
0335: throws IOException;
0336:
0337: /**
0338: * Returns the {@link java.io.OutputStream} associated with
0339: * the Client being handled.
0340: * @see #setOutputStream
0341: */
0342: public OutputStream getOutputStream() {
0343: return out;
0344: }
0345:
0346: /**
0347: * Set the {@link java.io.OutputStream} associated with
0348: * the Client being handled.
0349: * @since 1.1
0350: * @see #getOutputStream
0351: * @exception IOException if ObjectOutputStream could not be created.
0352: */
0353: public void setOutputStream(OutputStream out) throws IOException {
0354: this .out = out;
0355: if (getDataMode(DataType.OUT) == DataMode.STRING
0356: || getDataMode(DataType.OUT) == DataMode.BYTE
0357: || getDataMode(DataType.OUT) == DataMode.BINARY) {
0358: o_out = null;
0359: b_out = new BufferedOutputStream(out);
0360: } else if (getDataMode(DataType.OUT) == DataMode.OBJECT) {
0361: b_out = null;
0362: o_out = new ObjectOutputStream(out);
0363: o_out.flush();
0364: } else {
0365: throw new IllegalStateException("Unknown DataMode "
0366: + getDataMode(DataType.OUT));
0367: }
0368: }
0369:
0370: /**
0371: * Returns the {@link java.io.BufferedReader} associated with
0372: * the Client being handled. Note that this is only available under blocking mode.
0373: * @see #getBufferedWriter
0374: */
0375: public abstract BufferedReader getBufferedReader();
0376:
0377: /**
0378: * Returns the {@link java.io.BufferedWriter} associated with
0379: * the Client being handled.
0380: * @deprecated since 1.4.5 use getOutputStream()
0381: */
0382: public BufferedWriter getBufferedWriter() {
0383: return new BufferedWriter(new OutputStreamWriter(b_out));
0384: }
0385:
0386: /**
0387: * Returns the {@link java.io.ObjectOutputStream} associated with
0388: * the Client being handled.
0389: * It will be <code>null</code> if no {@link ClientObjectHandler}
0390: * was set in {@link QuickServer}.
0391: * @see #getObjectInputStream
0392: * @since 1.2
0393: */
0394: public ObjectOutputStream getObjectOutputStream() {
0395: return o_out;
0396: }
0397:
0398: /**
0399: * Returns the {@link java.io.ObjectInputStream} associated with
0400: * the Client being handled.
0401: * It will be <code>null</code> if no {@link ClientObjectHandler}
0402: * was set in {@link QuickServer}.
0403: * @see #getObjectOutputStream
0404: * @since 1.2
0405: */
0406: public ObjectInputStream getObjectInputStream() {
0407: return o_in;
0408: }
0409:
0410: /**
0411: * Sets the ClientEventHandler class that gets notified of client events.
0412: * @since 1.4.6
0413: */
0414: protected void setClientEventHandler(ClientEventHandler handler) {
0415: clientEventHandler = handler;
0416: }
0417:
0418: /**
0419: * Sets the ClientExtendedEventHandler class that gets notified of extended client events.
0420: * @since 1.4.6
0421: */
0422: protected void setClientExtendedEventHandler(
0423: ClientExtendedEventHandler handler) {
0424: clientExtendedEventHandler = handler;
0425: }
0426:
0427: /**
0428: * Sets the ClientCommandHandler class that interacts with
0429: * client sockets.
0430: */
0431: protected void setClientCommandHandler(ClientCommandHandler handler) {
0432: clientCommandHandler = handler;
0433: }
0434:
0435: /**
0436: * Sets the ClientObjectHandler class that interacts with
0437: * client sockets.
0438: * @param handler fully qualified name of the class that
0439: * implements {@link ClientObjectHandler}
0440: * @since 1.2
0441: */
0442: protected void setClientObjectHandler(ClientObjectHandler handler) {
0443: clientObjectHandler = handler;
0444: }
0445:
0446: /** Closes client socket associated. */
0447: public abstract void closeConnection();
0448:
0449: /** Returns client socket associated. */
0450: public Socket getSocket() {
0451: return socket;
0452: }
0453:
0454: /**
0455: * Returns client socket associated.
0456: * @since 1.4.0
0457: * @see #updateInputOutputStreams
0458: */
0459: public void setSocket(Socket socket) {
0460: this .socket = socket;
0461: }
0462:
0463: /**
0464: * Checks if the client is still connected.
0465: * @exception SocketException if Socket is not open.
0466: * @deprecated since 1.4.5 Use {@link #isConnected}
0467: */
0468: public boolean isConected() throws SocketException {
0469: return isConnected();
0470: }
0471:
0472: /**
0473: * Checks if the client is still connected.
0474: * @exception SocketException if Socket is not open.
0475: * @since 1.4.5
0476: */
0477: public boolean isConnected() throws SocketException {
0478: if (isOpen() == false)
0479: throw new SocketException("Connection is no more open!");
0480: else
0481: return true;
0482: }
0483:
0484: /**
0485: * Checks if the client is still connected and if socket is open. This is same as isConnected()
0486: * but does not throw SocketException.
0487: * @since 1.4.6
0488: */
0489: public boolean isOpen() {
0490: if (lost == true || socket == null
0491: || socket.isConnected() == false
0492: || socket.isClosed() == true)
0493: return false;
0494: else
0495: return true;
0496: }
0497:
0498: /**
0499: * Checks if the client is closed.
0500: * @since 1.4.1
0501: */
0502: public boolean isClosed() {
0503: if (socket == null || socket.isClosed() == true)
0504: return true;
0505: else
0506: return false;
0507: }
0508:
0509: /**
0510: * Send a String message to the connected client
0511: * it adds a new line{\r\n} to the end of the string.
0512: * If client is not connected it will just return.
0513: * @exception IOException
0514: * if Socket IO Error or Socket was closed by the client.
0515: */
0516: public void sendClientMsg(String msg) throws IOException {
0517: isConnected();
0518:
0519: if (dataModeOUT != DataMode.STRING)
0520: throw new IllegalStateException("Can't send String :"
0521: + "DataType.OUT is not in DataMode.STRING");
0522: if (getCommunicationLogging()) {
0523: appLogger.fine("Sending [" + getHostAddress() + "] : "
0524: + msg);
0525: }
0526: byte data[] = msg.getBytes(charset);
0527: b_out.write(data, 0, data.length);
0528: b_out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
0529: b_out.flush();
0530:
0531: updateLastCommunicationTime();
0532: }
0533:
0534: /**
0535: * Send a String message to the connected client as a string of bytes.
0536: * If client is not connected it will just return.
0537: * @since 1.3.1
0538: * @exception IOException
0539: * if Socket IO Error or Socket was closed by the client.
0540: */
0541: public void sendClientBytes(String msg) throws IOException {
0542: isConnected();
0543:
0544: if (dataModeOUT != DataMode.BYTE)
0545: throw new IllegalStateException("Can't send String :"
0546: + "DataType.OUT is not in DataMode.BYTE");
0547: if (getCommunicationLogging()) {
0548: appLogger.fine("Sending [" + getHostAddress() + "] : "
0549: + msg);
0550: }
0551: byte data[] = msg.getBytes(charset);
0552: b_out.write(data, 0, data.length);
0553: b_out.flush();
0554:
0555: updateLastCommunicationTime();
0556: }
0557:
0558: /**
0559: * Send a Object message to the connected client. The message Object
0560: * passed must be serializable. If client is not connected it
0561: * will just return.
0562: * @exception IOException if Socket IO Error or Socket was closed
0563: * by the client.
0564: * @exception IllegalStateException if DataType.OUT is not in
0565: * DataMode.OBJECT
0566: * @see #setDataMode
0567: * @since 1.2
0568: */
0569: public void sendClientObject(Object msg) throws IOException {
0570: isConnected();
0571:
0572: if (dataModeOUT != DataMode.OBJECT)
0573: throw new IllegalStateException(
0574: "Can't send Object : DataType.OUT is not in DataMode.OBJECT");
0575: if (getCommunicationLogging()) {
0576: appLogger.fine("Sending [" + getHostAddress() + "] : "
0577: + msg.toString());
0578: }
0579: o_out.writeObject(msg);
0580: o_out.flush();
0581:
0582: updateLastCommunicationTime();
0583: }
0584:
0585: /**
0586: * Send a String message to the logger associated with
0587: * {@link QuickServer#getAppLogger} with Level.INFO as its level.
0588: */
0589: public void sendSystemMsg(String msg) {
0590: sendSystemMsg(msg, Level.INFO);
0591: }
0592:
0593: /**
0594: * Send a String message to the logger associated with
0595: * {@link QuickServer#getAppLogger}.
0596: * @since 1.2
0597: */
0598: public void sendSystemMsg(String msg, Level level) {
0599: appLogger.log(level, msg);
0600: }
0601:
0602: /**
0603: * Send a String message to the system output stream.
0604: * @param newline indicates if new line required at the end.
0605: * @deprecated Use {@link #sendSystemMsg(java.lang.String)},
0606: * since it uses Logging.
0607: */
0608: public void sendSystemMsg(String msg, boolean newline) {
0609: if (newline)
0610: System.out.println(msg);
0611: else
0612: System.out.print(msg);
0613: }
0614:
0615: public abstract void run();
0616:
0617: protected void prepareForRun() throws SocketException, IOException {
0618: clientConnectedTime = new java.util.Date(); //v1.3.2
0619: lastCommunicationTime = clientConnectedTime;//v1.3.3
0620:
0621: setCharset(getServer().getBasicConfig().getAdvancedSettings()
0622: .getCharset());//1.4.5
0623: hostAddress = getSocket().getInetAddress().getHostAddress();//1.4.5
0624: port = getSocket().getPort();
0625:
0626: if (logger.isLoggable(Level.FINEST)) {
0627: StringBuffer sb = new StringBuffer();
0628: sb.append(getName());
0629: sb.append(" -> ");
0630: sb.append(hostAddress);
0631: sb.append(':');
0632: sb.append(port);
0633: logger.finest(sb.toString());
0634: }
0635:
0636: socket.setSoTimeout(socketTimeout);
0637: connection = true;
0638:
0639: dataModeIN = getServer().getDefaultDataMode(DataType.IN);
0640: dataModeOUT = getServer().getDefaultDataMode(DataType.OUT);
0641:
0642: updateInputOutputStreams();
0643: }
0644:
0645: protected void processMaxConnection(ClientEvent currentEvent)
0646: throws IOException {
0647: if (clientExtendedEventHandler != null) {
0648: if (clientExtendedEventHandler.handleMaxConnection(this )) {
0649: removeEvent(getThreadEvent());
0650: if (getThreadEvent() == ClientEvent.MAX_CON) {
0651: currentEvent = ClientEvent.ACCEPT;
0652: } else if (getThreadEvent() == ClientEvent.MAX_CON_BLOCKING) {
0653: currentEvent = ClientEvent.RUN_BLOCKING;
0654: } else {
0655: throw new IllegalArgumentException(
0656: "Unknown ClientEvent: " + getThreadEvent());
0657: }
0658: synchronized (clientEvents) {
0659: clientEvents.add(currentEvent);
0660: }
0661: threadEvent.set(currentEvent);
0662: }
0663: } else if (maxConnectionMsg.length() != 0) {
0664: out.write(maxConnectionMsg.getBytes(charset), 0,
0665: maxConnectionMsg.length());
0666: out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
0667: out.flush();
0668: }
0669: }
0670:
0671: protected AuthStatus processAuthorisation() throws SocketException,
0672: IOException, AppException {
0673: logger.finest("INSIDE");
0674: while (authorised == false && connection == true) {
0675: isConnected();
0676:
0677: counAuthTry++;
0678:
0679: if (authorised == false) {
0680: if (counAuthTry > maxAuthTry) {
0681: processMaxAuthTry();
0682: }
0683: }
0684:
0685: try {
0686: if (clientAuthenticationHandler != null) {
0687: return clientAuthenticationHandler
0688: .askAuthentication(this );
0689: } else if (authenticator != null) {
0690: authorised = authenticator.askAuthorisation(this );
0691: }
0692: } catch (NullPointerException e) {
0693: logger
0694: .severe("Authenticator implementation has not handled null properly."
0695: + " Input from client should be checked for null!");
0696: throw e;
0697: } catch (SocketTimeoutException e) {
0698: handleTimeout(e);
0699: }
0700:
0701: updateLastCommunicationTime();
0702: } //end of auth while
0703: return AuthStatus.SUCCESS;
0704: }
0705:
0706: private void processMaxAuthTry() throws SocketException,
0707: IOException, AppException {
0708: if (clientExtendedEventHandler != null) {
0709: clientExtendedEventHandler.handleMaxAuthTry(this );
0710: } else {
0711: String temp = maxAuthTryMsg;
0712: if (dataModeOUT == DataMode.STRING)
0713: temp = temp + NEW_LINE;
0714: if (dataModeOUT != DataMode.OBJECT) {
0715: out.write(temp.getBytes(charset));
0716: out.flush();
0717: }
0718: }
0719: appLogger.warning("Max Auth Try Reached - Client : "
0720: + getHostAddress());
0721: if (true)
0722: throw new AppException(maxAuthTryMsg);
0723: }
0724:
0725: protected void notifyCloseOrLost() throws IOException {
0726: synchronized (this ) {
0727: if (closeOrLostNotified == false) {
0728: if (lost == true) {
0729: clientEventHandler.lostConnection(this );
0730: } else {
0731: clientEventHandler.closingConnection(this );
0732: }
0733: closeOrLostNotified = true;
0734: }
0735: }
0736: }
0737:
0738: protected synchronized void returnClientData() {
0739: if (clientData == null
0740: || getServer().getClientDataPool() == null)
0741: return;
0742: logger.finest("Returning ClientData to pool");
0743: try {
0744: getServer().getClientDataPool().returnObject(clientData);
0745: clientData = null;
0746: } catch (Exception e) {
0747: logger
0748: .warning("IGNORED: Could not return ClientData to pool: "
0749: + e);
0750: }
0751: }
0752:
0753: protected void returnClientHandler() {
0754: try {
0755: synchronized (lockObj) {
0756: logger.finest(Thread.currentThread().getName()
0757: + " returning " + getName());
0758: getServer().getClientHandlerPool().returnObject(this );
0759: }
0760: } catch (Exception e) {
0761: logger
0762: .warning("IGNORED: Could not return ClientHandler to pool: "
0763: + e);
0764: }
0765: }
0766:
0767: /**
0768: * Returns the ClientHandler name
0769: * @since 1.4.6
0770: */
0771: public String getName() {
0772: return name;
0773: }
0774:
0775: /**
0776: * Returns the ClientHandler detailed information.
0777: * If ClientData is present and is ClientIdentifiable will return ClientInfo else
0778: * it will return Clients InetAddress and port information.
0779: */
0780: public String info() {
0781: StringBuffer sb = new StringBuffer();
0782: sb.append("{");
0783: sb.append(name);
0784: sb.append(" - ");
0785: String info = getClientIdentifiable(this );
0786: if (info != null) {
0787: sb.append("[ClientInfo: ");
0788: sb.append(info);
0789: sb.append(']');
0790: }
0791:
0792: if (getSocket() == null || getSocket().isClosed() == true) {
0793: sb.append("[non-connected]");
0794: } else if (info == null) {
0795: sb.append('[');
0796: sb.append(hostAddress);
0797: sb.append(':');
0798: sb.append(port);
0799: sb.append(']');
0800: }
0801: sb.append('}');
0802: return sb.toString();
0803: }
0804:
0805: /**
0806: * Returns the ClientHandler information.
0807: * If ClientData is present and is ClientIdentifiable will return ClientInfo else
0808: * it will return Clients InetAddress and port information.
0809: */
0810: public String toString() {
0811: StringBuffer sb = new StringBuffer();
0812: sb.append("{");
0813: sb.append(name);
0814: sb.append(" - ");
0815: if (getSocket() == null || getSocket().isClosed() == true) {
0816: sb.append("[non-connected]");
0817: } else if (hostAddress != null) {
0818: sb.append('[');
0819: sb.append(hostAddress);
0820: sb.append(':');
0821: sb.append(port);
0822: sb.append(']');
0823: }
0824: synchronized (clientEvents) {
0825: if (clientEvents.size() != 0) {
0826: sb.append(' ');
0827: sb.append(clientEvents);
0828: }
0829: }
0830: sb.append('}');
0831: return sb.toString();
0832: }
0833:
0834: protected static String getClientIdentifiable(
0835: ClientHandler foundClientHandler) {
0836: if (foundClientHandler == null)
0837: return null;
0838: ClientData foundClientData = null;
0839: foundClientData = foundClientHandler.getClientData();
0840: if (foundClientData == null)
0841: return null;
0842: else if (ClientIdentifiable.class.isInstance(foundClientData) == false)
0843: return null;
0844: else
0845: return ((ClientIdentifiable) foundClientData)
0846: .getClientInfo();
0847: }
0848:
0849: /**
0850: * Sets the {@link DataMode} for the ClientHandler
0851: *
0852: * Note: When mode is DataMode.OBJECT and type is DataType.IN
0853: * this call will block until the client ObjectOutputStream has
0854: * written and flushes the header.
0855: * @since 1.2
0856: * @exception IOException if mode could not be changed.
0857: * @param dataMode mode of data exchange - String or Object.
0858: * @param dataType type of data for which mode has to be set.
0859: */
0860: public abstract void setDataMode(DataMode dataMode,
0861: DataType dataType) throws IOException;
0862:
0863: protected void checkDataModeSet(DataMode dataMode, DataType dataType) {
0864: if (dataMode == DataMode.STRING && dataType == DataType.IN
0865: && clientCommandHandler == null) {
0866: throw new IllegalArgumentException(
0867: "Can't set DataType.IN mode to STRING when ClientCommandHandler is not set!");
0868: }
0869:
0870: if (dataMode == DataMode.BYTE && dataType == DataType.IN
0871: && clientCommandHandler == null) {
0872: throw new IllegalArgumentException(
0873: "Can't set DataType.IN mode to BYTE when ClientCommandHandler is not set!");
0874: }
0875:
0876: if (dataMode == DataMode.OBJECT && dataType == DataType.IN
0877: && clientObjectHandler == null) {
0878: throw new IllegalArgumentException(
0879: "Can't set DataType.IN mode to OBJECT when ClientObjectHandler is not set!");
0880: }
0881:
0882: if (dataMode == DataMode.BINARY && dataType == DataType.IN
0883: && clientBinaryHandler == null) {
0884: throw new IllegalArgumentException(
0885: "Can't set DataType.IN mode to BINARY when ClientBinaryHandler is not set!");
0886: }
0887: }
0888:
0889: /**
0890: * Returns the {@link DataMode} of the ClientHandler for the
0891: * DataType.
0892: * @since 1.2
0893: */
0894: public DataMode getDataMode(DataType dataType) {
0895: if (dataType == DataType.IN)
0896: return dataModeIN;
0897: else if (dataType == DataType.OUT)
0898: return dataModeOUT;
0899: else
0900: throw new IllegalArgumentException("Unknown DataType : "
0901: + dataType);
0902: }
0903:
0904: /**
0905: * Returns the {@link java.sql.Connection} object for the
0906: * DatabaseConnection that is identified by id passed. If id passed
0907: * does not match with any connection loaded by this class it will
0908: * return <code>null</code>.
0909: * This just calls <code>getServer().getDBPoolUtil().getConnection(id)</code>
0910: * @since 1.3
0911: * @deprecated as of v1.4.5 use <code>getServer().getDBPoolUtil().getConnection(id)</code>
0912: */
0913: public java.sql.Connection getConnection(String id)
0914: throws Exception {
0915: if (getServer() == null)
0916: throw new Exception(
0917: "ClientHandler no longer is associated with any client! Try to use quickserver.getDBPoolUtil().getConnection("
0918: + id + ")");
0919: return getServer().getDBPoolUtil().getConnection(id);
0920: }
0921:
0922: /**
0923: * Returns the date/time when the client socket was assigned to this
0924: * ClientHanlder. If no client is currently connected it will return
0925: * <code>null</code>
0926: * @since 1.3.1
0927: */
0928: public Date getClientConnectedTime() {
0929: return clientConnectedTime;
0930: }
0931:
0932: /**
0933: * Read the byte input. This will block till some data is
0934: * received from the stream.
0935: * @return The data as a String
0936: * @since 1.3.1
0937: */
0938: protected abstract byte[] readInputStream() throws IOException;
0939:
0940: protected static byte[] readInputStream(InputStream _in)
0941: throws IOException {
0942: byte data[] = null;
0943: if (_in == null)
0944: throw new IOException("InputStream can't be null!");
0945:
0946: int s = _in.read();
0947: if (s == -1) {
0948: return null; //Connection lost
0949: }
0950: int alength = _in.available();
0951: if (alength > 0) {
0952: data = new byte[alength + 1];
0953: _in.read(data, 1, alength);
0954: } else {
0955: data = new byte[1];
0956: }
0957: data[0] = (byte) s;
0958: return data;
0959: }
0960:
0961: /**
0962: * Read the byte input. This will block till some data is
0963: * received from the stream. Allowed only when
0964: * <code>DataType.IN</code> is in <code>DataMode.BYTE</code> mode.
0965: * @return The data as a String
0966: * @since 1.3.2
0967: */
0968: public String readBytes() throws IOException {
0969: if (dataModeIN != DataMode.BYTE)
0970: throw new IllegalStateException("Can't read Byte: "
0971: + "DataType.IN is not in DataMode.BYTE");
0972: byte data[] = readInputStream();
0973: if (data != null)
0974: return new String(data, charset);
0975: else
0976: return null;
0977: }
0978:
0979: /**
0980: * Sets the communication logging flag.
0981: * @see #getCommunicationLogging
0982: * @since 1.3.2
0983: */
0984: public void setCommunicationLogging(boolean communicationLogging) {
0985: this .communicationLogging = communicationLogging;
0986: }
0987:
0988: /**
0989: * Returns the communication logging flag.
0990: * @see #setCommunicationLogging
0991: * @since 1.3.2
0992: */
0993: public boolean getCommunicationLogging() {
0994: return communicationLogging;
0995: }
0996:
0997: /**
0998: * Returns the date/time when the client socket last sent a data to this
0999: * ClientHanlder. If no client is currently connected it will return
1000: * <code>null</code>
1001: * @since 1.3.3
1002: */
1003: public Date getLastCommunicationTime() {
1004: return lastCommunicationTime;
1005: }
1006:
1007: /**
1008: * Updates the last communication time for this client
1009: * @since 1.3.3
1010: */
1011: public void updateLastCommunicationTime() {
1012: lastCommunicationTime = new Date();
1013: }
1014:
1015: /**
1016: * Force the closing of the client by closing the associated socket.
1017: * @since 1.3.3
1018: */
1019: public synchronized void forceClose() throws IOException {
1020: if (getSelectionKey() != null)
1021: getSelectionKey().cancel();
1022: if (getSocketChannel() != null) {
1023: getSocketChannel().close();
1024: setSocketChannel(null);
1025: }
1026: if (getSocket() != null) {
1027: getSocket().close();
1028: setSocket(null);
1029: }
1030: }
1031:
1032: /**
1033: * Returns flag indicating if the client is connected in secure mode
1034: * (SSL or TLS).
1035: * @return secure flag
1036: * @since 1.4.0
1037: */
1038: public boolean isSecure() {
1039: return secure;
1040: }
1041:
1042: /**
1043: * Sets flag indicating if the client is connected in secure mode
1044: * (SSL or TLS).
1045: * @param secure
1046: * @since 1.4.0
1047: */
1048: public void setSecure(boolean secure) {
1049: this .secure = secure;
1050: }
1051:
1052: /**
1053: * Updates the InputStream and OutputStream for the ClientHandler for the
1054: * set Socket.
1055: * @since 1.4.0
1056: * @see #setSocket
1057: */
1058: public abstract void updateInputOutputStreams() throws IOException;
1059:
1060: /**
1061: * Makes current Client connection to secure protocol based on the
1062: * secure configuration set to the server. This method will just call
1063: * <code>makeSecure(false, false, true, null)</code>.
1064: * @throws IOException
1065: * @throws NoSuchAlgorithmException
1066: * @throws KeyManagementException
1067: * @since 1.4.0
1068: */
1069: public void makeSecure() throws IOException,
1070: NoSuchAlgorithmException, KeyManagementException {
1071: makeSecure(false, false, true, null);
1072: }
1073:
1074: /**
1075: * Makes current Client connection to secure protocol.
1076: * This method will just call <code>makeSecure(false, false, true, protocol)</code>.
1077: * @throws IOException
1078: * @throws NoSuchAlgorithmException
1079: * @throws KeyManagementException
1080: * @since 1.4.0
1081: */
1082: public void makeSecure(String protocol) throws IOException,
1083: NoSuchAlgorithmException, KeyManagementException {
1084: makeSecure(false, false, true, protocol);
1085: }
1086:
1087: /**
1088: * Makes current Client connection to secure protocol.
1089: * @param useClientMode falg if the socket should start its first handshake in "client" mode.
1090: * @param needClientAuth flag if the clients must authenticate themselves.
1091: * @param autoClose close the underlying socket when this socket is closed
1092: * @param protocol the standard name of the requested protocol. If <code>null</code> will use the protocol set in secure configuration of the server.
1093: * @throws IOException
1094: * @throws NoSuchAlgorithmException
1095: * @throws KeyManagementException
1096: * @since 1.4.0
1097: */
1098: public void makeSecure(boolean useClientMode,
1099: boolean needClientAuth, boolean autoClose, String protocol)
1100: throws IOException, NoSuchAlgorithmException,
1101: KeyManagementException {
1102: if (isSecure() == true) {
1103: throw new IllegalStateException(
1104: "Client is already in secure mode!");
1105: }
1106:
1107: appLogger.fine("Making secure - Protocol: " + protocol
1108: + ", Client: [" + getHostAddress() + "]");
1109:
1110: javax.net.ssl.SSLSocketFactory sslSf = getServer()
1111: .getSSLSocketFactory(protocol);
1112: String host = getServer().getBindAddr().getHostAddress();
1113: if (host.equals("0.0.0.0"))
1114: host = InetAddress.getLocalHost().getHostAddress();
1115: SSLSocket newSocket = (SSLSocket) sslSf.createSocket(
1116: getSocket(), host, getServer().getPort(), autoClose);
1117: newSocket.setNeedClientAuth(needClientAuth);
1118: newSocket.setUseClientMode(useClientMode);
1119: setSocket(newSocket);
1120: setSecure(true);
1121: updateInputOutputStreams();
1122: }
1123:
1124: /**
1125: * Send a binary data to the connected client.
1126: * If client is not connected it will just return.
1127: * @since 1.4
1128: * @exception IOException
1129: * if Socket IO Error or Socket was closed by the client.
1130: */
1131: public void sendClientBinary(byte data[]) throws IOException {
1132: sendClientBinary(data, 0, data.length);
1133: }
1134:
1135: /**
1136: * Send a binary data to the connected client.
1137: * If client is not connected it will just return.
1138: * @since 1.4.5
1139: * @exception IOException
1140: * if Socket IO Error or Socket was closed by the client.
1141: */
1142: public void sendClientBinary(byte data[], int off, int len)
1143: throws IOException {
1144: if (isConnected()) {
1145: if (dataModeOUT != DataMode.BINARY)
1146: throw new IllegalStateException("Can't send Binary :"
1147: + "DataType.OUT is not in DataMode.BINARY");
1148: if (getCommunicationLogging()) {
1149: appLogger.fine("Sending [" + getHostAddress() + "] : "
1150: + MyString.getMemInfo(len));
1151: }
1152: b_out.write(data, off, len);
1153: b_out.flush();
1154: } else {
1155: logger.warning("Client not connected.");
1156: }
1157: }
1158:
1159: /**
1160: * Read the binary input. This will block till some data is
1161: * received from the stream. Allowed only when
1162: * <code>DataType.IN</code> is in <code>DataMode.BINARY</code> mode.
1163: * @return The data as a String
1164: * @since 1.4
1165: */
1166: public byte[] readBinary() throws IOException {
1167: if (dataModeIN != DataMode.BINARY)
1168: throw new IllegalStateException("Can't read Binary :"
1169: + "DataType.IN is not in DataMode.BINARY");
1170: byte data[] = readInputStream();
1171: return data;
1172: }
1173:
1174: /**
1175: * Sets the ClientBinaryHandler class that interacts with
1176: * client sockets.
1177: * @param handler fully qualified name of the class that
1178: * implements {@link ClientBinaryHandler}
1179: * @since 1.4
1180: */
1181: protected void setClientBinaryHandler(ClientBinaryHandler handler) {
1182: clientBinaryHandler = handler;
1183: }
1184:
1185: /**
1186: * Returns client SelectionKey associated, if any.
1187: * @since 1.4.5
1188: */
1189: public Logger getAppLogger() {
1190: return appLogger;
1191: }
1192:
1193: /**
1194: * Sets the client socket's timeout.
1195: * @param time client socket timeout in milliseconds.
1196: * @see #getTimeout
1197: * @since 1.4.5
1198: */
1199: public void setTimeout(int time) {
1200: socketTimeout = time;
1201: }
1202:
1203: /**
1204: * Returns the Client socket timeout in milliseconds.
1205: * @see #setTimeout
1206: * @since 1.4.5
1207: */
1208: public int getTimeout() {
1209: return socketTimeout;
1210: }
1211:
1212: /**
1213: * Checks if this client has the event.
1214: * @since 1.4.5
1215: */
1216: public boolean hasEvent(ClientEvent event) {
1217: synchronized (clientEvents) {
1218: return clientEvents.contains(event);
1219: }
1220: }
1221:
1222: /**
1223: * Adds the ClientEvent.
1224: * @since 1.4.5
1225: */
1226: public void addEvent(ClientEvent event) {
1227: synchronized (clientEvents) {
1228: unprocessedClientEvents.add(event);
1229: clientEvents.add(event);
1230: }
1231: }
1232:
1233: /**
1234: * Removes the ClientEvent.
1235: * @since 1.4.5
1236: */
1237: public void removeEvent(ClientEvent event) {
1238: if (event == null)
1239: return;
1240:
1241: synchronized (clientEvents) {
1242: clientEvents.remove(event);
1243: }
1244:
1245: ClientEvent _clientEvent = (ClientEvent) threadEvent.get();
1246: if (_clientEvent != null && _clientEvent == event) {
1247: threadEvent.set(null);
1248: }
1249:
1250: }
1251:
1252: /**
1253: * Returns threads current event for this client.
1254: * @since 1.4.5
1255: */
1256: protected ClientEvent getThreadEvent() {
1257: return (ClientEvent) threadEvent.get();
1258: }
1259:
1260: /**
1261: * Sets message to be displayed when maximum connection reaches.
1262: * @since 1.4.5
1263: */
1264: public void setMaxConnectionMsg(String msg) {
1265: maxConnectionMsg = msg;
1266: }
1267:
1268: /**
1269: * Returns message to be displayed to the client when maximum
1270: * connection reaches.
1271: * @since 1.4.5
1272: */
1273: public String getMaxConnectionMsg() {
1274: return maxConnectionMsg;
1275: }
1276:
1277: /**
1278: * Sets client socket channel associated, if any.
1279: * @since 1.4.5
1280: */
1281: public abstract void setSocketChannel(SocketChannel socketChannel);
1282:
1283: /**
1284: * Returns client socket channel associated, if any.
1285: * @since 1.4.5
1286: */
1287: public abstract SocketChannel getSocketChannel();
1288:
1289: /**
1290: * Sets client SelectionKey associated, if any.
1291: * @since 1.4.5
1292: */
1293: public abstract void setSelectionKey(SelectionKey selectionKey);
1294:
1295: /**
1296: * Returns client SelectionKey associated, if any.
1297: * @since 1.4.5
1298: */
1299: public abstract SelectionKey getSelectionKey();
1300:
1301: public boolean getWillClean() {
1302: return willClean;
1303: }
1304:
1305: /**
1306: * Register OP_READ with the SelectionKey associated with the channel. If SelectionKey is
1307: * not set then it registers the channel with the Selector.
1308: * @since 1.4.5
1309: */
1310: public abstract void registerForRead() throws IOException,
1311: ClosedChannelException;
1312:
1313: /**
1314: * Register OP_WRITE with the SelectionKey associated with the channel.
1315: * @since 1.4.5
1316: */
1317: public abstract void registerForWrite() throws IOException,
1318: ClosedChannelException;
1319:
1320: /**
1321: * Sets the ClientWriteHandler class that interacts with
1322: * client sockets.
1323: * @param handler fully qualified name of the class that
1324: * implements {@link ClientWriteHandler}
1325: * @since 1.4.5
1326: */
1327: protected abstract void setClientWriteHandler(
1328: ClientWriteHandler handler);
1329:
1330: /**
1331: * Sets the Charset to be used for String decoding and encoding.
1332: * @param charset to be used for String decoding and encoding
1333: * @see #getCharset
1334: * @since 1.4.5
1335: */
1336: public void setCharset(String charset) {
1337: if (charset == null || charset.trim().length() == 0)
1338: return;
1339: this .charset = charset;
1340: }
1341:
1342: /**
1343: * Returns Charset to be used for String decoding and encoding..
1344: * @see #setCharset
1345: * @since 1.4.5
1346: */
1347: public String getCharset() {
1348: return charset;
1349: }
1350:
1351: /**
1352: * Returns cached socket host ip address.
1353: * @since 1.4.5
1354: */
1355: public String getHostAddress() {
1356: return hostAddress;
1357: }
1358:
1359: protected void assertionSystemExit() {
1360: logger
1361: .warning("[Assertions Was Enabled] Forcing program exit to help developer.");
1362: org.quickserver.net.qsadmin.QSAdminShell.tryFullThreadDump();//it can help debug.
1363: try {
1364: Thread.sleep(100);
1365: } catch (InterruptedException e) {
1366: logger.fine("Interrupted: " + e);
1367: }
1368: System.exit(-1);
1369: }
1370:
1371: /**
1372: * Checks if the passed ClientEvent is the one next for
1373: * processing if a thread is allowed through this object.
1374: * @since 1.4.6
1375: */
1376: public boolean isClientEventNext(ClientEvent clientEvent) {
1377: ClientEvent ce = null;
1378: synchronized (clientEvents) {
1379: if (unprocessedClientEvents.size() > 0)
1380: ce = (ClientEvent) unprocessedClientEvents.get(0);
1381: }
1382: return clientEvent == ce;
1383: }
1384:
1385: /**
1386: *Returns the {@link java.io.BufferedInputStream} associated with
1387: * the Client being handled. Can be null if not available at the time of method call.
1388: * @see #getBufferedOutputStream
1389: * @since 1.4.6
1390: */
1391: public BufferedInputStream getBufferedInputStream() {
1392: return b_in;
1393: }
1394:
1395: /**
1396: * Returns the {@link java.io.BufferedOutputStream} associated with
1397: * the Client being handled. Can be null if not available at the time of method call.
1398: * @see #getBufferedInputStream
1399: * @since 1.4.6
1400: */
1401: public BufferedOutputStream getBufferedOutputStream() {
1402: return b_out;
1403: }
1404:
1405: protected void handleTimeout(SocketTimeoutException e)
1406: throws SocketException, IOException {
1407: appLogger.fine("Timeout - Client [" + getHostAddress() + "]");
1408: appLogger.finest("SocketTimeoutException : " + e.getMessage());
1409:
1410: String temp = null;
1411: if (clientExtendedEventHandler != null) {
1412: clientExtendedEventHandler.handleTimeout(this );
1413: } else {
1414: temp = timeoutMsg;
1415: if (dataModeOUT == DataMode.STRING)
1416: temp = temp + NEW_LINE;
1417: if (dataModeOUT != DataMode.OBJECT) {
1418: out.write(temp.getBytes(charset));
1419: out.flush();
1420: }
1421: if (true)
1422: throw new SocketException("Timeout");
1423: }
1424: }
1425: }
|