summaryrefslogtreecommitdiff
path: root/java/netsvcs/Time/TSClerkProcessor.java
blob: d7b1773cc9eead1f38a44135fda3e0e9b730ab08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*************************************************
 *
 * = PACKAGE
 *    netsvcs.Time
 *
 * = FILENAME
 *    TSClerkProcessor.java
 *
 *@author Prashant Jain, Everett Anderson
 *
 *************************************************/
package netsvcs.Time;

import java.io.*;
import java.net.*;
import java.util.*;
import JACE.OS.*;
import JACE.Misc.*;
import JACE.Connection.*;
import JACE.Reactor.*;
import JACE.ASX.TimeValue;

/**
 *
 * <hr>
 * <p><h2>SYNOPSIS</h2>
 *
 * <blockquote>Monitors a specified port (default 7989) and launches
 * TSClerkHandlers when connections are made.  The handlers communicate
 * with servers and calculate the difference between the server time
 * and local time.  The Clerk Processor averages these differences
 * and reports them to clients.</blockquote>
 *
 * <p><h2>DESCRIPTION</h2>
 *
 * <blockquote>This doesn't actually change the system clock, but it
 * provides the average of the differences of the local and server
 * times.  A client could use this information to adjust the clock, or
 * just use the midpoint to determine the correct network time.</blockquote>
 *
 */
public class TSClerkProcessor extends Connector implements Runnable
{
  /**
   * Default constructor
   */
  public TSClerkProcessor ()
  {
    this.serverArray_ = new Vector ();

  }

  /**
   * Parse the command line, setup the TSRequestAcceptor, and run
   * the Clerk Processor in its own thread.
   */
  public int init (String [] args)
    {
      // Parse arguments
      this.parseArgs (args);
      
      TSRequestAcceptor ra = new TSRequestAcceptor (this);
      ra.init (args);

      // Run in own thread of control so that we don't block the caller
      new Thread (this).start ();
      return 0;
    }


  /**
   * Makes connections to the servers, schedules itself for intervals
   * to update the delta time.
   */
  public void run ()
  {

    // Set up connections with all servers
    Enumeration table = this.serverArray_.elements ();
    while (table.hasMoreElements ())
      {
	this.initiateConnection((TSClerkHandler)table.nextElement());
      }

    // Set up timer
    this.timer_id_ = this.tq_.scheduleTimer (this,
					     null,
					     new TimeValue (this.timeout_),
					     new TimeValue (this.timeout_));
  }

  /**
   * Makes connections to the servers.
   */
  public void initiateConnection (TSClerkHandler handler)
  {
    this.open (handler.hostname(), handler.port());

    try
      {
	// Connect to the server
	this.connect (handler);

	// Set the state of the Clerk Handler so it queries the
	// server at intervals.
	handler.state(TSClerkHandler.CONNECTED);
	
      }
    catch (UnknownHostException e)
      {
	ACE.ERROR (e);
      }
    catch (SocketException e)
      {
	ACE.ERROR ("Connection refused");
      }
    catch (InstantiationException e)
      {
	ACE.ERROR (e);
      }
    catch (IllegalAccessException e)
      {
	ACE.ERROR (e);
      }
    catch (IOException e)
      {
	ACE.ERROR (e);
      }
  }


  /**
   *
   * Called by the timer queue.  Calls updateTime().
   */
  public int handleTimeout (TimeValue tv, Object obj)
  {
    return this.updateTime ();
  }

  /**
   * Calculates the delta time by averaging the results from
   * Clerk Handler delta()'s.  It only includes handlers whose
   * state is currently CONNECTED.  If they're not connected, it
   * reschedules them to begin the error correction process of
   * trying to reconnect to the server (possible synch problems?).
   */
  protected int updateTime ()
  {
    TSClerkHandler handler;
    int count = 0;
    long totalDeltaTime = 0;

    Enumeration table = this.serverArray_.elements ();

    while (table.hasMoreElements ())
      {
	handler = (TSClerkHandler) table.nextElement ();

	if (handler.state() != TSClerkHandler.CONNECTED) {

	  // Reconnecting state means we don't need to put
	  // it in the timer queue again
	  if (handler.state() == TSClerkHandler.RECONNECTING)
	    continue;
	  else
	  if (handler.state() == TSClerkHandler.DISCONNECTED) 
	    handler.state(TSClerkHandler.RECONNECTING);

	  handler.errorRecovery();
	  continue;
	}

	long delta = handler.delta();

	ACE.DEBUG(handler.hostname() + ": " + delta);

	totalDeltaTime += delta;
	count++;
      }

    if (count > 0) {

      this.timeDelta_ = totalDeltaTime / count;

      ACE.DEBUG("Average deviation: " + totalDeltaTime/count);

    } else

      this.timeDelta_ = 0;

    return 0;
  }

  /**
   * Return the delta time.
   */
  public long getDelta()
    {
      return this.timeDelta_;
    }

  /**
   * Parse the command line.  Watches for -t <time> and
   * -h <machine:port> switches.  Must specify time
   * value before host switches!
   */
  protected void parseArgs (String args[])
  {
    String s;
    GetOpt opt = new GetOpt (args, "t:h:");
    for (int c; (c = opt.next ()) != -1; )
      {
	switch (c)
	  {
	  case 't':
	    s = opt.optarg ();
	    this.timeout_ = (new Integer (s)).intValue ();
	    break;
	  case 'h':
	    s = opt.optarg ();
	    this.addNewHandler (s);
	    break;
	  default:
	    ACE.ERROR ("Bad command line argument: " + c);

	    ACE.ERROR ("Valid arguments: -t <timeout> -h <hostname>:<port> -h ...");
	    break;
	  }
      }
  }
  
  /**
   *
   * Creates a new Clerk Handler and adds it to the serverArray_
   */
  private void addNewHandler (String s)
  {
    StringTokenizer tokens = new StringTokenizer (s, ":");
    String hostname = tokens.nextToken ();

    int port = (new Integer (tokens.nextToken ())).intValue ();
    
    // Create new handler and add it to array of servers
    this.serverArray_.addElement (new TSClerkHandler (hostname, 
						      port,
						      this.tq_,
						      this.timeout_,
						      this));
  }
  
  // Vector of TSClerkHandlers, one for each server
  private Vector serverArray_;

  // Default interval at which to update the time
  private int timeout_ = 1000;

  // Timer queue which calls handleTimeout when the Clerk Processor
  // is supposed to update the time.
  private TimerQueue tq_ = new TimerQueue (true);

  // Clerk Processor ID in the timer queue
  private int timer_id_;

  // Average of the differences of the local and server times.
  private long timeDelta_;
}