001/*
002 *  $Id: AjaxRestWebsocketListener.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;
054
055import javax.servlet.*;
056import javax.servlet.http.*;
057
058//import javax.json.Json;
059
060import javax.servlet.annotation.WebServlet;
061
062import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
063import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
064
065/**
066 * This class is a generic listener, implemented as a servlet, for responding to AJAX and/or Rest style requests.<br />
067 *
068 * @author     tgutwin
069 * @created    April 25, 2016
070 */
071@SuppressWarnings("serial")
072public class AjaxRestWebsocketListener extends  WebSocketServlet
073{
074  // implements SingleThreadModel
075
076  /** Class constant. **/
077  protected static final String className_ = "AjaxRestWebsocketListener";
078  private final static String SYSTEM_FILE_SEPERATOR = File.separator;
079  /**  Version String.  */
080  private final static String SERVLET_VERSION = Util.spacesToCapsInString("0.01_[$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_USERID = "clientSetting.lastFmUserID";
090  public static final String CLIENT_SETTING_NAME_LASTFM_USER_RECENT = "clientSetting.lastFmUserRecent";
091  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK_NUM = "clientSetting.lastFmPlayTrackNum";
092  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK = "clientSetting.lastFmPlayTrack";
093  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM = "clientSetting.lastFmPlayAlbum";
094  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST = "clientSetting.lastFmPlayArtist";
095  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_ALBUM_ARTIST = "clientSetting.lastFmPlayAlbumArtist";
096  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_TIME = "clientSetting.lastFmPlayTime";
097  public static final String CLIENT_SETTING_NAME_LASTFM_PLAY_DURATION = "clientSetting.lastFmPlayDuration";
098  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK = "clientSetting.lastFmScrobbleTrack";
099  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST = "clientSetting.lastFmScrobbleArtist";
100  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM = "clientSetting.lastFmScrobbleAlbum";
101  public static final String CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM_ARTIST= "clientSetting.lastFmScrobbleAlbumArtist";
102  public static final String CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK = "clientSetting.lastFmLoveTrack";
103  public static final String CLIENT_SETTING_NAME_LASTFM_LIST_LOVE_TRACK = "clientSetting.lastFmListLovedTracks";
104
105  /** DEFAULT Last.FM username to use: admin .**/
106  protected static final String LASTFM_DEFAULT_USERNAME = "tgutwin"; //"${lastfm.user}";
107  /** DEFAULT Last.FM password to use: admin .**/
108  protected static final String LASTFM_DEFAULT_PASSWORD = "~19AcrobatiC19~~~"; //"${lastfm.pass}";
109
110  /**  Build String. (yymmddhhss)  */
111  private final static String BUILD_TAG = "160425183718";
112
113  private static String webServerHostName_ = "zenbook.webarts.bc.ca";
114
115  private boolean debugOut_ = true;
116  protected static Log log_= Log.getInstance();
117
118  private int viewPortWidth_ = 0;
119  private int viewPortHeight_ = 0;
120  private String someParamYouWannaSet_ = "";
121
122  /** Control for rest response format XML(default) or JSON. **/
123  private boolean xmlResponse_ = false;
124
125  /** AJAX param - **/
126  private String debugOut = "";
127  private int viewPortWidth = 0;
128  private int viewPortHeight = 0;
129  private String browserName = "";
130  private String browserVersion = "";
131  private String lastFmSessionKey = "";
132  private String lastFmPlayTrack = "";
133  private String lastFmPlayArtist = "";
134  private String lastFmScrobbleTrack = "";
135  private String lastFmScrobbleArtist = "";
136  private String lastFmScrobbleAlbum = "";
137  private String lastFmLoveTrack = "";
138  private boolean xmlResponse = xmlResponse_;
139  private TunesHelper tunesHelper = null;
140  private String lastFmUserID_ = LASTFM_DEFAULT_USERNAME;
141  private String lastFmPassword_ = LASTFM_DEFAULT_PASSWORD;
142  private boolean lastFMInit_ = false;
143
144  /**
145   *  Gets the ServletInfo attribute of the AjaxRestWebsocketListener object
146   *
147   * @return    The ServletInfo value
148   */
149  public String getServletInfo()
150  {
151    final String methodName = "getServletInfo";
152    return "WebARTS Design AjaxRestWebsocketListener servlet. Version:" + SERVLET_VERSION +
153        "  Build:" + BUILD_TAG;
154  }
155
156
157  /**
158   * The one time servlet init stuff goes here. It sets the derbyDBDir based on the following prioritized varables:
159   * <ol><li>context init param: derbyDBDir</li><li>servlet init param (from web.xml): derbyDBDir</li>
160   * <li>default hardcoded variable: derbyDBDir</li></ol>If defined in multiple places, the higher priority item will
161   * be used.
162   **/
163  public void init()
164  {
165    System.out.println("\n~~~~~~~~\n~~~~~~~~\nInitializing ca.bc.webarts.servlet.AjaxRestWebsocketListener\n~~~~~~~~\n~~~~~~~~");
166
167    boolean notFoundInit = true;
168    boolean notFoundContext = true;
169    java.util.Enumeration <String> initEnum = getInitParameterNames();
170    for (; notFoundInit && initEnum.hasMoreElements();)
171    {
172      if(initEnum.nextElement().equals("someParamYouWannaSet"))
173      {
174        notFoundInit = false;
175        /* Do something with the init param */
176        someParamYouWannaSet_ = getInitParameter("someParamYouWannaSet");
177        System.out.println("\n~~~~~~~~\n     INIT ServletParam: someParamYouWannaSet="+someParamYouWannaSet_);
178      }
179    }
180
181    //also check context
182    initEnum = getServletConfig().getServletContext().getInitParameterNames();
183    for (; notFoundContext && initEnum.hasMoreElements();)
184    {
185      if(initEnum.nextElement().equals("someParamYouWannaSet"))
186      {
187        notFoundContext = false;
188        /* Do something with the init param */
189        someParamYouWannaSet_ = getServletConfig().getServletContext().getInitParameter("derbyDBDir");
190        System.out.println("\n~~~~~~~~\n     INIT ContextParam: someParamYouWannaSet="+someParamYouWannaSet_);
191      }
192    }
193
194    if(notFoundInit && notFoundContext )
195    {
196      /* Set the default values */
197      //eagleDBDir_ = pEye_.getDerbyDBDir();
198    }
199
200    try
201    {
202      webServerHostName_ = InetAddress.getLocalHost().getHostName();
203    }
204    catch (UnknownHostException ex)
205    {
206      webServerHostName_ = "red.webarts.bc.ca";
207    }
208  }
209
210
211  /**  Override to close Things  **/
212  public void destroy()
213  {
214    super.destroy();
215  }
216
217
218
219  @Override
220  public void configure(WebSocketServletFactory factory)
221  {
222      // set a 10 second timeout
223      factory.getPolicy().setIdleTimeout(10000);
224
225      // register MyEchoSocket as the WebSocket to create on Upgrade
226      factory.register(EventSocket.class);
227      //      factory.register(AjaxRestWebsocketListener.class);
228}
229
230
231  /**
232   * Returns the value of viewPortWidth_.
233   */
234  public int getViewPortWidth() {
235    return viewPortWidth_;
236  }
237
238
239  /**
240   * Sets the value of viewPortWidth_.
241   * @param viewPortWidth The value to assign viewPortWidth_.
242   */
243  public void setViewPortWidth(int viewPortWidth) {
244    this.viewPortWidth_ = viewPortWidth;
245  }
246
247
248  /**
249   * Returns the value of viewPortHeight_.
250   */
251  public int getViewPortHeight() {
252    return viewPortHeight_;
253  }
254
255
256  /**
257   * Sets the value of viewPortHeight_.
258   * @param viewPortWidth The value to assign viewPortWidth_.
259   */
260  public void setViewPortHeight(int viewPortHeight) {
261    this.viewPortHeight_ = viewPortHeight;
262  }
263
264
265  /**
266   * Returns the value of debugOut_.
267   */
268  public boolean getDebugOut()
269  {
270    return debugOut_;
271  }
272
273
274  /**
275   * Sets the value of debugOut_.
276   * @param debugOut The value to assign debugOut_.
277   */
278  public void setDebugOut(boolean debugOut)
279  {
280    this.debugOut_ = debugOut;
281    log_.setLogLevel((debugOut_?log_.DEBUG:log_.MINOR));
282  }
283
284
285  /**
286    * Get Method for class field 'xmlResponse'.
287    *
288    * @return boolean - The value the class field 'xmlResponse' XML (true) or JSON (false).
289    *
290    **/
291  public boolean getXmlResponse()
292  {
293    return xmlResponse_;
294  }  // getXmlResponse Method
295
296
297  /**
298    * Set Method for class field 'xmlResponse' that controls if teh rest response is in XML (true) or JSON (false).
299    *
300    * @param xmlResponse is the value to set this class field to XML (true) or JSON (false).
301    *
302    **/
303  public  void setXmlResponse(boolean xmlResponse)
304  {
305    this.xmlResponse_ = xmlResponse;
306  }  // setXmlResponse Method
307
308
309  /**
310   * streams to a zipped out stream (without creating a file).
311   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
312   * @param servletOutput is the stream to zip into
313   * @param dataName are the pseudo fileName that will get created in the zip (pseudo file) stream
314   * @param dataStream the data to zip
315   **/
316  private void streamZIP(OutputStream servletOut, String dataName, InputStream dataStream)
317  {
318    String[] dataNames = {dataName};
319    InputStream[] dataStreams = {dataStream};
320    streamZIP(servletOut, dataNames,dataStreams);
321  }
322
323
324  /**
325   * streams to a zipped out stream (without creating a file).
326   * from: http://www.coderanch.com/t/276892/java-io/java/Stream-data-ZipOutputStream
327   *
328   *  Sample servlet doGet...<br />
329   *  public void doGet(HttpServletRequest request,
330                        HttpServletResponse response) throws IOException{
331        response.setContentType("text/plain");
332        response.setHeader("Content-Disposition",
333                           "attachment;filename=downloadname.txt");
334        String[] dataNames = {dataName};
335        InputStream[] dataStreams = {dataStream};
336        OutputStream os = response.getOutputStream();
337        streamZIP(servletOut, dataNames,dataStreams);
338      }
339   *
340   *
341   * @param servletOutput is the stream to zip into
342   * @param dataNames are the pseudo fileNames that will get created in the zip (pseudo file) stream
343   * @param dataStreams the data to zip
344   **/
345  private void streamZIP(OutputStream servletOut,
346                        String[] dataNames,
347                        InputStream[] dataStreams)
348  {
349    ZipOutputStream zos = new ZipOutputStream(servletOut);
350    final int DATA_BLOCK_SIZE = 2048;
351    int byteCount;
352    byte[] data;
353
354    try
355    {
356      for(int i = 0; i < dataNames.length; i++)
357      {
358        ZipEntry ze = new ZipEntry(dataNames[i]);
359        zos.putNextEntry(ze);
360        data = new byte[DATA_BLOCK_SIZE];
361
362        while((byteCount = dataStreams[i].read(data, 0, DATA_BLOCK_SIZE)) != -1)
363        {
364          zos.write(data, 0, byteCount);
365        }
366
367        zos.flush();
368        zos.closeEntry();
369        dataStreams[i].close();
370      }
371      zos.close();
372    }
373    catch(Exception e)
374    {
375      System.err.println("Problem streaming zip data " + e.toString());
376    }
377  }
378
379
380  /** Parses the REST command string and prepares the response in XML format.
381    * @param restPath is the rest command part of the rest URL
382    * @return a string holding the XML rest response
383    **/
384  private String parseRestRequest(String restPath)
385  {
386    String restResponse = null;
387    System.out.println("REST Request: " +restPath);
388    String pre = "/";
389
390    DecimalFormat dfp = new DecimalFormat( "##0" );
391    DecimalFormat dfe = new DecimalFormat( "###0.000" );
392    if(restPath.equals(pre+"power/current"))
393    {
394      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
395      /*
396      restResponse = "<RestResponse succeeded=\"true\"><power unit=\"watts\" timetamp=\"current\">"+
397                      pEye_.getCurrentDemand()+
398                      "</power></RestResponse>";
399      */
400      sb.append("</RestResponse>");
401    }
402    else if(restPath.equals(pre+"power/recent"))
403    {
404      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
405      /*
406      ca.bc.webarts.tools.rainforest.InstantaneousDemand [] idmf = pEye_.getCurrentReadingsCache();
407      for (int i=0; i< idmf.length; i++)
408      {
409        if(idmf[i]!=null)
410        {
411          sb.append("<power unit=\"watts\" timetamp=\"");
412          sb.append(idmf[i].getLocalTimestampStr());
413          sb.append("\">");
414          sb.append(dfp.format(idmf[i].getCalculatedValue()));
415          sb.append("</power>");
416        }
417      }
418      */
419      sb.append("</RestResponse>");
420      restResponse = sb.toString();
421    }
422    else if(restPath.equals(pre+"energy/recent"))
423    {
424      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
425      /*
426      ca.bc.webarts.tools.rainforest.CurrentSummationDelivered [] csd = pEye_.getCurrentEnergyCache();
427      for (int i=0; i< csd.length; i++)
428      {
429        if(csd[i]!=null)
430        {
431          sb.append("<energy unit=\"wattHours\" timetamp=\"");
432          sb.append(csd[i].getLocalTimestampStr());
433          sb.append("\">");
434          sb.append(dfe.format(csd[i].getCalculatedValue()));
435          sb.append("</energy>");
436        }
437      }
438      */
439      sb.append("</RestResponse>");
440      restResponse = sb.toString();
441    }
442    else if(restPath.equals(pre+"energy/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/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    else if(restPath.equals(pre+"price/label/current"))
457    {
458      StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
459      /*  Add In the response content  */
460      sb.append("</RestResponse>");
461      restResponse = sb.toString();
462    }
463    return restResponse;
464  }
465
466
467  /** Parses any defined servlet params into the defined class vars. **/
468  private void parseParams(HttpServletRequest req)
469  {
470    ParameterParser parser = new ParameterParser(req);
471    if(debugOut_)
472    {
473      System.out.println("\nRequest Params:");
474      Enumeration<String> parmNames = req.getParameterNames();
475      for (; parmNames.hasMoreElements();)
476      {
477        String currParamName = (String) parmNames.nextElement();
478        System.out.println("  "+currParamName+"="+req.getParameter(currParamName));
479      }
480    }
481
482    /* parse out the request params into class vars  */
483    debugOut = parser.getStringParameter("debugOut", "");
484    viewPortWidth =  parser.getIntParameter("viewPortWidth",0);
485    viewPortHeight =  parser.getIntParameter("viewPortHeight",0);
486    browserName = parser.getStringParameter("clientBrowser","");
487    browserVersion = parser.getStringParameter("clientBrowserVersion","");
488    lastFmSessionKey = parser.getStringParameter("lastFmUserID","");
489    lastFmSessionKey = parser.getStringParameter("lastFmPassword","");
490    lastFmSessionKey = parser.getStringParameter("lastFmSessionKey","");
491    lastFmPlayTrack = parser.getStringParameter("lastFmPlayTrack","");
492    lastFmPlayArtist = parser.getStringParameter("lastFmPlayArtist","");
493    lastFmScrobbleTrack = parser.getStringParameter("lastFmScrobbleTrack","");
494    lastFmScrobbleAlbum = parser.getStringParameter("lastFmScrobbleAlbum","");
495    lastFmScrobbleArtist = parser.getStringParameter("lastFmScrobbleArtist","");
496    lastFmLoveTrack = parser.getStringParameter("lastFmLoveTrack","");
497    xmlResponse = parser.getBooleanParameter("xmlResponse",xmlResponse_);
498
499    if(debugOut_) { System.out.println("  PlayTrack="+lastFmPlayTrack+"   lastFmPlayArtist="+lastFmPlayArtist );}
500    if(debugOut_) { System.out.println("  ScrobbleTrack="+lastFmScrobbleTrack+"   lastFmScrobbleAlbum="+lastFmScrobbleAlbum+"   lastFmScrobbleArtist="+lastFmScrobbleArtist );}
501
502   }
503
504
505  /** Convert a well-formed (but not necessarily valid) XML string into a JSON String. **/
506  private String xmlToJson(String xml)
507  {
508     String jsonStr = "";
509     if (xml== null || xml.length()<2) xml = "";
510     org.json.JSONObject jsonObj = org.json.XML.toJSONObject(xml);
511     if(jsonObj!= null) jsonStr = jsonObj.toString(2);
512     return jsonStr;
513   }
514
515
516  /**
517   *  This method handles the "GET" submission - it is used for Ajax calls to set webApp parameters using JQuery.
518   *
519   * @param  req                   Description of the Parameter
520   * @param  res                   Description of the Parameter
521   * @exception  ServletException  Description of the Exception
522   * @exception  IOException       Description of the Exception
523   */
524  public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
525  {
526    parseParams(req);
527
528    ServletOutputStream out = res.getOutputStream();
529    String conPath = req.getContextPath();
530    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
531
532
533    if(debugOut_)
534    {
535      System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~");
536      System.out.println("AjaxRestWebsocketListener.doGet "+ "   debugOut="+debugOut);
537      System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
538    }
539
540    boolean ajaxCall = false;
541    boolean restRequest = false;
542    String ajaxResponse = "";
543    String restResponse = null;
544
545    res.setStatus(HttpServletResponse.SC_OK);  // default ids OK,, set again lower downif NOT
546
547    ajaxResponse = checkForAjaxRequest(req);
548    if (!ajaxResponse.equals("")) ajaxCall=true;
549
550    if(!ajaxCall) restResponse = checkForRestRequest(req);
551    if (restResponse!=null && !restResponse.equals("")) restRequest=true;
552
553    if(debugOut_) { System.out.println("ajaxCall="+ajaxCall+"   restRequest="+restRequest );}
554    /* * * * * *  * * * * * EMPTY * * * * *  * * * * *  */
555    /* ***********************************************  */
556    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
557    {
558      sendServletAdminPage(req, res);
559    }
560
561    if(ajaxCall)
562    {
563      res.setContentType("text/plain");
564      if(out!=null  )
565      {
566        if (debugOut_)
567          System.out.println("AJAX REPLY("+ajaxResponse.length()+")="+ajaxResponse);
568        out.print(ajaxResponse);
569      }
570      else
571        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
572    }
573    else if(restRequest)
574    {
575      if (xmlResponse_) res.setContentType("text/xml");
576      else res.setContentType("application/javascript");
577      if(restResponse!=null && !restResponse.equals(""))
578      {
579        res.setStatus(HttpServletResponse.SC_OK);
580      }
581      else
582      {
583        res.setStatus(HttpServletResponse.SC_NOT_FOUND);
584        restResponse = "<RestResponse succeeded=\"false\"><status>404</status></RestResponse>";
585      }
586      if(out!=null  )
587      {
588        if (!xmlResponse_) restResponse = xmlToJson(restResponse);
589        if (debugOut_)
590          System.out.println("REST REPLY("+restResponse.length()+")="+restResponse);
591        out.print(restResponse);
592      }
593      else
594        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
595    }
596    out.flush();
597    res.flushBuffer();
598  }
599
600
601  /**
602   *  This method handles the "POST" submissions.
603   *
604   * @param  req                   Description of Parameter
605   * @param  res                   Description of Parameter
606   * @exception  ServletException  Description of Exception
607   * @exception  IOException       Description of Exception
608   */
609  public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
610  {
611    //parseParams(req);
612
613    StringBuffer postBody = new StringBuffer("");
614    String line = null;
615   // try
616   // {
617      BufferedReader reader = req.getReader();
618      while ((line = reader.readLine()) != null)
619        postBody.append(line+"\n");
620
621      String postReply = "";
622
623      // Do something with the posted message
624      // send it to a helper class if desired
625      /*String postReply = pEye_.newMessage(postBody.toString()); */
626
627      // send back an empty reply
628      if ((postReply==null || postReply.equals("")))
629      {
630        // send back a 200 with an empty body
631        res.setStatus(HttpServletResponse.SC_OK);
632        res.setContentLength(0);
633        postReply = "";
634        if (debugOut_) System.out.println("AjaxRestWebsocketListener.doPost: emptyreply from newMessage");
635      }
636      /*
637      else if(!sendFastPoll_)
638      {
639        // send back the pEyeReply as the body
640        res.setStatus(HttpServletResponse.SC_OK);
641        res.setContentLength(pEyeReply.length());
642        if (debugOut_) System.out.println("AjaxRestWebsocketListener.doPost: SLOW Poll & newMessage reply existed");
643      }
644      else // if(sendFastPoll_)
645      {
646        MessageCommand mc = new MessageCommand(pEye_.getMeterMacId());
647        pEyeReply = mc.getSetFastPollCommandStr(fastPollFreq_,fastPollDur_);
648
649        res.setContentLength(0);   // send back the setFastPollCommand string as the body
650        //if (debugOut_) System.out.println(" *!*!*!*!  AjaxRestWebsocketListener.doPost: Send FAST Poll to:"+pEye_.getMeterMacId() );
651
652        // now reset fastPoll for next message
653        sendFastPoll_ = false;
654      }
655      */
656
657      postReply = "\n"+postReply;
658      res.setContentType("application/xml");
659      res.setStatus(HttpServletResponse.SC_OK);
660      res.setContentLength(postReply.length());
661
662      ServletOutputStream out = res.getOutputStream();
663      if(out!=null  )
664      {
665        if (debugOut_) System.out.println("Attempting to HttpServletResponse REPLY("+postReply.length()+")="+postReply);
666        out.print(postReply);
667        out.flush();
668      }
669      else
670        System.out.println("HttpServletResponse.ServletOutputStream is NOT ready");
671
672      res.flushBuffer();
673
674      /*
675    }
676    catch (Exception e)
677    {
678      //report an error
679      e.printStackTrace();
680    }
681    */
682  }
683
684
685  /** Parses the REST command string and prepares the response in XML format.
686    * @param restPath is the rest command part of the rest URL
687    * @return a string holding the XML rest response OR null if not a REST Request
688    **/
689  private String checkForRestRequest(HttpServletRequest req) throws ServletException, IOException
690  {
691    String restResponse = null;
692    String servletPath = req.getServletPath();
693    String restPath = req.getPathInfo();  // basically anything added to the end of the URL is considered a RESt request
694    String pre = "/";
695    boolean returnXml = xmlResponse_; // false means JSON
696
697    if (servletPath.equals("/rest") && restPath!=null && !restPath.equals("") )
698    {
699
700      DecimalFormat dfp = new DecimalFormat( "##0" );
701      DecimalFormat dfe = new DecimalFormat( "###0.000" );
702      if(restPath.equals(pre+"test"))
703      {
704        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
705        /*  Add In the response content  */
706        sb.append("<testNode className=\"");
707        sb.append(className_);
708        sb.append("\">");
709        sb.append("Hello");
710        sb.append("</testNode>");
711        sb.append("</RestResponse>");
712        restResponse = sb.toString();
713      }
714      if(restPath.equals(pre+"power/current"))
715      {
716        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
717        /*  Add In the response content  */
718        sb.append("</RestResponse>");
719        restResponse = sb.toString();
720      }
721      else if(restPath.equals(pre+"power/recent"))
722      {
723        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
724        /*  Add In the response content  */
725        sb.append("</RestResponse>");
726        restResponse = sb.toString();
727      }
728      else if(restPath.equals(pre+"energy/recent"))
729      {
730        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
731        /*  Add In the response content  */
732        sb.append("</RestResponse>");
733        restResponse = sb.toString();
734      }
735      else if(restPath.equals(pre+"energy/current"))
736      {
737        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
738        /*  Add In the response content  */
739        sb.append("</RestResponse>");
740        restResponse = sb.toString();
741      }
742      else if(restPath.equals(pre+"price/current"))
743      {
744        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
745        /*  Add In the response content  */
746        sb.append("</RestResponse>");
747        restResponse = sb.toString();
748      }
749      else if(restPath.equals(pre+"price/label/current"))
750      {
751        StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
752        /*  Add In the response content  */
753        sb.append("</RestResponse>");
754        restResponse = sb.toString();
755      }
756
757      // Database Queries
758      /*
759          - Dump the database tables directly to (JSON or ) XML using a REST call:
760            http://red.webarts.bc.ca:8080/powereye/rest/db/table/<tablename>
761              <tablename> can be reading, energy, load_device, load_type
762
763      or with the optional <number of rows>
764         positive number means sorted ascending (oldest 1st)
765         negative number means sorted descending (newest 1st)
766            http://red.webarts.bc.ca:8080/powereye/rest/db/table/<tablename>/100
767
768      These database calls are LOOOOooong so you might want to use a commandline URL query tool like
769      <a href="http://www.gnu.org/software/wget/">wget</a>, or perl or java or whatever.
770      */
771      else if(restPath.startsWith(pre+"db/table/"))
772      {
773        returnXml = true; // false means JSON
774        String content = "";
775        try
776        {
777          restResponse = "<RestResponse succeeded=\"false\"></RestResponse>";
778          int tblIndex = (pre+"db/table/").length();
779          int numRowsIndex = restPath.indexOf("/",tblIndex)+1; // /rest/db/table/reading/99
780          boolean numRowsWasSpecified = (numRowsIndex>0);
781          int numReadings =  (numRowsWasSpecified?Integer.parseInt(restPath.substring(numRowsIndex)):0); //0 means send all
782          boolean sortAscendingByDate = false;           // sort default is descending
783          if(numReadings<0) // negative numbers means ascending
784          {
785            sortAscendingByDate = true;
786            numReadings *= -1; // now normalize it to be a psitive rowCount
787          }
788          String tableName = (numRowsWasSpecified?restPath.substring(tblIndex,numRowsIndex-1):restPath.substring(tblIndex));
789
790          System.out.println("Dumping DB Table: "+tableName);
791          if(numRowsWasSpecified) System.out.println("         numRows: "+numReadings +" "+
792                                                    (sortAscendingByDate?"ascending":"descending"));
793          java.sql.ResultSet rs = null;
794          if(tableName.equalsIgnoreCase("reading"))
795            if(numRowsWasSpecified)
796            {
797              //rs = pEye_.dumpReadingTableToRS(sortAscendingByDate,(numReadings!=0?numReadings:-1));//dumpReadingTableToRS();
798            }
799            else
800            {
801              //rs = pEye_.dumpReadingTableToRS();
802            }
803          else if(tableName.equalsIgnoreCase("energy"))
804            if(numRowsWasSpecified)
805            {
806              //rs = pEye_.dumpEnergyTableToRS(sortAscendingByDate,(numReadings!=0?numReadings:-1));//dumpEnergyTableToRS();
807            }
808            else
809            {
810              //rs = pEye_.dumpEnergyTableToRS();
811            }
812          else if(tableName.equalsIgnoreCase("load_type"))
813            if(numRowsWasSpecified)
814            {
815              //rs = pEye_.dumpDBTableToRS("load_type",numReadings);//dumpLoadTypeTableToRS();
816            }
817            else
818            {
819              //rs = pEye_.dumpLoadTypeTableToRS();
820            }
821          else if(tableName.equalsIgnoreCase("load_device"))
822            if(numRowsWasSpecified)
823            {
824              //rs = pEye_.dumpDBTableToRS("load_device",numReadings);//dumpLoadDeviceTableToRS();
825            }
826            else
827            {
828              //rs = pEye_.dumpLoadDeviceTableToRS();
829            }
830
831          if(rs!=null)
832          {
833            ResultSetConverter rsc = new ResultSetConverter(rs);
834            if (returnXml) content = rsc.convertToXml().toXML().substring("<?xml version=\"1.0\"?>".length());
835            else content = rsc.convertToJson().toString();
836            StringBuilder sb = new StringBuilder("<RestResponse succeeded=\"true\">");
837            /*  Add In the response content  */
838              sb.append("<db table=\""+tableName+"\">");
839              sb.append(content);
840              sb.append("</db>");
841
842            sb.append("</RestResponse>");
843            restResponse = sb.toString();
844          }
845        }
846        catch(Exception ioEx)
847        {
848          restResponse = "<RestResponse succeeded=\"false\"></RestResponse>";
849        }
850      }
851    }
852
853    return restResponse;
854  }
855
856
857  /** Checks if this Request is an AJAX request and prepares the response in XML format.
858    * @param req the servlet request
859    * @return a string holding the response value to be returned OR null if not an expected/Valid AJAX Request
860    **/
861  private String checkForAjaxRequest(HttpServletRequest req) throws ServletException, IOException
862  {
863    String ajaxResponse = "";
864     // was  it a JQuery Ajax call for info...  (such as eagleDBDir_)
865    if (debugOut!=null &&!debugOut.equals("") )
866    {
867        debugOut_ =  Boolean.parseBoolean(debugOut);
868        setDebugOut(debugOut_);
869        //pEye_.setDebugOut(debugOut_);
870        System.out.println("AjaxRestWebsocketListener.doGet - debugOut_="+debugOut_);
871        ajaxResponse = ""+debugOut_;
872    }
873    else if (viewPortWidth!=0 )
874    {
875      // do something with the passed parms
876      viewPortWidth_=viewPortWidth;
877      req.getSession().setAttribute(CLIENT_SETTING_NAME_VIEWPORTWIDTH, viewPortWidth);
878      ajaxResponse = ""+viewPortWidth_;
879    }
880    else if (viewPortHeight!=0 )
881    {
882      // do something with the passed parms
883      viewPortHeight_=viewPortHeight;
884      req.getSession().setAttribute(CLIENT_SETTING_NAME_VIEWPORTHEIGHT, viewPortHeight);
885      ajaxResponse = ""+viewPortHeight_;
886    }
887    else if (browserName!=null &&!browserName.equals("") )
888    {
889      req.getSession().setAttribute(CLIENT_SETTING_NAME_BROWSER_NAME, browserName);
890      ajaxResponse = ""+browserName;
891    }
892    else if (browserVersion!=null &&!browserVersion.equals("") )
893    {
894      req.getSession().setAttribute(CLIENT_SETTING_NAME_BROWSER_VERSION, browserVersion);
895      ajaxResponse = ""+browserVersion;
896    }
897    // Ajax Call - lastFmPlayTrack
898    else if (lastFmPlayTrack!=null &&!lastFmPlayTrack.equals("") )
899    {
900      System.out.println("Ajax Call - lastFmPlayTrack="+lastFmPlayTrack);
901      HttpSession session = req.getSession(true);
902      System.out.println("  ajaxParam Found:  lastFmPlayTrack="+lastFmPlayTrack +"  lastFmPlayArtist="+lastFmPlayArtist);
903      System.out.println("  AjaxRestWebsocketListener SessionID: "+session.getId() );
904
905      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_TRACK, lastFmPlayTrack);
906      if (lastFmPlayArtist!=null && !lastFmPlayArtist.equals("") )
907      {
908        session.setAttribute(CLIENT_SETTING_NAME_LASTFM_PLAY_ARTIST, lastFmPlayArtist);
909      }
910      // Threaded call to send a play notification to last.fm
911      loginLastFM();
912      //HttpSession session = request.getSession();
913      session.setAttribute("AjaxRestWebsocketListener.tunesHelper", tunesHelper);
914
915      if ( lastFMInit_)
916      {
917        tunesHelper.nowPlaying(lastFmPlayArtist, lastFmPlayTrack);
918        ajaxResponse = ""+lastFmPlayArtist+"/"+lastFmPlayTrack;
919      }
920      else
921      {
922        ajaxResponse = "null/null";
923      }
924    }
925    // Ajax Call - lastFmScrobbleTrack
926    else if (lastFmScrobbleTrack!=null &&!lastFmScrobbleTrack.equals("") && (lastFmLoveTrack==null || lastFmLoveTrack.equals("")))
927    {
928      System.out.println("Ajax Call - lastFmScrobbleTrack="+lastFmScrobbleTrack);
929      HttpSession session = req.getSession(true);
930      System.out.println("  ajaxParam Found:  lastFmScrobbleTrack="+lastFmScrobbleTrack +"  lastFmScrobbleArtist="+lastFmScrobbleArtist);
931      System.out.println("  AjaxRestWebsocketListener SessionID: "+session.getId() );
932      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_TRACK, lastFmScrobbleTrack);
933      if (lastFmScrobbleAlbum !=null &&!lastFmScrobbleAlbum.equals("") )
934      {
935        System.out.println("  ajaxParam Found:  lastFmScrobbleAlbum="+lastFmScrobbleAlbum );
936        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ALBUM, lastFmScrobbleAlbum);
937      }
938      if (lastFmScrobbleArtist!=null &&!lastFmScrobbleArtist.equals("") )
939      {
940        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist);
941      }
942      // Threaded call to send a play notification to last.fm
943      loginLastFM();
944      //HttpSession session = request.getSession();
945      session.setAttribute("AjaxRestWebsocketListener.tunesHelper", tunesHelper);
946
947      if ( lastFMInit_)
948      {
949        tunesHelper.scrobble(lastFmScrobbleArtist, lastFmScrobbleAlbum, lastFmScrobbleTrack);
950        ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleAlbum+"/"+lastFmScrobbleTrack;
951      }
952      else
953      {
954        ajaxResponse = "null/null";
955      }
956    }
957    // Ajax Call - lastFmLoveTrack
958    else if (lastFmLoveTrack!=null && !lastFmLoveTrack.equals("") && lastFmScrobbleTrack!=null && !lastFmScrobbleTrack.equals("")  )
959    {
960      System.out.println("Ajax Call - lastFmLoveTrack="+lastFmLoveTrack);
961      HttpSession session = req.getSession(true);
962      if (lastFmScrobbleArtist!=null &&!lastFmScrobbleArtist.equals("") )
963      {
964        req.getSession().setAttribute(CLIENT_SETTING_NAME_LASTFM_SCROBBLE_ARTIST, lastFmScrobbleArtist);
965      }
966      System.out.println("  ajaxParam Found:  lastFmLoveTrack="+lastFmLoveTrack+
967                                         "lastFmScrobbleTrack="+lastFmScrobbleTrack +
968                                      "  lastFmScrobbleArtist="+lastFmScrobbleArtist);
969      System.out.println("  AjaxRestWebsocketListener SessionID: "+session.getId() );
970      session.setAttribute(CLIENT_SETTING_NAME_LASTFM_LOVE_TRACK, lastFmLoveTrack);
971      // Threaded call to send a play notification to last.fm
972      loginLastFM();
973      //HttpSession session = request.getSession();
974      session.setAttribute("AjaxRestWebsocketListener.tunesHelper", tunesHelper);
975
976      if ( lastFMInit_)
977      {
978        //http://10.0.0.253/tunes/ajax/?lastFmLoveTrack=1&lastFmScrobbleTrack=18TillIDie&lastFmScrobbleArtist=BryanAdams
979        ajaxResponse = tunesHelper.loveTrack(lastFmScrobbleArtist, lastFmScrobbleTrack);
980        //ajaxResponse = ""+lastFmScrobbleArtist+"/"+lastFmScrobbleTrack;
981      }
982      else
983      {
984        ajaxResponse = "null/null";
985      }
986    }
987
988    return ajaxResponse;
989  }
990
991
992  /**
993    * Logs into the last.fm session with the already provided user/pass.
994    * this method uses TunesHelper to do this.
995    *
996    * @return success or not
997    **/
998  private boolean loginLastFM()
999  {
1000      if(tunesHelper == null) tunesHelper = new TunesHelper();
1001      /* should probably also use the lastFM user pass sent by some AJAX call. */
1002      tunesHelper.setLastFmUserID(lastFmUserID_);
1003      tunesHelper.setLastFmPassword(lastFmPassword_);
1004
1005      if (tunesHelper!=null)
1006      {
1007        if (!lastFmSessionKey.equals("") )
1008        {
1009          if (tunesHelper.restoreLastFmUserSession(lastFmSessionKey))
1010          {
1011            lastFMInit_ = true;
1012          }
1013          else
1014          {
1015            lastFMInit_ = tunesHelper.initLastFmUserSession(true, lastFmUserID_, lastFmPassword_);
1016          }
1017        }
1018      }
1019      return lastFMInit_;
1020  }
1021
1022
1023  /** Prepares and returns basic info page about the server. **/
1024  private void sendServletAdminPage(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
1025  {
1026    ServletOutputStream out = res.getOutputStream();
1027    ServletContext context = getServletContext();
1028    java.util.Properties sysProps = System.getProperties();
1029    TimeZone localTz = Calendar.getInstance().getTimeZone();
1030    int offset = localTz.getRawOffset();
1031    // shift Daylight savings hour if needed
1032    Date dateNow = new Date();
1033    boolean dst = localTz.inDaylightTime(dateNow);
1034    int dstOffset = localTz.getDSTSavings();
1035    //if(dst) offset = offset + dstOffset;
1036
1037    String prop = "";
1038
1039      // Send Back a response to be presented to the users browser
1040      res.setContentType("text/html");
1041      out.println("<html>\n");
1042      out.println("<head>\n");
1043      out.println("<title>Ajax / REST Listener - Servlet Console</title>\n");
1044      out.println("<link rel=\"icon\"");
1045      out.println("     type=\"image/png\"");
1046      out.println("     href=\"images/20x20/Eagle_sml.png\" />");
1047      out.println("<style>");
1048      out.println("H1 { font-weight: bolder; background-color: #000033; color: #ffffbb;  font-size: 200%}");
1049      out.println("H2 { font-weight: bolder; color: #ffff99;  font-size: 160%; text-decoration: underline}");
1050      out.println("H3 { font-weight: bolder; color: #ffff44;  font-size: 140%}");
1051      out.println("H4, H5 { color: #ffffff }");
1052      out.println("A { color: #ffaadd }");
1053      out.println("DT { font-weight: bolder }");
1054      out.println("LI { line-height: 125% }");
1055      out.println("BODY { background-color: #101066; color: #ffff44; font-size: 100%; line-height: 110% }");
1056      out.println(".val { color: #ffffff; font: \"Lucida Console\", Monaco, monospace  }");
1057      out.println("");
1058      out.println("");
1059      out.println("");
1060      out.println("</style>");
1061
1062     //out.println("<META HTTP-EQUIV=\"refresh\" content=\"2;URL=\"javascript:history.go(-2);\">\n");
1063      out.println("</head>\n");
1064      out.println("<body>");
1065      out.println("<h1>Web<i>ARTS</i> Ajax / REST Listener</h1><h2>Servlet Console</h2>");
1066      out.println(getServletInfo());
1067      out.println(" <h3>Parameters</h3>");
1068      out.println("  <ul>");
1069      out.println("    <li>debug          = <span class=\"val\">"+debugOut_+"</span></li>");
1070       out.println("<br />");
1071      out.println("    <li>Servlet Init Params:");
1072      out.println("        <ul>");
1073
1074      java.util.Enumeration <String> initEnum = getInitParameterNames();
1075      String currParamName = "";
1076      for (; initEnum.hasMoreElements();)
1077      {
1078        currParamName = initEnum.nextElement();
1079        out.print("            <li>");
1080        out.print(currParamName+" = <span class=\"val\">"+getInitParameter(currParamName));
1081        out.println("</span></li>");
1082      }
1083      out.println("       </ul>");
1084      out.println("    </li>"); // servlet init params
1085
1086      out.println("<br />");
1087      out.println("    <li>Context Params for "+context.getServletContextName()+":");
1088      out.println("        <ul>");
1089        out.print("          <li>");
1090        out.print("ContextPath"+" = <span class=\"val\">"+context.getContextPath());
1091        out.println("</span></li>");
1092        out.print("          <li>");
1093        out.print("Real Path For Context Root"+" = <span class=\"val\">"+context.getRealPath("/"));
1094        out.println("</span></li>");
1095      out.println("       </ul>");
1096      out.println("  </li>"); // CONTEXT  params
1097
1098      out.println("<br />");
1099      out.println("  <li>Context <b>Init</b> Params:");
1100      out.println("      <ul>");
1101      initEnum = context.getInitParameterNames();
1102      currParamName = "";
1103      for (; initEnum.hasMoreElements();)
1104      {
1105        currParamName = initEnum.nextElement();
1106        out.print("          <li>");
1107        out.print(currParamName+" = <span class=\"val\">"+context.getInitParameter(currParamName));
1108        out.println("</span></li>");
1109      }
1110      out.println("       </ul>");
1111      out.println("    </li>"); // context init params
1112
1113      out.println("  </ul>");
1114
1115      out.println("<br /><br />");
1116      out.println("<h3>Server Status</h3>");
1117      out.println("  <ul>");
1118      out.println("    <li>Server name = <span class=\"val\">"+req.getServerName()+" ("+req.getLocalAddr()+")</span></li>");
1119      out.println("    <li>Server port = <span class=\"val\">"+req.getServerPort()+"</span></li>");
1120      out.println("    <li>WebApp Engine = <span class=\"val\">"+context.getServerInfo()+"</span></li>");
1121      out.println("    <li>Supported Servlet Version = <span class=\"val\">"+context.getMajorVersion()+"."+context.getMinorVersion()+"</span></li>");
1122
1123      if(context.getMajorVersion()>2 && context.getMinorVersion()>-1)
1124          out.println("      <ul><li>Effective Version = <span class=\"val\">"+
1125                      context.getEffectiveMajorVersion()+"."+context.getEffectiveMinorVersion()+"</span></li></ul>");
1126      out.println("      </li>");
1127      if(context.getMajorVersion()>2 && context.getMinorVersion()>0)
1128      {
1129        try {
1130          out.println("    <li>Logical Hostname = <span class=\"val\">"+context.getVirtualServerName()+"</span></li>");
1131        } catch (Exception ex) {}
1132      }
1133
1134      out.println("    <li>System Properties\n      <ul>");
1135      prop = "java.vendor";
1136      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1137      prop = "java.runtime.version";
1138      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1139      prop = "java.version";
1140      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1141      prop = "os.arch";
1142      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1143      prop = "os.name";
1144      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1145      prop = "os.version";
1146      out.println("       <li>"+prop+" = <span class=\"val\">"+sysProps.getProperty(prop)+"</span></li>");
1147
1148      out.println("      </ul>\n      </li>");
1149
1150      out.println("<br /><br />");
1151      out.println("<h3>Client Request</h3>");
1152      out.println("  <ul>");
1153      out.println("    <li>Client/proxy Host = <span class=\"val\">"+req.getRemoteHost()+" ("+req.getRemoteAddr()+")</span></li>");
1154      out.println("    <li>Client/proxy port = <span class=\"val\">"+req.getRemotePort()+"</span></li>");
1155      out.println("    <li>Protocol = <span class=\"val\">"+req.getProtocol()+"</span></li>");
1156      out.println("  </ul>");
1157
1158      out.println("<br /><br />");
1159      out.println("<h3>Client Parms Status</h3>");
1160      out.println("  <ul>");
1161      out.println("    <li>Browser = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_NAME)+" v"+
1162                  req.getSession().getAttribute(CLIENT_SETTING_NAME_BROWSER_VERSION)+"</span></li>");
1163      out.println("    <li>Device Type = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_DEVICE_TYPE)+"</span></li>");
1164      out.println("    <li>viewport.width = <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTWIDTH)+"</span></li>");
1165      out.println("    <li>viewport.height= <span class=\"val\">"+req.getSession().getAttribute(CLIENT_SETTING_NAME_VIEWPORTHEIGHT)+"</span></li>");
1166      out.println("  </ul>");
1167      out.println("<br /><br />");
1168      out.println("\n</body>\n</html>");
1169  }
1170}