View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.channel.local;
17  
18  import static org.jboss.netty.channel.Channels.*;
19  
20  import java.nio.channels.ClosedChannelException;
21  import java.nio.channels.NotYetConnectedException;
22  import java.util.Queue;
23  import java.util.concurrent.ConcurrentLinkedQueue;
24  import java.util.concurrent.atomic.AtomicInteger;
25  
26  import org.jboss.netty.channel.AbstractChannel;
27  import org.jboss.netty.channel.ChannelConfig;
28  import org.jboss.netty.channel.ChannelException;
29  import org.jboss.netty.channel.ChannelFactory;
30  import org.jboss.netty.channel.ChannelFuture;
31  import org.jboss.netty.channel.ChannelFutureListener;
32  import org.jboss.netty.channel.ChannelPipeline;
33  import org.jboss.netty.channel.ChannelSink;
34  import org.jboss.netty.channel.DefaultChannelConfig;
35  import org.jboss.netty.channel.MessageEvent;
36  import org.jboss.netty.util.internal.ThreadLocalBoolean;
37  
38  /**
39   */
40  final class DefaultLocalChannel extends AbstractChannel implements LocalChannel {
41  
42      // TODO Move the state management up to AbstractChannel to remove duplication.
43      private static final int ST_OPEN = 0;
44      private static final int ST_BOUND = 1;
45      private static final int ST_CONNECTED = 2;
46      private static final int ST_CLOSED = -1;
47      final AtomicInteger state = new AtomicInteger(ST_OPEN);
48  
49      private final ChannelConfig config;
50      private final ThreadLocalBoolean delivering = new ThreadLocalBoolean();
51  
52      final Queue<MessageEvent> writeBuffer = new ConcurrentLinkedQueue<MessageEvent>();
53  
54      volatile DefaultLocalChannel pairedChannel;
55      volatile LocalAddress localAddress;
56      volatile LocalAddress remoteAddress;
57  
58      DefaultLocalChannel(
59              LocalServerChannel parent, ChannelFactory factory, ChannelPipeline pipeline,
60              ChannelSink sink, DefaultLocalChannel pairedChannel) {
61          super(parent, factory, pipeline, sink);
62          this.pairedChannel = pairedChannel;
63          config = new DefaultChannelConfig();
64  
65          // TODO Move the state variable to AbstractChannel so that we don't need
66          //      to add many listeners.
67          getCloseFuture().addListener(new ChannelFutureListener() {
68              public void operationComplete(ChannelFuture future) throws Exception {
69                  state.set(ST_CLOSED);
70              }
71          });
72  
73          fireChannelOpen(this);
74      }
75  
76      public ChannelConfig getConfig() {
77          return config;
78      }
79  
80      @Override
81      public boolean isOpen() {
82          return state.get() >= ST_OPEN;
83      }
84  
85      public boolean isBound() {
86          return state.get() >= ST_BOUND;
87      }
88  
89      public boolean isConnected() {
90          return state.get() == ST_CONNECTED;
91      }
92  
93      void setBound() throws ClosedChannelException {
94          if (!state.compareAndSet(ST_OPEN, ST_BOUND)) {
95              switch (state.get()) {
96              case ST_CLOSED:
97                  throw new ClosedChannelException();
98              default:
99                  throw new ChannelException("already bound");
100             }
101         }
102     }
103 
104     void setConnected() {
105         if (state.get() != ST_CLOSED) {
106             state.set(ST_CONNECTED);
107         }
108     }
109 
110     @Override
111     protected boolean setClosed() {
112         return super.setClosed();
113     }
114 
115     public LocalAddress getLocalAddress() {
116         return localAddress;
117     }
118 
119     public LocalAddress getRemoteAddress() {
120         return remoteAddress;
121     }
122 
123     void closeNow(ChannelFuture future) {
124         LocalAddress localAddress = this.localAddress;
125         try {
126             // Close the self.
127             if (!setClosed()) {
128                 return;
129             }
130 
131             DefaultLocalChannel pairedChannel = this.pairedChannel;
132             if (pairedChannel != null) {
133                 this.pairedChannel = null;
134                 fireChannelDisconnected(this);
135                 fireChannelUnbound(this);
136             }
137             fireChannelClosed(this);
138 
139             // Close the peer.
140             if (pairedChannel == null || !pairedChannel.setClosed()) {
141                 return;
142             }
143 
144             DefaultLocalChannel me = pairedChannel.pairedChannel;
145             if (me != null) {
146                 pairedChannel.pairedChannel = null;
147                 fireChannelDisconnected(pairedChannel);
148                 fireChannelUnbound(pairedChannel);
149             }
150             fireChannelClosed(pairedChannel);
151         } finally {
152             future.setSuccess();
153             if (localAddress != null && getParent() == null) {
154                 LocalChannelRegistry.unregister(localAddress);
155             }
156         }
157     }
158 
159     void flushWriteBuffer() {
160         DefaultLocalChannel pairedChannel = this.pairedChannel;
161         if (pairedChannel != null) {
162             if (pairedChannel.isConnected()) {
163                 // Channel is open and connected and channelConnected event has
164                 // been fired.
165                 if (!delivering.get()) {
166                     delivering.set(true);
167                     try {
168                         for (;;) {
169                             MessageEvent e = writeBuffer.poll();
170                             if (e == null) {
171                                 break;
172                             }
173 
174                             e.getFuture().setSuccess();
175                             fireMessageReceived(pairedChannel, e.getMessage());
176                             fireWriteComplete(this, 1);
177                         }
178                     } finally {
179                         delivering.set(false);
180                     }
181                 }
182             } else {
183                 // Channel is open and connected but channelConnected event has
184                 // not been fired yet.
185             }
186         } else {
187             // Channel is closed or not connected yet - notify as failures.
188             Exception cause;
189             if (isOpen()) {
190                 cause = new NotYetConnectedException();
191             } else {
192                 cause = new ClosedChannelException();
193             }
194 
195             for (;;) {
196                 MessageEvent e = writeBuffer.poll();
197                 if (e == null) {
198                     break;
199                 }
200 
201                 e.getFuture().setFailure(cause);
202                 fireExceptionCaught(this, cause);
203             }
204         }
205     }
206 }