View Javadoc

1   /**
2    * BaseUnrealEnvironment, an implementation of the environment interface standard that 
3    * facilitates the connection between GOAL and the UT2004 engine. 
4    * 
5    * Copyright (C) 2012 BaseUnrealEnvironment authors.
6    * 
7    * This program is free software: you can redistribute it and/or modify it under
8    * the terms of the GNU General Public License as published by the Free Software
9    * Foundation, either version 3 of the License, or (at your option) any later
10   * version.
11   * 
12   * This program is distributed in the hope that it will be useful, but WITHOUT
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15   * details.
16   * 
17   * You should have received a copy of the GNU General Public License along with
18   * this program. If not, see <http://www.gnu.org/licenses/>.
19   */
20  package nl.tudelft.pogamut.base.server;
21  
22  import java.net.URI;
23  
24  import cz.cuni.amis.utils.exception.PogamutException;
25  import cz.cuni.amis.utils.flag.Flag;
26  import cz.cuni.amis.utils.flag.FlagListener;
27  
28  
29  /**
30   * States are:
31   * <ul>
32   * <li>Connecting</li>
33   * <li>Connected</li>
34   * <li>Waiting before another connection attempt</li>
35   * </ul>
36   * 
37   * @author ik
38   * @author M.P. Korstanje
39   * @since 2011/01/15 refactored to wrapper.
40   */
41  public final class ReconnectingServerDefinition<T> extends ServerDefinition<T> {
42  
43  	/**
44  	 * Generated serialVersionUID.
45  	 */
46  	private static final long serialVersionUID = 866197741748535623L;
47  
48  	private final FlagListener<T> serverListener = new FlagListener<T>() {
49  
50  		@Override
51  		public void flagChanged(T server) { 
52  			if (server == null) {
53  				startServer();
54  			} else {
55  
56  			}
57  		}
58  	};
59  
60  	private final ServerDefinition<T> serverDefinition;
61  
62  	private StartServerThread startServerThread;
63  
64  	public ReconnectingServerDefinition(ServerDefinition<T> serverDefinition) {
65  		this.serverDefinition = serverDefinition;
66  		this.serverDefinition.getServerFlag().addListener(serverListener);
67  	}
68  
69  	@Override
70  	public void setServerName(String name) {
71  		serverDefinition.setServerName(name);
72  	}
73  
74  	@Override
75  	public String getServerName() {
76  		return serverDefinition.getServerName();
77  	}
78  
79  	@Override
80  	public Flag<String> getServerNameFlag() {
81  		return serverDefinition.getServerNameFlag();
82  	}
83  
84  	@Override
85  	public void setUri(URI uri) {
86  		startServer(uri);
87  	}
88  
89  	@Override
90  	public URI getUri() {
91  
92  		// Parent class calls getUri in constructor,
93  		// before we could set the serverDefinition.
94  		if (serverDefinition == null)
95  			return null;
96  
97  		return serverDefinition.getUri();
98  	}
99  
100 	@Override
101 	public Flag<URI> getUriFlag() {
102 		return serverDefinition.getUriFlag();
103 	}
104 
105 	/**
106 	 * Change current server instance.
107 	 * 
108 	 * @param server
109 	 */
110 	@Override
111 	protected void setNewServer(T server) {
112 		serverDefinition.setNewServer(server);
113 	}
114 
115 	@Override
116 	public Flag<T> getServerFlag() {
117 		return serverDefinition.getServerFlag();
118 	}
119 
120 	/**
121 	 * Stops the server and any connection attempts being made.
122 	 * 
123 	 * Blocks while waiting for server and connection thread to be shut down.
124 	 */
125 	@Override
126 	public void stopServer() {
127 		// Remove listener that would cause reconnects
128 		serverDefinition.getServerFlag().removeListener(serverListener);
129 
130 		// If reconnecting
131 		if (startServerThread != null && startServerThread.isAlive()) {
132 			// Stop any ongoing reconnects and wait for them to finish.
133 			startServerThread.cancel();
134 		}
135 
136 		// Clear server thread.
137 		startServerThread = null;
138 
139 		// If server is connected
140 		serverDefinition.stopServer();
141 	}
142 
143 	/**
144 	 * Nonblocking implementation.
145 	 */
146 	@Override
147 	public void startServer() {
148 		startServer(serverDefinition.getUri());
149 	}
150 
151 	private void startServer(URI uri) {
152 		// Stop server and connection attempts first if any.
153 		stopServer();
154 
155 		// Add listener to ensure server connects on connection loss.
156 		this.serverDefinition.getServerFlag().addListener(serverListener);
157 
158 		// Start new task.
159 		startServerThread = new StartServerThread(uri);
160 		startServerThread.start();
161 	}
162 
163 	private class StartServerThread extends Thread {
164 
165 		/**
166 		 * Time to wait before trying to reconnect. 15 seconds.
167 		 */
168 		private static final long RECONNECTION_DELAY = 15 * 1000;
169 
170 		private boolean tryAgain = true;
171 		private boolean connected = false;
172 		private final URI serverAddres;
173 
174 		public StartServerThread(URI serverAddres) {
175 			super("ReconnectingServer: " + serverAddres);
176 			this.serverAddres = serverAddres;
177 		}
178 
179 		@Override
180 		public void run() {
181 
182 			while (!connected && tryAgain) {
183 				try {
184 					serverDefinition.setUri(serverAddres);
185 					connected = true;
186 				} catch (PogamutException pogamutException) {
187 					try {
188 						Thread.sleep(RECONNECTION_DELAY);
189 					} catch (InterruptedException interruptedException) {
190 						// interruptedException.printStackTrace();
191 					}
192 				}
193 			}
194 
195 		}
196 
197 		/**
198 		 * Stops the task from trying again.
199 		 * 
200 		 * @return
201 		 */
202 		public void cancel() {
203 			tryAgain = false;
204 		}
205 
206 		@SuppressWarnings("unused")
207 		public boolean isCancelled() {
208 			return tryAgain;
209 		}
210 
211 		@SuppressWarnings("unused")
212 		public boolean isDone() {
213 			return connected;
214 		}
215 
216 	}
217 
218 }