001 /**
002 * Copyright (C) 2012 FuseSource, Inc.
003 * http://fusesource.com
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.fusesource.hawtdispatch.transport;
019
020 import org.fusesource.hawtdispatch.*;
021
022 import java.io.EOFException;
023 import java.io.IOException;
024 import java.net.SocketAddress;
025 import java.net.URI;
026 import java.util.LinkedList;
027 import java.util.concurrent.atomic.AtomicBoolean;
028
029 /**
030 *
031 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
032 */
033 public class PipeTransport implements Transport {
034 static private final Object EOF_TOKEN = new Object();
035
036 final private PipeTransportServer server;
037 PipeTransport peer;
038 private TransportListener listener;
039 private SocketAddress remoteAddress;
040 private AtomicBoolean stopping = new AtomicBoolean();
041 private String name;
042 private boolean marshal;
043 private boolean trace;
044
045 private DispatchQueue dispatchQueue;
046 private CustomDispatchSource<Object,LinkedList<Object>> dispatchSource;
047 private boolean connected;
048
049 private long writeCounter = 0;
050 private long readCounter = 0;
051 private ProtocolCodec protocolCodec;
052
053 public PipeTransport(PipeTransportServer server) {
054 this.server = server;
055 }
056
057 public DispatchQueue getDispatchQueue() {
058 return dispatchQueue;
059 }
060 public void setDispatchQueue(DispatchQueue queue) {
061 this.dispatchQueue = queue;
062 }
063
064 public void start(final Runnable onCompleted) {
065 if (dispatchQueue == null) {
066 throw new IllegalArgumentException("dispatchQueue is not set");
067 }
068 server.dispatchQueue.execute(new Runnable(){
069 public void run() {
070 dispatchSource = Dispatch.createSource(EventAggregators.linkedList(), dispatchQueue);
071 dispatchSource.setEventHandler(new Runnable() {
072 public void run() {
073 try {
074 final LinkedList<Object> commands = dispatchSource.getData();
075 for (Object o : commands) {
076
077 if (o == EOF_TOKEN) {
078 throw new EOFException();
079 }
080 readCounter++;
081 listener.onTransportCommand(o);
082 }
083
084 // let the peer know that they have been processed.
085 peer.dispatchQueue.execute(new Runnable() {
086 public void run() {
087 outbound -= commands.size();
088 drainInbound();
089 }
090 });
091 } catch (IOException e) {
092 listener.onTransportFailure(e);
093 }
094
095 }
096 });
097 if( peer.dispatchSource != null ) {
098 fireConnected();
099 peer.fireConnected();
100 }
101 if( onCompleted!=null ) {
102 onCompleted.run();
103 }
104
105 }
106 });
107 }
108
109 private void fireConnected() {
110 dispatchQueue.execute(new Runnable() {
111 public void run() {
112 connected = true;
113 dispatchSource.resume();
114 listener.onTransportConnected();
115 drainInbound();
116 }
117 });
118 }
119
120 public void flush() {
121 listener.onRefill();
122 }
123
124 public void stop(Runnable onCompleted) {
125 if( connected ) {
126 peer.dispatchSource.merge(EOF_TOKEN);
127 }
128 if( dispatchSource!=null ) {
129 dispatchSource.setCancelHandler(onCompleted);
130 dispatchSource.cancel();
131 }
132 setDispatchQueue(null);
133 }
134
135 static final class OneWay {
136 final Object command;
137 final Retained retained;
138
139 public OneWay(Object command, Retained retained) {
140 this.command = command;
141 this.retained = retained;
142 }
143 }
144
145 int outbound = 0;
146 int maxOutbound = 100;
147
148 public boolean full() {
149 return outbound >= maxOutbound;
150 }
151
152 public boolean offer(Object command) {
153 if( !connected ) {
154 return false;
155 }
156 if( full() ) {
157 return false;
158 } else {
159 transmit(command);
160 return true;
161 }
162 }
163
164 private void drainInbound() {
165 if( !full() ) {
166 listener.onRefill();
167 }
168 }
169
170 private void transmit(Object command) {
171 writeCounter++;
172 outbound++;
173 peer.dispatchSource.merge(command);
174 }
175
176 /**
177 * @return The number of objects sent by the transport.
178 */
179 public long getWriteCounter() {
180 return writeCounter;
181 }
182
183 /**
184 * @return The number of objects received by the transport.
185 */
186 public long getReadCounter() {
187 return readCounter;
188 }
189
190 public SocketAddress getLocalAddress() {
191 return remoteAddress;
192 }
193
194 public SocketAddress getRemoteAddress() {
195 return remoteAddress;
196 }
197
198 public void suspendRead() {
199 dispatchSource.suspend();
200 }
201
202 public void resumeRead() {
203 dispatchSource.resume();
204 }
205
206 public void setRemoteAddress(final String remoteAddress) {
207 this.remoteAddress = new SocketAddress() {
208 @Override
209 public String toString() {
210 return remoteAddress;
211 }
212 };
213 if (name == null) {
214 name = remoteAddress;
215 }
216 }
217
218 public void setName(String name) {
219 this.name = name;
220 }
221
222 public TransportListener getTransportListener() {
223 return listener;
224 }
225 public void setTransportListener(TransportListener transportListener) {
226 this.listener = transportListener;
227 }
228
229 public ProtocolCodec getProtocolCodec() {
230 return protocolCodec;
231 }
232 public void setProtocolCodec(ProtocolCodec protocolCodec) {
233 this.protocolCodec = protocolCodec;
234 }
235
236
237 public boolean isTrace() {
238 return trace;
239 }
240
241 public void setTrace(boolean trace) {
242 this.trace = trace;
243 }
244
245 public boolean isMarshal() {
246 return marshal;
247 }
248 public void setMarshal(boolean marshall) {
249 this.marshal = marshall;
250 }
251
252 public boolean isConnected() {
253 return !stopping.get();
254 }
255 public boolean isClosed() {
256 return false;
257 }
258 }