/******************************************************************************* * Copyright (c) 2011, 2016 Eurotech and/or its affiliates * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.windows.clock; import java.util.Date; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.eclipse.kura.KuraErrorCode; import org.eclipse.kura.KuraException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractNtpClockSyncProvider implements ClockSyncProvider { private static final Logger s_logger = LoggerFactory.getLogger(AbstractNtpClockSyncProvider.class); protected Map<String, Object> m_properties; protected ClockSyncListener m_listener; protected String m_ntpHost; protected int m_ntpPort; protected int m_ntpTimeout; protected int m_retryInterval; protected int m_refreshInterval; protected Date m_lastSync; protected ScheduledExecutorService m_scheduler; protected int m_maxRetry; protected int m_numRetry; protected boolean m_isSynced; protected int m_syncCount; @Override public void init(Map<String, Object> properties, ClockSyncListener listener) throws KuraException { this.m_properties = properties; this.m_listener = listener; readProperties(); } @Override public void start() throws KuraException { if (this.m_refreshInterval < 0) { // Never do any update. So Nothing to do. s_logger.info("No clock update required"); if (this.m_scheduler != null) { this.m_scheduler.shutdown(); this.m_scheduler = null; } } else if (this.m_refreshInterval == 0) { // Perform one clock update - but in a thread. s_logger.info("Perform clock update just once"); if (this.m_scheduler != null) { this.m_scheduler.shutdown(); this.m_scheduler = null; } this.m_scheduler = Executors.newSingleThreadScheduledExecutor(); // call recursive retry method for setting the clock scheduleOnce(); } else { final int retryInt; if (this.m_retryInterval <= 0) { retryInt = 1; } else { retryInt = this.m_retryInterval; } // Perform periodic clock updates. s_logger.info("Perform periodic clock updates every {} sec", this.m_refreshInterval); if (this.m_scheduler != null) { this.m_scheduler.shutdown(); this.m_scheduler = null; } this.m_scheduler = Executors.newSingleThreadScheduledExecutor(); this.m_scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { Thread.currentThread().setName("AbstractNtpClockSyncProvider:schedule"); if (!AbstractNtpClockSyncProvider.this.m_isSynced) { AbstractNtpClockSyncProvider.this.m_syncCount = 0; try { s_logger.info("Try to sync clock ({})", AbstractNtpClockSyncProvider.this.m_numRetry); if (syncClock()) { s_logger.info("Clock synced"); AbstractNtpClockSyncProvider.this.m_isSynced = true; AbstractNtpClockSyncProvider.this.m_numRetry = 0; } } catch (KuraException e) { AbstractNtpClockSyncProvider.this.m_numRetry++; s_logger.error("Error Synchronizing Clock", e); if (AbstractNtpClockSyncProvider.this.m_numRetry >= AbstractNtpClockSyncProvider.this.m_maxRetry) { s_logger.error( "Failed to synchronize System Clock. Exhausted retry attempts, giving up"); AbstractNtpClockSyncProvider.this.m_isSynced = true; } } } else { AbstractNtpClockSyncProvider.this.m_syncCount++; if (AbstractNtpClockSyncProvider.this.m_syncCount * retryInt >= AbstractNtpClockSyncProvider.this.m_refreshInterval - 1) { AbstractNtpClockSyncProvider.this.m_isSynced = false; AbstractNtpClockSyncProvider.this.m_numRetry = 0; } } } }, 0, retryInt, TimeUnit.SECONDS); } } private void scheduleOnce() { if (this.m_scheduler != null) { this.m_scheduler.schedule(new Runnable() { @Override public void run() { Thread.currentThread().setName("AbstractNtpClockSyncProvider:scheduleOnce"); try { syncClock(); } catch (KuraException e) { s_logger.error("Error Synchronizing Clock - retrying", e); scheduleOnce(); } } }, 1, TimeUnit.SECONDS); } } @Override public void stop() throws KuraException { if (this.m_scheduler != null) { this.m_scheduler.shutdown(); this.m_scheduler = null; } } @Override public Date getLastSync() { return this.m_lastSync; } // ---------------------------------------------------------------- // // Private/Protected Methods // // ---------------------------------------------------------------- private void readProperties() throws KuraException { this.m_ntpHost = (String) this.m_properties.get("clock.ntp.host"); if (this.m_ntpHost == null) { throw new KuraException(KuraErrorCode.CONFIGURATION_REQUIRED_ATTRIBUTE_MISSING, "clock.ntp.host"); } this.m_ntpPort = 123; if (this.m_properties.containsKey("clock.ntp.port")) { this.m_ntpPort = (Integer) this.m_properties.get("clock.ntp.port"); } this.m_ntpTimeout = 10000; if (this.m_properties.containsKey("clock.ntp.timeout")) { this.m_ntpTimeout = (Integer) this.m_properties.get("clock.ntp.timeout"); } this.m_retryInterval = 0; if (this.m_properties.containsKey("clock.ntp.retry.interval")) { this.m_retryInterval = (Integer) this.m_properties.get("clock.ntp.retry.interval"); } this.m_refreshInterval = 0; if (this.m_properties.containsKey("clock.ntp.refresh-interval")) { this.m_refreshInterval = (Integer) this.m_properties.get("clock.ntp.refresh-interval"); } this.m_maxRetry = 0; if (this.m_properties.containsKey("clock.ntp.max-retry")) { this.m_maxRetry = (Integer) this.m_properties.get("clock.ntp.max-retry"); } } protected abstract boolean syncClock() throws KuraException; }