/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package staticContent.framework.clock; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.util.Timer; import java.util.TimerTask; import staticContent.framework.config.Settings; public class NtpClock { /** * The local clock's offset (compared to the SNTP server) (accuracy: ~10ms). */ private static long localClockOffset; /** Datagram socket to communicate with SNTP server. */ private static DatagramSocket timeServerSocket; /** * The SNTP server's address. A list of servers can be found * <a href="http://www.hullen.de/helmut/filebox/DCF77/ntpsrvr.html"> here * </a>. Supports URLs and IPv4 Addresses. */ private static InetAddress timeServerHost; /** Indicates whether to synchronize clock (with SNTP server) or not. */ private static boolean useSynchronizedClock; private Settings settings; public NtpClock(Settings settings) { this.settings = settings; clockSync(); } /** * Retrieves an SNTP message from a SNTP server and calculates the local * clock's offset. The local clock's offset calculation is implemented * according to the SNTP algorithm specified in RFC 2030. */ private static void synchronizeClocks() { System.out.println("(Clock) Trying to synchronize clocks."); try { // send request byte[] buffer = new NtpMessage().toByteArray(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, timeServerHost, 123); timeServerSocket.send(packet); // get response System.out.println("(Clock) Request sent. Waiting for response."); packet = new DatagramPacket(buffer, buffer.length); timeServerSocket.receive(packet); System.out.println("(Clock) Response received."); double localTime = (System.currentTimeMillis() / 1000.0) + 2208988800.0; // time server's format is relative to 1900, Java's to 1970 => + 2208988800.0 NtpMessage ntpMessage = new NtpMessage(packet.getData()); // calculate local clock's offset in ms localClockOffset = (long)((((ntpMessage.receiveTimestamp - ntpMessage.originateTimestamp) + (ntpMessage.transmitTimestamp - localTime)) / 2) * 1000 // use ms for appropriate // accuracy (before casting to // long) ); System.out.println("(Clock) Local clock's offset: " + localClockOffset + " ms"); } catch (IOException e) { e.printStackTrace(); } } /** * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT * (The typical accuracy of a SNTP client/server exchange is fractions of a * second.) * * @return The number of milliseconds since January 1, 1970, 00:00:00 GMT. */ public long getTime() { return ((useSynchronizedClock) ? (System.currentTimeMillis() + localClockOffset) : System.currentTimeMillis() ); } private void clockSync() { /** * Simple <code>TimerTask</code>, which calls the method <code> * synchronizeClocks()</code>. */ final class SynchronizationTask extends TimerTask { /** * Calls the method <code>synchronizeClocks()</code>. */ @Override public void run() { synchronizeClocks(); } } useSynchronizedClock = settings.getPropertyAsBoolean("USE_SYNCHRONIZED_CLOCK"); if (useSynchronizedClock) { try { timeServerSocket = new DatagramSocket(); timeServerHost = settings.getPropertyAsInetAddress("TIME_SERVER_HOST"); // synchronize clocks for the first time synchronizeClocks(); // synchronize clocks every "updateRate" ms long updateRate = settings.getPropertyAsLong("TIME_BETWEEN_CLOCK_SYNCHRONIZATIONS"); // time between clock synchronizations if (updateRate != 0) new Timer().schedule(new SynchronizationTask(), updateRate, updateRate); } catch (SocketException e) { e.printStackTrace(); } } } }