1 package cz.cuni.amis.pogamut.base.agent.utils;
2
3
4 import cz.cuni.amis.pogamut.base.agent.IAgent;
5 import cz.cuni.amis.pogamut.base.agent.state.WaitForAgentStateChange;
6 import cz.cuni.amis.pogamut.base.agent.state.level0.IAgentState;
7 import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateDown;
8 import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateGoingUp;
9 import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
10 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
11 import cz.cuni.amis.utils.ExceptionToString;
12 import cz.cuni.amis.utils.NullCheck;
13 import java.util.logging.Level;
14
15 public class AgentKeepAlive {
16
17 private Object keepAliveMutex = new Object();
18
19 private LogCategory log;
20
21 private KeepAlive keepAlive = null;
22
23 private boolean firstStart = false;
24
25 private IAgent agent;
26
27 private boolean running = false;
28
29 private long reconnectMillis;
30
31 public AgentKeepAlive(IAgent agent, long reconnectWaitMillis) {
32 this.agent = agent;
33 NullCheck.check(this.agent, "agent");
34 this.log = agent.getLogger().getCategory(getClass().getSimpleName());
35 NullCheck.check(this.log, "log initialization");
36 this.reconnectMillis = reconnectWaitMillis;
37 }
38
39 public void start() {
40 synchronized(keepAliveMutex) {
41 if (running) return;
42 IAgentState state = agent.getState().getFlag();
43 firstStart = true;
44 keepAlive = new KeepAlive();
45 }
46 }
47
48 public void stop() {
49 synchronized(keepAliveMutex) {
50 if (keepAlive != null) {
51 keepAlive.stop();
52 keepAlive = null;
53 }
54 }
55 }
56
57 public Long getNextRestart() {
58 synchronized(keepAliveMutex) {
59 if (!running) return null;
60 return keepAlive.getNextRestart();
61 }
62 }
63
64 public boolean isRunning() {
65 return running;
66 }
67
68 private class KeepAlive implements Runnable {
69
70 private Thread reconnectThread = null;
71
72 private boolean shouldRun = true;
73
74 private boolean shouldReconnect = true;
75
76 private long sleepMillis = -1;
77
78 public KeepAlive() {
79 this.reconnectThread = new Thread(this, agent.getName() + " reconnector");
80 this.reconnectThread.start();
81 }
82
83
84 public void stop() {
85 if (log.isLoggable(Level.WARNING)) log.warning("Stopping KeepAlive.");
86 shouldReconnect = false;
87 shouldRun = false;
88 reconnectThread.interrupt();
89 }
90
91 public Long getNextRestart() {
92 synchronized(keepAliveMutex) {
93 if (sleepMillis < 0) return null;
94 return System.currentTimeMillis() - sleepMillis - reconnectMillis;
95 }
96 }
97
98 @Override
99 public void run() {
100 Boolean result = null;
101 while (shouldRun && !Thread.currentThread().isInterrupted()) {
102 if (shouldReconnect) {
103 try {
104 if (!firstStart) {
105 synchronized(keepAliveMutex) {
106 sleepMillis = System.currentTimeMillis();
107 }
108 if (log.isLoggable(Level.FINER)) log.finer("Next reconnect attempt in " + reconnectMillis + " ms.");
109 Thread.sleep(reconnectMillis);
110 }
111 if (!shouldReconnect) break;
112 try {
113 IAgentState state = agent.getState().getFlag();
114 if (state instanceof IAgentStateUp || state instanceof IAgentStateGoingUp) {
115 shouldReconnect = false;
116 continue;
117 }
118 if (firstStart) {
119 if (log.isLoggable(Level.INFO)) log.info("Starting agent.");
120 firstStart = false;
121 } else {
122 if (log.isLoggable(Level.INFO)) log.info("Restarting agent.");
123 }
124
125 agent.start();
126 if (log.isLoggable(Level.INFO)) log.info("Agent started.");
127 } catch (Exception e1) {
128 if (!shouldReconnect) break;
129 if (log.isLoggable(Level.INFO)) log.info("Agent did not start, killing agent (cleaning up).");
130 try {
131 agent.kill();
132 } catch (Exception e2) {
133 }
134 }
135 } catch (Exception e) {
136 if (!shouldReconnect) break;
137 if (log.isLoggable(Level.SEVERE)) log.severe(ExceptionToString.process("Unhandled exception, stopping " + AgentKeepAlive.this.getClass().getSimpleName() + ".", e));
138 break;
139 }
140 }
141
142 try {
143 new WaitForAgentStateChange(agent.getState(), IAgentStateDown.class).await();
144 } catch (Exception e) {
145 }
146 shouldReconnect = agent.getState().getFlag() instanceof IAgentStateDown;
147 }
148 reconnectThread = null;
149 synchronized(keepAliveMutex) {
150 if (keepAlive == this) {
151 running = false;
152 keepAlive = null;
153 }
154 }
155 if (log.isLoggable(Level.WARNING)) log.warning("Stopped.");
156 }
157 }
158
159 }