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: 689627 $, $Date: 2008-08-27 23:31:49 +0200 (Wed, 27 Aug 2008) $
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 localAddress) throws IOException {
216         if (localAddress == null) {
217             throw new NullPointerException("localAddress");
218         }
219         
220         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
221         localAddresses.add(localAddress);
222         bind(localAddresses);
223     }
224 
225     /**
226      * {@inheritDoc}
227      */
228     public final void bind(
229             SocketAddress firstLocalAddress,
230             SocketAddress... otherLocalAddresses) throws IOException {
231         if (firstLocalAddress == null) {
232             throw new NullPointerException("firstLocalAddress");
233         }
234         if (otherLocalAddresses == null) {
235             throw new NullPointerException("otherLocalAddresses");
236         }
237         
238         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
239         localAddresses.add(firstLocalAddress);
240         Collections.addAll(localAddresses, otherLocalAddresses);
241         bind(localAddresses);
242     }
243 
244     /**
245      * {@inheritDoc}
246      */
247     public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
248         if (isDisposing()) {
249             throw new IllegalStateException("Already disposed.");
250         }
251         if (localAddresses == null) {
252             throw new NullPointerException("localAddresses");
253         }
254         
255         List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
256         for (SocketAddress a: localAddresses) {
257             checkAddressType(a);
258             localAddressesCopy.add(a);
259         }
260         if (localAddressesCopy.isEmpty()) {
261             throw new IllegalArgumentException("localAddresses is empty.");
262         }
263         
264         boolean activate = false;
265         synchronized (bindLock) {
266             if (boundAddresses.isEmpty()) {
267                 activate = true;
268             }
269 
270             if (getHandler() == null) {
271                 throw new IllegalStateException("handler is not set.");
272             }
273             
274             try {
275                 boundAddresses.addAll(bind0(localAddressesCopy));
276             } catch (IOException e) {
277                 throw e;
278             } catch (RuntimeException e) {
279                 throw e;
280             } catch (Throwable e) {
281                 throw new RuntimeIoException(
282                         "Failed to bind to: " + getLocalAddresses(), e);
283             }
284         }
285         
286         if (activate) {
287             getListeners().fireServiceActivated();
288         }
289     }
290 
291     /**
292      * {@inheritDoc}
293      */
294     public final void unbind() {
295         unbind(getLocalAddresses());
296     }
297 
298     /**
299      * {@inheritDoc}
300      */
301     public final void unbind(SocketAddress localAddress) {
302         if (localAddress == null) {
303             throw new NullPointerException("localAddress");
304         }
305         
306         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
307         localAddresses.add(localAddress);
308         unbind(localAddresses);
309     }
310 
311     /**
312      * {@inheritDoc}
313      */
314     public final void unbind(SocketAddress firstLocalAddress,
315             SocketAddress... otherLocalAddresses) {
316         if (firstLocalAddress == null) {
317             throw new NullPointerException("firstLocalAddress");
318         }
319         if (otherLocalAddresses == null) {
320             throw new NullPointerException("otherLocalAddresses");
321         }
322         
323         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
324         localAddresses.add(firstLocalAddress);
325         Collections.addAll(localAddresses, otherLocalAddresses);
326         unbind(localAddresses);
327     }
328 
329     /**
330      * {@inheritDoc}
331      */
332     public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
333         if (localAddresses == null) {
334             throw new NullPointerException("localAddresses");
335         }
336         
337         boolean deactivate = false;
338         synchronized (bindLock) {
339             if (boundAddresses.isEmpty()) {
340                 return;
341             }
342 
343             List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
344             int specifiedAddressCount = 0;
345             for (SocketAddress a: localAddresses) {
346                 specifiedAddressCount ++;
347                 if (a != null && boundAddresses.contains(a)) {
348                     localAddressesCopy.add(a);
349                 }
350             }
351             if (specifiedAddressCount == 0) {
352                 throw new IllegalArgumentException("localAddresses is empty.");
353             }
354             
355             if (!localAddressesCopy.isEmpty()) {
356                 try {
357                     unbind0(localAddressesCopy);
358                 } catch (RuntimeException e) {
359                     throw e;
360                 } catch (Throwable e) {
361                     throw new RuntimeIoException(
362                             "Failed to unbind from: " + getLocalAddresses(), e);
363                 }
364                 
365                 boundAddresses.removeAll(localAddressesCopy);
366                 if (boundAddresses.isEmpty()) {
367                     deactivate = true;
368                 }
369             }
370         }
371 
372         if (deactivate) {
373             getListeners().fireServiceDeactivated();
374         }
375     }
376 
377     /**
378      * Implement this method to perform the actual bind operation.
379      * @return the {@link Set} of the local addresses which is bound actually
380      */
381     protected abstract Set<SocketAddress> bind0(
382             List<? extends SocketAddress> localAddresses) throws Exception;
383 
384     /**
385      * Implement this method to perform the actual unbind operation.
386      */
387     protected abstract void unbind0(
388             List<? extends SocketAddress> localAddresses) throws Exception;
389     
390     @Override
391     public String toString() {
392         TransportMetadata m = getTransportMetadata();
393         return '(' + m.getProviderName() + ' ' + m.getName() + " acceptor: " + 
394                (isActive()?
395                        "localAddress(es): " + getLocalAddresses() +
396                        ", managedSessionCount: " + getManagedSessionCount() :
397                            "not bound") + ')'; 
398     }
399 
400     private void checkAddressType(SocketAddress a) {
401         if (a != null &&
402             !getTransportMetadata().getAddressType().isAssignableFrom(
403                         a.getClass())) {
404             throw new IllegalArgumentException("localAddress type: "
405                     + a.getClass().getSimpleName() + " (expected: "
406                     + getTransportMetadata().getAddressType().getSimpleName() + ")");
407         }
408     }
409     
410     public static class AcceptorOperationFuture extends ServiceOperationFuture {
411         private final List<SocketAddress> localAddresses;
412         
413         public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
414             this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
415         }
416         
417         public final List<SocketAddress> getLocalAddresses() {
418             return Collections.unmodifiableList(localAddresses);
419         }
420     }
421 }