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<SocketAddress>(); 046 047 private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections 048 .unmodifiableList(defaultLocalAddresses); 049 050 private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>(); 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 public SocketAddress getLocalAddress() { 084 Set<SocketAddress> localAddresses = getLocalAddresses(); 085 if (localAddresses.isEmpty()) { 086 return null; 087 } 088 089 return localAddresses.iterator().next(); 090 } 091 092 /** 093 * {@inheritDoc} 094 */ 095 public final Set<SocketAddress> getLocalAddresses() { 096 Set<SocketAddress> localAddresses = new HashSet<SocketAddress>(); 097 098 synchronized (boundAddresses) { 099 localAddresses.addAll(boundAddresses); 100 } 101 102 return localAddresses; 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 public SocketAddress getDefaultLocalAddress() { 109 if (defaultLocalAddresses.isEmpty()) { 110 return null; 111 } 112 return defaultLocalAddresses.iterator().next(); 113 } 114 115 /** 116 * {@inheritDoc} 117 */ 118 public final void setDefaultLocalAddress(SocketAddress localAddress) { 119 setDefaultLocalAddresses(localAddress); 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 public final List<SocketAddress> getDefaultLocalAddresses() { 126 return unmodifiableDefaultLocalAddresses; 127 } 128 129 /** 130 * {@inheritDoc} 131 * @org.apache.xbean.Property nestedType="java.net.SocketAddress" 132 */ 133 public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) { 134 if (localAddresses == null) { 135 throw new IllegalArgumentException("localAddresses"); 136 } 137 setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses); 138 } 139 140 /** 141 * {@inheritDoc} 142 */ 143 public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) { 144 if (localAddresses == null) { 145 throw new IllegalArgumentException("localAddresses"); 146 } 147 148 synchronized (bindLock) { 149 synchronized (boundAddresses) { 150 if (!boundAddresses.isEmpty()) { 151 throw new IllegalStateException("localAddress can't be set while the acceptor is bound."); 152 } 153 154 Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>(); 155 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 * {@inheritDoc} 173 * @org.apache.xbean.Property nestedType="java.net.SocketAddress" 174 */ 175 public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) { 176 if (otherLocalAddresses == null) { 177 otherLocalAddresses = new SocketAddress[0]; 178 } 179 180 Collection<SocketAddress> newLocalAddresses = 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 * {@inheritDoc} 192 */ 193 public final boolean isCloseOnDeactivation() { 194 return disconnectOnUnbind; 195 } 196 197 /** 198 * {@inheritDoc} 199 */ 200 public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) { 201 this.disconnectOnUnbind = disconnectClientsOnUnbind; 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 public final void bind() throws IOException { 208 bind(getDefaultLocalAddresses()); 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 public final void bind(SocketAddress localAddress) throws IOException { 215 if (localAddress == null) { 216 throw new IllegalArgumentException("localAddress"); 217 } 218 219 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1); 220 localAddresses.add(localAddress); 221 bind(localAddresses); 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 public final void bind(SocketAddress... addresses) throws IOException { 228 if ((addresses == null) || (addresses.length == 0)) { 229 bind(getDefaultLocalAddresses()); 230 return; 231 } 232 233 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(2); 234 235 for (SocketAddress address : addresses) { 236 localAddresses.add(address); 237 } 238 239 bind(localAddresses); 240 } 241 242 /** 243 * {@inheritDoc} 244 */ 245 public final void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException { 246 if (firstLocalAddress == null) { 247 bind(getDefaultLocalAddresses()); 248 } 249 250 if ((addresses == null) || (addresses.length == 0)) { 251 bind(getDefaultLocalAddresses()); 252 return; 253 } 254 255 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(2); 256 localAddresses.add(firstLocalAddress); 257 258 for (SocketAddress address : addresses) { 259 localAddresses.add(address); 260 } 261 262 bind(localAddresses); 263 } 264 265 /** 266 * {@inheritDoc} 267 */ 268 public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException { 269 if (isDisposing()) { 270 throw new IllegalStateException("The Accpetor disposed is being disposed."); 271 } 272 273 if (localAddresses == null) { 274 throw new IllegalArgumentException("localAddresses"); 275 } 276 277 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>(); 278 279 for (SocketAddress a : localAddresses) { 280 checkAddressType(a); 281 localAddressesCopy.add(a); 282 } 283 284 if (localAddressesCopy.isEmpty()) { 285 throw new IllegalArgumentException("localAddresses is empty."); 286 } 287 288 boolean activate = false; 289 synchronized (bindLock) { 290 synchronized (boundAddresses) { 291 if (boundAddresses.isEmpty()) { 292 activate = true; 293 } 294 } 295 296 if (getHandler() == null) { 297 throw new IllegalStateException("handler is not set."); 298 } 299 300 try { 301 Set<SocketAddress> addresses = bindInternal(localAddressesCopy); 302 303 synchronized (boundAddresses) { 304 boundAddresses.addAll(addresses); 305 } 306 } catch (IOException e) { 307 throw e; 308 } catch (RuntimeException e) { 309 throw e; 310 } catch (Exception e) { 311 throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e); 312 } 313 } 314 315 if (activate) { 316 getListeners().fireServiceActivated(); 317 } 318 } 319 320 /** 321 * {@inheritDoc} 322 */ 323 public final void unbind() { 324 unbind(getLocalAddresses()); 325 } 326 327 /** 328 * {@inheritDoc} 329 */ 330 public final void unbind(SocketAddress localAddress) { 331 if (localAddress == null) { 332 throw new IllegalArgumentException("localAddress"); 333 } 334 335 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1); 336 localAddresses.add(localAddress); 337 unbind(localAddresses); 338 } 339 340 /** 341 * {@inheritDoc} 342 */ 343 public final void unbind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) { 344 if (firstLocalAddress == null) { 345 throw new IllegalArgumentException("firstLocalAddress"); 346 } 347 if (otherLocalAddresses == null) { 348 throw new IllegalArgumentException("otherLocalAddresses"); 349 } 350 351 List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(); 352 localAddresses.add(firstLocalAddress); 353 Collections.addAll(localAddresses, otherLocalAddresses); 354 unbind(localAddresses); 355 } 356 357 /** 358 * {@inheritDoc} 359 */ 360 public final void unbind(Iterable<? extends SocketAddress> localAddresses) { 361 if (localAddresses == null) { 362 throw new IllegalArgumentException("localAddresses"); 363 } 364 365 boolean deactivate = false; 366 synchronized (bindLock) { 367 synchronized (boundAddresses) { 368 if (boundAddresses.isEmpty()) { 369 return; 370 } 371 372 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>(); 373 int specifiedAddressCount = 0; 374 375 for (SocketAddress a : localAddresses) { 376 specifiedAddressCount++; 377 378 if ((a != null) && boundAddresses.contains(a)) { 379 localAddressesCopy.add(a); 380 } 381 } 382 383 if (specifiedAddressCount == 0) { 384 throw new IllegalArgumentException("localAddresses is empty."); 385 } 386 387 if (!localAddressesCopy.isEmpty()) { 388 try { 389 unbind0(localAddressesCopy); 390 } catch (RuntimeException e) { 391 throw e; 392 } catch (Exception e) { 393 throw new RuntimeIoException("Failed to unbind from: " + getLocalAddresses(), e); 394 } 395 396 boundAddresses.removeAll(localAddressesCopy); 397 398 if (boundAddresses.isEmpty()) { 399 deactivate = true; 400 } 401 } 402 } 403 } 404 405 if (deactivate) { 406 getListeners().fireServiceDeactivated(); 407 } 408 } 409 410 /** 411 * Starts the acceptor, and register the given addresses 412 * 413 * @param localAddresses The address to bind to 414 * @return the {@link Set} of the local addresses which is bound actually 415 * @throws Exception If the bind failed 416 */ 417 protected abstract Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception; 418 419 /** 420 * Implement this method to perform the actual unbind operation. 421 * 422 * @param localAddresses The address to unbind from 423 * @throws Exception If the unbind failed 424 */ 425 protected abstract void unbind0(List<? extends SocketAddress> localAddresses) throws Exception; 426 427 @Override 428 public String toString() { 429 TransportMetadata m = getTransportMetadata(); 430 return '(' 431 + m.getProviderName() 432 + ' ' 433 + m.getName() 434 + " acceptor: " 435 + (isActive() ? "localAddress(es): " + getLocalAddresses() + ", managedSessionCount: " 436 + getManagedSessionCount() : "not bound") + ')'; 437 } 438 439 private void checkAddressType(SocketAddress a) { 440 if (a != null && !getTransportMetadata().getAddressType().isAssignableFrom(a.getClass())) { 441 throw new IllegalArgumentException("localAddress type: " + a.getClass().getSimpleName() + " (expected: " 442 + getTransportMetadata().getAddressType().getSimpleName() + ")"); 443 } 444 } 445 446 public static class AcceptorOperationFuture extends ServiceOperationFuture { 447 private final List<SocketAddress> localAddresses; 448 449 public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) { 450 this.localAddresses = new ArrayList<SocketAddress>(localAddresses); 451 } 452 453 public final List<SocketAddress> getLocalAddresses() { 454 return Collections.unmodifiableList(localAddresses); 455 } 456 457 /** 458 * @see Object#toString() 459 */ 460 public String toString() { 461 StringBuilder sb = new StringBuilder(); 462 463 sb.append("Acceptor operation : "); 464 465 if (localAddresses != null) { 466 boolean isFirst = true; 467 468 for (SocketAddress address : localAddresses) { 469 if (isFirst) { 470 isFirst = false; 471 } else { 472 sb.append(", "); 473 } 474 475 sb.append(address); 476 } 477 } 478 return sb.toString(); 479 } 480 } 481}