jaulib v1.3.0
Jau Support Library (C++, Java, ..)
SingletonInstanceServerSocket.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2010 Gothel Software e.K.
5 * Copyright (c) 2010 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27package jau.test.util.parallel.locks.impl;
28
29import java.io.IOException;
30import java.net.InetAddress;
31import java.net.ServerSocket;
32import java.net.Socket;
33import java.net.UnknownHostException;
34
35import org.jau.lang.ExceptionUtils;
36import org.jau.lang.InterruptSource;
37import org.jau.lang.InterruptedRuntimeException;
38import org.jau.lang.SourcedInterruptedException;
39
40import jau.test.util.parallel.locks.SingletonInstance;
41
43
44 private static int serverInstanceCount = 0;
45 private final Server singletonServer;
46 private final String fullName;
47
48 public SingletonInstanceServerSocket(final long poll_ms, final int portNumber) {
49 super(poll_ms);
50
51 // Gather the local InetAddress, loopback is prioritized
52 InetAddress ilh = null;
53 try {
54 ilh = InetAddress.getByName(null); // loopback
55 } catch (final UnknownHostException e1) { }
56 if(null == ilh) {
57 try {
58 ilh = InetAddress.getByName("localhost");
59 if(null!=ilh && !ilh.isLoopbackAddress()) { ilh = null; }
60 } catch (final UnknownHostException e1) { }
61 }
62 if(null == ilh) {
63 try {
64 ilh = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 } );
65 if(null!=ilh && !ilh.isLoopbackAddress()) { ilh = null; }
66 } catch (final UnknownHostException e) { }
67 }
68 if(null == ilh) {
69 try {
70 ilh = InetAddress.getLocalHost();
71 } catch (final UnknownHostException e) { }
72 }
73 if(null == ilh) {
74 throw new RuntimeException(infoPrefix()+" EEE Could not determine local InetAddress");
75 }
76
77 fullName = ilh.toString()+":"+portNumber;
78 singletonServer = new Server(ilh, portNumber);
79 Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() {
80 @Override
81 public void run() {
82 singletonServer.kill();
83 }
84 });
85 }
86
87 public final InetAddress getLocalInetAddress() {
88 return singletonServer.getLocalInetAddress();
89 }
90
91 public final int getPortNumber() {
92 return singletonServer.getPortNumber();
93 }
94
95 @Override
96 public final String getName() { return fullName; }
97
98 @Override
99 protected boolean tryLockImpl() {
100 if( singletonServer.isRunning() ) {
101 return false; // same JVM .. server socket already installed !
102 }
103
104 // check if other JVM's locked the server socket ..
105 final Socket clientSocket = singletonServer.connect();
106 if(null != clientSocket) {
107 try {
108 clientSocket.close();
109 } catch (final IOException e) { }
110 return false;
111 }
112
113 if( !singletonServer.start() ) {
114 return false;
115 }
116
117 return true;
118 }
119
120 @Override
121 protected boolean unlockImpl() {
122 return singletonServer.shutdown();
123 }
124
125 public class Server implements Runnable {
126 private final InetAddress localInetAddress;
127 private final int portNumber;
128
129 private volatile boolean shallQuit = false;
130 private volatile boolean alive = false;
131
132 private final Object syncOnStartStop = new Object();
133 private ServerSocket serverSocket = null;
134 private Thread serverThread = null; // allowing kill() to force-stop last server-thread
135
136 public Server(final InetAddress localInetAddress, final int portNumber) {
137 this.localInetAddress = localInetAddress;
138 this.portNumber = portNumber;
139 }
140
141 public final InetAddress getLocalInetAddress() { return localInetAddress; }
142 public final int getPortNumber() { return portNumber; }
143
144 public final boolean start() {
145 if(alive) return true;
146
147 final String sname;
148 synchronized (Server.class) {
149 serverInstanceCount++;
150 sname = "SingletonServerSocket"+serverInstanceCount+"-"+fullName;
151 }
152 synchronized (syncOnStartStop) {
153 shallQuit = false;
154 serverThread = new InterruptSource.Thread(null, this, sname);
155 serverThread.setDaemon(true); // be a daemon, don't keep the JVM running
156 serverThread.start();
157 try {
158 while( !alive && !shallQuit ) {
159 syncOnStartStop.wait();
160 }
161 } catch (final InterruptedException ie) {
162 final InterruptedException ie2 = SourcedInterruptedException.wrap(ie);
163 shutdown(false);
164 throw new InterruptedRuntimeException(ie2);
165 }
166 }
167 final boolean ok = isBound();
168 if(!ok) {
169 shutdown(true);
170 }
171 return ok;
172 }
173
174 public final boolean shutdown() {
175 return shutdown(true);
176 }
177 private final boolean shutdown(final boolean wait) {
178 if(!alive) return true;
179
180 try {
181 synchronized (syncOnStartStop) {
182 shallQuit = true;
183 connect();
184 if( wait ) {
185 try {
186 while( alive ) {
187 syncOnStartStop.wait();
188 }
189 } catch (final InterruptedException ie) {
190 throw new InterruptedRuntimeException(ie);
191 }
192 }
193 }
194 } finally {
195 if(alive) {
196 System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?");
197 kill();
198 }
199 }
200 return true;
201 }
202
203 /**
204 * Brutally kill server thread and close socket regardless.
205 * This is out last chance for JVM shutdown.
206 */
207 @SuppressWarnings("deprecation")
208 public final void kill() {
209 if(alive) {
210 System.err.println(infoPrefix()+" XXX "+getName()+" - Kill @ JVM Shutdown");
211 }
212 alive = false;
213 shallQuit = false;
214 if(null != serverThread && serverThread.isAlive() ) {
215 try {
216 serverThread.stop();
217 } catch(final Throwable t) { }
218 }
219 if(null != serverSocket) {
220 try {
221 final ServerSocket ss = serverSocket;
222 serverSocket = null;
223 ss.close();
224 } catch (final Throwable t) { }
225 }
226 }
227
228 public final boolean isRunning() { return alive; }
229
230 public final boolean isBound() {
231 return alive && null != serverSocket && serverSocket.isBound() ;
232 }
233
234 public final Socket connect() {
235 try {
236 return new Socket(localInetAddress, portNumber);
237 } catch (final Exception e) { }
238 return null;
239 }
240
241 @Override
242 public void run() {
243 if(DEBUG) {
244 System.err.println(infoPrefix()+" III - Start");
245 }
246 try {
247 synchronized (syncOnStartStop) {
248 try {
249 serverSocket = new ServerSocket(portNumber, 1, localInetAddress);
250 serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed
251 alive = true;
252 } catch (final IOException e) {
253 System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage());
254 shallQuit = true;
255 } finally {
256 syncOnStartStop.notifyAll();
257 }
258 }
259
260 while (!shallQuit) {
261 try {
262 final Socket clientSocket = serverSocket.accept();
263 clientSocket.close();
264 } catch (final IOException ioe) {
265 System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage());
266 }
267 }
268 } catch(final ThreadDeath td) {
269 if( DEBUG ) {
271 }
272 } finally {
273 synchronized (syncOnStartStop) {
274 if(DEBUG) {
275 System.err.println(infoPrefix()+" III - Stopping: alive "+alive+", shallQuit "+shallQuit+", hasSocket "+(null!=serverSocket));
276 }
277 if(null != serverSocket) {
278 try {
279 serverSocket.close();
280 } catch (final IOException e) {
281 System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage());
282 }
283 }
284 serverSocket = null;
285 alive = false;
286 syncOnStartStop.notifyAll();
287 }
288 }
289 }
290 }
291}
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
java.lang.Thread specialization implementing InterruptSource to track java.lang.Thread#interrupt() ca...
Unchecked exception propagating an InterruptedException where handling of the latter is not desired.
InterruptedException, which may include the source, see getInterruptSource().
static InterruptedException wrap(final InterruptedException ie)
Wraps the given InterruptedException into a SourcedInterruptedException if it is not yet of the desir...
Interface exposing java.lang.Thread#interrupt() source, intended for java.lang.Thread specializations...