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