diff --git a/modules/desktop/src/com/haulmont/cuba/desktop/App.java b/modules/desktop/src/com/haulmont/cuba/desktop/App.java index a9d4e7bc1c..e7d58fb55f 100644 --- a/modules/desktop/src/com/haulmont/cuba/desktop/App.java +++ b/modules/desktop/src/com/haulmont/cuba/desktop/App.java @@ -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() { diff --git a/modules/desktop/src/com/haulmont/cuba/desktop/DesktopConfig.java b/modules/desktop/src/com/haulmont/cuba/desktop/DesktopConfig.java index 2f7e83c642..5d6e1e0dbf 100644 --- a/modules/desktop/src/com/haulmont/cuba/desktop/DesktopConfig.java +++ b/modules/desktop/src/com/haulmont/cuba/desktop/DesktopConfig.java @@ -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(); diff --git a/modules/desktop/src/com/haulmont/cuba/desktop/sys/ClientTimeSynchronizer.java b/modules/desktop/src/com/haulmont/cuba/desktop/sys/ClientTimeSynchronizer.java new file mode 100644 index 0000000000..c2794233ef --- /dev/null +++ b/modules/desktop/src/com/haulmont/cuba/desktop/sys/ClientTimeSynchronizer.java @@ -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. + *

+ * Time zone is taken from server. + * Time is synchronized after each login. Additionally it can be performed by scheduled task (using spring scheduler). + *

+ * 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 { + protected ObtainServerTimeZoneTask() { + super(TIMEOUT_SEC); + } + + @Override + public Void run(TaskLifeCycle 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 { + public UpdateTimeOffsetTask() { + super(TIMEOUT_SEC); + } + + @Override + public Void run(TaskLifeCycle 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; + } + } +} diff --git a/modules/desktop/src/com/haulmont/cuba/desktop/sys/DesktopTimeSource.java b/modules/desktop/src/com/haulmont/cuba/desktop/sys/DesktopTimeSource.java index 0e22e6126d..cd0308a2e8 100644 --- a/modules/desktop/src/com/haulmont/cuba/desktop/sys/DesktopTimeSource.java +++ b/modules/desktop/src/com/haulmont/cuba/desktop/sys/DesktopTimeSource.java @@ -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; } }