001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.core.service; 021 022import java.io.IOException; 023import java.net.SocketAddress; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Set; 030import java.util.concurrent.Executor; 031import java.util.concurrent.Executors; 032 033import org.apache.mina.core.RuntimeIoException; 034import org.apache.mina.core.session.IoSession; 035import org.apache.mina.core.session.IoSessionConfig; 036 037/** 038 * A base implementation of {@link IoAcceptor}. 039 * 040 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 041 * @org.apache.xbean.XBean 042 */ 043public abstract class AbstractIoAcceptor extends AbstractIoService implements IoAcceptor { 044 045 private final List<SocketAddress> defaultLocalAddresses = new ArrayList<>(); 046 047 private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections 048 .unmodifiableList(defaultLocalAddresses); 049 050 private final Set<SocketAddress> boundAddresses = new HashSet<>(); 051 052 private boolean disconnectOnUnbind = true; 053 054 /** 055 * The lock object which is acquired while bind or unbind operation is performed. 056 * Acquire this lock in your property setters which shouldn't be changed while 057 * the service is bound. 058 */ 059 protected final Object bindLock = new Object(); 060 061 /** 062 * Constructor for {@link AbstractIoAcceptor}. You need to provide a default 063 * session configuration and an {@link Executor} for handling I/O events. If 064 * null {@link Executor} is provided, a default one will be created using 065 * {@link Executors#newCachedThreadPool()}. 066 * 067 * @see AbstractIoService#AbstractIoService(IoSessionConfig, Executor) 068 * 069 * @param sessionConfig 070 * the default configuration for the managed {@link IoSession} 071 * @param executor 072 * the {@link Executor} used for handling execution of I/O 073 * events. Can be <code>null</code>. 074 */ 075 protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) { 076 super(sessionConfig, executor); 077 defaultLocalAddresses.add(null); 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 @Override 084 public SocketAddress getLocalAddress() { 085 Set<SocketAddress> localAddresses = getLocalAddresses(); 086 if (localAddresses.isEmpty()) { 087 return null; 088 } 089 090 return localAddresses.iterator().next(); 091 } 092 093 /** 094 * {@inheritDoc} 095 */ 096 @Override 097 public final Set<SocketAddress> getLocalAddresses() { 098 Set<SocketAddress> localAddresses = new HashSet<>(); 099 100 synchronized (boundAddresses) { 101 localAddresses.addAll(boundAddresses); 102 } 103 104 return localAddresses; 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 @Override 111 public SocketAddress getDefaultLocalAddress() { 112 if (defaultLocalAddresses.isEmpty()) { 113 return null; 114 } 115 return defaultLocalAddresses.iterator().next(); 116 } 117 118 /** 119 * {@inheritDoc} 120 */ 121 @Override 122 public final void setDefaultLocalAddress(SocketAddress localAddress) { 123 setDefaultLocalAddresses(localAddress); 124 } 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override 130 public final List<SocketAddress> getDefaultLocalAddresses() { 131 return unmodifiableDefaultLocalAddresses; 132 } 133 134 /** 135 * {@inheritDoc} 136 * @org.apache.xbean.Property nestedType="java.net.SocketAddress" 137 */ 138 @Override 139 public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) { 140 if (localAddresses == null) { 141 throw new IllegalArgumentException("localAddresses"); 142 } 143 setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses); 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) { 151 if (localAddresses == null) { 152 throw new IllegalArgumentException("localAddresses"); 153 } 154 155 synchronized (bindLock) { 156 synchronized (boundAddresses) { 157 if (!boundAddresses.isEmpty()) { 158 throw new IllegalStateException("localAddress can't be set while the acceptor is bound."); 159 } 160 161 Collection<SocketAddress> newLocalAddresses = new ArrayList<>(); 162 163 for (SocketAddress a : localAddresses) { 164 checkAddressType(a); 165 newLocalAddresses.add(a); 166 } 167 168 if (newLocalAddresses.isEmpty()) { 169 throw new IllegalArgumentException("empty localAddresses"); 170 } 171 172 this.defaultLocalAddresses.clear(); 173 this.defaultLocalAddresses.addAll(newLocalAddresses); 174 } 175 } 176 } 177 178 /** 179 * {@inheritDoc} 180 * @org.apache.xbean.Property nestedType="java.net.SocketAddress" 181 */ 182 @Override 183 public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) { 184 if (otherLocalAddresses == null) { 185 otherLocalAddresses = new SocketAddress[0]; 186 } 187 188 Collection<SocketAddress> newLocalAddresses = new ArrayList<>(otherLocalAddresses.length + 1); 189 190 newLocalAddresses.add(firstLocalAddress); 191 for (SocketAddress a : otherLocalAddresses) { 192 newLocalAddresses.add(a); 193 } 194 195 setDefaultLocalAddresses(newLocalAddresses); 196 } 197 198 /** 199 * {@inheritDoc} 200 */ 201 @Override 202 public final boolean isCloseOnDeactivation() { 203 return disconnectOnUnbind; 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) { 211 this.disconnectOnUnbind = disconnectClientsOnUnbind; 212 } 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public final void bind() throws IOException { 219 bind(getDefaultLocalAddresses()); 220 } 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override 226 public final void bind(SocketAddress localAddress) throws IOException { 227 if (localAddress == null) { 228 throw new IllegalArgumentException("localAddress"); 229 } 230 231 List<SocketAddress> localAddresses = new ArrayList<>(1); 232 localAddresses.add(localAddress); 233 bind(localAddresses); 234 } 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override 240 public final void bind(SocketAddress... addresses) throws IOException { 241 if ((addresses == null) || (addresses.length == 0)) { 242 bind(getDefaultLocalAddresses()); 243 return; 244 } 245 246 List<SocketAddress> localAddresses = new ArrayList<>(2); 247 248 for (SocketAddress address : addresses) { 249 localAddresses.add(address); 250 } 251 252 bind(localAddresses); 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 @Override 259 public final void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException { 260 if (firstLocalAddress == null) { 261 bind(getDefaultLocalAddresses()); 262 } 263 264 if ((addresses == null) || (addresses.length == 0)) { 265 bind(getDefaultLocalAddresses()); 266 return; 267 } 268 269 List<SocketAddress> localAddresses = new ArrayList<>(2); 270 localAddresses.add(firstLocalAddress); 271 272 for (SocketAddress address : addresses) { 273 localAddresses.add(address); 274 } 275 276 bind(localAddresses); 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 @Override 283public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException { 284 if (isDisposing()) { 285 throw new IllegalStateException("The Accpetor disposed is being disposed."); 286 } 287 288 if (localAddresses == null) { 289 throw new IllegalArgumentException("localAddresses"); 290 } 291 292 List<SocketAddress> localAddressesCopy = new ArrayList<>(); 293 294 for (SocketAddress a : localAddresses) { 295 checkAddressType(a); 296 localAddressesCopy.add(a); 297 } 298 299 if (localAddressesCopy.isEmpty()) { 300 throw new IllegalArgumentException("localAddresses is empty."); 301 } 302 303 boolean activate = false; 304 synchronized (bindLock) { 305 synchronized (boundAddresses) { 306 if (boundAddresses.isEmpty()) { 307 activate = true; 308 } 309 } 310 311 if (getHandler() == null) { 312 throw new IllegalStateException("handler is not set."); 313 } 314 315 try { 316 Set<SocketAddress> addresses = bindInternal(localAddressesCopy); 317 318 synchronized (boundAddresses) { 319 boundAddresses.addAll(addresses); 320 } 321 } catch (IOException e) { 322 throw e; 323 } catch (RuntimeException e) { 324 throw e; 325 } catch (Exception e) { 326 throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e); 327 } 328 } 329 330 if (activate) { 331 getListeners().fireServiceActivated(); 332 } 333 } 334 335 /** 336 * {@inheritDoc} 337 */ 338 @Override 339 public final void unbind() { 340 unbind(getLocalAddresses()); 341 } 342 343 /** 344 * {@inheritDoc} 345 */ 346 @Override 347 public final void unbind(SocketAddress localAddress) { 348 if (localAddress == null) { 349 throw new IllegalArgumentException("localAddress"); 350 } 351 352 List<SocketAddress> localAddresses = new ArrayList<>(1); 353 localAddresses.add(localAddress); 354 unbind(localAddresses); 355 } 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override 361 public final void unbind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) { 362 if (firstLocalAddress == null) { 363 throw new IllegalArgumentException("firstLocalAddress"); 364 } 365 if (otherLocalAddresses == null) { 366 throw new IllegalArgumentException("otherLocalAddresses"); 367 } 368 369 List<SocketAddress> localAddresses = new ArrayList<>(); 370 localAddresses.add(firstLocalAddress); 371 Collections.addAll(localAddresses, otherLocalAddresses); 372 unbind(localAddresses); 373 } 374 375 /** 376 * {@inheritDoc} 377 */ 378 @Override 379 public final void unbind(Iterable<? extends SocketAddress> localAddresses) { 380 if (localAddresses == null) { 381 throw new IllegalArgumentException("localAddresses"); 382 } 383 384 boolean deactivate = false; 385 synchronized (bindLock) { 386 synchronized (boundAddresses) { 387 if (boundAddresses.isEmpty()) { 388 return; 389 } 390 391 List<SocketAddress> localAddressesCopy = new ArrayList<>(); 392 int specifiedAddressCount = 0; 393 394 for (SocketAddress a : localAddresses) { 395 specifiedAddressCount++; 396 397 if ((a != null) && boundAddresses.contains(a)) { 398 localAddressesCopy.add(a); 399 } 400 } 401 402 if (specifiedAddressCount == 0) { 403 throw new IllegalArgumentException("localAddresses is empty."); 404 } 405 406 if (!localAddressesCopy.isEmpty()) { 407 try { 408 unbind0(localAddressesCopy); 409 } catch (RuntimeException e) { 410 throw e; 411 } catch (Exception e) { 412 throw new RuntimeIoException("Failed to unbind from: " + getLocalAddresses(), e); 413 } 414 415 boundAddresses.removeAll(localAddressesCopy); 416 417 if (boundAddresses.isEmpty()) { 418 deactivate = true; 419 } 420 } 421 } 422 } 423 424 if (deactivate) { 425 getListeners().fireServiceDeactivated(); 426 } 427 } 428 429 /** 430 * Starts the acceptor, and register the given addresses 431 * 432 * @param localAddresses The address to bind to 433 * @return the {@link Set} of the local addresses which is bound actually 434 * @throws Exception If the bind failed 435 */ 436 protected abstract Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception; 437 438 /** 439 * Implement this method to perform the actual unbind operation. 440 * 441 * @param localAddresses The address to unbind from 442 * @throws Exception If the unbind failed 443 */ 444 protected abstract void unbind0(List<? extends SocketAddress> localAddresses) throws Exception; 445 446 @Override 447 public String toString() { 448 TransportMetadata m = getTransportMetadata(); 449 return '(' 450 + m.getProviderName() 451 + ' ' 452 + m.getName() 453 + " acceptor: " 454 + (isActive() ? "localAddress(es): " + getLocalAddresses() + ", managedSessionCount: " 455 + getManagedSessionCount() : "not bound") + ')'; 456 } 457 458 private void checkAddressType(SocketAddress a) { 459 if (a != null && !getTransportMetadata().getAddressType().isAssignableFrom(a.getClass())) { 460 throw new IllegalArgumentException("localAddress type: " + a.getClass().getSimpleName() + " (expected: " 461 + getTransportMetadata().getAddressType().getSimpleName() + ")"); 462 } 463 } 464 465 public static class AcceptorOperationFuture extends ServiceOperationFuture { 466 private final List<SocketAddress> localAddresses; 467 468 public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) { 469 this.localAddresses = new ArrayList<>(localAddresses); 470 } 471 472 public final List<SocketAddress> getLocalAddresses() { 473 return Collections.unmodifiableList(localAddresses); 474 } 475 476 /** 477 * @see Object#toString() 478 */ 479 public String toString() { 480 StringBuilder sb = new StringBuilder(); 481 482 sb.append("Acceptor operation : "); 483 484 if (localAddresses != null) { 485 boolean isFirst = true; 486 487 for (SocketAddress address : localAddresses) { 488 if (isFirst) { 489 isFirst = false; 490 } else { 491 sb.append(", "); 492 } 493 494 sb.append(address); 495 } 496 } 497 return sb.toString(); 498 } 499 } 500}