001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.system.audit; 020 021import java.io.IOException; 022import java.util.HashMap; 023import java.util.Map; 024 025import org.w3c.dom.Document; 026import org.w3c.dom.Node; 027 028import icy.file.FileUtil; 029import icy.gui.frame.progress.CancelableProgressFrame; 030import icy.gui.main.MainFrame; 031import icy.main.Icy; 032import icy.network.NetworkUtil; 033import icy.plugin.abstract_.Plugin; 034import icy.preferences.ApplicationPreferences; 035import icy.preferences.GeneralPreferences; 036import icy.preferences.XMLPreferences; 037import icy.system.IcyExceptionHandler; 038import icy.system.SystemUtil; 039import icy.system.thread.ThreadUtil; 040import icy.util.StringUtil; 041import icy.util.XMLUtil; 042 043/** 044 * General audit tools class. 045 * 046 * @author Stephane 047 */ 048public class Audit 049{ 050 // network URL 051 static final String URL_REGISTER = NetworkUtil.WEBSITE_URL + "register/registerClient.php?"; 052 static final String URL_LINK_USER = NetworkUtil.WEBSITE_URL + "register/linkUser.php?"; 053 static final String URL_GET_USER_INFO = NetworkUtil.WEBSITE_URL + "register/getLinkedUserInfo.php?"; 054 static final String URL_AUDIT_VERSION = NetworkUtil.WEBSITE_URL + "register/auditVersion.php?"; 055 static final String URL_AUDIT_PLUGIN = NetworkUtil.WEBSITE_URL + "register/auditPlugin.php?"; 056 057 // prefs & network id 058 static final String ID_REQUEST = "requestId"; 059 static final String ID_ACTION = "action"; 060 static final String ID_ICY_ID = "IcyId"; 061 static final String ID_CLIENT_ARCH = "clientArch"; 062 static final String ID_CLIENT_ID = "clientId"; 063 static final String ID_CLIENT_VERSION = "clientVersion"; 064 static final String ID_CLIENT_CPUNUMBER = "clientCpuNumber"; 065 static final String ID_CLIENT_TOTAL_MEMORY = "clientTotalMemory"; 066 static final String ID_CLIENT_MAXJAVA_MEMORY = "clientMaxJavaMemory"; 067 static final String ID_JAVA_NAME = "javaName"; 068 static final String ID_JAVA_VERSION = "javaVersion"; 069 static final String ID_JAVA_ARCH = "javaArch"; 070 static final String ID_LAST_UPLOAD_DATE = "lastUploadDate"; 071 072 // xml id 073 static final String XMLID_CLIENT_ID_REQUESTED = "client_id_requested"; 074 static final String XMLID_USER_LOGIN = "user_login"; 075 static final String XMLID_USER_NAME = "user_name"; 076 077 // directly use application preferences here 078 static XMLPreferences prefs; 079 080 static AuditStorage storage; 081 private static boolean initialized = false; 082 private static boolean auditDone; 083 084 /** 085 * Audit process on application start.<br> 086 * Check id, register... 087 */ 088 public static synchronized void prepare() 089 { 090 if (initialized) 091 return; 092 093 // get preferences 094 prefs = ApplicationPreferences.getPreferences(); 095 096 // probably a new installation --> need to reset id 097 if (needToResetId()) 098 { 099 // reset user info if needed 100 unlinkUser(); 101 // reset id 102 ApplicationPreferences.setId(-1); 103 } 104 105 // store current infos 106 storeInfos(); 107 // init audit storage 108 storage = new AuditStorage(); 109 110 final int id = ApplicationPreferences.getId(); 111 112 // id assigned ? 113 if (id != -1) 114 { 115 final long currentTime = System.currentTimeMillis(); 116 final long dayInterval = 1000 * 60 * 60 * 24; 117 118 // upload each 24 hours 119 if (currentTime > (prefs.getLong(ID_LAST_UPLOAD_DATE, 0L) + dayInterval)) 120 { 121 // save upload time whatever happened 122 prefs.putLong(ID_LAST_UPLOAD_DATE, System.currentTimeMillis()); 123 124 // do that in background as it can take sometime if website does not reply... 125 ThreadUtil.bgRun(new Runnable() 126 { 127 @Override 128 public void run() 129 { 130 // upload usage statistics 131 storage.upload(id); 132 } 133 }); 134 } 135 } 136 137 initialized = true; 138 auditDone = false; 139 } 140 141 /** 142 * Audit process on network connection 143 */ 144 public static void onConnect() 145 { 146 prepare(); 147 148 if (!auditDone) 149 processIdAudit(); 150 151 updateUserLink(); 152 153 // refresh user infos (in title) 154 final MainFrame frame = Icy.getMainInterface().getMainFrame(); 155 if (frame != null) 156 frame.refreshTitle(); 157 } 158 159 /** 160 * Save audit data 161 */ 162 public static void save() 163 { 164 // save audit data 165 if (initialized) 166 storage.save(); 167 } 168 169 /** 170 * Plugin launched event audit 171 */ 172 public static void pluginLaunched(Plugin plugin) 173 { 174 // we don't want to wait for initialization here (can lock the application loading for sometime) 175 // and we don't care about init usage stats (ROI and daemons plugins)... 176 if (!initialized) 177 return; 178 179 storage.pluginLaunched(plugin); 180 } 181 182 /** 183 * Plugin instanced event audit 184 */ 185 public static void pluginInstanced(Plugin plugin) 186 { 187 // we don't want to wait for initialization here (can lock the application loading for sometime) 188 // and we don't care about init usage stats (ROI and daemons plugins)... 189 if (!initialized) 190 return; 191 192 storage.pluginInstanced(plugin); 193 } 194 195 /** 196 * Returns <code>true</code> if we need to reset the internal id (usually mean new installation) 197 */ 198 private static boolean needToResetId() 199 { 200 if (!StringUtil.equals(ApplicationPreferences.getOs(), SystemUtil.getOSArchIdString())) 201 return true; 202 203 final int cpu = prefs.getInt(ID_CLIENT_CPUNUMBER, 0); 204 final long mem = prefs.getLong(ID_CLIENT_TOTAL_MEMORY, 0); 205 final String appFolder = ApplicationPreferences.getAppFolder(); 206 207 // ignore difference on first launch else it will regenerate id for everyone 208 if ((cpu != 0) && (cpu != SystemUtil.getNumberOfCPUs())) 209 return true; 210 if ((mem != 0) && (mem != SystemUtil.getTotalMemory())) 211 return true; 212 if (!StringUtil.isEmpty(appFolder) && !StringUtil.equals(appFolder, FileUtil.APPLICATION_DIRECTORY)) 213 return true; 214 215 return false; 216 } 217 218 /** 219 * Store system and application informations in preferences 220 */ 221 private static void storeInfos() 222 { 223 ApplicationPreferences.setOs(SystemUtil.getOSArchIdString()); 224 prefs.putInt(ID_CLIENT_CPUNUMBER, SystemUtil.getNumberOfCPUs()); 225 prefs.putLong(ID_CLIENT_TOTAL_MEMORY, SystemUtil.getTotalMemory()); 226 ApplicationPreferences.setAppFolder(FileUtil.APPLICATION_DIRECTORY); 227 } 228 229 private static void processIdAudit() 230 { 231 final Map<String, String> values = new HashMap<String, String>(); 232 final int id = ApplicationPreferences.getId(); 233 234 values.put(ID_CLIENT_ARCH, SystemUtil.getOSArchIdString()); 235 values.put(ID_CLIENT_VERSION, Icy.version.toString()); 236 237 // need to register 238 if (id == -1) 239 { 240 // ask for registration 241 values.put(ID_REQUEST, "1"); 242 243 final Document doc = XMLUtil.loadDocument(URL_REGISTER + NetworkUtil.getContentString(values)); 244 245 if (doc != null) 246 { 247 final Node root = XMLUtil.getRootElement(doc); 248 final int newId = XMLUtil.getElementIntValue(root, XMLID_CLIENT_ID_REQUESTED, -1); 249 250 // valid id --> save it 251 if (newId != -1) 252 ApplicationPreferences.setId(newId); 253 } 254 } 255 else 256 { 257 // just audit infos 258 values.put(ID_CLIENT_ID, Integer.toString(id)); 259 260 values.put(ID_CLIENT_CPUNUMBER, Integer.toString(SystemUtil.getNumberOfCPUs())); 261 values.put(ID_CLIENT_TOTAL_MEMORY, Long.toString(SystemUtil.getTotalMemory() / 10485760L)); 262 values.put(ID_CLIENT_MAXJAVA_MEMORY, Long.toString(SystemUtil.getJavaMaxMemory() / 10485760L)); 263 values.put(ID_JAVA_NAME, SystemUtil.getJavaName()); 264 values.put(ID_JAVA_VERSION, SystemUtil.getJavaVersion()); 265 values.put(ID_JAVA_ARCH, Integer.toString(SystemUtil.getJavaArchDataModel())); 266 267 try 268 { 269 NetworkUtil.postData(URL_AUDIT_VERSION, values); 270 } 271 catch (IOException e) 272 { 273 // silent fail... 274 // IcyExceptionHandler.showErrorMessage(e, false, false); 275 } 276 } 277 278 auditDone = true; 279 } 280 281 private static Map<String, String> getIdParam() 282 { 283 final int id = ApplicationPreferences.getId(); 284 285 // id ok ? 286 if (id != -1) 287 { 288 final Map<String, String> values = new HashMap<String, String>(); 289 290 // set id 291 values.put(ID_ICY_ID, Integer.toString(id)); 292 293 return values; 294 } 295 296 return null; 297 } 298 299 public static void updateUserLink() 300 { 301 final Map<String, String> params = getIdParam(); 302 303 // id param ok ? 304 if (params != null) 305 { 306 // and retrieve user infos 307 final Document doc = XMLUtil.loadDocument(URL_GET_USER_INFO + NetworkUtil.getContentString(params)); 308 309 if (doc != null) 310 { 311 final Node root = XMLUtil.getRootElement(doc); 312 313 // set attached user login and name 314 GeneralPreferences.setUserLogin(XMLUtil.getElementValue(root, XMLID_USER_LOGIN, "")); 315 GeneralPreferences.setUserName(XMLUtil.getElementValue(root, XMLID_USER_NAME, "")); 316 } 317 } 318 } 319 320 public static boolean isUserLinked() 321 { 322 return !StringUtil.isEmpty(GeneralPreferences.getUserLogin()); 323 } 324 325 public static void linkUser() 326 { 327 final int id = ApplicationPreferences.getId(); 328 329 // id ok ? 330 if (id != -1) 331 { 332 // launch browser with link identity request 333 NetworkUtil.openBrowser(URL_LINK_USER + ID_ICY_ID + "=" + id + "&" + ID_ACTION + "=link"); 334 335 if (!Icy.getMainInterface().isHeadLess()) 336 { 337 // display linking in progress 338 new Thread(new Runnable() 339 { 340 @Override 341 public void run() 342 { 343 final CancelableProgressFrame waitFrame = new CancelableProgressFrame( 344 "Waiting for user to link account..."); 345 346 while (!Thread.interrupted() && !waitFrame.isCancelRequested()) 347 { 348 try 349 { 350 Thread.sleep(1000); 351 updateUserLink(); 352 353 // user linked ! 354 if (isUserLinked()) 355 { 356 // stop wait 357 waitFrame.cancel(); 358 359 // refresh user infos (in title) 360 final MainFrame frame = Icy.getMainInterface().getMainFrame(); 361 if (frame != null) 362 frame.refreshTitle(); 363 } 364 } 365 catch (InterruptedException e) 366 { 367 waitFrame.cancel(); 368 } 369 } 370 371 // close wait frame 372 waitFrame.close(); 373 } 374 }).start(); 375 } 376 } 377 } 378 379 public static void unlinkUser() 380 { 381 final Map<String, String> params = getIdParam(); 382 383 // id param ok ? 384 if (params != null) 385 { 386 // do that in background as it can take sometime... 387 ThreadUtil.bgRun(new Runnable() 388 { 389 @Override 390 public void run() 391 { 392 // set action 393 params.put(ID_ACTION, "unlink"); 394 395 try 396 { 397 // and post 398 NetworkUtil.postData(URL_LINK_USER, params); 399 } 400 catch (IOException e) 401 { 402 // can't unlink on web site, not a big deal... 403 System.err.print("Warning: cannot unlink online user infos."); 404 IcyExceptionHandler.showErrorMessage(e, false, false); 405 } 406 } 407 }); 408 } 409 410 // reset attached user login and name 411 GeneralPreferences.setUserLogin(""); 412 GeneralPreferences.setUserName(""); 413 } 414}