001/*
002 *  $Id: AjaxRestListener.java 997 2015-08-21 00:06:40Z tgutwin $
003 *  $HeadURL:  $
004 *  $Revision: 997 $
005 *  $LastChangedDate: 2015-08-20 17:06:40 -0700 (Thu, 20 Aug 2015) $
006 *  $LastChangedBy: tgutwin $
007 *  Copyright (c) 2014-2015 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.widgets.ResultSetConverter;
027import ca.bc.webarts.widgets.tunes.TunesHelper;
028
029import com.oreilly.servlet.ParameterParser;
030import com.oreilly.servlet.ParameterNotFoundException;
031import com.oreilly.servlet.ServletUtils;
032
033import java.text.DecimalFormat;
034import java.io.BufferedReader;
035import java.io.File;
036import java.io.FileNotFoundException;
037import java.io.FileOutputStream;
038import java.io.FileReader;
039import java.io.FileWriter;
040import java.io.FileInputStream;
041import java.io.IOException;
042import java.io.InputStream;
043import java.io.OutputStream;
044import java.net.InetAddress;
045import java.net.MalformedURLException;
046import java.net.URL;
047import java.net.UnknownHostException;
048import java.util.Calendar;
049import java.util.Date;
050import java.util.Enumeration;
051import java.util.TimeZone;
052import java.util.zip.ZipEntry;
053import java.util.zip.ZipOutputStream;
054import java.text.DateFormat;
055import java.text.SimpleDateFormat;
056import java.time.Duration;
057import java.time.LocalDateTime;
058import java.time.ZonedDateTime;
059import java.time.format.DateTimeFormatter;
060
061import javax.servlet.*;
062import javax.servlet.http.*;
063
064//import javax.json.Json;
065
066/**
067 * This class is a generic listener, implemented as a servlet, for responding to AJAX and/or Rest style requests.<br />
068 *
069 * @author     tgutwin
070 * @created    April 25, 2016
071 */
072public class AjaxRestListener extends HttpServlet
073{
074  // implements SingleThreadModel
075
076  /** Class constant. **/
077  protected static final String className_ = "AjaxRestListener";
078  private final static String SYSTEM_FILE_SEPERATOR = File.separator;
079  /**  Version String.  */
080  private final static String SERVLET_VERSION = Util.spacesToCapsInString("0.99.RC.5_[$Rev: 1086 $]");
081
082  public static final String CLIENT_SETTING_NAME_VIEWPORTWIDTH = "clientSetting.viewPortWidth";
083  public static final String CLIENT_SETTING_NAME_VIEWPORTHEIGHT = "clientSetting.viewPortHeight";
084  public static final String CLIENT_SETTING_NAME_BROWSER_NAME = "clientSetting.browserName";
085  public static final String CLIENT_SETTING_NAME_BROWSER_VERSION = "clientSetting.browserVersion";
086  public static final String CLIENT_SETTING_NAME_DEVICE_TYPE = "clientSetting.deviceType";
087
088  public static final String CLIENT_SETTING_NAME_LASTFM_SESSION_KEY = "clientSetting.lastFmSession";
089  public static final String CLIENT_SETTING_NAME_LASTFM_USER_RECENT = "clientSetting.lastFmUserRecent";
090  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK_NUM = "clientSetting.lastFmPlayTrackNum";
091  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK = "clientSetting.lastFmPlayTrack";
092  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM = "clientSetting.lastFmPlayAlbum";
093  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST = "clientSetting.lastFmPlayArtist";
094  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM_ARTIST = "clientSetting.lastFmPlayAlbumArtist";
095  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TIME = "clientSetting.lastFmPlayTime";
096  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_DURATION = "clientSetting.lastFmPlayDuration";
097  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK = "clientSetting.lastFmScrobbleTrack";
098  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST = "clientSetting.lastFmScrobbleArtist";
099  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM = "clientSetting.lastFmScrobbleAlbum";
100  public static final String CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK = "clientSetting.lastFmLoveTrack";
101
102  /** DEFAULT Last.FM username to use: admin .**/
103  protected static final String LASTFM_DEFAULT_USERNAME = "tgutwin";  //"${lastfm.user}";
104  /** DEFAULT Last.FM password to use: admin .**/
105  protected static final String LASTFM_DEFAULT_PASSWORD = "~19AcrobatiC19~~~";  //"${lastfm.pass}";
106
107  /**  Build String. (yymmddhhss)  */
108  private final static String BUILD_TAG = "160425183718";
109
110  private static String webServerHostName_ = "warp4.webarts.bc.ca";
111
112  private boolean debugOut_ = true;
113  protected static Log log_= Log.getInstance();
114
115  private int viewPortWidth_ = 0;
116  private int viewPortHeight_ = 0;
117  private String someParamYouWannaSet_ = "";
118
119  /** Control for rest response format XML(default) or JSON. **/
120  private boolean xmlResponse_ = false;
121
122  /** AJAX param - **/
123  private String debugOut = "";
124  private int viewPortWidth = 0;
125  private int viewPortHeight = 0;
126  private String browserName = "";
127  private String browserVersion = "";
128  private String lastFmSessionKey = "";
129  private String lastFmUserRecent = "";
130  private String lastFmPlayTrack = "";
131  private int lastFmPlayTrackNum = 0;
132  private String lastFmPlayAlbum = "";
133  private String lastFmPlayArtist = "";
134  private String lastFmPlayAlbumArtist = "";
135  private int lastFmPlayTime = 0;
136  private int lastFmPlayDuration = 0;
137  private String lastFmScrobbleTrack = "";
138  private String lastFmPreviousScrobbleTrack = "";
139  private String lastFmScrobbleArtist = "";
140  private String lastFmScrobbleAlbum = "";
141  private String lastFmLoveTrack = "";
142  private boolean xmlResponse = xmlResponse_;
143  private TunesHelper tunesHelper = null;
144  private String lastFmUserID_ = LASTFM_DEFAULT_USERNAME;
145  private String lastFmPassword_ = LASTFM_DEFAULT_PASSWORD;
146  private boolean lastFMInit_ = false;
147  private java.util.Vector <de.umass.lastfm.Track > lastFmRecentTracks_ = null;
148
149  /**
150   *  Gets the ServletInfo attribute of the AjaxRestListener object
151   *
152   * @return    The ServletInfo value
153   */
154  public String getServletInfo()
155  {
156    final String methodName = "getServletInfo";
157    return "WebARTS Design AjaxRestListener servlet. Version:" + SERVLET_VERSION +
158        "  Build:" + BUILD_TAG;
159  }
160
161
162  /**
163   * The one time servlet init stuff goes here. It sets the derbyDBDir based on the following prioritized varables:
164   * <ol><li>context init param: derbyDBDir</li><li>servlet init param (from web.xml): derbyDBDir</li>
165   * <li>default hardcoded variable: derbyDBDir</li></ol>If defined in multiple places, the higher priority item will
166   * be used.
167   **/
168  public void init()
169  {
170    System.out.println("\n~~~~~~~~\n~~~~~~~~\nInitializing ca.bc.webarts.servlet.AjaxRestListener\n~~~~~~~~\n~~~~~~~~");
171
172    boolean notFoundInit = true;
173    boolean notFoundContext = true;
174    java.util.Enumeration <String> initEnum = getInitParameterNames();
175    for (; notFoundInit && initEnum.hasMoreElements();)
176    {
177      if(initEnum.nextElement().equals("someParamYouWannaSet"))
178      {
179        notFoundInit = false;
180        /* Do something with the init param */
181        someParamYouWannaSet_ = getInitParameter("someParamYouWannaSet");
182        System.out.println("\n~~~~~~~~\n     INIT ServletParam: someParamYouWannaSet="+someParamYouWannaSet_);
183      }
184    }
185
186    //also check context
187    initEnum = getServletConfig().getServletContext().getInitParameterNames();
188    for (; notFoundContext && initEnum.hasMoreElements();)
189    {
190      if(initEnum.nextElement().equals("someParamYouWannaSet"))
191      {
192        notFoundContext = false;
193        /* Do something with the init param */
194        someParamYouWannaSet_ = getServletConfig().getServletContext().getInitParameter("derbyDBDir");
195        System.out.println("\n~~~~~~~~\n     INIT ContextParam: someParamYouWannaSet="+someParamYouWannaSet_);
196      }
197    }
198
199    if(notFoundInit && notFoundContext )
200    {
201      /* Set the default values */
202      //eagleDBDir_ = pEye_.getDerbyDBDir();
203    }
204
205    try
206    {
207      webServerHostName_ = InetAddress.getLocalHost().getHostName();
208    }
209    catch (UnknownHostException ex)
210    {
211      webServerHostName_ = "red.webarts.bc.ca";
212    }
213  }
214
215
216  /**  Override to close Things  **/
217  public void destroy()
218  {
219    super.destroy();
220  }
221
222
223
224  /**
225   * Returns the value of viewPortWidth_.
226   */
227  public int getViewPortWidth() {
228    return viewPortWidth_;
229  }
230
231
232  /**
233   * Sets the value of viewPortWidth_.
234   * @param viewPortWidth The value to assign viewPortWidth_.
235   */
236  public void setViewPortWidth(int viewPortWidth) {
237    this.viewPortWidth_ = viewPortWidth;
238  }
239
240
241  /**
242   * Returns the value of viewPortHeight_.
243   */
244  public int getViewPortHeight() {
245    return viewPortHeight_;
246  }
247
248
249  /**
250   * Sets the value of viewPortHeight_.
251   * @param viewPortWidth The value to assign viewPortWidth_.
252   */
253  public void setViewPortHeight(int viewPortHeight) {
254    this.viewPortHeight_ = viewPortHeight;
255  }
256
257
258  /**
259   * Returns the value of debugOut_.
260   */
261  public boolean getDebugOut()
262  {
263    return debugOut_;
264  }
265
266
267  /**
268   * Sets the value of debugOut_.
269   * @param debugOut The value to assign debugOut_.
270   */
271  public void setDebugOut(boolean debugOut)
272  {
273    this.debugOut_ = debugOut;
274    log_.setLogLevel((debugOut_?log_.DEBUG:log_.MINOR));
275  }
276
277
278  /**
279    * Get Method for class field 'xmlResponse'.
280    *
281    * @return boolean - The value the class field 'xmlResponse' XML (true) or JSON (false).
282    *
283    **/
284  public boolean getXmlResponse()
285  {
286    return xmlResponse_;
287  }  // getXmlResponse Method
288
289
290  /**
291    * Set Method for class field 'xmlResponse' that controls if teh rest response is in XML (true) or JSON (false).
292    *
293    * @param xmlResponse is the value to set this class field to XML (true) or JSON (false).
294    *
295    **/
296  public  void setXmlResponse(boolean xmlResponse)
297  {
298    this.xmlResponse_ = xmlResponse;
299  }  // setXmlResponse Method
300
301
302  /**
303   * streams to a zipped out stream (without creating a file).
304   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
305   * @param servletOutput is the stream to zip into
306   * @param dataName are the pseudo fileName that will get created in the zip (pseudo file) stream
307   * @param dataStream the data to zip
308   **/
309  private void streamZIP(OutputStream servletOut, String dataName, InputStream dataStream)
310  {
311    String[] dataNames = {dataName};
312    InputStream[] dataStreams = {dataStream};
313    streamZIP(servletOut, dataNames,dataStreams);
314  }
315
316
317  /**
318   * streams to a zipped out stream (without creating a file).
319   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
320   *
321   *  Sample servlet doGet...<br />
322   *  public void doGet(HttpServletRequest request,
323                        HttpServletResponse response) throws IOException{
324        response.setContentType("text/plain");
325        response.setHeader("Content-Disposition",
326                           "attachment;filename=downloadname.txt");
327        String[] dataNames = {dataName};
328        InputStream[] dataStreams = {dataStream};
329        OutputStream os = response.getOutputStream();
330        streamZIP(servletOut, dataNames,dataStreams);
331      }
332   *
333   *
334   * @param servletOutput is the stream to zip into
335   * @param dataNames are the pseudo fileNames that will get created in the zip (pseudo file) stream
336   * @param dataStreams the data to zip
337   **/
338  private void streamZIP(OutputStream servletOut,
339                        String[] dataNames,
340                        InputStream[] dataStreams)
341  {
342    ZipOutputStream zos = new ZipOutputStream(servletOut);
343    final int DATA_BLOCK_SIZE = 2048;
344    int byteCount;
345    byte[] data;
346
347    try
348    {
349      for(int i = 0; i < dataNames.length; i++)
350      {
351        ZipEntry ze = new ZipEntry(dataNames[i]);
352        zos.putNextEntry(ze);
353        data = new byte[DATA_BLOCK_SIZE];
354
355        while((byteCount = dataStreams[i].read(data, 0, DATA_BLOCK_SIZE)) != -1)
356        {
357          zos.write(data, 0, byteCount);
358        }
359
360        zos.flush();
361        zos.closeEntry();
362        dataStreams[i].close();
363      }
364      zos.close();
365    }
366    catch(Exception e)
367    {
368      System.err.println("Problem streaming zip data " + e.toString());
369    }
370  }
371
372
373  /** Parses the REST command string and prepares the response in XML format.
374    * @param restPath is the rest command part of the rest URL
375    * @return a string holding the XML rest response
376    **/
377  private String parseRestRequest(String restPath)
378  {
379    String restResponse = null;
380    System.out.println("REST Request: " +restPath);
381    String pre = "/";
382
383    DecimalFormat dfp = new DecimalFormat( "##0" );
384    DecimalFormat dfe = new DecimalFormat( "###0.000" );
385    if(restPath.equals(pre+"power/current"))
386    {
387      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
388      /*
389      restResponse = "<RestResponse succeeded=\"true\"><power unit=\"watts\" timetamp=\"current\">"+
390                      pEye_.getCurrentDemand()+
391                      "</power></RestResponse>";
392      */
393      sb.append("</RestResponse>");
394    }
395    else if(restPath.equals(pre+"power/recent"))
396    {
397      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
398      /*
399      ca.bc.webarts.tools.rainforest.InstantaneousDemand [] idmf = pEye_.getCurrentReadingsCache();
400      for (int i=0; i< idmf.length; i++)
401      {
402        if(idmf[i]!=null)
403        {
404          sb.append("<power unit=\"watts\" timetamp=\"");
405          sb.append(idmf[i].getLocalTimestampStr());
406          sb.append("\">");
407          sb.append(dfp.format(idmf[i].getCalculatedValue()));
408          sb.append("</power>");
409        }
410      }
411      */
412      sb.append("</RestResponse>");
413      restResponse = sb.toString();
414    }
415    else if(restPath.equals(pre+"energy/recent"))
416    {
417      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
418      /*
419      ca.bc.webarts.tools.rainforest.CurrentSummationDelivered [] csd = pEye_.getCurrentEnergyCache();
420      for (int i=0; i< csd.length; i++)
421      {
422        if(csd[i]!=null)
423        {
424          sb.append("<energy unit=\"wattHours\" timetamp=\"");
425          sb.append(csd[i].getLocalTimestampStr());
426          sb.append("\">");
427          sb.append(dfe.format(csd[i].getCalculatedValue()));
428          sb.append("</energy>");
429        }
430      }
431      */
432      sb.append("</RestResponse>");
433      restResponse = sb.toString();
434    }
435    else if(restPath.equals(pre+"energy/current"))
436    {
437      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
438      /*  Add In the response content  */
439      sb.append("</RestResponse>");
440      restResponse = sb.toString();
441    }
442    else if(restPath.equals(pre+"price/current"))
443    {
444      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
445      /*  Add In the response content  */
446      sb.append("</RestResponse>");
447      restResponse = sb.toString();
448    }
449    else if(restPath.equals(pre+"price/label/current"))
450    {
451      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
452      /*  Add In the response content  */
453      sb.append("</RestResponse>");
454      restResponse = sb.toString();
455    }
456    return restResponse;
457  }
458
459
460  /** Parses any defined servlet params into the defined class vars. **/
461  private void parseParams(HttpServletRequest req)
462  {
463    ParameterParser parser = new ParameterParser(req);
464    if(debugOut_)
465    {
466      System.out.println("\nRequest Params:");
467      Enumeration<String> parmNames = req.getParameterNames();
468      for (; parmNames.hasMoreElements();)
469      {
470        String currParamName = (String) parmNames.nextElement();
471        System.out.println("  "+currParamName+"="+req.getParameter(currParamName));
472      }
473    }
474
475    /* parse out the request params into class vars  */
476    debugOut = parser.getStringParameter("debugOut", "");
477    viewPortWidth =  parser.getIntParameter("viewPortWidth",0);
478    viewPortHeight =  parser.getIntParameter("viewPortHeight",0);
479    browserName = parser.getStringParameter("clientBrowser","");
480    browserVersion = parser.getStringParameter("clientBrowserVersion","");
481
482    lastFmUserID_ = parser.getStringParameter("lastFmUserID",LASTFM_DEFAULT_USERNAME);
483    lastFmPassword_ = parser.getStringParameter("lastFmPassword",LASTFM_DEFAULT_PASSWORD);
484    lastFmSessionKey = parser.getStringParameter("lastFmSessionKey","");
485    lastFmUserRecent = parser.getStringParameter("lastFmUserRecent","");
486
487    lastFmPlayTrackNum = parser.getIntParameter("lastFmPlayTrackNum",0);
488    lastFmPlayTrack = parser.getStringParameter("lastFmPlayTrack","").replace("_"," ").trim();
489    lastFmPlayAlbum = parser.getStringParameter("lastFmPlayAlbum","").replace("_"," ").trim();
490    lastFmPlayArtist = parser.getStringParameter("lastFmPlayArtist","").replace("_"," ").trim();
491    lastFmPlayAlbumArtist = parser.getStringParameter("lastFmPlayAlbumArtist","").replace("_"," ").trim();
492    lastFmPlayTime =  parser.getIntParameter("lastFmPlayTime",0);
493    lastFmPlayDuration =  parser.getIntParameter("lastFmPlayDuration",0);
494
495    lastFmScrobbleTrack = parser.getStringParameter("lastFmScrobbleTrack","").replace("_"," ").trim();
496    lastFmScrobbleAlbum = parser.getStringParameter("lastFmScrobbleAlbum","").replace("_"," ").trim();
497    lastFmScrobbleArtist = parser.getStringParameter("lastFmScrobbleArtist","").replace("_"," ").trim();
498    lastFmLoveTrack = parser.getStringParameter("lastFmLoveTrack","").replace("_"," ").trim();
499    xmlResponse = parser.getBooleanParameter("xmlResponse",xmlResponse_);
500
501    if(debugOut_) { System.out.println("  PlayTrack="+lastFmPlayTrack+"   lastFmPlayArtist="+lastFmPlayArtist +"   lastFmPlayAlbumArtist="+lastFmPlayAlbumArtist );}
502    if(debugOut_) { System.out.println("  ScrobbleTrack="+lastFmScrobbleTrack+"   lastFmScrobbleAlbum="+lastFmScrobbleAlbum+"   lastFmScrobbleArtist="+lastFmScrobbleArtist );}
503
504   }
505
506
507  /** Convert a well-formed (but not necessarily valid) XML string into a JSON String. **/
508  private String xmlToJson(String xml)
509  {
510     String jsonStr = "";
511     if (xml== null || xml.length()<2) xml = "";
512     org.json.JSONObject jsonObj = org.json.XML.toJSONObject(xml);
513     if(jsonObj!= null) jsonStr = jsonObj.toString(2);
514     return jsonStr;
515   }
516
517
518  /**
519   *  This method handles the "GET" submission - it is used for Ajax calls to set webApp parameters using JQuery.
520   *
521   * @param  req                   Description of the Parameter
522   * @param  res                   Description of the Parameter
523   * @exception  ServletException  Description of the Exception
524   * @exception  IOException       Description of the Exception
525   */
526  public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
527  {
528    parseParams(req);
529
530    ServletOutputStream out = res.getOutputStream();
531    String conPath = req.getContextPath();
532    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
533
534
535    if(debugOut_)
536    {
537      System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~");
538      System.out.println("Tunes AjaxRestListener.doGet "+ "   debugOut="+debugOut_);
539      System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
540    }
541
542    boolean ajaxCall = false;
543    boolean restRequest = false;
544    String ajaxResponse = "";
545    String restResponse = null;
546
547    res.setStatus(HttpServletResponse.SC_OK);  // default ids OK,, set again lower downif NOT
548
549    ajaxResponse = checkForAjaxRequest(req);
550    if (!ajaxResponse.equals("")) ajaxCall=true;
551
552    if(!ajaxCall) restResponse = checkForRestRequest(req);
553    if (restResponse!=null && !restResponse.equals("")) restRequest=true;
554
555    if(debugOut_) { System.out.println("ajaxCall="+ajaxCall+"   restRequest="+restRequest );}
556    /* * * * * *  * * * * * EMPTY * * * * *  * * * * *  */
557    /* ***********************************************  */
558    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
559    {
560      sendServletAdminPage(req, res);
561    }
562
563    if(ajaxCall)
564    {
565      res.setContentType("text/plain");
566      // https://stackoverflow.com/questions/5750696/how-to-get-a-cross-origin-resource-sharing-cors-post-request-working
567      res.setHeader("Access-Control-Allow-Origin", "*");
568      if(out!=null  )
569      {
570        if (debugOut_)
571          System.out.println("AJAX REPLY("+ajaxResponse.length()+")="+ajaxResponse);
572        out.print(ajaxResponse);
573      }
574      else
575        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
576    }
577    else if(restRequest)
578    {
579      if (xmlResponse_) res.setContentType("text/xml");
580      else res.setContentType("application/javascript");
581      if(restResponse!=null && !restResponse.equals(""))
582      {
583        res.setStatus(HttpServletResponse.SC_OK);
584      }
585      else
586      {
587        res.setStatus(HttpServletResponse.SC_NOT_FOUND);
588        restResponse = "<RestResponse succeeded=\"false\"><status>404</status></RestResponse>";
589      }
590      if(out!=null  )
591      {
592        if (!xmlResponse_) restResponse = xmlToJson(restResponse);
593        if (debugOut_)
594          System.out.println("REST REPLY("+restResponse.length()+")="+restResponse);
595        out.print(restResponse);
596      }
597      else
598        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
599    }
600    out.flush();
601    res.flushBuffer();
602  }
603
604
605  /**
606   *  This method handles the "POST" submissions.
607   *
608   * @param  req                   Description of Parameter
609   * @param  res                   Description of Parameter
610   * @exception  ServletException  Description of Exception
611   * @exception  IOException       Description of Exception
612   */
613  public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
614  {
615    //parseParams(req);
616
617    StringBuffer postBody = new StringBuffer("");
618    String line = null;
619   // try
620   // {
621      BufferedReader reader = req.getReader();
622      while ((line = reader.readLine()) != null)
623        postBody.append(line+"\n");
624
625      String postReply = "";
626
627      // Do something with the posted message
628      // send it to a helper class if desired
629      /*String postReply = pEye_.newMessage(postBody.toString()); */
630
631      // send back an empty reply
632      if ((postReply==null || postReply.equals("")))
633      {
634        // send back a 200 with an empty body
635        res.setStatus(HttpServletResponse.SC_OK);
636        res.setContentLength(0);
637        postReply = "";
638        if (debugOut_) System.out.println("AjaxRestListener.doPost: emptyreply from newMessage");
639      }
640      /*
641      else if(!sendFastPoll_)
642      {
643        // send back the pEyeReply as the body
644        res.setStatus(HttpServletResponse.SC_OK);
645        res.setContentLength(pEyeReply.length());
646        if (debugOut_) System.out.println("AjaxRestListener.doPost: SLOW Poll & newMessage reply existed");
647      }
648      else // if(sendFastPoll_)
649      {
650        MessageCommand mc = new MessageCommand(pEye_.getMeterMacId());
651        pEyeReply = mc.getSetFastPollCommandStr(fastPollFreq_,fastPollDur_);
652
653        res.setContentLength(0);   // send back the setFastPollCommand string as the body
654        //if (debugOut_) System.out.println(" *!*!*!*!  AjaxRestListener.doPost: Send FAST Poll to:"+pEye_.getMeterMacId() );
655
656        // now reset fastPoll for next message
657        sendFastPoll_ = false;
658      }
659      */
660
661      postReply = "\n"+postReply;
662      res.setContentType("application/xml");
663      res.setStatus(HttpServletResponse.SC_OK);
664      res.setContentLength(postReply.length());
665
666      ServletOutputStream out = res.getOutputStream();
667      if(out!=null  )
668      {
669        if (debugOut_) System.out.println("Attempting to HttpServletResponse REPLY("+postReply.length()+")="+postReply);
670        out.print(postReply);
671        out.flush();
672      }
673      else
674        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
675
676      res.flushBuffer();
677
678      /*
679    }
680    catch (Exception e)
681    {
682      //report an error
683      e.printStackTrace();
684    }
685    */
686  }
687
688
689  /** Parses the REST command string and prepares the response in XML format.
690    * @param restPath is the rest command part of the rest URL
691    * @return a string holding the XML rest response OR null if not a REST Request
692    **/
693  private String checkForRestRequest(HttpServletRequest req) throws ServletException, IOException
694  {
695    String restResponse = null;
696    String servletPath = req.getServletPath();
697    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
698    String pre = "/";
699    boolean returnXml = xmlResponse_; // false means JSON
700
701    if (servletPath.equals("/rest") && restPath!=null && !restPath.equals("") )
702    {
703
704      DecimalFormat dfp = new DecimalFormat( "##0" );
705      DecimalFormat dfe = new DecimalFormat( "###0.000" );
706      if(restPath.equals(pre+"test"))
707      {
708        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
709        /*  Add In the response content  */
710        sb.append("<testNode className=\"");
711        sb.append(className_);
712        sb.append("\">");
713        sb.append("Hello");
714        sb.append("</testNode>");
715        sb.append("</RestResponse>");
716        restResponse = sb.toString();
717      }
718      if(restPath.equals(pre+"power/current"))
719      {
720        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
721        /*  Add In the response content  */
722        sb.append("</RestResponse>");
723        restResponse = sb.toString();
724      }
725      else if(restPath.equals(pre+"power/recent"))
726      {
727        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
728        /*  Add In the response content  */
729        sb.append("</RestResponse>");
730        restResponse = sb.toString();
731      }
732      else if(restPath.equals(pre+"energy/recent"))
733      {
734        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
735        /*  Add In the response content  */
736        sb.append("</RestResponse>");
737        restResponse = sb.toString();
738      }
739      else if(restPath.equals(pre+"energy/current"))
740      {
741        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
742        /*  Add In the response content  */
743        sb.append("</RestResponse>");
744        restResponse = sb.toString();
745      }
746      else if(restPath.equals(pre+"price/current"))
747      {
748        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
749        /*  Add In the response content  */
750        sb.append("</RestResponse>");
751        restResponse = sb.toString();
752      }
753      else if(restPath.equals(pre+"price/label/current"))
754      {
755        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
756        /*  Add In the response content  */
757        sb.append("</RestResponse>");
758        restResponse = sb.toString();
759      }
760
761      // Database Queries
762      /*
763          - Dump the database tables directly to (JSON or ) XML using a REST call:
764            http://red.webarts.bc.ca:8080/powereye/rest/db/table/<tablename>
765              <tablename> can be reading, energy, load_device, load_type
766
767      or with the optional <number of rows>
768         positive number means sorted ascending (oldest 1st)
769         negative number means sorted descending (newest 1st)
770            http://red.webarts.bc.ca:8080/powereye/rest/db/table/<tablename>/100
771
772      These database calls are LOOOOooong so you might want to use a commandline URL query tool like
773      <a href="http://www.gnu.org/software/wget/">wget</a>, or perl or java or whatever.
774      */
775      else if(restPath.startsWith(pre+"db/table/"))
776      {
777        returnXml = true; // false means JSON
778        String content = "";
779        try
780        {
781          restResponse = "<RestResponse succeeded=\"false\"></RestResponse>";
782          int tblIndex = (pre+"db/table/").length();
783          int numRowsIndex = restPath.indexOf("/",tblIndex)+1; // /rest/db/table/reading/99
784          boolean numRowsWasSpecified = (numRowsIndex>0);
785          int numReadings =  (numRowsWasSpecified?Integer.parseInt(restPath.substring(numRowsIndex)):0); //0 means send all
786          boolean sortAscendingByDate = false;           // sort default is descending
787          if(numReadings<0) // negative numbers means ascending
788          {
789            sortAscendingByDate = true;
790            numReadings *= -1; // now normalize it to be a psitive rowCount
791          }
792          String tableName = (numRowsWasSpecified?restPath.substring(tblIndex,numRowsIndex-1):restPath.substring(tblIndex));
793
794          System.out.println("Dumping DB Table: "+tableName);
795          if(numRowsWasSpecified) System.out.println("         numRows: "+numReadings +" "+
796                                                    (sortAscendingByDate?"ascending":"descending"));
797          java.sql.ResultSet rs = null;
798          if(tableName.equalsIgnoreCase("reading"))
799            if(numRowsWasSpecified)
800            {
801              //rs = pEye_.dumpReadingTableToRS(sortAscendingByDate,(numReadings!=0?numReadings:-1));//dumpReadingTableToRS();
802            }
803            else
804            {
805              //rs = pEye_.dumpReadingTableToRS();
806            }
807          else if(tableName.equalsIgnoreCase("energy"))
808            if(numRowsWasSpecified)
809            {
810              //rs = pEye_.dumpEnergyTableToRS(sortAscendingByDate,(numReadings!=0?numReadings:-1));//dumpEnergyTableToRS();
811            }
812            else
813            {
814              //rs = pEye_.dumpEnergyTableToRS();
815            }
816          else if(tableName.equalsIgnoreCase("load_type"))
817            if(numRowsWasSpecified)
818            {
819              //rs = pEye_.dumpDBTableToRS("load_type",numReadings);//dumpLoadTypeTableToRS();
820            }
821            else
822            {
823              //rs = pEye_.dumpLoadTypeTableToRS();
824            }
825          else if(tableName.equalsIgnoreCase("load_device"))
826            if(numRowsWasSpecified)
827            {
828              //rs = pEye_.dumpDBTableToRS("load_device",numReadings);//dumpLoadDeviceTableToRS();
829            }
830            else
831            {
832              //rs = pEye_.dumpLoadDeviceTableToRS();
833            }
834
835          if(rs!=null)
836          {
837            ResultSetConverter rsc = new ResultSetConverter(rs);
838            if (returnXml) content = rsc.convertToXml().toXML().substring("<?xml version=\"1.0\"?>".length());
839            else content = rsc.convertToJson().toString();
840            StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
841            /*  Add In the response content  */
842              sb.append("<db table=\""+tableName+"\">");
843              sb.append(content);
844              sb.append("</db>");
845
846            sb.append("</RestResponse>");
847            restResponse = sb.toString();
848          }
849        }
850        catch(Exception ioEx)
851        {
852          restResponse = "<RestResponse succeeded=\"false\"></RestResponse>";
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("AjaxRestListener.doGet - 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    // Ajax Call - lastFmPlayTrack
902    else if (lastFmPlayTrack!=null && !lastFmPlayTrack.equals("") )
903    {
904      boolean useFullData = true;
905      System.out.println("Ajax Call - lastFmPlayTrack="+lastFmPlayTrack.trim());
906      HttpSession session = req.getSession(true);
907      System.out.println("  ajaxParam Found:  lastFmPlayTrack="+lastFmPlayTrack +"  lastFmPlayArtist="+lastFmPlayArtist.trim()+"  lastFmPlayAlbumArtist="+lastFmPlayAlbumArtist.trim());
908      System.out.println("  AjaxRestListener JSessionID: "+session.getId() );
909
910      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK, lastFmPlayTrack);
911      if (lastFmPlayArtist!=null && !lastFmPlayArtist.equals("") )
912      {
913        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST, lastFmPlayArtist.trim());
914      } else useFullData = false;
915      if (lastFmPlayAlbumArtist!=null && !lastFmPlayAlbumArtist.equals("") )
916      {
917        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM_ARTIST, lastFmPlayAlbumArtist.trim());
918      } else useFullData = false;
919      if (lastFmPlayAlbum!=null && !lastFmPlayAlbum.equals("") )
920      {
921        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM, lastFmPlayAlbum.trim());
922      } else useFullData = false;
923      // Threaded call to send a play notification to last.fm
924      loginLastFM();
925      //HttpSession session = request.getSession();
926      session.setAttribute("AjaxRestListener.tunesHelper", tunesHelper);
927
928      if ( lastFMInit_)
929      {
930        if(useFullData)
931        {
932          tunesHelper.nowPlaying(lastFmPlayArtist.trim(), lastFmPlayTrack.trim(),
933                                 lastFmPlayTime, lastFmPlayDuration,
934                                 lastFmPlayAlbum.trim(), lastFmPlayAlbumArtist.trim(),
935                                 lastFmPlayTrackNum);
936        }
937        else
938        {
939          tunesHelper.nowPlaying(lastFmPlayArtist.trim(), lastFmPlayTrack.trim());
940        }
941        ajaxResponse = ""+lastFmPlayAlbumArtist.trim()+"/"+lastFmPlayTrack.trim();
942      }
943      else
944      {
945        ajaxResponse = "null/null";
946      }
947    }
948    // Ajax Call - lastFmScrobbleTrack
949    else if (lastFmScrobbleTrack!=null &&!lastFmScrobbleTrack.equals("") && (lastFmLoveTrack==null || lastFmLoveTrack.equals("")))
950    {
951      System.out.println("Ajax Call - lastFmScrobbleTrack="+lastFmScrobbleTrack.trim());
952      HttpSession session = req.getSession(true);
953      System.out.println("  ajaxParam Found:  lastFmScrobbleTrack="+lastFmScrobbleTrack.trim() +"  lastFmScrobbleArtist="+lastFmScrobbleArtist.trim());
954      System.out.println("  AjaxRestListener SessionID: "+session.getId() );
955      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK, lastFmScrobbleTrack);
956      if (lastFmScrobbleAlbum !=null &&!lastFmScrobbleAlbum.equals("") )
957      {
958        System.out.println("  ajaxParam Found:  lastFmScrobbleAlbum="+lastFmScrobbleAlbum );
959        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM, lastFmScrobbleAlbum.trim());
960      }
961      if (lastFmScrobbleArtist!=null &&!lastFmScrobbleArtist.equals("") )
962      {
963        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist.trim());
964      }
965      // Threaded call to send a scrobble notification to last.fm
966      loginLastFM();
967      //HttpSession session = request.getSession();
968      session.setAttribute("AjaxRestListener.tunesHelper", tunesHelper);
969
970      if ( lastFMInit_ && !lastFmScrobbleTrack.trim().equals(lastFmPreviousScrobbleTrack))
971      {
972        lastFmPreviousScrobbleTrack = lastFmScrobbleTrack.trim();
973        tunesHelper.scrobble(lastFmScrobbleArtist.trim(), lastFmScrobbleAlbum.trim(), lastFmScrobbleTrack.trim());
974        ajaxResponse = ""+lastFmScrobbleArtist.trim()+"/"+lastFmScrobbleAlbum.trim()+"/"+lastFmScrobbleTrack.trim();
975      }
976      else
977      {
978        ajaxResponse = "null/null";
979      }
980    }
981    // Ajax Call - lastFmLoveTrack
982    else if (lastFmLoveTrack!=null && !lastFmLoveTrack.equals("") && lastFmScrobbleTrack!=null && !lastFmScrobbleTrack.equals("")  )
983    {
984      System.out.println("Ajax Call - lastFmLoveTrack="+lastFmLoveTrack.trim());
985      HttpSession session = req.getSession(true);
986      if (lastFmScrobbleArtist!=null &&!lastFmScrobbleArtist.equals("") )
987      {
988        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist.trim());
989      }
990      System.out.println("  ajaxParam Found:  lastFmLoveTrack="+lastFmLoveTrack.trim()+
991                                         "lastFmScrobbleTrack="+lastFmScrobbleTrack.trim() +
992                                      "  lastFmScrobbleArtist="+lastFmScrobbleArtist.trim());
993      System.out.println("  AjaxRestListener SessionID: "+session.getId() );
994      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK, lastFmLoveTrack.trim());
995      // Threaded call to send a love notification to last.fm
996      loginLastFM();
997      //HttpSession session = request.getSession();
998      session.setAttribute("AjaxRestListener.tunesHelper", tunesHelper);
999
1000      if ( lastFMInit_)
1001      {
1002        //http://10.0.0.253/tunes/ajax/?lastFmLoveTrack=1&lastFmScrobbleTrack=18TillIDie&lastFmScrobbleArtist=BryanAdams
1003        ajaxResponse = tunesHelper.loveTrack(lastFmScrobbleArtist.trim(), lastFmScrobbleTrack.trim());
1004        //ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleTrack;
1005      }
1006      else
1007      {
1008        ajaxResponse = "null/null";
1009      }
1010    }
1011    // Ajax Call - lastFmUserRecent
1012    else if (lastFmUserRecent!=null && !lastFmUserRecent.equals("") )
1013    {
1014      ajaxResponse = "null/null";
1015      System.out.println("Ajax Call - lastFmUserRecent="+lastFmUserRecent.trim());
1016      HttpSession session = req.getSession(true);
1017      System.out.println("  AjaxRestListener SessionID: "+session.getId() );
1018      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_USER_RECENT, lastFmUserRecent.trim());
1019      // Threaded call to send a play notification to last.fm
1020      loginLastFM();
1021      //HttpSession session = request.getSession();
1022      session.setAttribute("AjaxRestListener.tunesHelper", tunesHelper);
1023
1024      if ( lastFMInit_)
1025      {
1026        //http://10.0.0.253/tunes/ajax/?lastFmLoveTrack=1&lastFmScrobbleTrack=18TillIDie&lastFmScrobbleArtist=BryanAdams
1027        de.umass.lastfm.Track [] recentTracks = tunesHelper.retrieveLastFmRecentSongs(lastFmUserRecent.trim());
1028
1029        // return html
1030        if(recentTracks!=null)
1031        {
1032          int [] trackPlayCount = new int[recentTracks.length];
1033
1034          Date pt = null;
1035          Calendar pc = null;
1036          //java.util.Date nowTime = new java.util.Date();
1037          //String currTime = DateFormat.getDateInstance().format(nowTime);
1038          DateFormat dParser = new SimpleDateFormat("EEE MMM d HH:mm:ss z YYYY");   //Sat May 19 22:03:59 PDT 2018
1039          DateTimeFormatter dtParser = DateTimeFormatter.ofPattern("EEE MMM d HH:mm:ss zzz YYYY");//Sat May 19 22:03:59 PDT 2018
1040          //Date convertedDate = null;
1041          //String output = "";
1042          ZonedDateTime zNow = ZonedDateTime.now();
1043          LocalDateTime lNow = LocalDateTime.now();
1044          LocalDateTime playedLDate = null;;
1045          ZonedDateTime playedZDate = null;
1046          Duration timeSincePlayed = null;
1047          String timeSincePlayedSTR = "";
1048          boolean shade = true;
1049
1050          //ajaxResponse = ""+recentTracks.length+" recent tracks";
1051          ajaxResponse = "<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;\">"+
1052                         " Recent Tracks: " +
1053                         tunesHelper.retrieveLastFmSessionUsername() +
1054                         " <img src=\""+tunesHelper.retrieveLastFmUserImageURL()+
1055                         "\" height=\"32\" width=\"32\">" ;
1056          ajaxResponse += "<div id=\"recentTracksTableDiv\"><table class=\"w3-left-align\">";
1057          for (int i=0; i<15 ; i++)
1058          {
1059            trackPlayCount[i] = tunesHelper.retrieveLastfmTrackPlayCount(recentTracks[i]);
1060            if(!recentTracks[i].isNowPlaying() || i==0)
1061            {
1062              pt = recentTracks[i].getPlayedWhen();   //Sat May 19 22:03:59 PDT 2018
1063              //System.out.println( recentTracks[i].getName()+" last played: " + pt);
1064              pc = toCalendar(pt);
1065              //System.out.println( recentTracks[i].getName()+" last calendar: " + pc);
1066              //convertedDate = dParser.parse(playTime);
1067              //timeOutput = formatter.format(convertedDate);
1068              //of(int year, int month, int dayOfMonth, int hour, int minute, int second)
1069              playedLDate =
1070                            LocalDateTime.of(pc.get(Calendar.YEAR) ,
1071                                             pc.get(Calendar.MONTH)+1 ,
1072                                             pc.get(Calendar.DAY_OF_MONTH) ,
1073                                             pc.get(Calendar.HOUR_OF_DAY) ,
1074                                             pc.get(Calendar.MINUTE) ,
1075                                             pc.get(Calendar.SECOND));
1076
1077              //playedLDate = LocalDateTime.parse(playTimeStr, dtParser);
1078              playedZDate = ZonedDateTime.of(playedLDate, zNow.getZone());
1079              //System.out.println( recentTracks[i].getName()+" last playedZDate: " + playedZDate);
1080              timeSincePlayed = Duration.between(playedZDate, zNow);
1081              //System.out.println( recentTracks[i].getName()+" Minutes since played: " + timeSincePlayed.toMinutes());
1082              timeSincePlayedSTR = ""+timeSincePlayed.toMinutes()+" mins";
1083              if(timeSincePlayed.toMinutes()<1) timeSincePlayedSTR = " seconds ago";
1084              else if(timeSincePlayed.toMinutes()>=120) timeSincePlayedSTR = ""+timeSincePlayed.toHours()+" hrs";
1085              else if(timeSincePlayed.toMinutes()>=1440) timeSincePlayedSTR = ""+timeSincePlayed.toDays()+" days";
1086
1087              String animSpeaker =
1088              "\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"+
1089              "                <desc>Created with Snap</desc><defs></defs>\n"+
1090              "                <g>\n"+
1091              "                <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"+
1092              "                <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"+
1093              "                <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"+
1094              "                <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"+
1095              "                </g>\n"+
1096              "              </svg></span>\n";
1097              animSpeaker = "";
1098
1099              if(recentTracks[i].isNowPlaying())
1100                timeSincePlayedSTR = "<div>"+
1101                                     "<img src=\"images/equalizerBars_anim.gif\" width=46 height=46 style=\"vertical-align:middle;margin-left: 12px;argin-bottom: 1px;margin-right: 5px;\"> "
1102                                     +animSpeaker+"<!--span style=\"\"><Playing now</span-->"+
1103                                     "</div>";
1104
1105              if(recentTracks[i].isNowPlaying())
1106                ajaxResponse += "<tr style=\""+"background-color: #88e;"+"\">";
1107              else
1108                ajaxResponse += "<tr style=\""+(shade?"background-color: #ccf;":"background-color: #ddf;")+"\">";
1109              ajaxResponse += "<td style=\"width: 48px;height: 48px;text-align: center;vertical-align: middle;\"><img src=\""+recentTracks[i].getImageURL(de.umass.lastfm.ImageSize.SMALL)+"\"></td>";
1110              ajaxResponse += "<td style=\"height: 48px;\"><b>"+recentTracks[i].getArtist()+"</b>, "+recentTracks[i].getAlbum()+"<br>";
1111              ajaxResponse += "<a href=\""+recentTracks[i].getUrl()+"\" target=\"_blank\">"+recentTracks[i].getName()+"</a>";
1112              ajaxResponse += "</td>";
1113              ajaxResponse += "<td id=\"trackPlayCount\" align=\"right\" style=\"padding: 15px;color: #002266;width: 140px;height: 48px;\">"+trackPlayCount[i];
1114              ajaxResponse += (trackPlayCount[i]>1?" plays</td>":" play</td>");
1115              ajaxResponse += "<td style=\""+(recentTracks[i].isNowPlaying()?"":"padding: 15px;")+"width: 160px;height: 48px;\">"+timeSincePlayedSTR+"</td>";
1116              ajaxResponse += "</tr>";
1117              shade = !shade;
1118            }
1119          }
1120
1121          ajaxResponse += "</table></div>";
1122        }
1123
1124        //ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleTrack;
1125      }
1126    }
1127
1128    return ajaxResponse;
1129  }
1130
1131
1132  private static Calendar toCalendar(Date date)
1133  {
1134    Calendar cal = Calendar.getInstance();
1135    if(date!=null) cal.setTime(date);
1136    return cal;
1137  }
1138
1139  /**
1140    * Logs into the last.fm session with the already provided user/pass.
1141    * this method uses TunesHelper to do this.
1142    *
1143    * @return success or not
1144    **/
1145  private boolean loginLastFM()
1146  {
1147      if(tunesHelper == null) tunesHelper = new TunesHelper();
1148      /* should probably also use the lastFM user pass sent by some AJAX call. */
1149      System.out.println("\n   AjaxRestListener.loginLastFM()  lastFmUserID_="+lastFmUserID_+"   lastFmPassword_="+lastFmPassword_);
1150      tunesHelper.setLastFmUserID(lastFmUserID_);
1151      tunesHelper.setLastFmPassword(lastFmPassword_);
1152
1153      if (tunesHelper!=null)
1154      {
1155        if (!lastFmSessionKey.equals("") )
1156        {
1157          if (tunesHelper.restoreLastFmUserSession(lastFmSessionKey))
1158          {
1159            lastFMInit_ = true;
1160          }
1161          else
1162          {
1163            System.out.println("\n******** Ajax Login LastFM CANT Restre... re-Initing ");
1164            lastFMInit_ = tunesHelper.initLastFmUserSession(true, lastFmUserID_, lastFmPassword_);
1165          }
1166        }
1167      }
1168      if (!lastFMInit_)System.out.println("\n *************\n  -> AjaxRestListener Login ERROR - Last.fm *************\n");
1169      return lastFMInit_;
1170  }
1171
1172
1173  /** Prepares and returns basic info page about the server. **/
1174  private void sendServletAdminPage(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
1175  {
1176    ServletOutputStream out = res.getOutputStream();
1177    ServletContext context = getServletContext();
1178    java.util.Properties sysProps = System.getProperties();
1179    TimeZone localTz = Calendar.getInstance().getTimeZone();
1180    int offset = localTz.getRawOffset();
1181    // shift Daylight savings hour if needed
1182    Date dateNow = new Date();
1183    boolean dst = localTz.inDaylightTime(dateNow);
1184    int dstOffset = localTz.getDSTSavings();
1185    //if(dst) offset = offset + dstOffset;
1186
1187    String prop = "";
1188
1189      // Send Back a response to be presented to the users browser
1190      res.setContentType("text/html");
1191      out.println("<html>\n");
1192      out.println("<head>\n");
1193      out.println("<title>Ajax / REST Listener - Servlet Console</title>\n");
1194      out.println("<link rel=\"icon\"");
1195      out.println("     type=\"image/png\"");
1196      out.println("     href=\"images/20x20/Eagle_sml.png\" />");
1197      out.println("<style>");
1198      out.println("H1 { font-weight: bolder; background-color: #000033; color: #ffffbb;  font-size: 200%}");
1199      out.println("H2 { font-weight: bolder; color: #ffff99;  font-size: 160%; text-decoration: underline}");
1200      out.println("H3 { font-weight: bolder; color: #ffff44;  font-size: 140%}");
1201      out.println("H4, H5 { color: #ffffff }");
1202      out.println("A { color: #ffaadd }");
1203      out.println("DT { font-weight: bolder }");
1204      out.println("LI { line-height: 125% }");
1205      out.println("BODY { background-color: #101066; color: #ffff44; font-size: 100%; line-height: 110% }");
1206      out.println(".val { color: #ffffff; font: \"Lucida Console\", Monaco, monospace  }");
1207      out.println("");
1208      out.println("");
1209      out.println("");
1210      out.println("</style>");
1211
1212     //out.println("<META HTTP-EQUIV=\"refresh\" content=\"2;URL=\"javascript:history.go(-2);\">\n");
1213      out.println("</head>\n");
1214      out.println("<body>");
1215      out.println("<h1>Web<i>ARTS</i> Ajax / REST Listener</h1><h2>Servlet Console</h2>");
1216      out.println(getServletInfo());
1217      out.println(" <h3>Parameters</h3>");
1218      out.println("  <ul>");
1219      out.println("    <li>debug          = <span class=\"val\">"+debugOut_+"</span></li>");
1220       out.println("<br />");
1221      out.println("    <li>Servlet Init Params:");
1222      out.println("        <ul>");
1223
1224      java.util.Enumeration <String> initEnum = getInitParameterNames();
1225      String currParamName = "";
1226      for (; initEnum.hasMoreElements();)
1227      {
1228        currParamName = initEnum.nextElement();
1229        out.print("            <li>");
1230        out.print(currParamName+" = <span class=\"val\">"+getInitParameter(currParamName));
1231        out.println("</span></li>");
1232      }
1233      out.println("       </ul>");
1234      out.println("    </li>"); // servlet init params
1235
1236      out.println("<br />");
1237      out.println("    <li>Context Params for "+context.getServletContextName()+":");
1238      out.println("        <ul>");
1239        out.print("          <li>");
1240        out.print("ContextPath"+" = <span class=\"val\">"+context.getContextPath());
1241        out.println("</span></li>");
1242        out.print("          <li>");
1243        out.print("Real Path For Context Root"+" = <span class=\"val\">"+context.getRealPath("/"));
1244        out.println("</span></li>");
1245      out.println("       </ul>");
1246      out.println("  </li>"); // CONTEXT  params
1247
1248      out.println("<br />");
1249      out.println("  <li>Context <b>Init</b> Params:");
1250      out.println("      <ul>");
1251      initEnum = context.getInitParameterNames();
1252      currParamName = "";
1253      for (; initEnum.hasMoreElements();)
1254      {
1255        currParamName = initEnum.nextElement();
1256        out.print("          <li>");
1257        out.print(currParamName+" = <span class=\"val\">"+context.getInitParameter(currParamName));
1258        out.println("</span></li>");
1259      }
1260      out.println("       </ul>");
1261      out.println("    </li>"); // context init params
1262
1263      out.println("  </ul>");
1264
1265      out.println("<br /><br />");
1266      out.println("<h3>Server Status</h3>");
1267      out.println("  <ul>");
1268      out.println("    <li>Server name = <span class=\"val\">"+req.getServerName()+" ("+req.getLocalAddr()+")</span></li>");
1269      out.println("    <li>Server port = <span class=\"val\">"+req.getServerPort()+"</span></li>");
1270      out.println("    <li>WebApp Engine = <span class=\"val\">"+context.getServerInfo()+"</span></li>");
1271      out.println("    <li>Supported Servlet Version = <span class=\"val\">"+context.getMajorVersion()+"."+context.getMinorVersion()+"</span></li>");
1272
1273      if(context.getMajorVersion()>2 && context.getMinorVersion()>-1)
1274          out.println("      <ul><li>Effective Version = <span class=\"val\">"+
1275                      context.getEffectiveMajorVersion()+"."+context.getEffectiveMinorVersion()+"</span></li></ul>");
1276      out.println("      </li>");
1277      if(context.getMajorVersion()>2 && context.getMinorVersion()>0)
1278      {
1279        try {
1280          out.println("    <li>Logical Hostname = <span class=\"val\">"+context.getVirtualServerName()+"</span></li>");
1281        } catch (Exception ex) {}
1282      }
1283
1284      out.println("    <li>System Properties\n      <ul>");
1285      prop = "java.vendor";
1286      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1287      prop = "java.runtime.version";
1288      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1289      prop = "java.version";
1290      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1291      prop = "os.arch";
1292      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1293      prop = "os.name";
1294      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1295      prop = "os.version";
1296      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1297
1298      out.println("      </ul>\n      </li>");
1299
1300      out.println("<br /><br />");
1301      out.println("<h3>Client Request</h3>");
1302      out.println("  <ul>");
1303      out.println("    <li>Client/proxy Host = <span class=\"val\">"+req.getRemoteHost()+" ("+req.getRemoteAddr()+")</span></li>");
1304      out.println("    <li>Client/proxy port = <span class=\"val\">"+req.getRemotePort()+"</span></li>");
1305      out.println("    <li>Protocol = <span class=\"val\">"+req.getProtocol()+"</span></li>");
1306      out.println("  </ul>");
1307
1308      out.println("<br /><br />");
1309      out.println("<h3>Client Parms Status</h3>");
1310      out.println("  <ul>");
1311      out.println("    <li>Browser = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_NAME)+" v"+
1312                  req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_VERSION)+"</span></li>");
1313      out.println("    <li>Device Type = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_DEVICE_TYPE)+"</span></li>");
1314      out.println("    <li>viewport.width = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTWIDTH)+"</span></li>");
1315      out.println("    <li>viewport.height= <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTHEIGHT)+"</span></li>");
1316      out.println("  </ul>");
1317      out.println("<br /><br />");
1318      out.println("\n</body>\n</html>");
1319  }
1320}