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 } else {
90 return localAddresses.iterator().next();
91 }
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 public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) {
133 if (localAddresses == null) {
134 throw new NullPointerException("localAddresses");
135 }
136 setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
137 }
138
139
140
141
142 public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
143 if (localAddresses == null) {
144 throw new NullPointerException("localAddresses");
145 }
146
147 synchronized (bindLock) {
148 if (!boundAddresses.isEmpty()) {
149 throw new IllegalStateException(
150 "localAddress can't be set while the acceptor is bound.");
151 }
152
153 Collection<SocketAddress> newLocalAddresses =
154 new ArrayList<SocketAddress>();
155 for (SocketAddress a: localAddresses) {
156 checkAddressType(a);
157 newLocalAddresses.add(a);
158 }
159
160 if (newLocalAddresses.isEmpty()) {
161 throw new IllegalArgumentException("empty localAddresses");
162 }
163
164 this.defaultLocalAddresses.clear();
165 this.defaultLocalAddresses.addAll(newLocalAddresses);
166 }
167 }
168
169
170
171
172 public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
173 if (otherLocalAddresses == null) {
174 otherLocalAddresses = new SocketAddress[0];
175 }
176
177 Collection<SocketAddress> newLocalAddresses =
178 new ArrayList<SocketAddress>(otherLocalAddresses.length + 1);
179
180 newLocalAddresses.add(firstLocalAddress);
181 for (SocketAddress a: otherLocalAddresses) {
182 newLocalAddresses.add(a);
183 }
184
185 setDefaultLocalAddresses(newLocalAddresses);
186 }
187
188
189
190
191 public final boolean isCloseOnDeactivation() {
192 return disconnectOnUnbind;
193 }
194
195
196
197
198 public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
199 this.disconnectOnUnbind = disconnectClientsOnUnbind;
200 }
201
202
203
204
205 public final void bind() throws IOException {
206 bind(getDefaultLocalAddresses());
207 }
208
209
210
211
212 public final void bind(SocketAddress localAddress) throws IOException {
213 if (localAddress == null) {
214 throw new NullPointerException("localAddress");
215 }
216
217 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
218 localAddresses.add(localAddress);
219 bind(localAddresses);
220 }
221
222
223
224
225 public final void bind(
226 SocketAddress firstLocalAddress,
227 SocketAddress... otherLocalAddresses) throws IOException {
228 if (firstLocalAddress == null) {
229 throw new NullPointerException("firstLocalAddress");
230 }
231 if (otherLocalAddresses == null) {
232 throw new NullPointerException("otherLocalAddresses");
233 }
234
235 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
236 localAddresses.add(firstLocalAddress);
237 Collections.addAll(localAddresses, otherLocalAddresses);
238 bind(localAddresses);
239 }
240
241
242
243
244 public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
245 if (isDisposing()) {
246 throw new IllegalStateException("Already disposed.");
247 }
248 if (localAddresses == null) {
249 throw new NullPointerException("localAddresses");
250 }
251
252 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
253 for (SocketAddress a: localAddresses) {
254 checkAddressType(a);
255 localAddressesCopy.add(a);
256 }
257 if (localAddressesCopy.isEmpty()) {
258 throw new IllegalArgumentException("localAddresses is empty.");
259 }
260
261 boolean activate = false;
262 synchronized (bindLock) {
263 if (boundAddresses.isEmpty()) {
264 activate = true;
265 }
266
267 if (getHandler() == null) {
268 throw new IllegalStateException("handler is not set.");
269 }
270
271 try {
272 boundAddresses.addAll(bind0(localAddressesCopy));
273 } catch (IOException e) {
274 throw e;
275 } catch (RuntimeException e) {
276 throw e;
277 } catch (Throwable e) {
278 throw new RuntimeIoException(
279 "Failed to bind to: " + getLocalAddresses(), e);
280 }
281 }
282
283 if (activate) {
284 getListeners().fireServiceActivated();
285 }
286 }
287
288
289
290
291 public final void unbind() {
292 unbind(getLocalAddresses());
293 }
294
295
296
297
298 public final void unbind(SocketAddress localAddress) {
299 if (localAddress == null) {
300 throw new NullPointerException("localAddress");
301 }
302
303 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
304 localAddresses.add(localAddress);
305 unbind(localAddresses);
306 }
307
308
309
310
311 public final void unbind(SocketAddress firstLocalAddress,
312 SocketAddress... otherLocalAddresses) {
313 if (firstLocalAddress == null) {
314 throw new NullPointerException("firstLocalAddress");
315 }
316 if (otherLocalAddresses == null) {
317 throw new NullPointerException("otherLocalAddresses");
318 }
319
320 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
321 localAddresses.add(firstLocalAddress);
322 Collections.addAll(localAddresses, otherLocalAddresses);
323 unbind(localAddresses);
324 }
325
326
327
328
329 public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
330 if (localAddresses == null) {
331 throw new NullPointerException("localAddresses");
332 }
333
334 boolean deactivate = false;
335 synchronized (bindLock) {
336 if (boundAddresses.isEmpty()) {
337 return;
338 }
339
340 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
341 int specifiedAddressCount = 0;
342 for (SocketAddress a: localAddresses) {
343 specifiedAddressCount ++;
344 if (a != null && boundAddresses.contains(a)) {
345 localAddressesCopy.add(a);
346 }
347 }
348 if (specifiedAddressCount == 0) {
349 throw new IllegalArgumentException("localAddresses is empty.");
350 }
351
352 if (!localAddressesCopy.isEmpty()) {
353 try {
354 unbind0(localAddressesCopy);
355 } catch (RuntimeException e) {
356 throw e;
357 } catch (Throwable e) {
358 throw new RuntimeIoException(
359 "Failed to unbind from: " + getLocalAddresses(), e);
360 }
361
362 boundAddresses.removeAll(localAddressesCopy);
363 if (boundAddresses.isEmpty()) {
364 deactivate = true;
365 }
366 }
367 }
368
369 if (deactivate) {
370 getListeners().fireServiceDeactivated();
371 }
372 }
373
374
375
376
377
378 protected abstract Set<SocketAddress> bind0(
379 List<? extends SocketAddress> localAddresses) throws Exception;
380
381
382
383
384 protected abstract void unbind0(
385 List<? extends SocketAddress> localAddresses) throws Exception;
386
387 @Override
388 public String toString() {
389 TransportMetadata m = getTransportMetadata();
390 return '(' + m.getProviderName() + ' ' + m.getName() + " acceptor: " +
391 (isActive()?
392 "localAddress(es): " + getLocalAddresses() +
393 ", managedSessionCount: " + getManagedSessionCount() :
394 "not bound") + ')';
395 }
396
397 private void checkAddressType(SocketAddress a) {
398 if (a != null &&
399 !getTransportMetadata().getAddressType().isAssignableFrom(
400 a.getClass())) {
401 throw new IllegalArgumentException("localAddress type: "
402 + a.getClass().getSimpleName() + " (expected: "
403 + getTransportMetadata().getAddressType().getSimpleName() + ")");
404 }
405 }
406
407 public static class AcceptorOperationFuture extends ServiceOperationFuture {
408 private final List<SocketAddress> localAddresses;
409
410 public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
411 this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
412 }
413
414 public final List<SocketAddress> getLocalAddresses() {
415 return Collections.unmodifiableList(localAddresses);
416 }
417 }
418 }