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 }