1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.core.service;
21
22 import java.io.IOException;
23 import java.net.SocketAddress;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.concurrent.Executor;
31 import java.util.concurrent.Executors;
32
33 import org.apache.mina.core.RuntimeIoException;
34 import org.apache.mina.core.session.IoSession;
35 import org.apache.mina.core.session.IoSessionConfig;
36
37
38
39
40
41
42
43
44 public abstract class AbstractIoAcceptor
45 extends AbstractIoService implements IoAcceptor {
46
47 private final List<SocketAddress> defaultLocalAddresses =
48 new ArrayList<SocketAddress>();
49 private final List<SocketAddress> unmodifiableDefaultLocalAddresses =
50 Collections.unmodifiableList(defaultLocalAddresses);
51 private final Set<SocketAddress> boundAddresses =
52 new HashSet<SocketAddress>();
53
54 private boolean disconnectOnUnbind = true;
55
56
57
58
59
60
61 protected final Object bindLock = new Object();
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
78 super(sessionConfig, executor);
79 defaultLocalAddresses.add(null);
80 }
81
82
83
84
85 public SocketAddress getLocalAddress() {
86 Set<SocketAddress> localAddresses = getLocalAddresses();
87 if (localAddresses.isEmpty()) {
88 return null;
89 }
90
91 return localAddresses.iterator().next();
92 }
93
94
95
96
97 public final Set<SocketAddress> getLocalAddresses() {
98 Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
99 synchronized (bindLock) {
100 localAddresses.addAll(boundAddresses);
101 }
102 return localAddresses;
103 }
104
105
106
107
108 public SocketAddress getDefaultLocalAddress() {
109 if (defaultLocalAddresses.isEmpty()) {
110 return null;
111 }
112 return defaultLocalAddresses.iterator().next();
113 }
114
115
116
117
118 public final void setDefaultLocalAddress(SocketAddress localAddress) {
119 setDefaultLocalAddresses(localAddress);
120 }
121
122
123
124
125 public final List<SocketAddress> getDefaultLocalAddresses() {
126 return unmodifiableDefaultLocalAddresses;
127 }
128
129
130
131
132
133 public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) {
134 if (localAddresses == null) {
135 throw new NullPointerException("localAddresses");
136 }
137 setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
138 }
139
140
141
142
143 public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
144 if (localAddresses == null) {
145 throw new NullPointerException("localAddresses");
146 }
147
148 synchronized (bindLock) {
149 if (!boundAddresses.isEmpty()) {
150 throw new IllegalStateException(
151 "localAddress can't be set while the acceptor is bound.");
152 }
153
154 Collection<SocketAddress> newLocalAddresses =
155 new ArrayList<SocketAddress>();
156 for (SocketAddress a: localAddresses) {
157 checkAddressType(a);
158 newLocalAddresses.add(a);
159 }
160
161 if (newLocalAddresses.isEmpty()) {
162 throw new IllegalArgumentException("empty localAddresses");
163 }
164
165 this.defaultLocalAddresses.clear();
166 this.defaultLocalAddresses.addAll(newLocalAddresses);
167 }
168 }
169
170
171
172
173
174 public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
175 if (otherLocalAddresses == null) {
176 otherLocalAddresses = new SocketAddress[0];
177 }
178
179 Collection<SocketAddress> newLocalAddresses =
180 new ArrayList<SocketAddress>(otherLocalAddresses.length + 1);
181
182 newLocalAddresses.add(firstLocalAddress);
183 for (SocketAddress a: otherLocalAddresses) {
184 newLocalAddresses.add(a);
185 }
186
187 setDefaultLocalAddresses(newLocalAddresses);
188 }
189
190
191
192
193 public final boolean isCloseOnDeactivation() {
194 return disconnectOnUnbind;
195 }
196
197
198
199
200 public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
201 this.disconnectOnUnbind = disconnectClientsOnUnbind;
202 }
203
204
205
206
207 public final void bind() throws IOException {
208 bind(getDefaultLocalAddresses());
209 }
210
211
212
213
214 public final void bind(SocketAddress localAddress) throws IOException {
215 if (localAddress == null) {
216 throw new NullPointerException("localAddress");
217 }
218
219 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
220 localAddresses.add(localAddress);
221 bind(localAddresses);
222 }
223
224
225
226
227
228 public final void bind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) throws IOException {
229 if (firstLocalAddress == null) {
230 bind(getDefaultLocalAddresses());
231 return;
232 }
233
234 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(2);
235 localAddresses.add(firstLocalAddress);
236
237 if (otherLocalAddresses != null) {
238 for (SocketAddress address:otherLocalAddresses) {
239 localAddresses.add(address);
240 }
241 }
242
243 bind(localAddresses);
244 }
245
246
247
248
249 public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
250 if (isDisposing()) {
251 throw new IllegalStateException("Already disposed.");
252 }
253
254 if (localAddresses == null) {
255 throw new NullPointerException("localAddresses");
256 }
257
258 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
259
260 for (SocketAddress a: localAddresses) {
261 checkAddressType(a);
262 localAddressesCopy.add(a);
263 }
264
265 if (localAddressesCopy.isEmpty()) {
266 throw new IllegalArgumentException("localAddresses is empty.");
267 }
268
269 boolean activate = false;
270 synchronized (bindLock) {
271 if (boundAddresses.isEmpty()) {
272 activate = true;
273 }
274
275 if (getHandler() == null) {
276 throw new IllegalStateException("handler is not set.");
277 }
278
279 try {
280 boundAddresses.addAll(bindInternal(localAddressesCopy));
281 } catch (IOException e) {
282 throw e;
283 } catch (RuntimeException e) {
284 throw e;
285 } catch (Throwable e) {
286 throw new RuntimeIoException(
287 "Failed to bind to: " + getLocalAddresses(), e);
288 }
289 }
290
291 if (activate) {
292 getListeners().fireServiceActivated();
293 }
294 }
295
296
297
298
299 public final void unbind() {
300 unbind(getLocalAddresses());
301 }
302
303
304
305
306 public final void unbind(SocketAddress localAddress) {
307 if (localAddress == null) {
308 throw new NullPointerException("localAddress");
309 }
310
311 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
312 localAddresses.add(localAddress);
313 unbind(localAddresses);
314 }
315
316
317
318
319 public final void unbind(SocketAddress firstLocalAddress,
320 SocketAddress... otherLocalAddresses) {
321 if (firstLocalAddress == null) {
322 throw new NullPointerException("firstLocalAddress");
323 }
324 if (otherLocalAddresses == null) {
325 throw new NullPointerException("otherLocalAddresses");
326 }
327
328 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
329 localAddresses.add(firstLocalAddress);
330 Collections.addAll(localAddresses, otherLocalAddresses);
331 unbind(localAddresses);
332 }
333
334
335
336
337 public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
338 if (localAddresses == null) {
339 throw new NullPointerException("localAddresses");
340 }
341
342 boolean deactivate = false;
343 synchronized (bindLock) {
344 if (boundAddresses.isEmpty()) {
345 return;
346 }
347
348 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
349 int specifiedAddressCount = 0;
350 for (SocketAddress a: localAddresses) {
351 specifiedAddressCount ++;
352 if (a != null && boundAddresses.contains(a)) {
353 localAddressesCopy.add(a);
354 }
355 }
356 if (specifiedAddressCount == 0) {
357 throw new IllegalArgumentException("localAddresses is empty.");
358 }
359
360 if (!localAddressesCopy.isEmpty()) {
361 try {
362 unbind0(localAddressesCopy);
363 } catch (RuntimeException e) {
364 throw e;
365 } catch (Throwable e) {
366 throw new RuntimeIoException(
367 "Failed to unbind from: " + getLocalAddresses(), e);
368 }
369
370 boundAddresses.removeAll(localAddressesCopy);
371 if (boundAddresses.isEmpty()) {
372 deactivate = true;
373 }
374 }
375 }
376
377 if (deactivate) {
378 getListeners().fireServiceDeactivated();
379 }
380 }
381
382
383
384
385
386 protected abstract Set<SocketAddress> bindInternal(
387 List<? extends SocketAddress> localAddresses) throws Exception;
388
389
390
391
392 protected abstract void unbind0(
393 List<? extends SocketAddress> localAddresses) throws Exception;
394
395 @Override
396 public String toString() {
397 TransportMetadata m = getTransportMetadata();
398 return '(' + m.getProviderName() + ' ' + m.getName() + " acceptor: " +
399 (isActive()?
400 "localAddress(es): " + getLocalAddresses() +
401 ", managedSessionCount: " + getManagedSessionCount() :
402 "not bound") + ')';
403 }
404
405 private void checkAddressType(SocketAddress a) {
406 if (a != null &&
407 !getTransportMetadata().getAddressType().isAssignableFrom(
408 a.getClass())) {
409 throw new IllegalArgumentException("localAddress type: "
410 + a.getClass().getSimpleName() + " (expected: "
411 + getTransportMetadata().getAddressType().getSimpleName() + ")");
412 }
413 }
414
415 public static class AcceptorOperationFuture extends ServiceOperationFuture {
416 private final List<SocketAddress> localAddresses;
417
418 public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
419 this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
420 }
421
422 public final List<SocketAddress> getLocalAddresses() {
423 return Collections.unmodifiableList(localAddresses);
424 }
425
426
427
428
429 public String toString() {
430 StringBuilder sb = new StringBuilder();
431
432 sb.append( "Acceptor operation : " );
433
434 if (localAddresses != null) {
435 boolean isFirst = true;
436
437 for (SocketAddress address:localAddresses) {
438 if (isFirst) {
439 isFirst = false;
440 } else {
441 sb.append(", ");
442 }
443
444 sb.append(address);
445 }
446 }
447 return sb.toString();
448 }
449 }
450 }