001/*
002 *  $URL:$
003 *  $Author: tgutwin $
004 *  $Revision: $
005 *  $Date: 2014-01-26 20:27:05 -0800 (Sun, 26 Jan 2014) $
006 */
007/*
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2014 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.bc.ca
012 *
013 *  This program is free software; you can redistribute it and/or modify
014 *  it under the terms of the GNU General Public License as published by
015 *  the Free Software Foundation; either version 2 of the License, or
016 *  (at your option) any later version.
017 *
018 *  This program is distributed in the hope that it will be useful,
019 *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
020 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 *  GNU General Public License for more details.
022 *
023 *  You should have received a copy of the GNU General Public License
024 *  along with this program; if not, write to the Free Software
025 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
026 */
027package ca.bc.webarts.android;
028
029import java.io.DataInputStream;
030import java.io.PrintStream;
031import java.io.BufferedReader;
032import java.io.InputStreamReader;
033import java.io.IOException;
034import java.net.Socket;
035import java.net.UnknownHostException;
036
037/** A simple but functional TCP Socket client that sends messages to a designated IP port. **/
038public class TCPSocketClient implements Runnable
039{
040
041  private static String DEFAULT_SERVER_HOST = "localhost";
042
043  private int debug_ = 0;
044
045  /** The client socket. **/
046  private  Socket clientSocket_ = null;
047  /** The output stream. **/
048  private PrintStream os_ = null;
049  /** The input stream. **/
050  private DataInputStream is_ = null;
051
052  private String host_ = "localhost";
053  private int port_ = 8765;
054
055  private Thread listener_ = null;
056  private boolean listening_ = false;
057  private  boolean waitingForResponse_ = false;
058  private boolean errorResponse_ = false;
059 // private boolean listeningLock_ = false;
060  //private  boolean responseLock_ = false;
061
062
063  /** default Constructor uses DEFAULTS from this class for IP and port. **/
064  public TCPSocketClient()
065  {
066  }
067
068
069  /** Instatiates the client with custom IP and port.**/
070  public TCPSocketClient(String hostName, int portNum)
071  {
072    host_=hostName;
073    port_=portNum;
074  }
075
076
077  /** Initializes the socket, and associated in and out streams. **/
078  public synchronized Socket initSocket() throws UnknownHostException, IOException
079  {
080    if (debug_>1) System.out.println("initSocket() and isInit: "+isInit());
081    if(!isInit())
082    {
083      clientSocket_ = new Socket(host_, port_);
084      os_ = new PrintStream(clientSocket_.getOutputStream());
085      is_ = new DataInputStream(clientSocket_.getInputStream());
086    }
087    return clientSocket_;
088  }
089
090
091  /** Starts the listener Thread, AFTER checking that everything is initialized and ready to go (initsocket).**/
092  public boolean listenToSocket() throws IOException
093  {
094    if (debug_>1) System.out.println("listenToSocket() and isInit: "+isInit());
095    if(isInit())
096    {
097      listener_ = new Thread(this, "SocketListener");
098      listener_.start();
099      int waiter = 0;int sleepTime=200;
100      while(!isListening() && waiter<1000) {ca.bc.webarts.android.Util.sleep(sleepTime);waiter+=sleepTime;}
101    }
102    return isListening();
103  }
104
105
106  public void signalClose()
107  {
108    if (debug_>1) System.out.println("signalClose()");
109    if (listener_!=null && listener_.isAlive())
110      try{listener_.interrupt();}catch(SecurityException sEx){}
111  }
112
113
114  public synchronized void closeSocket() throws IOException
115  {
116    if (debug_>1) System.out.println("closeSocket() and isInit: "+isInit());
117    signalClose();
118
119     int waiter = 0;int sleepTime=75;int maxBlockTime_ms = 2000;
120     while(isWaitngForResponse() && waiter<maxBlockTime_ms) {ca.bc.webarts.android.Util.sleep(sleepTime);waiter+=sleepTime;}
121
122     if (debug_>1) System.out.println("  CloseWaitComplete="+waiter+"/"+maxBlockTime_ms);
123
124     if( os_!=null ) os_.close();
125     if( is_!=null ) is_.close();
126     if( clientSocket_!=null ) clientSocket_.close();
127     //listening_=false;
128  }
129
130
131  /** Only a new thread  should call this. **/
132  private synchronized boolean setListening(boolean l)
133  {
134    //getListeningLock();
135    listening_=l;
136    //releaseListeningLock();
137    return l;
138  }
139
140
141  private boolean setListening()
142  {
143    return setListening(true);
144  }
145
146
147  /** Confirms if a listener Thread has started and is running/listening for a server response. Listeners start by calling the listenToSocket() method.**/
148  private synchronized boolean isListening()
149  {
150    boolean l = false;
151   // getListeningLock();
152    l=listening_;
153    //releaseListeningLock();
154    return l;
155  }
156
157
158  private synchronized boolean setWaitngForResponse(boolean l)
159  {
160    //getWaitngingLock();
161    waitingForResponse_=l;
162    //releaseWaitngingLock();
163    return l;
164  }
165
166
167  private boolean setWaitngForResponse()
168  {
169    return setWaitngForResponse(true);
170  }
171
172
173  private synchronized boolean isWaitngForResponse()
174  {
175    boolean l = false;
176    //getWaitngingLock();
177    l=waitingForResponse_;
178    //releaseWaitngingLock();
179    return l;
180  }
181
182/*
183  synchronized private boolean getWaitngingLock()
184  {
185    if(!responseLock_)
186      responseLock_=true;
187    return responseLock_;
188  }
189
190
191  synchronized private boolean getListeningLock()
192  {
193    if(!listeningLock_)
194      listeningLock_=true;
195    return listeningLock_;
196  }
197*/
198
199
200  public boolean isInit()
201  {
202    return (clientSocket_!=null && !clientSocket_.isClosed() &&  os_!=null && is_!=null );
203  }
204
205
206  public void send(String message) throws IOException
207  { sendAndWait(message,0);}
208
209
210  public void sendAndWait(String message, int maxBlockTime_ms) throws IOException
211  {
212    if (debug_>1) System.out.println("sendAndWait()");
213    if(isInit())
214    {
215      //setWaitngForResponse();
216      if(listenToSocket())
217      {
218        if (debug_>1) System.out.println("  ...sendingMessage: "+message);
219        os_.println(message+"\n");
220        int waiter = 0;int sleepTime=75;
221        while(isWaitngForResponse() && waiter<maxBlockTime_ms) {ca.bc.webarts.android.Util.sleep(sleepTime);waiter+=sleepTime;}
222        if (debug_>0) System.out.println("SendWaitComplete="+waiter+"/"+maxBlockTime_ms);
223      }
224      else
225      {
226        // try again
227        if(listenToSocket())
228        {
229          os_.println(message+"\n");
230          int waiter = 0;int sleepTime=100;
231          while(isWaitngForResponse() && waiter<maxBlockTime_ms) {ca.bc.webarts.android.Util.sleep(sleepTime);waiter+=sleepTime;}
232          if (debug_>0) System.out.println("SendWaitComplete="+waiter+"/"+maxBlockTime_ms);
233        }
234      }
235
236    }
237    else System.out.println("Socket NOT init");
238  }
239
240
241  public static void main(String[] args)
242  {
243    TCPSocketClient instance= null;
244    if (args.length < 2)
245    {
246      instance = new TCPSocketClient();
247      System.out.println("Usage: java TCPSocketClient <host> <portNumber>\n" +
248                         "Now using host=" + instance.host_ + ", portNumber=" + instance.port_);
249    }
250    else
251    {
252      //host = args[0];
253      //portNumber = Integer.valueOf(args[1]).intValue();
254      System.out.println("Usage: java TCPSocketClient <host> <portNumber>\n" +
255                         "Now using host=" +  args[0] + ", portNumber=" + Integer.valueOf(args[1]).intValue());
256      instance = new TCPSocketClient( args[0], Integer.valueOf(args[1]).intValue());
257    }
258
259    try
260    {
261        if (instance.debug_>0) System.out.print((instance.isListening()?"?":"-"));
262        BufferedReader inputLine = null;
263        inputLine = new BufferedReader(new InputStreamReader(System.in));
264        String req = "";
265
266        while (!req.trim().equalsIgnoreCase("Bye") &&
267               !req.trim().equalsIgnoreCase("Quit") &&
268               !req.trim().equalsIgnoreCase("Exit"))
269        {
270          try
271          {
272            if ( //instance.isListening() &&
273                !req.trim().equals(""))
274            {
275              //req = inputLine.readLine().trim();
276              if (instance.debug_>1) System.out.println("Request: "+req);
277              instance.initSocket();
278              if (instance.isInit())
279              {
280                instance.sendAndWait(req,1000);
281                instance.closeSocket();
282              }
283              else
284                System.err.println("Socket NOT init");
285            }
286          }
287          catch (IOException e)
288          {
289            System.err.println("IOException:  " + e);
290            e.printStackTrace();
291          }
292          //Go Back for another message
293          System.out.print("?");
294          req = inputLine.readLine().trim();
295        }
296
297    }
298    catch (UnknownHostException e)
299    {
300      System.err.println("Don't know about host " + instance.host_);
301    }
302    catch (IOException ioEx)
303    {
304      ioEx.printStackTrace();
305      System.err.println("Couldn't get I/O for the connection to the host " + instance.host_);
306    }
307  }
308
309
310  public void run()
311  {
312    setListening();
313    if (debug_>0) System.out.print("\nListening: "+ isListening());
314    /*
315     * Keep on reading from the socket till we receive "Bye" from the
316     * server. Once we received that then we want to break.
317     */
318    String responseLine = null;
319    try
320    {
321      errorResponse_ = true;
322      setWaitngForResponse();
323      if (debug_>0) System.out.println(" is_ is null: "+(is_ == null));
324      while (is_ != null && (responseLine = is_.readLine()) != null)
325      {
326        System.out.println("ServerResponse: "+ responseLine);
327        if (responseLine.startsWith("END") ) // lirc response
328        {
329          setWaitngForResponse(false);
330          setListening(false);
331          break;
332        }
333
334        if (responseLine.startsWith("ERROR") ) // lirc response
335        {
336          setWaitngForResponse(false);
337          errorResponse_ = true;
338        }
339        if (responseLine.startsWith("SUCCESS") ) // lirc response
340        {
341          setWaitngForResponse(false);
342        }
343
344      }
345      if (debug_>0) System.out.println("NOT Listening & responseLine==null is "+(responseLine==null));
346    }
347    catch (java.nio.channels.ClosedByInterruptException intEx)
348    {
349      if (debug_>1) System.err.println("listenerThread Interupted");
350    }
351    catch (IOException e)
352    {
353      System.err.println("runIOException:  " + e);
354      e.printStackTrace();
355    }
356    finally
357    {
358      setWaitngForResponse(false);
359      setListening(false);
360    }
361    if (debug_>1) System.out.println("Exiting Listener Thread.Run");
362  }
363}