Merge pull request #40 from dgray16/master
Add possibility to send data usage to LibrePlan server.
This commit is contained in:
commit
a77c73b3bf
9 changed files with 261 additions and 43 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -10,7 +10,7 @@ ganttzk/target
|
|||
.project
|
||||
.settings/
|
||||
|
||||
# ignore IDEA configuration files
|
||||
# ignore Intellij IDEA configuration files
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
* published. It checks the last version against a URL.
|
||||
*
|
||||
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
|
||||
* @author Vova Perebykivskiy <vova@libreplan-enterprise.com>
|
||||
*/
|
||||
public class VersionInformation {
|
||||
|
||||
|
|
@ -118,14 +119,12 @@ public class VersionInformation {
|
|||
/**
|
||||
* Returns true if a new version of the project is published.
|
||||
*
|
||||
* @param allowToGatherUsageStatsEnabled
|
||||
* @param isCheckNewVersionEnabled
|
||||
* If true LibrePlan developers will process the requests to check
|
||||
* the new versions to generate usages statistics
|
||||
*/
|
||||
public static boolean isNewVersionAvailable(
|
||||
boolean allowToGatherUsageStatsEnabled) {
|
||||
return singleton
|
||||
.checkIsNewVersionAvailable(allowToGatherUsageStatsEnabled);
|
||||
public static boolean isNewVersionAvailable(boolean isCheckNewVersionEnabled) {
|
||||
return singleton.checkIsNewVersionAvailable(isCheckNewVersionEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,13 +132,12 @@ public class VersionInformation {
|
|||
* Otherwise, during one day it returns the cached value. And it checks it
|
||||
* again after that time.
|
||||
*/
|
||||
private boolean checkIsNewVersionAvailable(
|
||||
boolean allowToGatherUsageStatsEnabled) {
|
||||
if (!newVersionCached) {
|
||||
private boolean checkIsNewVersionAvailable(boolean isCheckNewVersionEnabled) {
|
||||
if ( !newVersionCached ) {
|
||||
long oneDayLater = lastVersionCachedDate.getTime()
|
||||
+ DELAY_TO_CHECK_URL;
|
||||
if (oneDayLater < new Date().getTime()) {
|
||||
loadNewVersionFromURL(allowToGatherUsageStatsEnabled);
|
||||
if ( oneDayLater < new Date().getTime() ) {
|
||||
loadNewVersionFromURL(isCheckNewVersionEnabled);
|
||||
}
|
||||
}
|
||||
return newVersionCached;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
|
|||
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
|
||||
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
|
||||
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
|
||||
* @author Vova Perebykivskiy <vova@libreplan-enterprise.com>
|
||||
*/
|
||||
public class Configuration extends BaseEntity {
|
||||
|
||||
|
|
@ -102,7 +103,7 @@ public class Configuration extends BaseEntity {
|
|||
|
||||
private Boolean checkNewVersionEnabled = true;
|
||||
|
||||
private Boolean allowToGatherUsageStatsEnabled = false;
|
||||
private Boolean allowToGatherUsageStatsEnabled = true;
|
||||
|
||||
private Boolean generateCodeForExpenseSheets = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ public class UserDAO extends GenericDAOHibernate<User, Long>
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Number getRowCount() {
|
||||
return (Number) getSession().createCriteria(User.class).setProjection(Projections.rowCount()).uniqueResult();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
statsPage http://stats.libreplan-enterprise.com/
|
||||
|
|
@ -31,6 +31,8 @@ import java.util.ArrayList;
|
|||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
|
@ -49,6 +51,7 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.cxf.jaxrs.client.WebClient;
|
||||
import org.libreplan.business.calendars.entities.BaseCalendar;
|
||||
import org.libreplan.business.common.daos.IConfigurationDAO;
|
||||
import org.libreplan.business.common.entities.Configuration;
|
||||
import org.libreplan.business.common.entities.Connector;
|
||||
import org.libreplan.business.common.entities.ConnectorProperty;
|
||||
|
|
@ -61,10 +64,12 @@ import org.libreplan.business.common.entities.PredefinedConnectors;
|
|||
import org.libreplan.business.common.entities.ProgressType;
|
||||
import org.libreplan.business.common.exceptions.ValidationException;
|
||||
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
|
||||
import org.libreplan.business.users.daos.IUserDAO;
|
||||
import org.libreplan.business.users.entities.UserRole;
|
||||
import org.libreplan.importers.JiraRESTClient;
|
||||
import org.libreplan.importers.TimSoapClient;
|
||||
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
|
||||
import org.libreplan.web.orders.IOrderModel;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
|
||||
|
|
@ -125,6 +130,12 @@ public class ConfigurationController extends GenericForwardComposer {
|
|||
|
||||
private IConfigurationModel configurationModel;
|
||||
|
||||
private IConfigurationDAO configurationDAO;
|
||||
|
||||
private IUserDAO userDAO;
|
||||
|
||||
private IOrderModel orderModel;
|
||||
|
||||
private IMessagesForUser messages;
|
||||
|
||||
private Component messagesContainer;
|
||||
|
|
@ -157,6 +168,8 @@ public class ConfigurationController extends GenericForwardComposer {
|
|||
|
||||
private Textbox emailSenderTextbox;
|
||||
|
||||
private boolean isGatheredStatsAlreadySent = false;
|
||||
|
||||
@Override
|
||||
public void doAfterCompose(Component comp) throws Exception {
|
||||
super.doAfterCompose(comp);
|
||||
|
|
@ -242,24 +255,34 @@ public class ConfigurationController extends GenericForwardComposer {
|
|||
|
||||
public void save() throws InterruptedException {
|
||||
|
||||
if ( getSelectedConnector() != null && getSelectedConnector().getName().equals("E-mail") &&
|
||||
isEmailFieldsValid() == false ) {
|
||||
if (getSelectedConnector() != null && getSelectedConnector().getName().equals("E-mail") &&
|
||||
isEmailFieldsValid() == false) {
|
||||
messages.showMessage(Level.ERROR, _("Check all fields"));
|
||||
|
||||
} else {
|
||||
ConstraintChecker.isValid(configurationWindow);
|
||||
if (checkValidEntitySequenceRows()) {
|
||||
try {
|
||||
configurationModel.confirm();
|
||||
configurationModel.init();
|
||||
messages.showMessage(Level.INFO, _("Changes saved"));
|
||||
if (getSelectedConnector() != null
|
||||
&& !configurationModel
|
||||
.scheduleOrUnscheduleJobs(getSelectedConnector())) {
|
||||
messages.showMessage(
|
||||
Level.ERROR,
|
||||
_("Scheduling or unscheduling of jobs for this connector is not completed"));
|
||||
}
|
||||
ConstraintChecker.isValid(configurationWindow);
|
||||
if (checkValidEntitySequenceRows()) {
|
||||
try {
|
||||
configurationModel.confirm();
|
||||
configurationModel.init();
|
||||
messages.showMessage(Level.INFO, _("Changes saved"));
|
||||
|
||||
// Send data to server
|
||||
if (!isGatheredStatsAlreadySent && configurationDAO.getConfigurationWithReadOnlyTransaction()
|
||||
.isAllowToGatherUsageStatsEnabled()) {
|
||||
GatheredUsageStats gatheredUsageStats = new GatheredUsageStats();
|
||||
gatheredUsageStats.setupNotAutowiredClasses(userDAO, orderModel);
|
||||
gatheredUsageStats.sendGatheredUsageStatsToServer();
|
||||
isGatheredStatsAlreadySent = true;
|
||||
}
|
||||
|
||||
if (getSelectedConnector() != null
|
||||
&& !configurationModel
|
||||
.scheduleOrUnscheduleJobs(getSelectedConnector())) {
|
||||
messages.showMessage(
|
||||
Level.ERROR,
|
||||
_("Scheduling or unscheduling of jobs for this connector is not completed"));
|
||||
}
|
||||
reloadWindow();
|
||||
reloadEntitySequences();
|
||||
reloadConnectors();
|
||||
|
|
@ -274,7 +297,6 @@ public class ConfigurationController extends GenericForwardComposer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void cancel() throws InterruptedException {
|
||||
|
|
@ -1186,7 +1208,6 @@ public class ConfigurationController extends GenericForwardComposer {
|
|||
|
||||
private void appendValueTextbox(Row row,
|
||||
final ConnectorProperty property) {
|
||||
|
||||
final Textbox textbox = new Textbox();
|
||||
textbox.setWidth("400px");
|
||||
textbox.setConstraint(checkPropertyValue(property));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
package org.libreplan.web.common;
|
||||
|
||||
import org.libreplan.business.common.VersionInformation;
|
||||
import org.libreplan.business.users.daos.IUserDAO;
|
||||
import org.libreplan.web.orders.IOrderModel;
|
||||
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 java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
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.
|
||||
*
|
||||
* Created by
|
||||
* @author Vova Perebykivskiy <vova@libreplan-enterprise.com>
|
||||
* on 02/08/2016.
|
||||
*/
|
||||
|
||||
public class GatheredUsageStats {
|
||||
|
||||
private IUserDAO userDAO;
|
||||
|
||||
private IOrderModel orderModel;
|
||||
|
||||
|
||||
// Version of this statistics implementation
|
||||
private int jsonObjectVersion = 1;
|
||||
|
||||
// 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;
|
||||
|
||||
private Number getUserRows(){
|
||||
return userDAO.getRowCount();
|
||||
}
|
||||
|
||||
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 = null;
|
||||
StringBuffer 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 StringBuffer();
|
||||
for (int i = 0; i < encoded.length; i++) sb.append(Integer.toString((encoded[i] & 0xff) + 0x100, 16)
|
||||
.substring(1));
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// It needed because i do not need to call default constructor on Autowiring
|
||||
private void myConstructor(){
|
||||
setId(generateID());
|
||||
setUsers(getUserRows());
|
||||
setProjects(orderModel.getOrders().size());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
HttpURLConnection connection = null;
|
||||
|
||||
Properties properties = new Properties();
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
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 id
|
||||
connection.getInputStream();
|
||||
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if ( connection != null ) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setupNotAutowiredClasses(IUserDAO userDAO, IOrderModel orderModel){
|
||||
this.userDAO = userDAO;
|
||||
this.orderModel = orderModel;
|
||||
myConstructor();
|
||||
}
|
||||
|
||||
public int getJsonObjectVersion() {
|
||||
return jsonObjectVersion;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Number getUsers() {
|
||||
return users;
|
||||
}
|
||||
public void setUsers(Number users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public int getProjects() {
|
||||
return projects;
|
||||
}
|
||||
public void setProjects(int projects) {
|
||||
this.projects = projects;
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ public class TemplateController extends GenericForwardComposer {
|
|||
if (templateModel.isScenariosVisible()) {
|
||||
window = (Window) comp.getFellow("changeScenarioWindow");
|
||||
windowMessages = new MessagesForUser(window
|
||||
.getFellow("messagesContainer"));
|
||||
.getFellow("messagesContainer"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,12 +219,11 @@ public class TemplateController extends GenericForwardComposer {
|
|||
}
|
||||
|
||||
public boolean isNewVersionAvailable() {
|
||||
if (!templateModel.isCheckNewVersionEnabled()) {
|
||||
if ( !templateModel.isCheckNewVersionEnabled() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return VersionInformation.isNewVersionAvailable(templateModel
|
||||
.isAllowToGatherUsageStatsEnabled());
|
||||
return VersionInformation.isNewVersionAvailable(templateModel.isCheckNewVersionEnabled());
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ import org.libreplan.business.orders.entities.OrderElement;
|
|||
import org.libreplan.business.planner.entities.TaskElement;
|
||||
import org.libreplan.business.resources.daos.IResourcesSearcher;
|
||||
import org.libreplan.business.templates.entities.OrderTemplate;
|
||||
import org.libreplan.business.users.daos.IUserDAO;
|
||||
import org.libreplan.business.users.entities.UserRole;
|
||||
import org.libreplan.web.common.ConfirmCloseUtil;
|
||||
import org.libreplan.web.common.GatheredUsageStats;
|
||||
import org.libreplan.web.common.entrypoints.EntryPointsHandler;
|
||||
import org.libreplan.web.common.entrypoints.URLHandlerRegistry;
|
||||
import org.libreplan.web.dashboard.DashboardController;
|
||||
|
|
@ -44,6 +46,7 @@ import org.libreplan.web.dashboard.DashboardControllerGlobal;
|
|||
import org.libreplan.web.limitingresources.LimitingResourcesController;
|
||||
import org.libreplan.web.logs.LogsController;
|
||||
import org.libreplan.web.montecarlo.MonteCarloController;
|
||||
import org.libreplan.web.orders.IOrderModel;
|
||||
import org.libreplan.web.orders.OrderCRUDController;
|
||||
import org.libreplan.web.planner.allocation.AdvancedAllocationController.IBack;
|
||||
import org.libreplan.web.planner.company.CompanyPlanningController;
|
||||
|
|
@ -76,18 +79,16 @@ import org.zkoss.zk.ui.event.EventListener;
|
|||
import org.zkoss.zk.ui.event.Events;
|
||||
import org.zkoss.zk.ui.util.Composer;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Creates and handles several tabs
|
||||
*
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
|
||||
* @author Vova Perebykivskiy <vova@libreplan-enterprise.com>
|
||||
*/
|
||||
@Component
|
||||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class
|
||||
MultipleTabsPlannerController implements Composer,
|
||||
public class MultipleTabsPlannerController implements Composer,
|
||||
IGlobalViewEntryPoints {
|
||||
|
||||
public static String WELCOME_URL = "-- no URL provided --";
|
||||
|
|
@ -219,6 +220,15 @@ public class
|
|||
@Autowired
|
||||
private URLHandlerRegistry registry;
|
||||
|
||||
// Cannot Autowire it in GatheredUsageStats class
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
||||
@Autowired
|
||||
private IOrderModel orderModel;
|
||||
|
||||
private boolean isGatheredStatsAlreadySent = false;
|
||||
|
||||
private TabsConfiguration buildTabsConfiguration(final Desktop desktop) {
|
||||
|
||||
Map<String, String[]> parameters = getURLQueryParametersMap();
|
||||
|
|
@ -266,7 +276,7 @@ public class
|
|||
|
||||
@Override
|
||||
public void goToTaskResourceAllocation(Order order,
|
||||
TaskElement task) {
|
||||
TaskElement task) {
|
||||
orderPlanningController.setShowedTask(task);
|
||||
orderPlanningController.setCurrentControllerToShow(orderPlanningController.getEditTaskController());
|
||||
getTabsRegistry()
|
||||
|
|
@ -499,6 +509,13 @@ public class
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isGatheredStatsAlreadySent && configurationDAO.getConfiguration().isAllowToGatherUsageStatsEnabled() ){
|
||||
GatheredUsageStats gatheredUsageStats = new GatheredUsageStats();
|
||||
gatheredUsageStats.setupNotAutowiredClasses(userDAO, orderModel);
|
||||
gatheredUsageStats.sendGatheredUsageStatsToServer();
|
||||
isGatheredStatsAlreadySent = true;
|
||||
}
|
||||
}
|
||||
|
||||
private TabsRegistry getTabsRegistry() {
|
||||
|
|
@ -516,11 +533,6 @@ public class
|
|||
getTabsRegistry().show(resourceLoadTab);
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void goToLogs() {
|
||||
getTabsRegistry().show(logsTab);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void goToCompanyLimitingResources() {
|
||||
getTabsRegistry().show(limitingResourcesTab);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue