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    }