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