001    package org.hackystat.projectbrowser.page.telemetry;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.Date;
007    import java.util.HashMap;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.logging.Logger;
011    import org.apache.wicket.PageParameters;
012    import org.apache.wicket.model.IModel;
013    import org.apache.wicket.model.Model;
014    import org.hackystat.projectbrowser.ProjectBrowserApplication;
015    import org.hackystat.projectbrowser.ProjectBrowserSession;
016    import org.hackystat.projectbrowser.page.ProjectBrowserBasePage;
017    import org.hackystat.projectbrowser.page.telemetry.datapanel.TelemetryChartDataModel;
018    import org.hackystat.sensorbase.resource.projects.jaxb.Project;
019    import org.hackystat.telemetry.service.client.TelemetryClient;
020    import org.hackystat.telemetry.service.client.TelemetryClientException;
021    import org.hackystat.telemetry.service.resource.chart.jaxb.ParameterDefinition;
022    import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryChartDefinition;
023    import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryChartRef;
024    import org.hackystat.telemetry.service.resource.chart.jaxb.Type;
025    import org.hackystat.utilities.tstamp.Tstamp;
026    
027    /**
028     * Session to hold state for telemetry.
029     * 
030     * @author Shaoxuan Zhang
031     */
032    public class TelemetrySession implements Serializable {
033    
034      /** Support serialization. */
035      private static final long serialVersionUID = 1L;
036    
037      /** The parameter key of telemetry. */
038      public static final String TELEMETRY_KEY = "0";
039      /** The parameter key of granularity. */
040      public static final String GRANULARITY_KEY = "1";
041      /** The parameter key of start date. */
042      public static final String START_DATE_KEY = "2";
043      /** The parameter key of end date. */
044      public static final String END_DATE_KEY = "3";
045      /** The parameter key of selectedProjects. */
046      public static final String SELECTED_PROJECTS_KEY = "4";
047      /** The parameter key of telemetry parameters. */
048      public static final String TELEMETRY_PARAMERTERS_KEY = "5";
049      /**
050       * The last parameter key that is required. The telemetry parameters key is optional because not
051       * all telemetries have parameter.
052       */
053      private static final String LAST_REQUIRED_KEY = "4";
054      /** The parameter instruction message. */
055      public static final String PARAMETER_ORDER_MESSAGE = "Correct parameter order is : "
056          + "/<telemetryName>/<granularity>/<startDate>/<endDate>/<projects>/<param>";
057    
058      /** The analysis this user has selected. */
059      private String telemetryName = null;
060      /** The granularity of the chart. Either Day, Week, or Month. */
061      private String granularity = "Day";
062      /** The start date this user has selected. */
063      private long startDate = ProjectBrowserBasePage.getDateBefore(7).getTime();
064      /** The end date this user has selected. */
065      private long endDate = ProjectBrowserBasePage.getDateBefore(1).getTime();
066      /** The projects this user has selected. */
067      private List<Project> selectedProjects = new ArrayList<Project>();
068    
069      /** The descriptions for all telemetries. */
070      private final Map<String, TelemetryChartDefinition> telemetryDefs = 
071        new HashMap<String, TelemetryChartDefinition>();
072      /** The available granularities. */
073      private final List<String> granularityList = new ArrayList<String>();
074      /** The parameters for telemetry chart. */
075      private final List<IModel> parameters = new ArrayList<IModel>();
076      /** The data model to hold state for data panel. */
077      private final TelemetryChartDataModel dataModel = new TelemetryChartDataModel();
078    
079      /** the feedback string. */
080      private String feedback = "";
081      /** Error message when parsing page paramters. */
082      private String paramErrorMessage = "";
083    
084      /**
085       * Create the instance.
086       */
087      public TelemetrySession() {
088        granularityList.add("Day");
089        granularityList.add("Week");
090        granularityList.add("Month");
091      }
092    
093      /**
094       * @return the parameters
095       */
096      public List<IModel> getParameters() {
097        return this.parameters;
098      }
099    
100      /**
101       * Returns the list of the parameters in a single String, separated by comma.
102       * 
103       * @return a String.
104       */
105      public String getParametersAsString() {
106        StringBuffer stringBuffer = new StringBuffer();
107        for (IModel model : this.parameters) {
108          if (model != null) {
109            stringBuffer.append(model.getObject());
110            stringBuffer.append(ProjectBrowserSession.PARAMETER_VALUE_SEPARATOR);
111          }
112        }
113        String param = stringBuffer.toString();
114        if (param.endsWith(ProjectBrowserSession.PARAMETER_VALUE_SEPARATOR)) {
115          param = param.substring(0, param.length() - 1);
116        }
117        return param;
118      }
119    
120      /**
121       * @param feedback the feedback to set
122       */
123      public void setFeedback(String feedback) {
124        this.feedback = feedback;
125      }
126    
127      /**
128       * @return the feedback
129       */
130      public String getFeedback() {
131        String returnString = this.feedback;
132        this.feedback = "";
133        return returnString;
134      }
135    
136      /**
137       * @param telemetryName the telemetry to set
138       */
139      public void setTelemetryName(String telemetryName) {
140        this.telemetryName = telemetryName;
141      }
142    
143      /**
144       * @return the telemetry
145       */
146      public String getTelemetryName() {
147        return this.telemetryName;
148      }
149    
150      /**
151       * Return the TelemetryList. Initialize it if it is null.
152       * 
153       * @return the telemetryList
154       */
155      public List<String> getTelemetryList() {
156        List<String> telemetryList = new ArrayList<String>();
157        if (this.getTelemetryDefs().isEmpty()) {
158          this.initializeTelemetryList();
159        }
160        telemetryList.addAll(this.getTelemetryDefs().keySet());
161        Collections.sort(telemetryList);
162        return telemetryList;
163      }
164    
165      /**
166       * Initialize the telemetry definition list.
167       */
168      private void initializeTelemetryList() {
169        Logger logger = ProjectBrowserSession.get().getLogger();
170        TelemetryClient client = ProjectBrowserSession.get().getTelemetryClient();
171        try {
172          logger.info("Retrieving data for Telemetry chart definitions.");
173          for (TelemetryChartRef chartRef : client.getChartIndex().getTelemetryChartRef()) {
174            getTelemetryDefs().put(chartRef.getName(), client.getChartDefinition(chartRef.getName()));
175          }
176          logger.info("Finished retrieving data for Telemetry chart definitions.");
177        }
178        catch (TelemetryClientException e) {
179          this.feedback = "Exception when retrieving Telemetry chart definition: " + e.getMessage();
180          logger.warning("Error when retrieving Telemetry chart definition: " + e.getMessage());
181        }
182      }
183    
184      /**
185       * Return the list of ParameterDefinition under telemetry type in this session.
186       * 
187       * @return list of ParameterDefinition.
188       */
189      public List<ParameterDefinition> getParameterList() {
190        return this.getParameterList(this.telemetryName);
191      }
192    
193      /**
194       * Return the list of ParameterDefinition under the given telemetry.
195       * 
196       * @param telemetryName the telemetry
197       * @return list of ParameterDefinition.
198       */
199      public List<ParameterDefinition> getParameterList(String telemetryName) {
200        Logger logger = ProjectBrowserSession.get().getLogger();
201        if (this.telemetryDefs.isEmpty()) {
202          this.initializeTelemetryList();
203        }
204        if (telemetryName != null) {
205          TelemetryChartDefinition teleDef = this.getTelemetryDefs().get(telemetryName);
206          if (teleDef == null) {
207            TelemetryClient client = ProjectBrowserSession.get().getTelemetryClient();
208            try {
209              logger.info("Retrieving telemetry chart definition: " + telemetryName);
210              teleDef = client.getChartDefinition(telemetryName);
211              this.getTelemetryDefs().put(telemetryName, teleDef);
212              return teleDef.getParameterDefinition();
213            }
214            catch (TelemetryClientException e) {
215              this.feedback = "Error when retrieving chart definition of telemetry: "
216                  + this.telemetryName + ">>" + e.getMessage();
217              logger.warning("Error when retrieving chart definition of telemetry: "
218                  + this.telemetryName + ">>" + e.getMessage());
219            }
220          }
221          else {
222            return teleDef.getParameterDefinition();
223          }
224        }
225        return new ArrayList<ParameterDefinition>();
226      }
227    
228      /**
229       * @param startDate the startDate to set
230       */
231      public void setStartDate(Date startDate) {
232        this.startDate = startDate.getTime();
233      }
234    
235      /**
236       * @return the startDate
237       */
238      public Date getStartDate() {
239        return new Date(startDate);
240      }
241    
242      /**
243       * @param endDate the endDate to set
244       */
245      public void setEndDate(Date endDate) {
246        this.endDate = endDate.getTime();
247      }
248    
249      /**
250       * @return the endDate
251       */
252      public Date getEndDate() {
253        return new Date(endDate);
254      }
255    
256      /**
257       * Returns the start date in yyyy-MM-dd format.
258       * 
259       * @return The date as a simple string.
260       */
261      /*
262       * public String getStartDateString() { SimpleDateFormat format = new
263       * SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, Locale.ENGLISH); return format.format(new
264       * Date(this.startDate)); }
265       */
266    
267      /**
268       * Returns the end date in yyyy-MM-dd format.
269       * 
270       * @return The date as a simple string.
271       */
272      /*
273       * public String getEndDateString() { SimpleDateFormat format = new
274       * SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, Locale.ENGLISH); return format.format(new
275       * Date(this.endDate)); }
276       */
277    
278      /**
279       * @param granularity the granularity to set
280       */
281      public void setGranularity(String granularity) {
282        this.granularity = granularity;
283      }
284    
285      /**
286       * @return the granularity
287       */
288      public String getGranularity() {
289        return granularity;
290      }
291    
292      /**
293       * @return the granularityList
294       */
295      public List<String> getGranularityList() {
296        return granularityList;
297      }
298    
299      /**
300       * Update the data model. If execute in background process is determined in
301       * ProjectBrowserProperties.
302       */
303      public void updateDataModel() {
304        boolean backgroundProcessEnable = ((ProjectBrowserApplication) ProjectBrowserApplication.get())
305            .isBackgroundProcessEnable("telemetry");
306        String userEmail = ProjectBrowserSession.get().getEmail();
307        dataModel.setModel(getStartDate(), getEndDate(), selectedProjects, telemetryName, granularity,
308            parameters, ((ProjectBrowserApplication) ProjectBrowserApplication.get())
309                .getTelemetryHost(), userEmail, ProjectBrowserSession.get().getPassword());
310    
311        ProjectBrowserSession.get().logUsage(
312            "TELEMETRY: {invoked} "
313                + ProjectBrowserSession.get().printPageParameters(this.getPageParameters()));
314    
315        if (backgroundProcessEnable) {
316          Thread thread = new Thread() {
317            @Override
318            public void run() {
319              dataModel.loadData();
320            }
321          };
322          thread.start();
323        }
324        else {
325          dataModel.loadData();
326        }
327    
328      }
329    
330      /**
331       * Cancel data model's update.
332       */
333      public void cancelDataUpdate() {
334        dataModel.cancelDataLoading();
335      }
336    
337      /**
338       * @return the dataModel
339       */
340      public TelemetryChartDataModel getDataModel() {
341        return dataModel;
342      }
343    
344      /**
345       * @param selectedProjects the selectedProjects to set
346       */
347      public void setSelectedProjects(List<Project> selectedProjects) {
348        this.selectedProjects = selectedProjects;
349      }
350    
351      /**
352       * @return the selectedProjects
353       */
354      public List<Project> getSelectedProjects() {
355        return selectedProjects;
356      }
357    
358      /**
359       * @return the telemetrys
360       */
361      public Map<String, TelemetryChartDefinition> getTelemetryDefs() {
362        return telemetryDefs;
363      }
364    
365      /**
366       * @return the list of TelemetryChartDefinition.
367       */
368      public List<TelemetryChartDefinition> getChartDescriptions() {
369        List<TelemetryChartDefinition> chartDef = new ArrayList<TelemetryChartDefinition>();
370        for (String telemetryName : this.getTelemetryList()) {
371          chartDef.add(this.telemetryDefs.get(telemetryName));
372        }
373        return chartDef;
374      }
375    
376    
377      /**
378       * Load data from URL parameters into this session.
379       * 
380       * @param parameters the URL parameters
381       * @return true if all parameters are loaded correctly
382       */
383      public boolean loadPageParameters(PageParameters parameters) {
384        boolean isLoadSucceed = true;
385        boolean isTelemetryLoaded = false;
386        Logger logger = ProjectBrowserSession.get().getLogger();
387        if (!parameters.containsKey(LAST_REQUIRED_KEY)) {
388          isLoadSucceed = false;
389          String error = "Some parameters are missing, should be " + LAST_REQUIRED_KEY + "\n"
390              + PARAMETER_ORDER_MESSAGE;
391          logger.warning(error);
392          this.paramErrorMessage = error + "\n";
393          return false;
394        }
395        StringBuffer errorMessage = new StringBuffer(1000);
396        // load telemetry name
397        if (parameters.containsKey(TELEMETRY_KEY)) {
398          String telemetryString = parameters.getString(TELEMETRY_KEY);
399          if (this.getTelemetryList().contains(telemetryString)) {
400            this.setTelemetryName(telemetryString);
401            isTelemetryLoaded = true;
402          }
403          else {
404            isLoadSucceed = false;
405            String error = "Telemetry from URL parameter is unknown: " + telemetryString;
406            logger.warning(error);
407            errorMessage.append(error);
408            errorMessage.append('\n');
409          }
410        }
411        else {
412          isLoadSucceed = false;
413          errorMessage.append("Telemetry key is missing in URL parameters.\n");
414        }
415        // load granularity
416        if (parameters.containsKey(GRANULARITY_KEY)) {
417          String granularityString = parameters.getString(GRANULARITY_KEY);
418          if (this.granularityList.contains(granularityString)) {
419            this.setGranularity(granularityString);
420          }
421          else {
422            isLoadSucceed = false;
423            String error = "Granularity is not supported: " + granularityString;
424            logger.warning(error);
425            errorMessage.append(error);
426            errorMessage.append('\n');
427          }
428        }
429        else {
430          isLoadSucceed = false;
431          errorMessage.append("granularity key is missing in URL parameters.\n");
432        }
433        // load start date
434        if (parameters.containsKey(START_DATE_KEY)) {
435          String startDateString = parameters.getString(START_DATE_KEY);
436          try {
437            this.startDate = Tstamp.makeTimestamp(startDateString).toGregorianCalendar()
438                .getTimeInMillis();
439          }
440          catch (Exception e) {
441            isLoadSucceed = false;
442            String error = "Errors when parsing start date from URL parameter: " + startDateString;
443            logger.warning(error + " > " + e.getMessage());
444            errorMessage.append(error);
445            errorMessage.append('\n');
446          }
447        }
448        else {
449          isLoadSucceed = false;
450          errorMessage.append("startDate key is missing in URL parameters.\n");
451        }
452        // load end date
453        if (parameters.containsKey(END_DATE_KEY)) {
454          String endDateString = parameters.getString(END_DATE_KEY);
455          try {
456            this.endDate = Tstamp.makeTimestamp(endDateString).toGregorianCalendar().getTimeInMillis();
457          }
458          catch (Exception e) {
459            isLoadSucceed = false;
460            String error = "Errors when parsing end date from URL parameter: " + endDateString;
461            logger.warning(error + " > " + e.getMessage());
462            errorMessage.append(error);
463            errorMessage.append('\n');
464          }
465        }
466        else {
467          isLoadSucceed = false;
468          errorMessage.append("endDate key is missing in URL parameters.\n");
469        }
470        // load seletecd project
471        if (parameters.containsKey(SELECTED_PROJECTS_KEY)) {
472          String[] projectsStringArray = parameters.getString(SELECTED_PROJECTS_KEY).split(
473              ProjectBrowserSession.PARAMETER_VALUE_SEPARATOR);
474          List<Project> projectsList = new ArrayList<Project>();
475          for (String projectString : projectsStringArray) {
476            int index = projectString.lastIndexOf(ProjectBrowserSession.PROJECT_NAME_OWNER_SEPARATR);
477            String projectName = projectString;
478            String projectOwner = null;
479            if (index > 0 && index < projectString.length()) {
480              projectName = projectString.substring(0, index);
481              projectOwner = projectString.substring(
482                  index + ProjectBrowserSession.PROJECT_NAME_OWNER_SEPARATR.length());
483              /*
484               * isLoadSucceed = false; 
485               * String error = "Error URL parameter: project: " + projectString + " >>
486               * project name and owner are missing or not formatted correctly."; logger.warning(error);
487               * errorMessage.append(error); errorMessage.append('\n'); continue;
488               */
489            }
490            Project project = ProjectBrowserSession.get().getProject(projectName, projectOwner);
491            if (project == null) {
492              isLoadSucceed = false;
493              String error = "Error URL parameter: project: " + projectString
494                  + " >> matching project not found under user: "
495                  + ProjectBrowserSession.get().getEmail();
496              logger.warning(error);
497              errorMessage.append(error);
498              errorMessage.append('\n');
499            }
500            else {
501              projectsList.add(project);
502            }
503          }
504          if (!projectsList.isEmpty()) {
505            this.setSelectedProjects(projectsList);
506          }
507        }
508        else {
509          isLoadSucceed = false;
510          errorMessage.append("projects key is missing in URL parameters.\n");
511        }
512        // load telemetry parameters
513        if (parameters.containsKey(TELEMETRY_PARAMERTERS_KEY)) {
514          String paramString = parameters.getString(TELEMETRY_PARAMERTERS_KEY);
515          String[] paramStringArray = paramString.split(
516              ProjectBrowserSession.PARAMETER_VALUE_SEPARATOR);
517          this.parameters.clear();
518          if (isTelemetryLoaded) {
519            List<ParameterDefinition> paramDefList = this.getTelemetryDefs().get(this.telemetryName)
520                .getParameterDefinition();
521            if (paramStringArray.length == paramDefList.size()) {
522              for (int i = 0; i < paramStringArray.length; ++i) {
523                if (isValueMatchType(paramStringArray[i], paramDefList.get(i).getType())) {
524                  this.parameters.add(new Model(paramStringArray[i]));
525                }
526                else {
527                  isLoadSucceed = false;
528                  String error = "Telemetry parameter: " + paramStringArray[i] + " is not matched to"
529                      + " type: " + paramDefList.get(i).getType().getName();
530                  logger.warning(error);
531                  errorMessage.append(error);
532                  errorMessage.append('\n');
533                }
534              }
535            }
536            else {
537              isLoadSucceed = false;
538              String error = "Error in URL parameters: telemetry parameters: "
539                  + parameters.getString(TELEMETRY_PARAMERTERS_KEY) + "(" + paramStringArray.length
540                  + ") >> number of parameters not matched to definition within telemetry: "
541                  + this.telemetryName + "(" + paramDefList.size() + ")";
542              logger.warning(error);
543              errorMessage.append(error);
544              errorMessage.append('\n');
545            }
546          }
547        }
548        if (errorMessage.length() > 0) {
549          this.paramErrorMessage = errorMessage.toString() + PARAMETER_ORDER_MESSAGE;
550        }
551        return isLoadSucceed;
552      }
553    
554      /**
555       * Checks if the given value is of the given type.
556       * 
557       * @param value the given value.
558       * @param type the given type.
559       * @return true if the value and type are matched.
560       */
561      public static boolean isValueMatchType(String value, Type type) {
562        if (value == null) {
563          return false;
564        }
565        if ("Enumerated".equals(type.getName())) {
566          return type.getValue().contains(value);
567        }
568        else if ("Boolean".equals(type.getName())) {
569          return "true".equals(value) || "false".equals(value);
570        }
571        else if ("Integer".equals(type.getName())) {
572          try {
573            Integer.valueOf(value);
574            return true;
575          }
576          catch (NumberFormatException e) {
577            return false;
578          }
579        }
580        else if ("Text".equals(type.getName())) {
581          return true;
582        }
583        return false;
584      }
585    
586      /**
587       * Returns a PageParameters instance that represents the content of the input form.
588       * 
589       * @return a PageParameters instance.
590       */
591      public PageParameters getPageParameters() {
592        PageParameters parameters = new PageParameters();
593    
594        parameters.put(TELEMETRY_KEY, this.getTelemetryName());
595        parameters.put(GRANULARITY_KEY, this.getGranularity());
596        parameters.put(START_DATE_KEY, ProjectBrowserSession.getFormattedDateString(this.startDate));
597        parameters.put(END_DATE_KEY, ProjectBrowserSession.getFormattedDateString(this.endDate));
598        parameters.put(SELECTED_PROJECTS_KEY, 
599            ProjectBrowserSession.convertProjectListToString(this.getSelectedProjects()));
600        parameters.put(TELEMETRY_PARAMERTERS_KEY, this.getParametersAsString());
601    
602        return parameters;
603      }
604    
605      /**
606       * @return the paramErrorMessage
607       */
608      public String getParamErrorMessage() {
609        String temp = this.paramErrorMessage;
610        this.clearParamErrorMessage();
611        return temp;
612      }
613    
614      /**
615       * Clears the paramErrorMessage.
616       */
617      public void clearParamErrorMessage() {
618        this.paramErrorMessage = "";
619      }
620    
621    }