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: 751415 $, $Date: 2009-03-08 14:17:11 +0100 (Sun, 08 Mar 2009) $
43   * @org.apache.xbean.XBean
44   */
45  public abstract class AbstractIoAcceptor 
46          extends AbstractIoService implements IoAcceptor {
47      
48      private final List<SocketAddress> defaultLocalAddresses =
49          new ArrayList<SocketAddress>();
50      private final List<SocketAddress> unmodifiableDefaultLocalAddresses =
51          Collections.unmodifiableList(defaultLocalAddresses);
52      private final Set<SocketAddress> boundAddresses =
53          new HashSet<SocketAddress>();
54  
55      private boolean disconnectOnUnbind = true;
56  
57      /**
58       * The lock object which is acquired while bind or unbind operation is performed.
59       * Acquire this lock in your property setters which shouldn't be changed while
60       * the service is bound.
61       */
62      protected final Object bindLock = new Object();
63  
64      /**
65  	 * Constructor for {@link AbstractIoAcceptor}. You need to provide a default
66  	 * session configuration and an {@link Executor} for handling I/O events. If
67  	 * null {@link Executor} is provided, a default one will be created using
68  	 * {@link Executors#newCachedThreadPool()}.
69       *
70  	 * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
71  	 * 
72  	 * @param sessionConfig
73  	 *            the default configuration for the managed {@link IoSession}
74  	 * @param executor
75  	 *            the {@link Executor} used for handling execution of I/O
76  	 *            events. Can be <code>null</code>.
77  	 */
78      protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
79          super(sessionConfig, executor);
80          defaultLocalAddresses.add(null);
81      }
82  
83      /**
84       * {@inheritDoc}
85       */
86      public SocketAddress getLocalAddress() {
87          Set<SocketAddress> localAddresses = getLocalAddresses();
88          if (localAddresses.isEmpty()) {
89              return null;
90          } else {
91              return localAddresses.iterator().next();
92          }
93      }
94  
95      /**
96       * {@inheritDoc}
97       */
98      public final Set<SocketAddress> getLocalAddresses() {
99          Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
100         synchronized (bindLock) {
101             localAddresses.addAll(boundAddresses);
102         }
103         return localAddresses;
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     public SocketAddress getDefaultLocalAddress() {
110         if (defaultLocalAddresses.isEmpty()) {
111             return null;
112         }
113         return defaultLocalAddresses.iterator().next();
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     public final void setDefaultLocalAddress(SocketAddress localAddress) {
120         setDefaultLocalAddresses(localAddress);
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     public final List<SocketAddress> getDefaultLocalAddresses() {
127         return unmodifiableDefaultLocalAddresses;
128     }
129 
130     /**
131      * {@inheritDoc}
132      * @org.apache.xbean.Property nestedType="java.net.SocketAddress"
133      */
134     public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) {
135         if (localAddresses == null) {
136             throw new NullPointerException("localAddresses");
137         }
138         setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
139     }
140 
141     /**
142      * {@inheritDoc}
143      */
144     public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
145         if (localAddresses == null) {
146             throw new NullPointerException("localAddresses");
147         }
148         
149         synchronized (bindLock) {
150             if (!boundAddresses.isEmpty()) {
151                 throw new IllegalStateException(
152                         "localAddress can't be set while the acceptor is bound.");
153             }
154 
155             Collection<SocketAddress> newLocalAddresses = 
156                 new ArrayList<SocketAddress>();
157             for (SocketAddress a: localAddresses) {
158                 checkAddressType(a);
159                 newLocalAddresses.add(a);
160             }
161             
162             if (newLocalAddresses.isEmpty()) {
163                 throw new IllegalArgumentException("empty localAddresses");
164             }
165             
166             this.defaultLocalAddresses.clear();
167             this.defaultLocalAddresses.addAll(newLocalAddresses);
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 =
181             new ArrayList<SocketAddress>(otherLocalAddresses.length + 1);
182         
183         newLocalAddresses.add(firstLocalAddress);
184         for (SocketAddress a: otherLocalAddresses) {
185             newLocalAddresses.add(a);
186         }
187         
188         setDefaultLocalAddresses(newLocalAddresses);
189     }
190 
191     /**
192      * {@inheritDoc}
193      */
194     public final boolean isCloseOnDeactivation() {
195         return disconnectOnUnbind;
196     }
197 
198     /**
199      * {@inheritDoc}
200      */
201     public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
202         this.disconnectOnUnbind = disconnectClientsOnUnbind;
203     }
204 
205     /**
206      * {@inheritDoc}
207      */
208     public final void bind() throws IOException {
209         bind(getDefaultLocalAddresses());
210     }
211 
212     /**
213      * {@inheritDoc}
214      */
215     public final void bind(SocketAddress... addresses) throws IOException {
216         if ((addresses == null) || (addresses.length == 0)) {
217             bind(getDefaultLocalAddresses());
218         }
219         
220         if (addresses.length == 1) {
221             List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(addresses.length);
222             for (SocketAddress address:addresses) {
223                 localAddresses.add(address);
224             }
225             
226             bind(localAddresses);
227         }
228     }
229 
230     /**
231      * {@inheritDoc}
232      */
233     public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
234         if (isDisposing()) {
235             throw new IllegalStateException("Already disposed.");
236         }
237         
238         if (localAddresses == null) {
239             throw new NullPointerException("localAddresses");
240         }
241         
242         List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
243         
244         for (SocketAddress a: localAddresses) {
245             checkAddressType(a);
246             localAddressesCopy.add(a);
247         }
248         
249         if (localAddressesCopy.isEmpty()) {
250             throw new IllegalArgumentException("localAddresses is empty.");
251         }
252         
253         boolean activate = false;
254         synchronized (bindLock) {
255             if (boundAddresses.isEmpty()) {
256                 activate = true;
257             }
258 
259             if (getHandler() == null) {
260                 throw new IllegalStateException("handler is not set.");
261             }
262             
263             try {
264                 boundAddresses.addAll(bindInternal(localAddressesCopy));
265             } catch (IOException e) {
266                 throw e;
267             } catch (RuntimeException e) {
268                 throw e;
269             } catch (Throwable e) {
270                 throw new RuntimeIoException(
271                         "Failed to bind to: " + getLocalAddresses(), e);
272             }
273         }
274         
275         if (activate) {
276             getListeners().fireServiceActivated();
277         }
278     }
279 
280     /**
281      * {@inheritDoc}
282      */
283     public final void unbind() {
284         unbind(getLocalAddresses());
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     public final void unbind(SocketAddress localAddress) {
291         if (localAddress == null) {
292             throw new NullPointerException("localAddress");
293         }
294         
295         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
296         localAddresses.add(localAddress);
297         unbind(localAddresses);
298     }
299 
300     /**
301      * {@inheritDoc}
302      */
303     public final void unbind(SocketAddress firstLocalAddress,
304             SocketAddress... otherLocalAddresses) {
305         if (firstLocalAddress == null) {
306             throw new NullPointerException("firstLocalAddress");
307         }
308         if (otherLocalAddresses == null) {
309             throw new NullPointerException("otherLocalAddresses");
310         }
311         
312         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
313         localAddresses.add(firstLocalAddress);
314         Collections.addAll(localAddresses, otherLocalAddresses);
315         unbind(localAddresses);
316     }
317 
318     /**
319      * {@inheritDoc}
320      */
321     public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
322         if (localAddresses == null) {
323             throw new NullPointerException("localAddresses");
324         }
325         
326         boolean deactivate = false;
327         synchronized (bindLock) {
328             if (boundAddresses.isEmpty()) {
329                 return;
330             }
331 
332             List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
333             int specifiedAddressCount = 0;
334             for (SocketAddress a: localAddresses) {
335                 specifiedAddressCount ++;
336                 if (a != null && boundAddresses.contains(a)) {
337                     localAddressesCopy.add(a);
338                 }
339             }
340             if (specifiedAddressCount == 0) {
341                 throw new IllegalArgumentException("localAddresses is empty.");
342             }
343             
344             if (!localAddressesCopy.isEmpty()) {
345                 try {
346                     unbind0(localAddressesCopy);
347                 } catch (RuntimeException e) {
348                     throw e;
349                 } catch (Throwable e) {
350                     throw new RuntimeIoException(
351                             "Failed to unbind from: " + getLocalAddresses(), e);
352                 }
353                 
354                 boundAddresses.removeAll(localAddressesCopy);
355                 if (boundAddresses.isEmpty()) {
356                     deactivate = true;
357                 }
358             }
359         }
360 
361         if (deactivate) {
362             getListeners().fireServiceDeactivated();
363         }
364     }
365 
366     /**
367      * Starts the acceptor, and register the given addresses
368      * @return the {@link Set} of the local addresses which is bound actually
369      */
370     protected abstract Set<SocketAddress> bindInternal(
371             List<? extends SocketAddress> localAddresses) throws Exception;
372 
373     /**
374      * Implement this method to perform the actual unbind operation.
375      */
376     protected abstract void unbind0(
377             List<? extends SocketAddress> localAddresses) throws Exception;
378     
379     @Override
380     public String toString() {
381         TransportMetadata m = getTransportMetadata();
382         return '(' + m.getProviderName() + ' ' + m.getName() + " acceptor: " + 
383                (isActive()?
384                        "localAddress(es): " + getLocalAddresses() +
385                        ", managedSessionCount: " + getManagedSessionCount() :
386                            "not bound") + ')'; 
387     }
388 
389     private void checkAddressType(SocketAddress a) {
390         if (a != null &&
391             !getTransportMetadata().getAddressType().isAssignableFrom(
392                         a.getClass())) {
393             throw new IllegalArgumentException("localAddress type: "
394                     + a.getClass().getSimpleName() + " (expected: "
395                     + getTransportMetadata().getAddressType().getSimpleName() + ")");
396         }
397     }
398     
399     public static class AcceptorOperationFuture extends ServiceOperationFuture {
400         private final List<SocketAddress> localAddresses;
401         
402         public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
403             this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
404         }
405         
406         public final List<SocketAddress> getLocalAddresses() {
407             return Collections.unmodifiableList(localAddresses);
408         }
409         
410         /**
411          * @see Object#toString()
412          */
413         public String toString() {
414             StringBuilder sb = new StringBuilder();
415             
416             sb.append( "Acceptor operation : " );
417             
418             if (localAddresses != null) {
419                 boolean isFirst = true;
420                 
421                 for (SocketAddress address:localAddresses) {
422                     if (isFirst) {
423                         isFirst = false;
424                     } else {
425                         sb.append(", ");
426                     }
427                     
428                     sb.append(address);
429                 }
430             }
431             return sb.toString(); 
432         }
433     }
434 }