-
Notifications
You must be signed in to change notification settings - Fork 172
/
RecoveryManager.java
497 lines (414 loc) · 14.2 KB
/
RecoveryManager.java
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1999-2001 by HP Bluestone Software, Inc. All rights Reserved.
*
* HP Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: RecoveryManager.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.arjuna.recovery;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Vector;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.utils.Utility;
import com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery;
import com.arjuna.ats.internal.arjuna.recovery.RecoveryManagerImple;
import com.arjuna.ats.internal.arjuna.recovery.RecoveryManagerStatus;
import com.arjuna.common.util.ConfigurationInfo;
class ScanThread extends Thread
{
public ScanThread (RecoveryManagerImple theImple, RecoveryScan callback)
{
super("RecoveryManagerScanThread");
_theImple = theImple;
_callback = callback;
setDaemon(true);
}
public void run ()
{
if (_theImple != null)
{
_theImple.scan();
if (_callback != null)
_callback.completed();
}
}
private RecoveryManagerImple _theImple;
private RecoveryScan _callback;
}
/**
* The RecoveryManager daemon.
*/
public class RecoveryManager
{
/**
* In this mode the recovery manager runs periodically but may
* also be driven through messages or via the scan operation if
* it is embedded.
*/
public static final int INDIRECT_MANAGEMENT = 0;
/**
* In this mode the recovery manager does not run periodically and
* will only work if driven through messages or via the scan
* operation if it is embedded.
*/
public static final int DIRECT_MANAGEMENT = 1;
/**
* Obtain a reference to the RecoveryManager singleton. If it hasn't
* been created yet then it will be. The manager will be created in the
* INDIRECT_MANAGEMENT mode.
*
* @throws IllegalArgumentException thrown if the manager has already been
* created in a different mode to that requested.
*
* @return the manager.
*/
public static synchronized final RecoveryManager manager () throws IllegalArgumentException
{
if (_recoveryManager == null)
return manager(RecoveryManager.INDIRECT_MANAGEMENT);
return _recoveryManager;
}
/**
* Obtain a reference to the RecoveryManager singleton. If it hasn't
* been created yet then it will be. The manager can be created in a
* management mode defined by the parameter.
*
* @param mode the management mode for the manager.
*
* @throws IllegalArgumentException thrown if the manager has already been
* created in a different mode to that requested.
*
* @return the manager.
*/
public static synchronized final RecoveryManager manager (int mode) throws IllegalArgumentException
{
if (_recoveryManager == null)
_recoveryManager = new RecoveryManager(mode);
else
{
if (_recoveryManager.mode() != mode)
throw new IllegalArgumentException(tsLogger.i18NLogger.get_recovery_manager_already_started_in_different_mode(_recoveryManager.mode(), mode));
}
return _recoveryManager;
}
/**
* Delay the start of the recovery manager thread when creating an indirect recovery manager.
*/
public static synchronized void delayRecoveryManagerThread()
{
delayRecoveryManagerThread = true ;
}
/**
* Force a recovery scan now. This is a blocking operation
* and will only return once the recovery scan has completed.
*
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final void scan ()
{
checkState();
_theImple.scan();
}
/**
* Force a recovery scan now. This is a non-blocking operation
* and will return immediately. Notification of completion of the
* scan is done through the RecoveryScan object.
*
* @param callback callback The callback mechanism used to
* inform users that the scan has completed. If this is <code>null</code>
* then no callback will happen and asynchronous scanning will occur.
*
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final void scan (RecoveryScan callback)
{
checkState();
ScanThread st = new ScanThread(_theImple, callback);
st.start();
}
/**
* Terminate and cleanup the recovery manager. There is no going back from this. This is a
* synchronous operation so return means that the recovery has completed.
*
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final void terminate ()
{
terminate(false);
}
/**
* Terminate and cleanup the recovery manager. There is no going back from this. Can be called
* synchronous or asynchronously. If you have any intention of restarting the recovery manager
* later then you MUST use the async=false option.
*
* @param async false means wait for any recovery scan in progress to complete.
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final synchronized void terminate (boolean async)
{
checkState();
synchronized (RecoveryManager.class) {
_recoveryManager = null;
}
_theImple.stop(async);
_theImple = null;
}
/**
* If the recovery manager has been shutdown previously then recreate it in
* the same mode as before. Otherwise ignore.
*/
public final synchronized void initialize ()
{
if (_theImple == null)
{
if ((_mode == RecoveryManager.INDIRECT_MANAGEMENT) && !delayRecoveryManagerThread)
_theImple = new RecoveryManagerImple(true);
else
_theImple = new RecoveryManagerImple(false);
}
}
// does nothing when running embedded.
/**
* wait for the recovery thread to be shutdown. n.b. this will not return unless and until shutdown
* is called.
*
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public void waitForTermination ()
{
checkState();
_theImple.waitForTermination();
_theImple = null;
}
/**
* Suspend the recovery manager. If the recovery manager is in the process of
* doing recovery scans then it will be suspended afterwards, in order to
* preserve data integrity.
*
* @param async false means wait for the recovery manager to finish any scans before returning.
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public void suspend (boolean async)
{
trySuspend(async);
}
public RecoveryManagerStatus trySuspend(boolean async) {
checkState();
PeriodicRecovery.Mode mode = _theImple.trySuspendScan(async);
switch (mode) {
case ENABLED:
return RecoveryManagerStatus.ENABLED;
case SUSPENDED:
return RecoveryManagerStatus.SUSPENDED;
case TERMINATED:
return RecoveryManagerStatus.TERMINATED;
default:
throw new IllegalArgumentException("incompatible enum types");
}
}
/**
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public void resume ()
{
checkState();
_theImple.resumeScan();
}
/**
* Start the recovery manager thread.
*
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public void startRecoveryManagerThread()
{
checkState();
_theImple.start() ;
}
/**
* Add a recovery module to the system.
*
* @param module module The module to add.
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final void addModule (RecoveryModule module)
{
checkState();
_theImple.addModule(module);
}
/**
* Remove a recovery module from the system.
*
* @param module The module to remove.
* @param waitOnScan true if the remove operation should wait for any in-progress scan to complete
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final void removeModule (RecoveryModule module, boolean waitOnScan)
{
checkState();
_theImple.removeModule(module, waitOnScan);
}
/**
* Remove all modules.
*
* WARNING: Use with extreme care as this will stop recovery from doing anything!
*/
public final void removeAllModules (boolean waitOnScan)
{
checkState();
_theImple.removeAllModules(waitOnScan);
}
/**
* Obtain a snapshot list of available recovery modules.
*
* @return a snapshot list of the currently installed recovery modules
* @throws IllegalStateException if the recovery manager has been shutdown.
*/
public final Vector<RecoveryModule> getModules ()
{
checkState();
return _theImple.getModules();
}
/**
* Indicates what mode (INDIRECT_MANAGEMENT or DIRECT_MANAGEMENT)
* the recovery manager is configured for.
*
* @return the management mode.
*/
public final int mode ()
{
return _mode;
}
public static InetAddress getRecoveryManagerHost() throws UnknownHostException
{
String host = recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryAddress();
return Utility.hostNameToInetAddress(host);
}
public static int getRecoveryManagerPort()
{
return recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryPort();
}
/**
* Obtain a client connection to the recovery manager
*
* @return a bound client socket connection to the recovery manager
* @throws IOException
*/
public static Socket getClientSocket () throws IOException
{
Socket socket = new Socket(getRecoveryManagerHost(), getRecoveryManagerPort());
tsLogger.i18NLogger.info_recovery_RecoveryManager_4(socket.getInetAddress().getHostAddress(), Integer.toString(socket.getLocalPort()));
return socket;
}
/**
* Run the RecoveryManager. See Administration manual for details.
*/
public static void main (String[] args)
{
boolean testMode = false;
for (int i = 0; i < args.length; i++)
{
if (args[i].compareTo("-help") == 0)
{
System.out.println("Usage: com.arjuna.ats.arjuna.recovery.RecoveryManager [-help] [-test] [-version]");
System.exit(0);
}
if (args[i].compareTo("-version") == 0)
{
System.out.println("Version " + ConfigurationInfo.getVersion());
System.exit(0);
}
if (args[i].compareTo("-test") == 0)
{
testMode = true;
}
}
try
{
RecoveryManager manager = null;
try
{
manager = manager();
}
catch(Throwable e)
{
if(testMode)
{
// in some test cases the recovery manager is killed and restarted in quick succession.
// sometimes the O/S does not free up the port fast enough, so we can't reclaim it on restart.
// For test mode only, we therefore have a simple backoff-retry kludge:
System.err.println("Warning: got exception '"+e.toString()+"' on startup, will retry in 5 seconds in the hope it is transient.");
try
{
Thread.sleep(5000);
}
catch(InterruptedException interruptedException)
{
System.err.println("The retry attempt was interrupted. Exiting...");
throw new IllegalStateException(interruptedException);
}
manager = manager();
}
else
{
throw e;
}
}
if (testMode)
{
System.out.println("Ready");
}
// this is never going to return because it only returns when shutdown is called and
// there is nothing which is going to call shutdown. we probably aught to provide a
// clean way of terminating this process.
manager.waitForTermination();
}
catch (Throwable e)
{
e.printStackTrace();
}
}
private RecoveryManager (int mode)
{
if ((mode == RecoveryManager.INDIRECT_MANAGEMENT) && !delayRecoveryManagerThread)
_theImple = new RecoveryManagerImple(true);
else
_theImple = new RecoveryManagerImple(false);
_mode = mode;
}
private final void checkState ()
{
if (_theImple == null)
throw new IllegalStateException(tsLogger.i18NLogger.get_recovery_manager_implementation_is_not_set());
}
private RecoveryManagerImple _theImple = null;
private int _mode;
private static RecoveryManager _recoveryManager = null;
private static boolean delayRecoveryManagerThread ;
}