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