package org.libreplan.web.common;
import org.libreplan.business.common.VersionInformation;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.users.daos.IUserDAO;
import org.libreplan.web.orders.IOrderModel;
import org.libreplan.web.expensesheet.IExpenseSheetModel;
import org.libreplan.web.materials.IMaterialsModel;
import org.libreplan.web.orders.IAssignedTaskQualityFormsToOrderElementModel;
import org.libreplan.web.resources.machine.IMachineModel;
import org.libreplan.web.resources.worker.IWorkerModel;
import org.libreplan.web.workreports.IWorkReportModel;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.zkoss.json.JSONObject;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zkplus.spring.SpringUtil;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
/**
* Class represents all data that will be sent to server.
*
* If you want to use it, just create a new object and set 2 private variables.
* All needed data will be already in object.
*
* @author Vova Perebykivskyi <vova@libreplan-enterprise.com>
* @author Bogdan Bodnarjuk <bogdan@libreplan-enterprise.com>
*/
public class GatheredUsageStats {
private IUserDAO userDAO;
private IOrderModel orderModel;
private IWorkReportModel workReportModel;
private IWorkerModel workerModel;
private IMachineModel machineModel;
private IExpenseSheetModel expenseSheetModel;
private IMaterialsModel materialsModel;
private IAssignedTaskQualityFormsToOrderElementModel assignedTaskQualityFormsToOrderElementModel;
/**
* Version of this statistics implementation.
* Just increment it, if you will change something related to JSON object.
*/
private int jsonObjectVersion = 3;
/**
* Unique system identifier (MD5 - ip + hostname).
*/
private String id;
/**
* Version of LibrePlan.
*/
private String version = VersionInformation.getVersion();
/**
* Number of users in application.
*/
private Number users;
/**
* Number of projects in application.
*/
private int projects;
/**
* Number of timesheets in application.
*/
private int timesheets;
/**
* Number of workers in application.
*/
private int workers;
/**
* Number of machines in application.
*/
private int machines;
/**
* Number of expense sheets in application.
*/
private int expensesheets;
/**
* Number of materials in application.
*/
private int materials;
/**
* Number of assigned quality forms in application.
*/
private int assignedQualityForms;
/**
* The oldest date of project.
*/
private String oldestDate;
public GatheredUsageStats() {
if ( this.userDAO == null ) {
this.userDAO = (IUserDAO) SpringUtil.getBean("userDAO");
}
if ( this.orderModel == null ) {
this.orderModel = (IOrderModel) SpringUtil.getBean("orderModel");
}
if ( this.workReportModel == null ) {
this.workReportModel = (IWorkReportModel) SpringUtil.getBean("workReportModel");
}
if ( this.workerModel == null ) {
this.workerModel = (IWorkerModel) SpringUtil.getBean("workerModel");
}
if ( this.machineModel == null ) {
this.machineModel = (IMachineModel) SpringUtil.getBean("machineModel");
}
if ( this.expenseSheetModel == null ) {
this.expenseSheetModel = (IExpenseSheetModel) SpringUtil.getBean("expenseSheetModel");
}
if ( this.materialsModel == null ) {
this.materialsModel = (IMaterialsModel) SpringUtil.getBean("materialsModel");
}
if ( this.assignedTaskQualityFormsToOrderElementModel == null ) {
this.assignedTaskQualityFormsToOrderElementModel = (IAssignedTaskQualityFormsToOrderElementModel)
SpringUtil.getBean("assignedTaskQualityFormsToOrderElementModel");
}
initialize();
}
private String generateID() {
// Make hash of ip + hostname
WebAuthenticationDetails details =
(WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
String ip = details.getRemoteAddress();
Execution execution = Executions.getCurrent();
String hostname = execution.getServerName();
String message = ip + hostname;
byte[] encoded;
StringBuilder sb = null;
try {
byte[] bytesOfMessage = message.getBytes("UTF-8");
MessageDigest md5 = MessageDigest.getInstance("MD5");
encoded = md5.digest(bytesOfMessage);
// Convert bytes to hex format
sb = new StringBuilder();
for (byte anEncoded : encoded) {
sb.append(Integer.toString((anEncoded & 0xff) + 0x100, 16).substring(1));
}
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ignored) {
}
return sb.toString();
}
private void initialize() {
setId(generateID());
setUsers(getUserRows());
setProjects(orderModel.getOrders().size());
setTimesheets(workReportModel.getWorkReportDTOs().size());
setWorkers(workerModel.getWorkers().size());
setMachines(machineModel.getMachines().size());
setExpensesheets(expenseSheetModel.getExpenseSheets().size());
setMaterials(materialsModel.getMaterials().size());
setQualityForms(assignedTaskQualityFormsToOrderElementModel.getAssignedQualityForms().size());
setOldestDate(orderModel.getOrders());
}
public void sendGatheredUsageStatsToServer(){
JSONObject json = new JSONObject();
json.put("json-version", jsonObjectVersion);
json.put("id", id);
json.put("version", version);
json.put("users", users);
json.put("projects", projects);
json.put("timesheets", timesheets);
json.put("workers", workers);
json.put("machines", machines);
json.put("expensesheets", expensesheets);
json.put("materials", materials);
json.put("assigned-quality-forms", assignedQualityForms);
json.put("oldestDate", oldestDate);
HttpURLConnection connection = null;
Properties properties = new Properties();
InputStream inputStream;
try {
// You can find it in libreplan-business/src/main/resources
String filename = "libreplan.properties";
inputStream = GatheredUsageStats.class.getClassLoader().getResourceAsStream(filename);
properties.load(inputStream);
URL url = new URL(properties.getProperty("statsPage"));
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-urlencoded");
connection.setRequestProperty("Content-Language", "en-GB");
connection.setRequestProperty("Content-Length", Integer.toString(json.toJSONString().getBytes().length));
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
// If the connection lasts > 2 sec throw Exception
connection.setConnectTimeout(2000);
// Send request
DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
dataOutputStream.writeBytes(json.toJSONString());
dataOutputStream.flush();
dataOutputStream.close();
// No needed code, but it is not working without it
connection.getInputStream();
} catch (IOException ignored) {
} finally {
if ( connection != null ) {
connection.disconnect();
}
}
}
private Number getUserRows() {
return userDAO.getRowCount();
}
public void setId(String id) {
this.id = id;
}
public void setUsers(Number users) {
this.users = users;
}
public void setProjects(int projects) {
this.projects = projects;
}
public void setTimesheets(int timesheets) {
this.timesheets = timesheets;
}
public void setWorkers(int workers) {
this.workers = workers;
}
public void setMachines(int machines) {
this.machines = machines;
}
public void setExpensesheets(int expensesheets) {
this.expensesheets = expensesheets;
}
public void setMaterials(int materials) {
this.materials = materials;
}
public void setQualityForms(int qualityForms) {
this.assignedQualityForms = qualityForms;
}
private void setOldestDate(List<Order> list) {
if ( !list.isEmpty() ) {
Date date = list.get(0).getInitDate();
for ( Order item : list ) {
if (item.getInitDate().compareTo(date) < 0) {
date = item.getInitDate();
}
}
this.oldestDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S'Z'").format(date);
} else {
this.oldestDate = "0";
}
}
}