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        @Deprecated
065        public void start(final Runnable onCompleted) {
066            start(new TaskWrapper(onCompleted));
067        }
068        public void start(final Task onCompleted) {
069            if (dispatchQueue == null) {
070                throw new IllegalArgumentException("dispatchQueue is not set");
071            }
072            server.dispatchQueue.execute(new Task(){
073                public void run() {
074                    dispatchSource = Dispatch.createSource(EventAggregators.linkedList(), dispatchQueue);
075                    dispatchSource.setEventHandler(new Task() {
076                        public void run() {
077                            try {
078                                final LinkedList<Object> commands = dispatchSource.getData();
079                                for (Object o : commands) {
080    
081                                    if (o == EOF_TOKEN) {
082                                        throw new EOFException();
083                                    }
084                                    readCounter++;
085                                    listener.onTransportCommand(o);
086                                }
087    
088                                // let the peer know that they have been processed.
089                                peer.dispatchQueue.execute(new Task() {
090                                    public void run() {
091                                        outbound -= commands.size();
092                                        drainInbound();
093                                    }
094                                });
095                            } catch (IOException e) {
096                                listener.onTransportFailure(e);
097                            }
098    
099                        }
100                    });
101                    if( peer.dispatchSource != null ) {
102                        fireConnected();
103                        peer.fireConnected();
104                    }
105                    if( onCompleted!=null ) {
106                        onCompleted.run();
107                    }
108    
109                }
110            });
111        }
112    
113        private void fireConnected() {
114            dispatchQueue.execute(new Task() {
115                public void run() {
116                    connected = true;
117                    dispatchSource.resume();
118                    listener.onTransportConnected();
119                    drainInbound();
120                }
121            });
122        }
123    
124        public void flush() {
125            listener.onRefill();
126        }
127    
128        @Deprecated
129        public void stop(final Runnable onCompleted) {
130            stop(new TaskWrapper(onCompleted));
131        }
132        public void stop(Task onCompleted)  {
133            if( connected ) {
134                peer.dispatchSource.merge(EOF_TOKEN);
135            }
136            if( dispatchSource!=null ) {
137                dispatchSource.setCancelHandler(onCompleted);
138                dispatchSource.cancel();
139            }
140            setDispatchQueue(null);
141        }
142    
143        static final class OneWay {
144            final Object command;
145            final Retained retained;
146    
147            public OneWay(Object command, Retained retained) {
148                this.command = command;
149                this.retained = retained;
150            }
151        }
152    
153        int outbound = 0;
154        int maxOutbound = 100;
155    
156        public boolean full() {
157            return outbound >= maxOutbound;
158        }
159    
160        public boolean offer(Object command) {
161            if( !connected ) {
162                return false;
163            }
164            if( full() ) {
165                return false;
166            } else {
167                transmit(command);
168                return true;
169            }
170        }
171    
172        private void drainInbound() {
173            if( !full() ) {
174                listener.onRefill();
175            }
176        }
177    
178        private void transmit(Object command) {
179            writeCounter++;
180            outbound++;
181            peer.dispatchSource.merge(command);
182        }
183    
184        /**
185         * @return The number of objects sent by the transport.
186         */
187        public long getWriteCounter() {
188            return writeCounter;
189        }
190    
191        /**
192         * @return The number of objects received by the transport.
193         */
194        public long getReadCounter() {
195            return readCounter;
196        }
197    
198        public SocketAddress getLocalAddress() {
199            return remoteAddress;
200        }
201    
202        public SocketAddress getRemoteAddress() {
203            return remoteAddress;
204        }
205    
206        public void suspendRead() {
207            dispatchSource.suspend();
208        }
209    
210        public void resumeRead() {
211            dispatchSource.resume();
212        }
213    
214        public void setRemoteAddress(final String remoteAddress) {
215            this.remoteAddress = new SocketAddress() {
216                @Override
217                public String toString() {
218                    return remoteAddress;
219                }
220            };
221            if (name == null) {
222                name = remoteAddress;
223            }
224        }
225    
226        public void setName(String name) {
227            this.name = name;
228        }
229    
230        public TransportListener getTransportListener() {
231            return listener;
232        }
233        public void setTransportListener(TransportListener transportListener) {
234            this.listener = transportListener;
235        }
236    
237        public ProtocolCodec getProtocolCodec() {
238            return protocolCodec;
239        }
240        public void setProtocolCodec(ProtocolCodec protocolCodec) {
241            this.protocolCodec = protocolCodec;
242        }
243    
244    
245        public boolean isTrace() {
246            return trace;
247        }
248    
249        public void setTrace(boolean trace) {
250            this.trace = trace;
251        }
252    
253        public boolean isMarshal() {
254            return marshal;
255        }
256        public void setMarshal(boolean marshall) {
257            this.marshal = marshall;
258        }
259    
260        public boolean isConnected() {
261            return !stopping.get();
262        }
263        public boolean isClosed() {
264            return false;
265        }
266    }