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.transport.vmpipe;
21  
22  import java.io.IOException;
23  import java.net.SocketAddress;
24  import java.util.HashSet;
25  import java.util.Set;
26  
27  import org.apache.mina.common.AbstractIoConnector;
28  import org.apache.mina.common.ConnectFuture;
29  import org.apache.mina.common.DefaultConnectFuture;
30  import org.apache.mina.common.ExceptionMonitor;
31  import org.apache.mina.common.IdleStatusChecker;
32  import org.apache.mina.common.IoFilterChain;
33  import org.apache.mina.common.IoFuture;
34  import org.apache.mina.common.IoFutureListener;
35  import org.apache.mina.common.IoHandler;
36  import org.apache.mina.common.IoSessionInitializer;
37  import org.apache.mina.common.TransportMetadata;
38  
39  /**
40   * Connects to {@link IoHandler}s which is bound on the specified
41   * {@link VmPipeAddress}.
42   *
43   * @author The Apache MINA Project (dev@mina.apache.org)
44   * @version $Rev: 607163 $, $Date: 2007-12-27 20:20:07 -0700 (Thu, 27 Dec 2007) $
45   */
46  public final class VmPipeConnector extends AbstractIoConnector {
47  
48      /**
49       * Creates a new instance.
50       */
51      public VmPipeConnector() {
52          super(new DefaultVmPipeSessionConfig());
53      }
54  
55      public TransportMetadata getTransportMetadata() {
56          return VmPipeSessionImpl.METADATA;
57      }
58  
59      @Override
60      public VmPipeSessionConfig getSessionConfig() {
61          return (VmPipeSessionConfig) super.getSessionConfig();
62      }
63  
64      @Override
65      protected ConnectFuture connect0(SocketAddress remoteAddress,
66                                        SocketAddress localAddress,
67                                        IoSessionInitializer<? extends ConnectFuture> sessionInitializer) {
68          VmPipe entry = VmPipeAcceptor.boundHandlers.get(remoteAddress);
69          if (entry == null) {
70              return DefaultConnectFuture.newFailedFuture(new IOException(
71                      "Endpoint unavailable: " + remoteAddress));
72          }
73  
74          DefaultConnectFuture future = new DefaultConnectFuture();
75  
76          // Assign the local address dynamically,
77          VmPipeAddress actualLocalAddress;
78          try {
79              actualLocalAddress = nextLocalAddress();
80          } catch (IOException e) {
81              return DefaultConnectFuture.newFailedFuture(e);
82          }
83  
84          VmPipeSessionImpl localSession = new VmPipeSessionImpl(this,
85                  getListeners(), actualLocalAddress, getHandler(), entry);
86  
87          finishSessionInitialization(localSession, future, sessionInitializer);
88  
89          // and reclaim the local address when the connection is closed.
90          localSession.getCloseFuture().addListener(LOCAL_ADDRESS_RECLAIMER);
91  
92          // initialize connector session
93          try {
94              IoFilterChain filterChain = localSession.getFilterChain();
95              this.getFilterChainBuilder().buildFilterChain(filterChain);
96  
97              // The following sentences don't throw any exceptions.
98              getListeners().fireSessionCreated(localSession);
99              IdleStatusChecker.getInstance().addSession(localSession);
100         } catch (Throwable t) {
101             future.setException(t);
102             return future;
103         }
104 
105         // initialize acceptor session
106         VmPipeSessionImpl remoteSession = localSession.getRemoteSession();
107         ((VmPipeAcceptor) remoteSession.getService()).doFinishSessionInitialization(remoteSession, null);
108         try {
109             IoFilterChain filterChain = remoteSession.getFilterChain();
110             entry.getAcceptor().getFilterChainBuilder().buildFilterChain(
111                     filterChain);
112 
113             // The following sentences don't throw any exceptions.
114             entry.getListeners().fireSessionCreated(remoteSession);
115             IdleStatusChecker.getInstance().addSession(remoteSession);
116         } catch (Throwable t) {
117             ExceptionMonitor.getInstance().exceptionCaught(t);
118             remoteSession.close();
119         }
120 
121         // Start chains, and then allow and messages read/written to be processed. This is to ensure that
122         // sessionOpened gets received before a messageReceived
123         ((VmPipeFilterChain) localSession.getFilterChain()).start();
124         ((VmPipeFilterChain) remoteSession.getFilterChain()).start();
125 
126         return future;
127     }
128 
129     @Override
130     protected IoFuture dispose0() throws Exception {
131         return null;
132     }
133 
134     private static final Set<VmPipeAddress> TAKEN_LOCAL_ADDRESSES = new HashSet<VmPipeAddress>();
135 
136     private static int nextLocalPort = -1;
137 
138     private static final IoFutureListener<IoFuture> LOCAL_ADDRESS_RECLAIMER = new LocalAddressReclaimer();
139 
140     private static VmPipeAddress nextLocalAddress() throws IOException {
141         synchronized (TAKEN_LOCAL_ADDRESSES) {
142             if (nextLocalPort >= 0) {
143                 nextLocalPort = -1;
144             }
145             for (int i = 0; i < Integer.MAX_VALUE; i++) {
146                 VmPipeAddress answer = new VmPipeAddress(nextLocalPort--);
147                 if (!TAKEN_LOCAL_ADDRESSES.contains(answer)) {
148                     TAKEN_LOCAL_ADDRESSES.add(answer);
149                     return answer;
150                 }
151             }
152         }
153 
154         throw new IOException("Can't assign a local VM pipe port.");
155     }
156 
157     private static class LocalAddressReclaimer implements IoFutureListener<IoFuture> {
158         public void operationComplete(IoFuture future) {
159             synchronized (TAKEN_LOCAL_ADDRESSES) {
160                 TAKEN_LOCAL_ADDRESSES.remove(future.getSession()
161                         .getLocalAddress());
162             }
163         }
164     }
165 }