refs #1746 Synchronize time for desktop client

This commit is contained in:
Alexander Budarov 2013-01-28 13:36:02 +00:00
parent b0b94e155f
commit 68dadbb35a
4 changed files with 134 additions and 55 deletions

View File

@ -7,15 +7,11 @@
package com.haulmont.cuba.desktop;
import com.haulmont.cuba.client.sys.MessagesClientImpl;
import com.haulmont.cuba.core.app.ServerInfoService;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.remoting.ClusterInvocationSupport;
import com.haulmont.cuba.desktop.exception.ExceptionHandlers;
import com.haulmont.cuba.desktop.sys.DesktopAppContextLoader;
import com.haulmont.cuba.desktop.sys.DesktopWindowManager;
import com.haulmont.cuba.desktop.sys.MainWindowProperties;
import com.haulmont.cuba.desktop.sys.MenuBuilder;
import com.haulmont.cuba.desktop.sys.*;
import com.haulmont.cuba.desktop.theme.DesktopTheme;
import com.haulmont.cuba.desktop.theme.DesktopThemeLoader;
import com.haulmont.cuba.gui.AppConfig;
@ -495,7 +491,7 @@ public class App implements ConnectionListener {
windowManager.setTabsPane(tabsPane);
initExceptionHandlers(true);
initTimeZone();
initClientTime();
SwingUtilities.invokeLater(new Runnable() {
@Override
@ -555,14 +551,10 @@ public class App implements ConnectionListener {
}
}
protected void initTimeZone() {
DesktopConfig desktopConfig = configuration.getConfig(DesktopConfig.class);
if (desktopConfig.isUseServerTimeZone()) {
ServerInfoService serverInfoService = AppBeans.get(ServerInfoService.NAME);
TimeZone serverTimeZone = serverInfoService.getTimeZone();
TimeZone.setDefault(serverTimeZone);
log.info("Time zone set to " + serverTimeZone);
}
protected void initClientTime() {
ClientTimeSynchronizer clientTimeSynchronizer = AppBeans.get(ClientTimeSynchronizer.NAME);
clientTimeSynchronizer.syncTimeZone();
clientTimeSynchronizer.syncTime();
}
public TopLevelFrame getMainFrame() {

View File

@ -69,6 +69,9 @@ public interface DesktopConfig extends Config {
@DefaultBoolean(true)
boolean isUseServerTimeZone();
/**
* @return true if application should synchronize its time source with server time
*/
@Property("cuba.desktop.useServerTime")
@DefaultBoolean(true)
boolean isUseServerTime();

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.desktop.sys;
import com.haulmont.cuba.core.app.ServerInfoService;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.desktop.App;
import com.haulmont.cuba.desktop.DesktopConfig;
import com.haulmont.cuba.gui.executors.BackgroundTask;
import com.haulmont.cuba.gui.executors.BackgroundWorker;
import com.haulmont.cuba.gui.executors.TaskLifeCycle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.ManagedBean;
import javax.inject.Inject;
import java.util.TimeZone;
/**
* Maintains desktop client time zone and time.
* <p/>
* Time zone is taken from server.
* Time is synchronized after each login. Additionally it can be performed by scheduled task (using spring scheduler).
* <p/>
* All requests to server are performed in background thread to avoid delays in UI.
*
* @author Alexander Budarov
* @version $Id$
*/
@ManagedBean(ClientTimeSynchronizer.NAME)
public class ClientTimeSynchronizer {
public static final String NAME = "cuba_ClientTimeSynchronizer";
protected static final int TIMEOUT_SEC = 60;
private Log log = LogFactory.getLog(ClientTimeSynchronizer.class);
@Inject
protected ServerInfoService serverInfoService;
@Inject
protected Configuration configuration;
@Inject
protected BackgroundWorker backgroundWorker;
@Inject
protected DesktopTimeSource timeSource;
/**
* @see com.haulmont.cuba.desktop.DesktopConfig#isUseServerTimeZone()
*/
public void syncTimeZone() {
boolean useServerTimeZone = configuration.getConfig(DesktopConfig.class).isUseServerTimeZone();
if (useServerTimeZone) {
backgroundWorker.handle(new ObtainServerTimeZoneTask()).execute();
}
}
/**
* @see com.haulmont.cuba.desktop.DesktopConfig#isUseServerTime()
*/
public void syncTime() {
if (!AppContext.isStarted()) {
return;
}
boolean useServerTime = configuration.getConfig(DesktopConfig.class).isUseServerTime();
boolean connected = App.getInstance() != null && App.getInstance().getConnection() != null
&& App.getInstance().getConnection().isConnected();
if (useServerTime && connected) {
backgroundWorker.handle(new UpdateTimeOffsetTask()).execute();
}
}
protected class ObtainServerTimeZoneTask extends BackgroundTask<Void, Void> {
protected ObtainServerTimeZoneTask() {
super(TIMEOUT_SEC);
}
@Override
public Void run(TaskLifeCycle<Void> taskLifeCycle) throws Exception {
TimeZone serverTimeZone = serverInfoService.getTimeZone();
TimeZone.setDefault(serverTimeZone); // works OK from any thread
log.info("Time zone set to " + serverTimeZone);
return null;
}
}
protected class UpdateTimeOffsetTask extends BackgroundTask<Void, Void> {
public UpdateTimeOffsetTask() {
super(TIMEOUT_SEC);
}
@Override
public Void run(TaskLifeCycle<Void> taskLifeCycle) throws Exception {
long serverTime = serverInfoService.getTimeMillis();
long timeOffset = serverTime - System.currentTimeMillis();
timeSource.setTimeOffset(timeOffset); // works OK from any thread
log.info("Using server time, offset=" + timeOffset + "ms");
return null;
}
}
}

View File

@ -6,18 +6,8 @@
package com.haulmont.cuba.desktop.sys;
import com.haulmont.cuba.core.app.ServerInfoService;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.desktop.App;
import com.haulmont.cuba.desktop.Connection;
import com.haulmont.cuba.desktop.ConnectionListener;
import com.haulmont.cuba.desktop.DesktopConfig;
import com.haulmont.cuba.security.global.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.inject.Inject;
import java.util.Date;
/**
@ -27,37 +17,14 @@ import java.util.Date;
* @author krivopustov
* @version $Id$
*/
public class DesktopTimeSource implements TimeSource, ConnectionListener {
public class DesktopTimeSource implements TimeSource {
protected Log log = LogFactory.getLog(getClass());
protected boolean useServerTime;
protected volatile long timeOffset;
@Inject
protected ServerInfoService serverInfo;
public DesktopTimeSource() {
App app = App.getInstance();
if (app != null) // can be null in tests
app.getConnection().addListener(this);
}
@Inject
public void setConfiguration(Configuration configuration) {
useServerTime = configuration.getConfig(DesktopConfig.class).isUseServerTime();
}
@Override
public void connectionStateChanged(Connection connection) throws LoginException {
if (connection.isConnected() && useServerTime) {
long serverTime = serverInfo.getTimeMillis();
timeOffset = serverTime - System.currentTimeMillis();
log.info("Using server time, offset=" + timeOffset + "ms");
}
}
/**
* Time offset (time difference between server and client machine).
* It will be used to correct time obtained from system clock.
*/
// @GuardedBy(this)
protected long timeOffset;
@Override
public Date currentTimestamp() {
@ -66,6 +33,15 @@ public class DesktopTimeSource implements TimeSource, ConnectionListener {
@Override
public long currentTimeMillis() {
return System.currentTimeMillis() + timeOffset;
return System.currentTimeMillis() + getTimeOffset();
}
/* Must be used to access time offset */
protected synchronized long getTimeOffset() {
return timeOffset;
}
public synchronized void setTimeOffset(long timeOffset) {
this.timeOffset = timeOffset;
}
}