001/*
002 *  $Id: TunesAjaxRestListener.java 185 2020-09-07 02:43:14Z tgutwin $
003 *  $HeadURL: svn://svn.webarts.bc.ca/closed/trunk/www/tunes/WEB-INF/src/ca/bc/webarts/servlet/TunesAjaxRestListener.java $
004 *  $Revision: 185 $
005 *  $LastChangedDate: 2020-09-06 19:43:14 -0700 (Sun, 06 Sep 2020) $
006 *  $LastChangedBy: tgutwin $
007 *  Copyright (c) 2014-2020 Tom B. Gutwin P.Eng. North Vancouver BC Canada
008 *
009 *  This program is free software; you can redistribute it and/or
010 *  modify it under the terms of the GNU General Public License
011 *  as published by the Free Software Foundation; either version 3
012 *  of the License, or any later version.
013 *
014 *  This program is distributed in the hope that it will be useful,
015 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
016 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 *  GNU General Public License for more details.
018 *
019 *  You should have received a copy of the GNU General Public License
020 *  along with this program; If not, see <http://www.gnu.org/licenses/>.
021 */
022package ca.bc.webarts.servlet;
023
024import ca.bc.webarts.tools.Log;
025import ca.bc.webarts.widgets.Util;
026import ca.bc.webarts.tools.sockets.*;
027import ca.bc.webarts.widgets.ResultSetConverter;
028import ca.bc.webarts.widgets.tunes.TunesHelper;
029
030import com.oreilly.servlet.ParameterParser;
031import com.oreilly.servlet.ParameterNotFoundException;
032import com.oreilly.servlet.ServletUtils;
033
034import java.text.DecimalFormat;
035import java.io.BufferedReader;
036import java.io.File;
037import java.io.FileNotFoundException;
038import java.io.FileOutputStream;
039import java.io.FileReader;
040import java.io.FileWriter;
041import java.io.FileInputStream;
042import java.io.IOException;
043import java.io.InputStream;
044import java.io.OutputStream;
045import java.io.BufferedReader;
046import java.io.InputStreamReader;
047import java.io.IOException;
048import java.net.Socket;
049import java.net.UnknownHostException;
050import java.net.InetAddress;
051import java.net.MalformedURLException;
052import java.net.URL;
053import java.net.UnknownHostException;
054import java.util.Calendar;
055import java.util.Date;
056import java.util.Enumeration;
057import java.util.TimeZone;
058import java.util.Vector;
059import java.util.zip.ZipEntry;
060import java.util.zip.ZipOutputStream;
061import java.text.DateFormat;
062import java.text.SimpleDateFormat;
063import java.time.Duration;
064import java.time.LocalDateTime;
065import java.time.ZonedDateTime;
066import java.time.format.DateTimeFormatter;
067
068import javax.servlet.http.HttpServlet;
069import javax.servlet.http.HttpServletRequest;
070import javax.servlet.*;
071import javax.servlet.http.*;
072
073//import javax.json.Json;
074
075/**
076 * This class is a generic listener, implemented as a servlet, for responding to AJAX and/or Rest style requests.<br />
077 *
078 * @author     tgutwin
079 * @created    April 25, 2016
080 */
081public class TunesAjaxRestListener extends javax.servlet.http.HttpServlet
082{
083  // implements SingleThreadModel
084
085  /** Class constant. **/
086  protected static final String className_ = "TunesAjaxRestListener";
087  private final static String SYSTEM_FILE_SEPERATOR = File.separator;
088  /**  Version String.  */
089  private final static String SERVLET_VERSION = Util.spacesToCapsInString("0.99.RC.6_[$Rev: 185 $]");
090
091  public static final String CLIENT_SETTING_NAME_VIEWPORTWIDTH = "clientSetting.viewPortWidth";
092  public static final String CLIENT_SETTING_NAME_VIEWPORTHEIGHT = "clientSetting.viewPortHeight";
093  public static final String CLIENT_SETTING_NAME_BROWSER_NAME = "clientSetting.browserName";
094  public static final String CLIENT_SETTING_NAME_BROWSER_VERSION = "clientSetting.browserVersion";
095  public static final String CLIENT_SETTING_NAME_DEVICE_TYPE = "clientSetting.deviceType";
096
097  public static final String CLIENT_SETTING_NAME_LASTFM_SESSION_KEY = "clientSetting.lastFmSession";
098  public static final String CLIENT_SETTING_NAME_LASTFM_USERID = "clientSetting.lastFmUserID";
099  public static final String CLIENT_SETTING_NAME_LASTFM_USER_RECENT = "clientSetting.lastFmUserRecent";
100  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK_NUM = "clientSetting.lastFmPlayTrackNum";
101  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK = "clientSetting.lastFmPlayTrack";
102  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM = "clientSetting.lastFmPlayAlbum";
103  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST = "clientSetting.lastFmPlayArtist";
104  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM_ARTIST = "clientSetting.lastFmPlayAlbumArtist";
105  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TIME = "clientSetting.lastFmPlayTime";
106  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_DURATION = "clientSetting.lastFmPlayDuration";
107  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK = "clientSetting.lastFmScrobbleTrack";
108  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST = "clientSetting.lastFmScrobbleArtist";
109  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM = "clientSetting.lastFmScrobbleAlbum";
110  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM_ARTIST= "clientSetting.lastFmScrobbleAlbumArtist";
111  public static final String CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK = "clientSetting.lastFmLoveTrack";
112  public static final String CLIENT_SETTING_NAME_LASTFM_LIST_LOVE_TRACK = "clientSetting.lastFmListLovedTracks";
113
114  /** DEFAULT Last.FM username to use: admin .**/
115  protected static final String LASTFM_DEFAULT_USERNAME = "tgutwin";  //"${lastfm.user}";
116  /** DEFAULT Last.FM password to use: admin .**/
117  protected static final String LASTFM_DEFAULT_PASSWORD = "~19AcrobatiC19~~~";  //"${lastfm.pass}";
118
119  /** DEFAULT WebSocket PORT on pi. **/
120  public static final String PIWEBSOCKET_DEFAULT_SERVER = "pi2.webarts.bc.ca";
121  /** DEFAULT WebSocket PORT on pi. **/
122  public static final int PIWEBSOCKET_DEFAULT_PORT = 44448;
123  /**  Build String. (yymmddhhss)  */
124  private final static String BUILD_TAG = "160425183718";
125
126  /** The webServer hostname this servlet is alive on. **/
127  private static String webServerHostName_ = "fred.webarts.bc.ca";
128
129  private boolean debugOut_ = true;
130  protected static Log log_= Log.getInstance();
131
132  private int viewPortWidth_ = 0;
133  private int viewPortHeight_ = 0;
134  private String someParamYouWannaSet_ = "";
135
136  /** Control for rest response format XML(default) or JSON. **/
137  private boolean xmlResponse_ = false;
138
139  /** AJAX param - **/
140  private String debugOut = "";
141  private String dataType = "";
142  private int viewPortWidth = 0;
143  private int viewPortHeight = 0;
144  private String browserName = "";
145  private String browserVersion = "";
146  private String lastFmSessionKey = "";
147  private String lastFmUserRecent = "";
148  private String lastFmPlayTrack = "";
149  private int lastFmPlayTrackNum = 0;
150  private String lastFmPlayAlbum = "";
151  private String lastFmPlayArtist = "";
152  private String lastFmPlayAlbumArtist = "";
153  private int lastFmPlayTime = 0;
154  private int lastFmPlayDuration = 0;
155  private String lastFmScrobbleTrack = "";
156  private String lastFmPreviousScrobbleTrack = "";
157  private String lastFmScrobbleArtist = "";
158  private String lastFmScrobbleAlbum = "";
159  private String lastFmScrobbleAlbumArtist = "";
160  private String lastFmLoveTrack = "";
161  private String lastFmListLovedTracks = "";
162  private boolean xmlResponse = xmlResponse_;
163  private TunesHelper tunesHelper = null;
164  private String lastFmLogin = "";
165  private String lastFmUserID_ = LASTFM_DEFAULT_USERNAME;
166  private String lastFmPassword_ = LASTFM_DEFAULT_PASSWORD;
167  private boolean lastFMInit_ = false;
168  private java.util.Vector <de.umass.lastfm.Track> lastFmRecentTracks_ = null;
169  private String [] ajaxCommands_ = { "lastFmPlayTrack",
170                                      "lastFmLogin",
171                                      "lastFmScrobbleTrack",
172                                      "lastFmLoveTrack",
173                                      "lastFmListLovedTracks",
174                                      "lastFmUserRecent"};
175  private String [] restCommands_ = { "test",
176                                      "lastFmListLovedTracks",
177                                      "playOnPi"};
178  /** true if using TCP Socket listener running on pi2  false if using ssh access or (the NEW recommended) WebSocket.  **/
179  private boolean piWebSocketAccess_ = false;
180  private String piWebSocketServer_ = PIWEBSOCKET_DEFAULT_SERVER;
181  private int piWebSocketPort_ = PIWEBSOCKET_DEFAULT_PORT;
182  private org.eclipse.jetty.websocket.client.WebSocketClient piWebSocket_ = new org.eclipse.jetty.websocket.client.WebSocketClient();
183
184  /** true if using TCP Socket listener running on pi2  false if using ssh access or (the NEW recommended) WebSocket.  **/
185  private boolean piSocketAccess_ = true;
186  private String piTunesTCPSocketServer_ = "pi2.webarts.bc.ca";
187  private int piTunesTCPSocket_ = TCPSocketServer.DEFAULT_PORT;
188
189  /**
190   *  Gets the ServletInfo attribute of the TunesAjaxRestListener object
191   *
192   * @return    The ServletInfo value
193   */
194  public String getServletInfo()
195  {
196    final String methodName = "getServletInfo";
197    return "WebARTS Design TunesAjaxRestListener servlet. Version:" + SERVLET_VERSION +
198        "  Build:" + BUILD_TAG;
199  }
200
201
202  /**
203   * The one time servlet init stuff goes here. It sets the derbyDBDir based on the following prioritized varables:
204   * <ol><li>context init param: derbyDBDir</li><li>servlet init param (from web.xml): derbyDBDir</li>
205   * <li>default hardcoded variable: derbyDBDir</li></ol>If defined in multiple places, the higher priority item will
206   * be used.
207   **/
208  public void init()
209  {
210    System.out.println("\n~~~~~~~~\n~~~~~~~~\nInitializing ca.bc.webarts.servlet.TunesAjaxRestListener\n~~~~~~~~\n~~~~~~~~");
211
212    boolean notFoundInit = true;
213    boolean notFoundContext = true;
214    java.util.Enumeration <String> initEnum = getInitParameterNames();
215    for (; notFoundInit && initEnum.hasMoreElements();)
216    {
217      if(initEnum.nextElement().equals("someParamYouWannaSet"))
218      {
219        notFoundInit = false;
220        /* Do something with the init param */
221        someParamYouWannaSet_ = getInitParameter("someParamYouWannaSet");
222        System.out.println("\n~~~~~~~~\n     INIT ServletParam: someParamYouWannaSet="+someParamYouWannaSet_);
223      }
224    }
225
226    //also check context
227    initEnum = getServletConfig().getServletContext().getInitParameterNames();
228    for (; notFoundContext && initEnum.hasMoreElements();)
229    {
230      if(initEnum.nextElement().equals("someParamYouWannaSet"))
231      {
232        notFoundContext = false;
233        /* Do something with the init param */
234        someParamYouWannaSet_ = getServletConfig().getServletContext().getInitParameter("derbyDBDir");
235        System.out.println("\n~~~~~~~~\n     INIT ContextParam: someParamYouWannaSet="+someParamYouWannaSet_);
236      }
237    }
238
239    if(notFoundInit && notFoundContext )
240    {
241      /* Set the default values */
242      //eagleDBDir_ = pEye_.getDerbyDBDir();
243    }
244
245    try
246    {
247      webServerHostName_ = InetAddress.getLocalHost().getHostName();
248    }
249    catch (UnknownHostException ex)
250    {
251      webServerHostName_ = "fred.webarts.bc.ca";
252    }
253  }
254
255
256  /**  Override to close Things  **/
257  public void destroy()
258  {
259    super.destroy();
260  }
261
262
263  /**
264   * Returns the value of viewPortWidth_.
265   */
266  public int getViewPortWidth() {
267    return viewPortWidth_;
268  }
269
270
271  /**
272   * Sets the value of viewPortWidth_.
273   * @param viewPortWidth The value to assign viewPortWidth_.
274   */
275  public void setViewPortWidth(int viewPortWidth) {
276    this.viewPortWidth_ = viewPortWidth;
277  }
278
279
280  /**
281   * Returns the value of viewPortHeight_.
282   */
283  public int getViewPortHeight() {
284    return viewPortHeight_;
285  }
286
287
288  /**
289   * Sets the value of viewPortHeight_.
290   * @param viewPortWidth The value to assign viewPortWidth_.
291   */
292  public void setViewPortHeight(int viewPortHeight) {
293    this.viewPortHeight_ = viewPortHeight;
294  }
295
296
297  /**
298   * Returns the value of debugOut_.
299   */
300  public boolean getDebugOut()
301  {
302    return debugOut_;
303  }
304
305
306  /**
307   * Sets the value of debugOut_.
308   * @param debugOut The value to assign debugOut_.
309   */
310  public void setDebugOut(boolean debugOut)
311  {
312    this.debugOut_ = debugOut;
313    log_.setLogLevel((debugOut_?log_.DEBUG:log_.MINOR));
314  }
315
316
317  /**
318    * Get Method for class field 'xmlResponse'.
319    *
320    * @return boolean - The value the class field 'xmlResponse' XML (true) or JSON (false).
321    *
322    **/
323  public boolean getXmlResponse()
324  {
325    return xmlResponse_;
326  }  // getXmlResponse Method
327
328
329  /**
330    * Set Method for class field 'xmlResponse' that controls if teh rest response is in XML (true) or JSON (false).
331    *
332    * @param xmlResponse is the value to set this class field to XML (true) or JSON (false).
333    *
334    **/
335  public  void setXmlResponse(boolean xmlResponse)
336  {
337    this.xmlResponse_ = xmlResponse;
338  }  // setXmlResponse Method
339
340
341  /**
342   * streams to a zipped out stream (without creating a file).
343   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
344   * @param servletOutput is the stream to zip into
345   * @param dataName are the pseudo fileName that will get created in the zip (pseudo file) stream
346   * @param dataStream the data to zip
347   **/
348  private void streamZIP(OutputStream servletOut, String dataName, InputStream dataStream)
349  {
350    String[] dataNames = {dataName};
351    InputStream[] dataStreams = {dataStream};
352    streamZIP(servletOut, dataNames,dataStreams);
353  }
354
355
356  /**
357   * streams to a zipped out stream (without creating a file).
358   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
359   *
360   *  Sample servlet doGet...<br />
361   *  public void doGet(HttpServletRequest request,
362                        HttpServletResponse response) throws IOException{
363        response.setContentType("text/plain");
364        response.setHeader("Content-Disposition",
365                           "attachment;filename=downloadname.txt");
366        String[] dataNames = {dataName};
367        InputStream[] dataStreams = {dataStream};
368        OutputStream os = response.getOutputStream();
369        streamZIP(servletOut, dataNames,dataStreams);
370      }
371   *
372   *
373   * @param servletOutput is the stream to zip into
374   * @param dataNames are the pseudo fileNames that will get created in the zip (pseudo file) stream
375   * @param dataStreams the data to zip
376   **/
377  private void streamZIP(OutputStream servletOut,
378                        String[] dataNames,
379                        InputStream[] dataStreams)
380  {
381    ZipOutputStream zos = new ZipOutputStream(servletOut);
382    final int DATA_BLOCK_SIZE = 2048;
383    int byteCount;
384    byte[] data;
385
386    try
387    {
388      for(int i = 0; i < dataNames.length; i++)
389      {
390        ZipEntry ze = new ZipEntry(dataNames[i]);
391        zos.putNextEntry(ze);
392        data = new byte[DATA_BLOCK_SIZE];
393
394        while((byteCount = dataStreams[i].read(data, 0, DATA_BLOCK_SIZE)) != -1)
395        {
396          zos.write(data, 0, byteCount);
397        }
398
399        zos.flush();
400        zos.closeEntry();
401        dataStreams[i].close();
402      }
403      zos.close();
404    }
405    catch(Exception e)
406    {
407      System.err.println("Problem streaming zip data " + e.toString());
408    }
409  }
410
411
412  /** Parses any defined servlet params into the defined class vars. **/
413  private void parseParams(HttpServletRequest req)
414  {
415    ParameterParser parser = new ParameterParser(req);
416    if(debugOut_)
417    {
418      System.out.println("\nRequest Params:");
419      Enumeration<String> parmNames = req.getParameterNames();
420      for (; parmNames.hasMoreElements();)
421      {
422        String currParamName = (String) parmNames.nextElement();
423        System.out.println("  "+currParamName+"="+req.getParameter(currParamName));
424      }
425    }
426
427    /* parse out the request params into class vars  */
428    debugOut = parser.getStringParameter("debugOut", "");
429    viewPortWidth =  parser.getIntParameter("viewPortWidth",0);
430    viewPortHeight =  parser.getIntParameter("viewPortHeight",0);
431    browserName = parser.getStringParameter("clientBrowser","");
432    browserVersion = parser.getStringParameter("clientBrowserVersion","");
433    dataType = parser.getStringParameter("dataType","json");
434
435    lastFmLogin = parser.getStringParameter("lastFmLogin","");  // Ajax Request
436    lastFmUserID_ = parser.getStringParameter("lastFmUserID",LASTFM_DEFAULT_USERNAME);
437    lastFmPassword_ = parser.getStringParameter("lastFmPassword",LASTFM_DEFAULT_PASSWORD);
438    lastFmSessionKey = parser.getStringParameter("lastFmSessionKey","");
439    lastFmUserRecent = parser.getStringParameter("lastFmUserRecent","");  // Ajax Request
440
441    lastFmPlayTrackNum = parser.getIntParameter("lastFmPlayTrackNum",0);
442    lastFmPlayTrack = parser.getStringParameter("lastFmPlayTrack","").replace("_"," ").trim();  // Ajax Request
443    lastFmPlayAlbum = parser.getStringParameter("lastFmPlayAlbum","").replace("_"," ").trim();
444    lastFmPlayArtist = parser.getStringParameter("lastFmPlayArtist","").replace("_"," ").trim();
445    lastFmPlayAlbumArtist = parser.getStringParameter("lastFmPlayAlbumArtist","").replace("_"," ").trim();
446    lastFmPlayTime =  parser.getIntParameter("lastFmPlayTime",0);
447    lastFmPlayDuration =  parser.getIntParameter("lastFmPlayDuration",0);
448
449    lastFmScrobbleTrack = parser.getStringParameter("lastFmScrobbleTrack","").replace("_"," ").trim();  // Ajax Request
450    lastFmScrobbleAlbum = parser.getStringParameter("lastFmScrobbleAlbum","").replace("_"," ").trim();
451    lastFmScrobbleArtist = parser.getStringParameter("lastFmScrobbleArtist","").replace("_"," ").trim();
452    lastFmScrobbleAlbumArtist = parser.getStringParameter("lastFmScrobbleAlbumArtist","").replace("_"," ").trim();
453    lastFmLoveTrack = parser.getStringParameter("lastFmLoveTrack","").replace("_"," ").trim();  // Ajax Request
454    lastFmListLovedTracks = parser.getStringParameter("lastFmListLovedTracks","").replace("_"," ").trim();  // Ajax Request
455    xmlResponse = parser.getBooleanParameter("xmlResponse",xmlResponse_);
456
457    if(debugOut_) { System.out.println("  PlayTrack="+lastFmPlayTrack+"   lastFmPlayArtist="+lastFmPlayArtist +"   lastFmPlayAlbumArtist="+lastFmPlayAlbumArtist );}
458    if(debugOut_) { System.out.println("  ScrobbleTrack="+lastFmScrobbleTrack+"   lastFmScrobbleAlbum="+lastFmScrobbleAlbum+
459                                       "   lastFmScrobbleAlbumArtist="+lastFmScrobbleAlbumArtist+"   lastFmScrobbleArtist="+lastFmScrobbleArtist );}
460
461   }
462
463
464  /** Convert a well-formed (but not necessarily valid) XML string into a JSON String. **/
465  private String xmlToJson(String xml)
466  {
467     String jsonStr = "";
468     org.json.JSONObject jsonObj = null;
469     if (xml== null || xml.length()<2) xml = "";
470     try
471     {
472       jsonObj = org.json.XML.toJSONObject(xml.replaceAll(";", "&#59;").replaceAll("&", "&#38;").replaceAll("%", "&#37;"));
473     }
474     catch (org.json.JSONException jsonEx)
475     {
476       System.out.println("!!!JSON xml Parse ERROR: " + jsonEx.getMessage());
477       System.out.println(xml);
478       System.out.println("\n!!! JSON xml Parse ERROR: " + jsonEx.getMessage());
479       jsonStr = "";
480     }
481     if(jsonObj!= null) jsonStr = jsonObj.toString(2);
482     return jsonStr;
483   }
484
485
486  /**
487   *  This method handles the "GET" submission - it is used for Ajax calls to set webApp parameters using JQuery.
488   *
489   * @param  req                   Description of the Parameter
490   * @param  res                   Description of the Parameter
491   * @exception  ServletException  Description of the Exception
492   * @exception  IOException       Description of the Exception
493   */
494  public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
495  {
496    parseParams(req);
497
498    ServletOutputStream out = res.getOutputStream();
499    String conPath = req.getContextPath();
500    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
501
502    if(debugOut_)
503    {
504      System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~");
505      System.out.println("Tunes TunesAjaxRestListener.doGet "+ "   debugOut="+debugOut_);
506      System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
507    }
508
509    boolean ajaxCall = false;
510    boolean restRequest = false;
511    String ajaxResponse = "";
512    String restResponse = null;
513
514    res.setStatus(HttpServletResponse.SC_OK);  // default ids OK,, set again lower downif NOT
515
516    ajaxResponse = checkForAjaxRequest(req);
517    if (!ajaxResponse.equals("")) ajaxCall=true;
518
519    if(!ajaxCall) restResponse = checkForRestRequest(req);
520    if (restResponse!=null && !restResponse.equals("")) restRequest=true;
521
522    if(debugOut_) { System.out.println("ajaxCall="+ajaxCall+"   restRequest="+restRequest );}
523    /* * * * * *  * * * * * EMPTY * * * * *  * * * * *  */
524    /* ***********************************************  */
525    if( !ajaxCall && !restRequest) // EMPTY query and NO parms... it was *NOT* a JQuery Ajax call or a REST call, so just present some status back to user
526    {
527      sendServletAdminPage(req, res);
528    }
529
530    if(ajaxCall)
531    {
532      res.setContentType("text/plain");
533      // https://stackoverflow.com/questions/5750696/how-to-get-a-cross-origin-resource-sharing-cors-post-request-working
534      res.setHeader("Access-Control-Allow-Origin", "*");
535      if(out!=null  )
536      {
537        if (debugOut_)
538          System.out.println("AJAX REPLY("+ajaxResponse.length()+")="+ajaxResponse);
539        out.print(ajaxResponse);
540      }
541      else
542        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
543    }
544    else if(restRequest)
545    {
546      if (xmlResponse_) res.setContentType("text/xml");
547      else res.setContentType("application/json");
548      if(restResponse!=null && !restResponse.equals(""))
549      {
550        res.setStatus(HttpServletResponse.SC_OK);
551      }
552      else
553      {
554        res.setStatus(HttpServletResponse.SC_NOT_FOUND);
555        restResponse = "<RestResponse succeeded=\"false\"><status>404</status></RestResponse>";
556      }
557      if(out!=null  )
558      {
559        if (debugOut_)
560          System.out.println("XML REPLY("+restResponse.length()+")="+restResponse);
561        if (!xmlResponse_) restResponse = xmlToJson(restResponse);
562        if (debugOut_)
563          System.out.println("REST REPLY("+restResponse.length()+")="+restResponse);
564        out.print(restResponse);
565      }
566      else
567        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
568    }
569    out.flush();
570    res.flushBuffer();
571  }
572
573
574  /**
575   *  This method handles the "POST" submissions.
576   *
577   * @param  req                   Description of Parameter
578   * @param  res                   Description of Parameter
579   * @exception  ServletException  Description of Exception
580   * @exception  IOException       Description of Exception
581   */
582  public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
583  {
584    //parseParams(req);
585
586    StringBuffer postBody = new StringBuffer("");
587    String line = null;
588   // try
589   // {
590      BufferedReader reader = req.getReader();
591      while ((line = reader.readLine()) != null)
592        postBody.append(line+"\n");
593
594      String postReply = "";
595
596      // Do something with the posted message
597      // send it to a helper class if desired
598      /*String postReply = pEye_.newMessage(postBody.toString()); */
599
600      // send back an empty reply
601      if ((postReply==null || postReply.equals("")))
602      {
603        // send back a 200 with an empty body
604        res.setStatus(HttpServletResponse.SC_OK);
605        res.setContentLength(0);
606        postReply = "";
607        if (debugOut_) System.out.println("TunesAjaxRestListener.doPost: emptyreply from newMessage");
608      }
609      /*
610      else if(!sendFastPoll_)
611      {
612        // send back the pEyeReply as the body
613        res.setStatus(HttpServletResponse.SC_OK);
614        res.setContentLength(pEyeReply.length());
615        if (debugOut_) System.out.println("TunesAjaxRestListener.doPost: SLOW Poll & newMessage reply existed");
616      }
617      else // if(sendFastPoll_)
618      {
619        MessageCommand mc = new MessageCommand(pEye_.getMeterMacId());
620        pEyeReply = mc.getSetFastPollCommandStr(fastPollFreq_,fastPollDur_);
621
622        res.setContentLength(0);   // send back the setFastPollCommand string as the body
623        //if (debugOut_) System.out.println(" *!*!*!*!  TunesAjaxRestListener.doPost: Send FAST Poll to:"+pEye_.getMeterMacId() );
624
625        // now reset fastPoll for next message
626        sendFastPoll_ = false;
627      }
628      */
629
630      postReply = "\n"+postReply;
631      res.setContentType("application/xml");
632      res.setStatus(HttpServletResponse.SC_OK);
633      res.setContentLength(postReply.length());
634
635      ServletOutputStream out = res.getOutputStream();
636      if(out!=null  )
637      {
638        if (debugOut_) System.out.println("Attempting to HttpServletResponse REPLY("+postReply.length()+")="+postReply);
639        out.print(postReply);
640        out.flush();
641      }
642      else
643        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
644
645      res.flushBuffer();
646
647      /*
648    }
649    catch (Exception e)
650    {
651      //report an error
652      e.printStackTrace();
653    }
654    */
655  }
656
657
658  /** Parses the REST command string and prepares the response in XML format.
659    * @param restPath is the rest command part of the rest URL
660    * @return a string holding the XML rest response OR null if not a REST Request
661    **/
662  private String checkForRestRequest(HttpServletRequest req) throws ServletException, IOException
663  {
664    String restResponse = null;
665    String servletPath = req.getServletPath();
666    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
667    String pre = "/";
668    boolean returnXml = xmlResponse_; // false means JSON
669
670    if (servletPath.equals("/rest") && restPath!=null && !restPath.equals("") )
671    {
672
673      DecimalFormat dfp = new DecimalFormat( "##0" );
674      DecimalFormat dfe = new DecimalFormat( "###0.000" );
675      if(restPath.equals(pre+"test"))
676      {
677        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
678        /*  Add In the response content  */
679        sb.append("<testNode className=\"");
680        sb.append(className_);
681        sb.append("\">");
682        sb.append("Hello");
683        sb.append("</testNode>");
684        sb.append("</RestResponse>");
685        restResponse = sb.toString();
686      }
687      //
688      // REST Commands
689      // ------------------------
690      // REST Request - lastFmListLovedTracks
691      else if(restPath.equals(pre+"lastFmListLovedTracks"))
692      {
693        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
694        /*  Add In the response content  */
695        sb.append("  <lastFmLovedTrack index=\"");
696        sb.append("1");
697        sb.append("\">");
698        sb.append("    NOT Implemented YET");
699        sb.append("  </lastFmLovedTrack>");
700        sb.append("</RestResponse>");
701        restResponse = sb.toString();
702      }
703      // ------------------------
704      // REST Request - playOnPi
705      else if(restPath.startsWith(pre+"playOnPi") && restPath.length() >  (pre+"playOnPi").length() )
706      {
707        System.out.println("TunesAjaxRestListener runOnPi2 - restPath="+restPath);
708        String pi2Cmd = "/bin/runOnPi2";
709        int tuneFileIndex = -1;
710        String currentTuneFile = "";
711        String pi2TunePath = "";
712        java.util.Vector<String> cmdResults = new java.util.Vector<String>();
713
714        /* check for a 2nd '/' to index where the tuneFileIndex starts */
715        if(restPath.substring((pre+"playOnPi/").length()).indexOf("/") > -1)
716        {
717          tuneFileIndex = restPath.substring((pre+"playOnPi/").length()).indexOf("/") + (pre+"playOnPi/").length();
718          System.out.println("TunesAjaxRestListener runOnPi2 - tuneFileIndex="+tuneFileIndex);
719
720          currentTuneFile = restPath.substring(tuneFileIndex+1);
721          System.out.println("TunesAjaxRestListener runOnPi2 - currentTuneFile="+currentTuneFile);
722
723          pi2TunePath = currentTuneFile.replace("tunes/","");  //  "/mnt/nas/snd/ogg/");   // let piListener handle the re-mangling of the final pathon the pi
724          String [] cmdParms2 = {"/usr/bin/ogg123","-q", pi2TunePath ,"&"};
725          System.out.println("\nTune Path="+pi2TunePath);
726
727          /* send msg to TCPsocket */
728          if(piSocketAccess_)
729          {
730            pi2Cmd = restPath.substring((pre+"playOnPi/").length(), tuneFileIndex);
731            System.out.println("TunesAjaxRestListener runOnPi2Socket - pi2Cmd="+pi2Cmd);
732            if(!pi2Cmd.equalsIgnoreCase("artistList") &&!pi2Cmd.equalsIgnoreCase("artistAlbumsList"))
733            {
734              cmdResults = sendPiSocketMessage(pi2Cmd, pi2TunePath);
735            }
736            else // process on server
737            {
738              //TBD... list the Artists dirs  and/or the recursed Artist/Album dirs
739              if(tunesHelper == null) tunesHelper = new TunesHelper();
740              tunesHelper.parseTunesDir();
741              Vector <ca.bc.webarts.widgets.tunes.Artist> artists = tunesHelper.getArtists();
742              String currResult = "";
743              System.out.println("\n Tunes Dirpath="+ tunesHelper.getTunesDirPath()+"\n Num Artists="+artists.size());
744
745              for (ca.bc.webarts.widgets.tunes.Artist currArtist: artists)
746              {
747
748                currResult = "\n    <artist name=\""+currArtist.artistName()+"\" artistDirName=\""+currArtist.name(true)+"\" numAlbums="+currArtist.getNumberOfAlbums()+">";
749                for (ca.bc.webarts.widgets.tunes.Album currAlbum: currArtist.getAlbums())
750                {
751                  currResult += "\n      <album name=\""+currAlbum.albumTitle()+"\" albumDirName=\""+currAlbum.name(true)+"\">"+"</album>"; //.replaceAll("<", "&#60;").replaceAll(">", "&#62;")
752                }
753                currResult += "  </artist>\n";
754                cmdResults.add(currResult);
755              }
756            }
757
758          }
759          else // deprecated
760          {
761            // Execute the ssh to pi2
762            //  a bash script is available called 'runOnPi2'   that does this...   /bin/su -s /bin/sh tgutwin -c "uname -a"
763            //String [] cmdParms = {"uname","-a"};
764            String [] cmdParms = {"/home/pi/.local/bin/killer","ogg123"};
765            //cmdResults = Util.executeNativeApp(pi2Cmd, cmdParms, false);
766            Util.executeNativeApp(pi2Cmd, cmdParms, false);
767            System.out.println("\n ***** Now Play a new Tune ***");
768
769            //cmdResults = Util.executeNativeApp(pi2Cmd, cmdParms2, false);
770            Util.executeNativeApp(pi2Cmd, cmdParms2, false);
771
772          }
773
774          StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\"");
775            /*  Add In the response content  */
776            sb.append("  request=\"playOnPi\"");
777            sb.append("  pi2Cmd=\"");
778            sb.append(pi2Cmd);
779            sb.append("\"");
780            sb.append("  cmdParm=\"");
781            sb.append(pi2TunePath);
782            sb.append("\"");
783            sb.append("  piSocketAccess=\"");
784            sb.append(piSocketAccess_);
785            sb.append("\"");
786            if(!piSocketAccess_)
787            {
788              sb.append("  cmdParms2=\"");
789              sb.append(java.util.Arrays.toString(cmdParms2));
790              sb.append("\"");
791            }
792            sb.append(">");
793              sb.append("  <cmdResults numResults=\""+cmdResults.size()+"\">");
794              for( String s: cmdResults) sb.append("\n  <cmdResult>"+s+"</cmdResult>");
795              sb.append("  </cmdResults>");
796            sb.append("</RestResponse>");
797          xmlResponse_=false;
798          restResponse = sb.toString(); // this gets convertedto JSON before sending back
799        }
800        else if(PirateTunesClientThread.getCommandWithNoOptionsList().contains(  restPath.substring((pre+"playOnPi/").length()) ))
801        {
802          pi2TunePath = "";
803          StringBuilder sb = new StringBuilder("<RestResponse succeeded=");     //\"false\"");
804          String suc = "\"true\"";
805            suc = "\"true\"";
806            pi2Cmd = restPath.substring((pre+"playOnPi/").length());
807            System.out.println("TunesAjaxRestListener runOnPi2 - pi2Cmd="+pi2Cmd);
808            cmdResults = sendPiSocketMessage(pi2Cmd);
809          /*  Add In the response content  */
810          sb.append(suc);
811          sb.append("  request=\"playOnPi\"");
812          sb.append("  pi2Cmd=\"");
813          sb.append(pi2Cmd);
814          sb.append("\"");
815          sb.append("  filename=\"");
816          sb.append(pi2TunePath);
817          sb.append("\"");
818          sb.append("  piSocketAccess=\"");
819          sb.append(piSocketAccess_);
820          sb.append("\"");
821          sb.append(">");
822            sb.append("  <cmdResults numResults=\""+cmdResults.size()+"\">");
823            for( String s: cmdResults) sb.append("<cmdResult>"+s+"  </cmdResult>");
824            sb.append("  </cmdResults>");
825          sb.append("</RestResponse>");
826          xmlResponse_=false;
827          restResponse = sb.toString(); // this gets convertedto JSON before sending back
828        }
829        else
830        {
831          StringBuilder sb = new StringBuilder("<RestResponse succeeded=");     //\"false\"");
832          String suc = "\"false\"";
833          /*  Add In the response content  */
834          sb.append(suc);
835          sb.append("  request=\"playOnPi\"");
836          sb.append("  pi2Cmd=\"");
837          sb.append(pi2Cmd);
838          sb.append("\"");
839          sb.append("  cmdParm=\"");
840          sb.append(pi2TunePath);
841          sb.append("\"");
842          sb.append("  piSocketAccess=\"");
843          sb.append(piSocketAccess_);
844          sb.append("\"");
845          sb.append(">");
846            sb.append("  <cmdResults numResults=\""+cmdResults.size()+"\">");
847            for( String s: cmdResults) sb.append("<cmdResult>"+s+"</cmdResult>");
848            sb.append("  </cmdResults>");
849          sb.append("</RestResponse>");
850          xmlResponse_=false;
851          restResponse = sb.toString(); // this gets convertedto JSON before sending back
852
853        }
854      }
855    }
856
857    return restResponse;
858  }
859
860
861  /** Checks if this Request is an AJAX request and prepares the response in XML format.
862    * @param req the servlet request
863    * @return a string holding the response value to be returned OR null if not an expected/Valid AJAX Request
864    **/
865  private String checkForAjaxRequest(HttpServletRequest req) throws ServletException, IOException
866  {
867    String ajaxResponse = "";
868     // was  it a JQuery Ajax call for info...  (such as eagleDBDir_)
869    if (debugOut!=null &&!debugOut.equals("") )
870    {
871        debugOut_ =  Boolean.parseBoolean(debugOut);
872        setDebugOut(debugOut_);
873        //pEye_.setDebugOut(debugOut_);
874        System.out.println("TunesAjaxRestListener.checkForAjaxRequest - debugOut_="+debugOut_);
875        ajaxResponse = ""+debugOut_;
876    }
877    else if (viewPortWidth!=0 )
878    {
879      // do something with the passed parms
880      viewPortWidth_=viewPortWidth;
881      req.getSession().setAttribute(CLIENT_SETTING_NAME_VIEWPORTWIDTH, viewPortWidth);
882      ajaxResponse = ""+viewPortWidth_;
883    }
884    else if (viewPortHeight!=0 )
885    {
886      // do something with the passed parms
887      viewPortHeight_=viewPortHeight;
888      req.getSession().setAttribute(CLIENT_SETTING_NAME_VIEWPORTHEIGHT, viewPortHeight);
889      ajaxResponse = ""+viewPortHeight_;
890    }
891    else if (browserName!=null &&!browserName.equals("") )
892    {
893      req.getSession().setAttribute(CLIENT_SETTING_NAME_BROWSER_NAME, browserName);
894      ajaxResponse = ""+browserName;
895    }
896    else if (browserVersion!=null &&!browserVersion.equals("") )
897    {
898      req.getSession().setAttribute(CLIENT_SETTING_NAME_BROWSER_VERSION, browserVersion);
899      ajaxResponse = ""+browserVersion;
900    }
901    //
902    //  AJAX Calls
903    // Ajax Call - lastFmPlayTrack
904    else if (lastFmPlayTrack!=null && !lastFmPlayTrack.equals("") )
905    {
906      boolean useFullData = true;
907      System.out.println("Ajax Call - lastFmPlayTrack="+lastFmPlayTrack.trim());
908      HttpSession session = req.getSession(true);
909      System.out.println("  ajaxParam Found:  lastFmPlayTrack="+lastFmPlayTrack +"  lastFmPlayArtist="+lastFmPlayArtist.trim()+"  lastFmPlayAlbumArtist="+lastFmPlayAlbumArtist.trim());
910      System.out.println("  TunesAjaxRestListener JSessionID: "+session.getId() );
911
912      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK, lastFmPlayTrack);
913      if (lastFmPlayArtist!=null && !lastFmPlayArtist.equals("") )
914      {
915        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST, lastFmPlayArtist.trim());
916      } else useFullData = false;
917      if (lastFmPlayAlbumArtist!=null && !lastFmPlayAlbumArtist.equals("") )
918      {
919        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM_ARTIST, lastFmPlayAlbumArtist.trim());
920      } else useFullData = false;
921      if (lastFmPlayAlbum!=null && !lastFmPlayAlbum.equals("") )
922      {
923        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM, lastFmPlayAlbum.trim());
924      } else useFullData = false;
925      // Threaded call to send a play notification to last.fm
926      loginLastFM();
927      //HttpSession session = request.getSession();
928      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
929
930      if ( lastFMInit_)
931      {
932        if(useFullData)
933        {
934          tunesHelper.nowPlaying(lastFmPlayArtist.trim(), lastFmPlayTrack.trim(),
935                                 lastFmPlayTime, lastFmPlayDuration,
936                                 lastFmPlayAlbum.trim(), lastFmPlayAlbumArtist.trim(),
937                                 lastFmPlayTrackNum);
938        }
939        else
940        {
941          tunesHelper.nowPlaying(lastFmPlayArtist.trim(), lastFmPlayTrack.trim());
942        }
943        ajaxResponse = ""+lastFmPlayAlbumArtist.trim()+"/"+lastFmPlayTrack.trim();
944      }
945      else
946      {
947        ajaxResponse = "null/null";
948      }
949    }
950    // Ajax Call - lastFmLogin
951    else if (!lastFmLogin.equals(""))
952    {
953      System.out.println("\n\n !*!*!*!*!*!*!*!*!*\nAjax Call - lastFmLogin="+lastFmLogin+"\n !*!*!*!*!*!*!*!*!*");
954      HttpSession session = req.getSession(true);
955      System.out.println("  TunesAjaxRestListener SessionID: "+session.getId() );
956      if (lastFmScrobbleAlbum !=null &&!lastFmScrobbleAlbum.equals("") )
957      {
958        System.out.println("  ajaxParam Found:  lastFmUserID="+lastFmUserID_.trim() +"  lastFmPassword_="+lastFmPassword_.trim());
959        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_USERID, lastFmUserID_.trim());
960      }
961      // Threaded call to send a scrobble notification to last.fm
962      loginLastFM();
963      if ( lastFMInit_ )
964      {
965        ajaxResponse = "SuccessfullLogin/"+lastFmUserID_.trim()+"/"+lastFmPassword_.trim();
966      }
967      else
968      {
969        ajaxResponse = "null/null";
970      }
971      //HttpSession session = request.getSession();
972      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
973    }
974    // Ajax Call - lastFmScrobbleTrack
975    else if (lastFmScrobbleTrack!=null &&!lastFmScrobbleTrack.equals("") && (lastFmLoveTrack==null || lastFmLoveTrack.equals("")))
976    {
977      System.out.println("Ajax Call - lastFmScrobbleTrack="+lastFmScrobbleTrack.trim());
978      HttpSession session = req.getSession(true);
979      System.out.println("  ajaxParam Found:  lastFmScrobbleTrack="+lastFmScrobbleTrack.trim() +"  lastFmScrobbleArtist="+lastFmScrobbleArtist.trim());
980      System.out.println("  TunesAjaxRestListener SessionID: "+session.getId() );
981      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK, lastFmScrobbleTrack);
982      if (lastFmScrobbleAlbum !=null && !lastFmScrobbleAlbum.equals("") )
983      {
984        System.out.println("  ajaxParam Found:  lastFmScrobbleAlbum="+lastFmScrobbleAlbum );
985        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM, lastFmScrobbleAlbum.trim());
986      }
987      if (lastFmScrobbleAlbumArtist !=null && !lastFmScrobbleAlbumArtist.equals("") )
988      {
989        System.out.println("  ajaxParam Found:  lastFmScrobbleAlbumArtist="+lastFmScrobbleAlbumArtist );
990        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM_ARTIST, lastFmScrobbleAlbumArtist.trim());
991      }
992      if (lastFmScrobbleArtist!=null && !lastFmScrobbleArtist.equals("") )
993      {
994        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist.trim());
995      }
996      // Threaded call to send a scrobble notification to last.fm
997      loginLastFM();
998      //HttpSession session = request.getSession();
999      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
1000
1001      if ( lastFMInit_ && !lastFmScrobbleTrack.trim().equals(lastFmPreviousScrobbleTrack))
1002      {
1003        lastFmPreviousScrobbleTrack = lastFmScrobbleTrack.trim();
1004        tunesHelper.scrobble(lastFmScrobbleArtist.trim(), lastFmScrobbleAlbumArtist.trim(), lastFmScrobbleAlbum.trim(), lastFmScrobbleTrack.trim());
1005        ajaxResponse = ""+lastFmScrobbleArtist.trim()+"/"+lastFmScrobbleAlbumArtist.trim()+"/"+lastFmScrobbleAlbum.trim()+"/"+lastFmScrobbleTrack.trim();
1006      }
1007      else
1008      {
1009        ajaxResponse = "null/null";
1010      }
1011    }
1012    // Ajax Call - lastFmLoveTrack
1013    else if (lastFmLoveTrack!=null && !lastFmLoveTrack.equals("") && lastFmScrobbleTrack!=null && !lastFmScrobbleTrack.equals("")  )
1014    {
1015      System.out.println("Ajax Call - lastFmLoveTrack="+lastFmLoveTrack.trim());
1016      HttpSession session = req.getSession(true);
1017      if (lastFmScrobbleArtist!=null &&!lastFmScrobbleArtist.equals("") )
1018      {
1019        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist.trim());
1020      }
1021      System.out.println("  ajaxParam Found:  lastFmLoveTrack="+lastFmLoveTrack.trim()+
1022                                         "lastFmScrobbleTrack="+lastFmScrobbleTrack.trim() +
1023                                      "  lastFmScrobbleArtist="+lastFmScrobbleArtist.trim());
1024      System.out.println("  TunesAjaxRestListener SessionID: "+session.getId() );
1025      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK, lastFmLoveTrack.trim());
1026      // Threaded call to send a love notification to last.fm
1027      loginLastFM();
1028      //HttpSession session = request.getSession();
1029      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
1030
1031      if ( lastFMInit_)
1032      {
1033        //http://10.0.0.253/tunes/ajax/?lastFmLoveTrack=1&lastFmScrobbleTrack=18TillIDie&lastFmScrobbleArtist=BryanAdams
1034        ajaxResponse = tunesHelper.loveTrack(lastFmScrobbleArtist.trim(), lastFmScrobbleTrack.trim());
1035        //ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleTrack;
1036      }
1037      else
1038      {
1039        ajaxResponse = "null/null";
1040      }
1041    }
1042    // Ajax Call - lastFmListLovedTracks
1043    else if (lastFmListLovedTracks!=null && !lastFmListLovedTracks.equals("")  )
1044    {
1045      System.out.println("Ajax Call - lastFmListLovedTracks="+lastFmListLovedTracks.trim());
1046      HttpSession session = req.getSession(true);
1047       System.out.println("  TunesAjaxRestListener SessionID: "+session.getId() );
1048      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_LIST_LOVE_TRACK, lastFmListLovedTracks.trim());
1049      // Threaded call to send a love notification to last.fm
1050      loginLastFM();
1051      //HttpSession session = request.getSession();
1052      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
1053
1054      if ( lastFMInit_)
1055      {
1056        Vector<String> lTks = tunesHelper.retrieveLastFmLovedTrackNames();
1057        ajaxResponse = "[";
1058        int counter = 0; int tot = lTks.size();
1059        for (String currT : lTks)
1060        {
1061          ajaxResponse += "\""+currT+"\"";
1062          if (counter++<(tot-1)) ajaxResponse += "," ;
1063        }
1064        ajaxResponse += "]";
1065
1066        //String [] ltArray = new String[lTks.size()];
1067        //ltArray = (String []) lTks.toArray(ltArray);
1068        //ajaxResponse = java.util.Arrays.toString(ltArray);
1069      }
1070      else
1071      {
1072        ajaxResponse = "null/null";
1073      }
1074    }
1075    // Ajax Call - lastFmUserRecent
1076    else if (lastFmUserRecent!=null && !lastFmUserRecent.equals("") )
1077    {
1078      int numberRecentsReturned = 15;
1079      ajaxResponse = "null/null";
1080      boolean returnHtml = true;
1081      if (dataType!=null && dataType.equalsIgnoreCase("json") ) returnHtml = false;
1082      if (dataType!=null && dataType.equalsIgnoreCase("html") ) returnHtml = true;
1083      String htmlResponse = "";
1084      String jsonResponse = "{";
1085      System.out.println("Ajax Call - lastFmUserRecent="+lastFmUserRecent.trim());
1086      HttpSession session = req.getSession(true);
1087      System.out.println("  TunesAjaxRestListener SessionID: "+session.getId() );
1088      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_USER_RECENT, lastFmUserRecent.trim());
1089      // Threaded call to send a play notification to last.fm
1090      loginLastFM();
1091      //HttpSession session = request.getSession();
1092      session.setAttribute("TunesAjaxRestListener.tunesHelper", tunesHelper);
1093
1094      if ( lastFMInit_)
1095      {
1096        //http://10.0.0.253/tunes/ajax/?lastFmLoveTrack=1&lastFmScrobbleTrack=18TillIDie&lastFmScrobbleArtist=BryanAdams
1097        de.umass.lastfm.Track [] recentTracks = tunesHelper.retrieveLastFmRecentSongs(lastFmUserRecent.trim());
1098        java.util.Vector<String> lovedTrackNames = tunesHelper.getLastFmLovedTrackNames();
1099
1100        // return html
1101        if(recentTracks!=null)
1102        {
1103          int [] trackPlayCount = new int[recentTracks.length];
1104
1105          Date pt = null;
1106          Calendar pc = null;
1107          //java.util.Date nowTime = new java.util.Date();
1108          //String currTime = DateFormat.getDateInstance().format(nowTime);
1109          DateFormat dParser = new SimpleDateFormat("EEE MMM d HH:mm:ss z YYYY");   //Sat May 19 22:03:59 PDT 2018
1110          DateTimeFormatter dtParser = DateTimeFormatter.ofPattern("EEE MMM d HH:mm:ss zzz YYYY");//Sat May 19 22:03:59 PDT 2018
1111          //Date convertedDate = null;
1112          //String output = "";
1113          ZonedDateTime zNow = ZonedDateTime.now();
1114          LocalDateTime lNow = LocalDateTime.now();
1115          LocalDateTime playedLDate = null;;
1116          ZonedDateTime playedZDate = null;
1117          Duration timeSincePlayed = null;
1118          String timeSincePlayedSTR = "";
1119          boolean shade = true;
1120
1121          //ajaxResponse = ""+recentTracks.length+" recent tracks";
1122          htmlResponse += "<img src=\"images/lastfm_SquareLogo.png\" height=\"32\" width=\"32\" style=\"margin-top: 2px; margin-bottom: 2px; margin-left: 9px; margin-right: 20px; float: left;\">"+
1123                         " Recent Tracks: " +
1124                         tunesHelper.retrieveLastFmSessionUsername() + " <img src=\""+tunesHelper.retrieveLastFmUserImageURL()+"\" height=\"32\" width=\"32\">" ;
1125          jsonResponse += "  \"LastFmUsername\": \""+tunesHelper.retrieveLastFmSessionUsername()+"\",\n";
1126          jsonResponse += "  \"LastFmUserImageURL\": \""+tunesHelper.retrieveLastFmUserImageURL()+"\",\n";
1127          jsonResponse += "  \"numberRecentsReturned\": "+numberRecentsReturned+",\n";
1128          jsonResponse += "  \"recentTracks\": [\n";
1129          htmlResponse += "<div id=\"recentTracksTableDiv\"><table class=\"w3-left-align\">";
1130          for (int i=0; i<numberRecentsReturned ; i++)
1131          {
1132            trackPlayCount[i] = tunesHelper.retrieveLastfmTrackPlayCount(recentTracks[i]);
1133            jsonResponse += "                 {"+"\n";
1134            if(!recentTracks[i].isNowPlaying() || i==0)
1135            {
1136              jsonResponse += "                       \"recentNum\": "+(i+1)+",\n";
1137              jsonResponse += "                    \"isNowPlaying\": "+recentTracks[i].isNowPlaying()+",\n";
1138              jsonResponse += "                       \"PlayCount\": "+trackPlayCount[i]+",\n";
1139              pt = recentTracks[i].getPlayedWhen();   //Sat May 19 22:03:59 PDT 2018
1140              //System.out.println( recentTracks[i].getName()+" last played: " + pt);
1141              pc = toCalendar(pt);
1142              //System.out.println( recentTracks[i].getName()+" last calendar: " + pc);
1143              //convertedDate = dParser.parse(playTime);
1144              //timeOutput = formatter.format(convertedDate);
1145              //of(int year, int month, int dayOfMonth, int hour, int minute, int second)
1146              playedLDate =
1147                            LocalDateTime.of(pc.get(Calendar.YEAR) ,
1148                                             pc.get(Calendar.MONTH)+1 ,
1149                                             pc.get(Calendar.DAY_OF_MONTH) ,
1150                                             pc.get(Calendar.HOUR_OF_DAY) ,
1151                                             pc.get(Calendar.MINUTE) ,
1152                                             pc.get(Calendar.SECOND));
1153
1154              //playedLDate = LocalDateTime.parse(playTimeStr, dtParser);
1155              playedZDate = ZonedDateTime.of(playedLDate, zNow.getZone());
1156              //System.out.println( recentTracks[i].getName()+" last playedZDate: " + playedZDate);
1157              timeSincePlayed = Duration.between(playedZDate, zNow);
1158              //System.out.println( recentTracks[i].getName()+" Minutes since played: " + timeSincePlayed.toMinutes());
1159              timeSincePlayedSTR = ""+timeSincePlayed.toMinutes()+" mins";
1160              if(timeSincePlayed.toMinutes()<1) timeSincePlayedSTR = " seconds ago";
1161              else if(timeSincePlayed.toMinutes()>=120) timeSincePlayedSTR = ""+timeSincePlayed.toHours()+" hrs";
1162              else if(timeSincePlayed.toMinutes()>=1440) timeSincePlayedSTR = ""+timeSincePlayed.toDays()+" days";
1163
1164              String animSpeaker =
1165              "\n              <span class=\"si-icon si-icon-volume\" data-icon-name=\"volume\"><svg height=\"64\" version=\"1.1\" width=\"64\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\">\n"+
1166              "                <desc>Created with Snap</desc><defs></defs>\n"+
1167              "                <g>\n"+
1168              "                <path style=\"opacity: 1;\" fill=\"#fff\" d=\"m 37.991813,22.751636 -3.083782,3.083783 c 1.588254,1.572528 2.571897,3.753462 2.571897,6.164853 0,2.410848 -0.983643,4.592324 -2.571897,6.16431 l 3.083782,3.083782 c 2.364757,-2.368011 3.82775,-5.637242 3.82775,-9.248093 0,-3.611392 -1.462993,-6.880083 -3.82775,-9.248636 z\" transform=\"matrix(0,0,0,0,22,32)\"></path>\n"+
1169              "                <path style=\"opacity: 1;\" fill=\"#fff\" d=\"m 50.496123,32 c 0,-6.005432 -2.440672,-11.440956 -6.383379,-15.369023 L 41.05987,19.68385 c 3.149937,3.15319 5.097703,7.506925 5.097703,12.31615 0,4.808682 -1.948309,9.162417 -5.097703,12.31615 l 3.052874,3.052873 C 48.055451,43.440956 50.496123,38.005431 50.496123,32 z\" transform=\"matrix(0,0,0,0,22,32)\"></path>\n"+
1170              "                <path style=\"opacity: 1;\" fill=\"#fff\" d=\"m 59.173768,32.000271 c 0,-8.402181 -3.412386,-16.006711 -8.925453,-21.505137 l -3.068057,3.068056 c 4.72843,4.713247 7.654417,11.233274 7.654417,18.437081 0,7.203264 -2.925987,13.723833 -7.654417,18.436538 l 3.068057,3.068057 c 5.513067,-5.497884 8.925453,-13.102957 8.925453,-21.504595 z\" transform=\"matrix(0,0,0,0,22,32)\"></path>\n"+
1171              "                <polygon fill=\"#fff\" points=\"21.085,66 44,87.208 44,12.791 21.517,34 0,34 0,66 \" transform=\"matrix(0.57539192,0,0,0.57539192,3.3431808,3.2306919)\"></polygon>\n"+
1172              "                </g>\n"+
1173              "              </svg></span>\n";
1174              animSpeaker = "";
1175
1176              if(recentTracks[i].isNowPlaying())
1177              {
1178                jsonResponse += "                 \"TimeSincePlayed\": \"Now Playing\",\n";
1179                timeSincePlayedSTR = "<div>"+
1180                                     "<img src=\"images/equalizerBars_anim.gif\" width=46 height=46 style=\"vertical-align:middle;margin-left: 12px;argin-bottom: 1px;margin-right: 5px;\"> "
1181                                     +animSpeaker+"<!--span style=\"\"><Playing now</span-->"+
1182                                     "</div>";
1183              }
1184              else
1185                jsonResponse += "                 \"TimeSincePlayed\": \""+timeSincePlayedSTR+"\",\n";
1186
1187              jsonResponse += "                  \"LastPlayedWhen\": \""+playedZDate+"\",\n";
1188              jsonResponse += "                          \"Artist\": \""+recentTracks[i].getArtist()+"\",\n";
1189              jsonResponse += "                           \"Album\": \""+recentTracks[i].getAlbum()+"\",\n";
1190              jsonResponse += "                        \"SongName\": \""+recentTracks[i].getName()+"\",\n";
1191              jsonResponse += "                         \"SongURL\": \""+recentTracks[i].getUrl()+"\",\n";
1192              jsonResponse += "                           \"Loved\": \""+lovedTrackNames.contains(recentTracks[i].getName().trim())+"\",\n";
1193
1194              if(recentTracks[i].isNowPlaying())
1195                htmlResponse += "<tr style=\""+"background-color: #88e;"+"\">";
1196              else
1197                htmlResponse += "<tr style=\""+(shade?"background-color: #ccf;":"background-color: #ddf;")+"\">";
1198              htmlResponse += "<td style=\"width: 48px;height: 48px;text-align: center;vertical-align: middle;\"><img src=\""+recentTracks[i].getImageURL(de.umass.lastfm.ImageSize.SMALL)+"\"></td>";
1199              htmlResponse += "<td style=\"height: 48px;\"><b>"+recentTracks[i].getArtist()+"</b>, "+recentTracks[i].getAlbum()+"<br>";
1200              htmlResponse += "<a href=\""+recentTracks[i].getUrl()+"\" target=\"_blank\">"+recentTracks[i].getName()+"</a>";
1201              htmlResponse += "</td>";
1202              htmlResponse += "<td id=\"trackPlayCount\" align=\"right\" style=\"padding: 15px;color: #002266;width: 140px;height: 48px;\">"+trackPlayCount[i];
1203              htmlResponse += (trackPlayCount[i]>1?" plays</td>":" play</td>");
1204              htmlResponse += "<td style=\""+(recentTracks[i].isNowPlaying()?"":"padding: 15px;")+"width: 160px;height: 48px;\">"+timeSincePlayedSTR+"</td>";
1205              htmlResponse += "</tr>";
1206              shade = !shade;
1207              jsonResponse += "              \"AlbumCoverImageURL\": \""+recentTracks[i].getImageURL(de.umass.lastfm.ImageSize.SMALL)+"\"\n";
1208            }
1209            jsonResponse += "                }";
1210            if (i<(numberRecentsReturned-1)) jsonResponse += ",";
1211            jsonResponse += "\n";
1212          }
1213
1214          htmlResponse += "</table></div>";
1215          jsonResponse += "                 ]\n";
1216        }
1217
1218        //ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleTrack;
1219      }
1220      jsonResponse += "}";
1221      ajaxResponse = (returnHtml?htmlResponse:jsonResponse);
1222    }
1223
1224    return ajaxResponse;
1225  }
1226
1227
1228  private static Calendar toCalendar(Date date)
1229  {
1230    Calendar cal = Calendar.getInstance();
1231    if(date!=null) cal.setTime(date);
1232    return cal;
1233  }
1234
1235
1236  private java.util.Vector<String> sendPiSocketMessage(String msg) { return sendPiSocketMessage(msg,""); }
1237  private java.util.Vector<String> sendPiSocketMessage(String msg, String cmdParm)
1238  {
1239    java.util.Vector<String> retVal = new java.util.Vector<String>(); retVal.add("NOT_Sent: "+msg);
1240    TCPSocketClient socketClient = new TCPSocketClient( piTunesTCPSocketServer_, piTunesTCPSocket_);
1241    Vector<String> availCommands = ca.bc.webarts.tools.sockets.PirateTunesClientThread.getCommandList();
1242    try
1243    {
1244
1245      /* Read From The Commandline */
1246      BufferedReader inputLine = null;
1247      inputLine = new BufferedReader(new InputStreamReader(System.in));
1248      String req = "";
1249
1250      if (availCommands.contains(msg.trim()))
1251      {
1252        try
1253        {
1254          if ( //instance.isListening() &&
1255              !msg.trim().equals(""))
1256          {
1257            //req = inputLine.readLine().trim();
1258            req = (msg + " "+cmdParm).trim();
1259            System.out.print("Request:  "+req);
1260            socketClient.initSocket();
1261            if (socketClient.isInit())
1262            {
1263              System.out.println(" being SENT... ");
1264              socketClient.sendAndWait(req, 2500);// Sends and starts it listening here
1265              //socketClient.send(req); // same as sendAndWait(req, 0)
1266              retVal = new java.util.Vector<String>();   //"Sent: "+req;
1267              socketClient.closeSocket();
1268              java.util.Vector<String> responseList = socketClient.getResponseList();
1269              for (String r: responseList)
1270              {
1271
1272                if ( !(    r.startsWith(TCPSocketServer.END)
1273                        || r.startsWith(TCPSocketServer.ERROR)
1274                        || r.startsWith(TCPSocketServer.SUCCESS)
1275                      )
1276                    )
1277                {
1278                  retVal.add(r);
1279                }
1280              }
1281            }
1282            else
1283              System.err.println(" Socket NOT init");
1284          }
1285        }
1286        catch (IOException e)
1287        {
1288          System.err.println("IOException:  " + e);
1289          e.printStackTrace();
1290        }
1291      }
1292      else
1293      {
1294        System.err.println("playOnPi Rest command ERROR: non-standard command");
1295        retVal = new java.util.Vector<String>();
1296        retVal.add("NOT_Sent: "+msg);
1297        retVal.add("  >non-standard command");
1298      }
1299    }
1300    catch (Exception ioEx)
1301    {
1302      ioEx.printStackTrace();
1303    }
1304
1305    return retVal;
1306  }
1307
1308
1309  /**
1310    * Logs into the last.fm session with the already provided user/pass.
1311    * this method uses TunesHelper to do this.
1312    *
1313    * @return success or not
1314    **/
1315  private boolean loginLastFM()
1316  {
1317      if(tunesHelper == null) tunesHelper = new TunesHelper();
1318      /* should probably also use the lastFM user pass sent by some AJAX call. */
1319      System.out.println("\n   TunesAjaxRestListener.loginLastFM()  lastFmUserID_="+lastFmUserID_+"   lastFmPassword_="+lastFmPassword_);
1320      tunesHelper.setLastFmUserID(lastFmUserID_);
1321      tunesHelper.setLastFmPassword(lastFmPassword_);
1322
1323      if (tunesHelper!=null)
1324      {
1325        if (!lastFmSessionKey.equals("") )
1326        {
1327          if (tunesHelper.restoreLastFmUserSession(lastFmSessionKey))
1328          {
1329            lastFMInit_ = true;
1330          }
1331          else
1332          {
1333            System.out.println("\n******** Ajax Login LastFM CANT Restre... re-Initing ");
1334            lastFMInit_ = tunesHelper.initLastFmUserSession(true, lastFmUserID_, lastFmPassword_);
1335          }
1336        }
1337      }
1338      if (!lastFMInit_)System.out.println("\n *************\n  -> TunesAjaxRestListener Login ERROR - Last.fm *************\n");
1339      return lastFMInit_;
1340  }
1341
1342
1343  /** Prepares and returns basic info page about the servlet and server. **/
1344  private void sendServletAdminPage(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
1345  {
1346    ServletOutputStream out = res.getOutputStream();
1347    ServletContext context = getServletContext();
1348    java.util.Properties sysProps = System.getProperties();
1349    TimeZone localTz = Calendar.getInstance().getTimeZone();
1350    int offset = localTz.getRawOffset();
1351    // shift Daylight savings hour if needed
1352    Date dateNow = new Date();
1353    boolean dst = localTz.inDaylightTime(dateNow);
1354    int dstOffset = localTz.getDSTSavings();
1355    //if(dst) offset = offset + dstOffset;
1356
1357    String prop = "";
1358
1359      // Send Back a response to be presented to the users browser
1360      res.setContentType("text/html");
1361      out.println("<html>\n");
1362      out.println("<head>\n");
1363      out.println("<title>Ajax / REST Listener - Servlet Console</title>\n");
1364      out.println("<meta charset=\"UTF-8\">");
1365      out.println("<link rel=\"icon\"");
1366      out.println("     type=\"image/png\"");
1367      out.println("     href=\"images/2blueeighthnotes-16x16.png\" />");
1368      out.println("<style>");
1369      out.println("H1 { font-weight: bolder; background-color: #000033; color: #ffffbb;  font-size: 200%;padding-top: 20px;padding-bottom:20px}");
1370      out.println("H2 { font-weight: bolder; color: #ffff99;  font-size: 160%; text-decoration: underline}");
1371      out.println("H3 { font-weight: bolder; color: #ffff44;  font-size: 140%}");
1372      out.println("H4, H5 { color: #ffffff }");
1373      out.println("A { color: #ffaadd }");
1374      out.println("DT { font-weight: bolder }");
1375      out.println("LI { line-height: 125% }");
1376      out.println("BODY { background-color: #101066; color: #ffff44; font-size: 100%; line-height: 110% }");
1377      out.println(".val { color: #ffffff; font: \"Lucida Console\", Monaco, monospace  }");
1378      out.println("");
1379      out.println("");
1380      out.println("");
1381      out.println("</style>");
1382
1383     //out.println("<META HTTP-EQUIV=\"refresh\" content=\"2;URL=\"javascript:history.go(-2);\">\n");
1384      out.println("</head>\n");
1385      out.println("<body>");
1386      out.println("<h1>Web<i>ARTS</i> Ajax / REST Listener</h1><h2>Servlet Console</h2>");
1387      out.println("  <ul>");
1388      String info = getServletInfo();
1389      out.println("    <li>");
1390      out.println(ca.bc.webarts.widgets.Util.left(info, info.indexOf(".") ).trim());
1391      out.println("    </li>");
1392      out.println("    <li>");
1393      out.println(info.substring(info.indexOf(".")+1 ).trim());
1394      out.println("    </li>");
1395      out.println("  </ul>");
1396      out.println(" <h3>Servlet Parameters</h3>");
1397      out.println("  <ul>");
1398      out.println("    <li>debug          = <span class=\"val\">"+debugOut_+"</span></li>");
1399      out.println("<br />");
1400      out.println("    <li>Servlet Init Params:");
1401      out.println("        <ul>");
1402
1403      java.util.Enumeration <String> initEnum = getInitParameterNames();
1404      String currParamName = "";
1405      for (; initEnum.hasMoreElements();)
1406      {
1407        currParamName = initEnum.nextElement();
1408        out.print("            <li>");
1409        out.print(currParamName+" = <span class=\"val\">"+getInitParameter(currParamName));
1410        out.println("</span></li>");
1411      }
1412      out.println("       </ul>");
1413      out.println("    </li>"); // servlet init params
1414
1415      out.println("<br />");
1416      out.println("    <li>Context Params for "+context.getServletContextName()+":");
1417      out.println("        <ul>");
1418        out.print("          <li>");
1419        out.print("ContextPath"+" = <span class=\"val\">"+context.getContextPath());
1420        out.println("</span></li>");
1421        out.print("          <li>");
1422        out.print("Real Path For Context Root"+" = <span class=\"val\">"+context.getRealPath("/"));
1423        out.println("</span></li>");
1424      out.println("       </ul>");
1425      out.println("  </li>"); // CONTEXT  params
1426
1427      out.println("<br />");
1428      out.println("  <li>Context <b>Init</b> Params:");
1429      out.println("      <ul>");
1430      initEnum = context.getInitParameterNames();
1431      currParamName = "";
1432      for (; initEnum.hasMoreElements();)
1433      {
1434        currParamName = initEnum.nextElement();
1435        out.print("          <li>");
1436        out.print(currParamName+" = <span class=\"val\">"+context.getInitParameter(currParamName));
1437        out.println("</span></li>");
1438      }
1439      out.println("       </ul>");
1440      out.println("    </li>"); // context init params
1441
1442      out.println("  </ul>");
1443
1444      out.println("<br /><br />");
1445      out.println("<h3>Available Rest request commands</h3>");
1446      out.println("  <ul>");
1447      for (int i=0; i<restCommands_.length; i++)
1448      {
1449        out.println("    <li><span class=\"val\">"+restCommands_[i]+"</span></li>");
1450      }
1451      out.println("  </ul>");
1452
1453      out.println("<br /><br />");
1454      out.println("<h3>Available AJAX commands</h3>");
1455      out.println("  <ul>");
1456      for (int i=0; i<ajaxCommands_.length; i++)
1457      {
1458        out.println("    <li><span class=\"val\">"+ajaxCommands_[i]+"</span></li>");
1459      }
1460      out.println("  </ul>");
1461
1462      out.println("<br /><br />");
1463      out.println("<h3>Server Status</h3>");
1464      out.println("  <ul>");
1465      out.println("    <li>Server name = <span class=\"val\">"+req.getServerName()+" ("+req.getLocalAddr()+")</span></li>");
1466      out.println("    <li>Server port = <span class=\"val\">"+req.getServerPort()+"</span></li>");
1467      out.println("    <li>WebApp Engine = <span class=\"val\">"+context.getServerInfo()+"</span></li>");
1468      out.println("    <li>Supported Servlet Version = <span class=\"val\">"+context.getMajorVersion()+"."+context.getMinorVersion()+"</span></li>");
1469
1470      if(context.getMajorVersion()>2 && context.getMinorVersion()>-1)
1471          out.println("      <ul><li>Effective Version = <span class=\"val\">"+
1472                      context.getEffectiveMajorVersion()+"."+context.getEffectiveMinorVersion()+"</span></li></ul>");
1473      out.println("      </li>");
1474      if(context.getMajorVersion()>2 && context.getMinorVersion()>0)
1475      {
1476        try {
1477          out.println("    <li>Logical Hostname = <span class=\"val\">"+context.getVirtualServerName()+"</span></li>");
1478        } catch (Exception ex) {}
1479      }
1480
1481      out.println("    <li>System Properties\n      <ul>");
1482      prop = "java.vendor";
1483      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1484      prop = "java.runtime.version";
1485      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1486      prop = "java.version";
1487      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1488      prop = "os.arch";
1489      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1490      prop = "os.name";
1491      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1492      prop = "os.version";
1493      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1494
1495      out.println("      </ul>\n      </li>");
1496      out.println("  </ul> <!-- Server Status -->");
1497
1498      out.println("<br /><br />");
1499      out.println("<h3>Client Request</h3>");
1500      out.println("  <ul>");
1501      out.println("    <li>Client/proxy Host = <span class=\"val\">"+req.getRemoteHost()+" ("+req.getRemoteAddr()+")</span></li>");
1502      out.println("    <li>Client/proxy port = <span class=\"val\">"+req.getRemotePort()+"</span></li>");
1503      out.println("    <li>Protocol = <span class=\"val\">"+req.getProtocol()+"</span></li>");
1504      out.println("    <li>reqest.PathInfo= <span class=\"val\">"+req.getPathInfo()+"</span></li>");
1505      out.println("  </ul>");
1506
1507      out.println("<br /><br />");
1508      out.println("<h3>Client Parms Status</h3>");
1509      out.println("  <ul>");
1510      out.println("    <li>Browser = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_NAME)+" v"+
1511                  req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_VERSION)+"</span></li>");
1512      out.println("    <li>Device Type = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_DEVICE_TYPE)+"</span></li>");
1513      out.println("    <li>viewport.width = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTWIDTH)+"</span></li>");
1514      out.println("    <li>viewport.height= <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTHEIGHT)+"</span></li>");
1515
1516      Enumeration<String> parmNames = req.getParameterNames();
1517      out.println("    <li><b>Request Parameters</b>\n      <ul>");
1518      for (; parmNames.hasMoreElements();)
1519      {
1520        currParamName = (String) parmNames.nextElement();
1521        out.println("        <li>"+currParamName+"= <span class=\"val\">"+req.getParameter(currParamName)+"</span></li>");
1522      }
1523      out.println("      </ul>\n    </li>");
1524      out.println("  </ul>");
1525
1526      out.println("<br /><br />");
1527      out.println("\n</body>\n</html>");
1528  }
1529}