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  /**
39   * A base implementation of {@link IoAcceptor}.
40   *
41   * @author The Apache MINA Project (dev@mina.apache.org)
42   * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (jeu, 26 jun 2008) $
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       * The lock object which is acquired while bind or unbind operation is performed.
58       * Acquire this lock in your property setters which shouldn't be changed while
59       * the service is bound.
60       */
61      protected final Object bindLock = new Object();
62  
63      /**
64  	 * Constructor for {@link AbstractIoAcceptor}. You need to provide a default
65  	 * session configuration and an {@link Executor} for handling I/O events. If
66  	 * null {@link Executor} is provided, a default one will be created using
67  	 * {@link Executors#newCachedThreadPool()}.
68       *
69  	 * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
70  	 * 
71  	 * @param sessionConfig
72  	 *            the default configuration for the managed {@link IoSession}
73  	 * @param executor
74  	 *            the {@link Executor} used for handling execution of I/O
75  	 *            events. Can be <code>null</code>.
76  	 */
77      protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
78          super(sessionConfig, executor);
79          defaultLocalAddresses.add(null);
80      }
81  
82      /**
83       * {@inheritDoc}
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       * {@inheritDoc}
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      * {@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      */
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      * {@inheritDoc}
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      * {@inheritDoc}
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      * {@inheritDoc}
190      */
191     public final boolean isCloseOnDeactivation() {
192         return disconnectOnUnbind;
193     }
194 
195     /**
196      * {@inheritDoc}
197      */
198     public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
199         this.disconnectOnUnbind = disconnectClientsOnUnbind;
200     }
201 
202     /**
203      * {@inheritDoc}
204      */
205     public final void bind() throws IOException {
206         bind(getDefaultLocalAddresses());
207     }
208 
209     /**
210      * {@inheritDoc}
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      * {@inheritDoc}
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      * {@inheritDoc}
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      * {@inheritDoc}
290      */
291     public final void unbind() {
292         unbind(getLocalAddresses());
293     }
294 
295     /**
296      * {@inheritDoc}
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      * {@inheritDoc}
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      * {@inheritDoc}
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      * Implement this method to perform the actual bind operation.
376      * @return the {@link Set} of the local addresses which is bound actually
377      */
378     protected abstract Set<SocketAddress> bind0(
379             List<? extends SocketAddress> localAddresses) throws Exception;
380 
381     /**
382      * Implement this method to perform the actual unbind operation.
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 }