Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
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 ) {
270 ExceptionUtils.dumpThrowable("", td);
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}