View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
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   * A base implementation of {@link IoAcceptor}.
39   *
40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
41   * @org.apache.xbean.XBean
42   */
43  public abstract class AbstractIoAcceptor extends AbstractIoService implements IoAcceptor {
44  
45      private final List<SocketAddress> defaultLocalAddresses = new ArrayList<>();
46  
47      private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections
48              .unmodifiableList(defaultLocalAddresses);
49  
50      private final Set<SocketAddress> boundAddresses = new HashSet<>();
51  
52      private boolean disconnectOnUnbind = true;
53  
54      /**
55       * The lock object which is acquired while bind or unbind operation is performed.
56       * Acquire this lock in your property setters which shouldn't be changed while
57       * the service is bound.
58       */
59      protected final Object bindLock = new Object();
60  
61      /**
62       * Constructor for {@link AbstractIoAcceptor}. You need to provide a default
63       * session configuration and an {@link Executor} for handling I/O events. If
64       * null {@link Executor} is provided, a default one will be created using
65       * {@link Executors#newCachedThreadPool()}.
66       * 
67       * @see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)
68       * 
69       * @param sessionConfig
70       *            the default configuration for the managed {@link IoSession}
71       * @param executor
72       *            the {@link Executor} used for handling execution of I/O
73       *            events. Can be <code>null</code>.
74       */
75      protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
76          super(sessionConfig, executor);
77          defaultLocalAddresses.add(null);
78      }
79  
80      /**
81       * {@inheritDoc}
82       */
83      @Override
84      public SocketAddress getLocalAddress() {
85          Set<SocketAddress> localAddresses = getLocalAddresses();
86          if (localAddresses.isEmpty()) {
87              return null;
88          }
89  
90          return localAddresses.iterator().next();
91      }
92  
93      /**
94       * {@inheritDoc}
95       */
96      @Override
97      public final Set<SocketAddress> getLocalAddresses() {
98          Set<SocketAddress> localAddresses = new HashSet<>();
99  
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
283 public 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 }