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<SocketAddress>();
46  
47      private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections
48              .unmodifiableList(defaultLocalAddresses);
49  
50      private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>();
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      public SocketAddress getLocalAddress() {
84          Set<SocketAddress> localAddresses = getLocalAddresses();
85          if (localAddresses.isEmpty()) {
86              return null;
87          }
88  
89          return localAddresses.iterator().next();
90      }
91  
92      /**
93       * {@inheritDoc}
94       */
95      public final Set<SocketAddress> getLocalAddresses() {
96          Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
97  
98          synchronized (boundAddresses) {
99              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("Already 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 (Throwable 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 (Throwable 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      * @return the {@link Set} of the local addresses which is bound actually
413      */
414     protected abstract Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception;
415 
416     /**
417      * Implement this method to perform the actual unbind operation.
418      */
419     protected abstract void unbind0(List<? extends SocketAddress> localAddresses) throws Exception;
420 
421     @Override
422     public String toString() {
423         TransportMetadata m = getTransportMetadata();
424         return '('
425                 + m.getProviderName()
426                 + ' '
427                 + m.getName()
428                 + " acceptor: "
429                 + (isActive() ? "localAddress(es): " + getLocalAddresses() + ", managedSessionCount: "
430                         + getManagedSessionCount() : "not bound") + ')';
431     }
432 
433     private void checkAddressType(SocketAddress a) {
434         if (a != null && !getTransportMetadata().getAddressType().isAssignableFrom(a.getClass())) {
435             throw new IllegalArgumentException("localAddress type: " + a.getClass().getSimpleName() + " (expected: "
436                     + getTransportMetadata().getAddressType().getSimpleName() + ")");
437         }
438     }
439 
440     public static class AcceptorOperationFuture extends ServiceOperationFuture {
441         private final List<SocketAddress> localAddresses;
442 
443         public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
444             this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
445         }
446 
447         public final List<SocketAddress> getLocalAddresses() {
448             return Collections.unmodifiableList(localAddresses);
449         }
450 
451         /**
452          * @see Object#toString()
453          */
454         public String toString() {
455             StringBuilder sb = new StringBuilder();
456 
457             sb.append("Acceptor operation : ");
458 
459             if (localAddresses != null) {
460                 boolean isFirst = true;
461 
462                 for (SocketAddress address : localAddresses) {
463                     if (isFirst) {
464                         isFirst = false;
465                     } else {
466                         sb.append(", ");
467                     }
468 
469                     sb.append(address);
470                 }
471             }
472             return sb.toString();
473         }
474     }
475 }