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.common;
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  
31  
32  /**
33   * A base implementation of {@link IoAcceptor}.
34   *
35   * @author The Apache MINA Project (dev@mina.apache.org)
36   * @version $Rev: 601236 $, $Date: 2007-12-05 00:53:03 -0700 (Wed, 05 Dec 2007) $
37   */
38  public abstract class AbstractIoAcceptor 
39          extends AbstractIoService implements IoAcceptor {
40      
41      private final List<SocketAddress> defaultLocalAddresses =
42          new ArrayList<SocketAddress>();
43      private final List<SocketAddress> unmodifiableDefaultLocalAddresses =
44          Collections.unmodifiableList(defaultLocalAddresses);
45      private final Set<SocketAddress> boundAddresses =
46          new HashSet<SocketAddress>();
47  
48      private boolean disconnectOnUnbind = true;
49  
50      /**
51       * The lock object which is acquired while bind or unbind operation is performed.
52       * Acquire this lock in your property setters which shouldn't be changed while
53       * the service is bound.
54       */
55      protected final Object bindLock = new Object();
56  
57      protected AbstractIoAcceptor(IoSessionConfig sessionConfig) {
58          super(sessionConfig);
59          defaultLocalAddresses.add(null);
60      }
61  
62      public SocketAddress getLocalAddress() {
63          Set<SocketAddress> localAddresses = getLocalAddresses();
64          if (localAddresses.isEmpty()) {
65              return null;
66          } else {
67              return localAddresses.iterator().next();
68          }
69      }
70  
71      public final Set<SocketAddress> getLocalAddresses() {
72          Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
73          synchronized (bindLock) {
74              localAddresses.addAll(boundAddresses);
75          }
76          return localAddresses;
77      }
78  
79      public SocketAddress getDefaultLocalAddress() {
80          if (defaultLocalAddresses.isEmpty()) {
81              return null;
82          }
83          return defaultLocalAddresses.iterator().next();
84      }
85  
86      public final void setDefaultLocalAddress(SocketAddress localAddress) {
87          setDefaultLocalAddresses(localAddress);
88      }
89  
90      public final List<SocketAddress> getDefaultLocalAddresses() {
91          return unmodifiableDefaultLocalAddresses;
92      }
93  
94      public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) {
95          if (localAddresses == null) {
96              throw new NullPointerException("localAddresses");
97          }
98          setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
99      }
100 
101     public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
102         if (localAddresses == null) {
103             throw new NullPointerException("localAddresses");
104         }
105         
106         synchronized (bindLock) {
107             if (!boundAddresses.isEmpty()) {
108                 throw new IllegalStateException(
109                         "localAddress can't be set while the acceptor is bound.");
110             }
111 
112             Collection<SocketAddress> newLocalAddresses = 
113                 new ArrayList<SocketAddress>();
114             for (SocketAddress a: localAddresses) {
115                 checkAddressType(a);
116                 newLocalAddresses.add(a);
117             }
118             
119             if (newLocalAddresses.isEmpty()) {
120                 throw new IllegalArgumentException("empty localAddresses");
121             }
122             
123             this.defaultLocalAddresses.clear();
124             this.defaultLocalAddresses.addAll(newLocalAddresses);
125         }
126     }
127 
128     public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
129         if (otherLocalAddresses == null) {
130             otherLocalAddresses = new SocketAddress[0];
131         }
132         
133         Collection<SocketAddress> newLocalAddresses =
134             new ArrayList<SocketAddress>(otherLocalAddresses.length + 1);
135         
136         newLocalAddresses.add(firstLocalAddress);
137         for (SocketAddress a: otherLocalAddresses) {
138             newLocalAddresses.add(a);
139         }
140         
141         setDefaultLocalAddresses(newLocalAddresses);
142     }
143 
144     public final boolean isCloseOnDeactivation() {
145         return disconnectOnUnbind;
146     }
147 
148     public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
149         this.disconnectOnUnbind = disconnectClientsOnUnbind;
150     }
151 
152     public final void bind() throws IOException {
153         bind(getDefaultLocalAddresses());
154     }
155 
156     public final void bind(SocketAddress localAddress) throws IOException {
157         if (localAddress == null) {
158             throw new NullPointerException("localAddress");
159         }
160         
161         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
162         localAddresses.add(localAddress);
163         bind(localAddresses);
164     }
165 
166     public final void bind(
167             SocketAddress firstLocalAddress,
168             SocketAddress... otherLocalAddresses) throws IOException {
169         if (firstLocalAddress == null) {
170             throw new NullPointerException("firstLocalAddress");
171         }
172         if (otherLocalAddresses == null) {
173             throw new NullPointerException("otherLocalAddresses");
174         }
175         
176         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
177         localAddresses.add(firstLocalAddress);
178         Collections.addAll(localAddresses, otherLocalAddresses);
179         bind(localAddresses);
180     }
181 
182     public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
183         if (isDisposing()) {
184             throw new IllegalStateException("Already disposed.");
185         }
186         if (localAddresses == null) {
187             throw new NullPointerException("localAddresses");
188         }
189         
190         List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
191         for (SocketAddress a: localAddresses) {
192             checkAddressType(a);
193             localAddressesCopy.add(a);
194         }
195         if (localAddressesCopy.isEmpty()) {
196             throw new IllegalArgumentException("localAddresses is empty.");
197         }
198         
199         boolean activate = false;
200         synchronized (bindLock) {
201             if (boundAddresses.isEmpty()) {
202                 activate = true;
203             }
204 
205             if (getHandler() == null) {
206                 throw new IllegalStateException("handler is not set.");
207             }
208             
209             try {
210                 boundAddresses.addAll(bind0(localAddressesCopy));
211             } catch (IOException e) {
212                 throw e;
213             } catch (RuntimeException e) {
214                 throw e;
215             } catch (Throwable e) {
216                 throw new RuntimeIoException(
217                         "Failed to bind to: " + getLocalAddresses(), e);
218             }
219         }
220         
221         if (activate) {
222             getListeners().fireServiceActivated();
223         }
224     }
225 
226     public final void unbind() {
227         unbind(getLocalAddresses());
228     }
229 
230     public final void unbind(SocketAddress localAddress) {
231         if (localAddress == null) {
232             throw new NullPointerException("localAddress");
233         }
234         
235         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
236         localAddresses.add(localAddress);
237         unbind(localAddresses);
238     }
239 
240     public final void unbind(SocketAddress firstLocalAddress,
241             SocketAddress... otherLocalAddresses) {
242         if (firstLocalAddress == null) {
243             throw new NullPointerException("firstLocalAddress");
244         }
245         if (otherLocalAddresses == null) {
246             throw new NullPointerException("otherLocalAddresses");
247         }
248         
249         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
250         localAddresses.add(firstLocalAddress);
251         Collections.addAll(localAddresses, otherLocalAddresses);
252         unbind(localAddresses);
253     }
254 
255     public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
256         if (localAddresses == null) {
257             throw new NullPointerException("localAddresses");
258         }
259         
260         boolean deactivate = false;
261         synchronized (bindLock) {
262             if (boundAddresses.isEmpty()) {
263                 return;
264             }
265 
266             List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
267             int specifiedAddressCount = 0;
268             for (SocketAddress a: localAddresses) {
269                 specifiedAddressCount ++;
270                 if (a != null && boundAddresses.contains(a)) {
271                     localAddressesCopy.add(a);
272                 }
273             }
274             if (specifiedAddressCount == 0) {
275                 throw new IllegalArgumentException("localAddresses is empty.");
276             }
277             
278             if (!localAddressesCopy.isEmpty()) {
279                 try {
280                     unbind0(localAddressesCopy);
281                 } catch (RuntimeException e) {
282                     throw e;
283                 } catch (Throwable e) {
284                     throw new RuntimeIoException(
285                             "Failed to unbind from: " + getLocalAddresses(), e);
286                 }
287                 
288                 boundAddresses.removeAll(localAddressesCopy);
289                 if (boundAddresses.isEmpty()) {
290                     deactivate = true;
291                 }
292             }
293         }
294 
295         if (deactivate) {
296             getListeners().fireServiceDeactivated();
297         }
298     }
299 
300     /**
301      * Implement this method to perform the actual bind operation.
302      * @return the {@link Set} of the local addresses which is bound actually
303      */
304     protected abstract Set<SocketAddress> bind0(
305             List<? extends SocketAddress> localAddresses) throws Exception;
306 
307     /**
308      * Implement this method to perform the actual unbind operation.
309      */
310     protected abstract void unbind0(
311             List<? extends SocketAddress> localAddresses) throws Exception;
312     
313     @Override
314     public String toString() {
315         TransportMetadata m = getTransportMetadata();
316         return '(' + m.getProviderName() + ' ' + m.getName() + " acceptor: " + 
317                (isActive()?
318                        "localAddress(es): " + getLocalAddresses() +
319                        ", managedSessionCount: " + getManagedSessionCount() :
320                            "not bound") + ')'; 
321     }
322 
323     private void checkAddressType(SocketAddress a) {
324         if (a != null &&
325             !getTransportMetadata().getAddressType().isAssignableFrom(
326                         a.getClass())) {
327             throw new IllegalArgumentException("localAddress type: "
328                     + a.getClass().getSimpleName() + " (expected: "
329                     + getTransportMetadata().getAddressType().getSimpleName() + ")");
330         }
331     }
332     
333     protected static class AcceptorOperationFuture extends ServiceOperationFuture {
334         private final List<SocketAddress> localAddresses;
335         
336         public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
337             this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
338         }
339         
340         public final List<SocketAddress> getLocalAddresses() {
341             return Collections.unmodifiableList(localAddresses);
342         }
343     }
344 }