View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.transport.serial;
21  
22  import gnu.io.SerialPort;
23  import gnu.io.SerialPortEvent;
24  import gnu.io.SerialPortEventListener;
25  import gnu.io.UnsupportedCommOperationException;
26  
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.util.TooManyListenersException;
31  
32  import org.apache.mina.core.buffer.IoBuffer;
33  import org.apache.mina.core.filterchain.DefaultIoFilterChain;
34  import org.apache.mina.core.filterchain.IoFilterChain;
35  import org.apache.mina.core.service.DefaultTransportMetadata;
36  import org.apache.mina.core.service.IoProcessor;
37  import org.apache.mina.core.service.IoServiceListenerSupport;
38  import org.apache.mina.core.service.TransportMetadata;
39  import org.apache.mina.core.session.AbstractIoSession;
40  import org.apache.mina.core.write.WriteRequest;
41  import org.apache.mina.util.ExceptionMonitor;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * An imlpementation of {@link SerialSession}.
47   *
48   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
49   */
50  class SerialSessionImpl extends AbstractIoSession implements SerialSession, SerialPortEventListener {
51  
52      static final TransportMetadata METADATA = new DefaultTransportMetadata("rxtx", "serial", false, true,
53              SerialAddress.class, SerialSessionConfig.class, IoBuffer.class);
54  
55      private final IoProcessor<SerialSessionImpl> processor = new SerialIoProcessor();
56  
57      private final IoFilterChain filterChain;
58  
59      private final IoServiceListenerSupport serviceListeners;
60  
61      private final SerialAddress address;
62  
63      private final SerialPort port;
64  
65      private final Logger log;
66  
67      private InputStream inputStream;
68  
69      private OutputStream outputStream;
70  
71      SerialSessionImpl(SerialConnector service, IoServiceListenerSupport serviceListeners, SerialAddress address,
72              SerialPort port) {
73          super(service);
74          config = new DefaultSerialSessionConfig();
75          this.serviceListeners = serviceListeners;
76          filterChain = new DefaultIoFilterChain(this);
77          this.port = port;
78          this.address = address;
79  
80          log = LoggerFactory.getLogger(SerialSessionImpl.class);
81      }
82  
83      public SerialSessionConfig getConfig() {
84          return (SerialSessionConfig) config;
85      }
86  
87      public IoFilterChain getFilterChain() {
88          return filterChain;
89      }
90  
91      public TransportMetadata getTransportMetadata() {
92          return METADATA;
93      }
94  
95      public SerialAddress getLocalAddress() {
96          return null; // not applicable
97      }
98  
99      public SerialAddress getRemoteAddress() {
100         return address;
101     }
102 
103     @Override
104     public SerialAddress getServiceAddress() {
105         return (SerialAddress) super.getServiceAddress();
106     }
107 
108     public void setDTR(boolean dtr) {
109         port.setDTR(dtr);
110     }
111 
112     public boolean isDTR() {
113         return port.isDTR();
114     }
115 
116     public void setRTS(boolean rts) {
117         port.setRTS(rts);
118     }
119 
120     public boolean isRTS() {
121         return port.isRTS();
122     }
123 
124     /**
125      * start handling streams
126      *
127      * @throws IOException
128      * @throws TooManyListenersException
129      */
130     void start() throws IOException, TooManyListenersException {
131         inputStream = port.getInputStream();
132         outputStream = port.getOutputStream();
133         ReadWorker w = new ReadWorker();
134         w.start();
135         port.addEventListener(this);
136         ((SerialConnector) getService()).getIdleStatusChecker0().addSession(this);
137         try {
138             getService().getFilterChainBuilder().buildFilterChain(getFilterChain());
139             serviceListeners.fireSessionCreated(this);
140         } catch (Throwable e) {
141             getFilterChain().fireExceptionCaught(e);
142             processor.remove(this);
143         }
144     }
145 
146     private final Object writeMonitor = new Object();
147 
148     private WriteWorker writeWorker;
149 
150     private class WriteWorker extends Thread {
151         @Override
152         public void run() {
153             while (isConnected() && !isClosing()) {
154                 flushWrites();
155 
156                 // wait for more data
157                 synchronized (writeMonitor) {
158                     try {
159                         writeMonitor.wait();
160                     } catch (InterruptedException e) {
161                         log.error("InterruptedException", e);
162                     }
163                 }
164             }
165         }
166     }
167 
168     private void flushWrites() {
169         for (;;) {
170             WriteRequest req = getCurrentWriteRequest();
171             if (req == null) {
172                 req = getWriteRequestQueue().poll(this);
173                 if (req == null) {
174                     break;
175                 }
176             }
177 
178             IoBuffer buf = (IoBuffer) req.getMessage();
179             if (buf.remaining() == 0) {
180                 setCurrentWriteRequest(null);
181                 buf.reset();
182                 this.getFilterChain().fireMessageSent(req);
183                 continue;
184             }
185 
186             int writtenBytes = buf.remaining();
187             try {
188                 outputStream.write(buf.array(), buf.position(), writtenBytes);
189                 buf.position(buf.position() + writtenBytes);
190 
191                 // increase written bytes
192                 increaseWrittenBytes(writtenBytes, System.currentTimeMillis());
193 
194                 setCurrentWriteRequest(null);
195                 buf.reset();
196 
197                 // fire the message sent event
198                 getFilterChain().fireMessageSent(req);
199             } catch (IOException e) {
200                 this.getFilterChain().fireExceptionCaught(e);
201             }
202 
203         }
204     }
205 
206     private final Object readReadyMonitor = new Object();
207 
208     private class ReadWorker extends Thread {
209         @Override
210         public void run() {
211             while (isConnected() && !isClosing()) {
212                 synchronized (readReadyMonitor) {
213                     try {
214                         readReadyMonitor.wait();
215                     } catch (InterruptedException e) {
216                         log.error("InterruptedException", e);
217                     }
218                     if (isClosing() || !isConnected()) {
219                         break;
220                     }
221                     int dataSize;
222                     try {
223                         dataSize = inputStream.available();
224                         byte[] data = new byte[dataSize];
225                         int readBytes = inputStream.read(data);
226 
227                         if (readBytes > 0) {
228                             IoBuffer buf = IoBuffer.wrap(data, 0, readBytes);
229                             buf.put(data, 0, readBytes);
230                             buf.flip();
231                             getFilterChain().fireMessageReceived(buf);
232                         }
233                     } catch (IOException e) {
234                         getFilterChain().fireExceptionCaught(e);
235                     }
236                 }
237             }
238         }
239     }
240 
241     public void serialEvent(SerialPortEvent evt) {
242         if (evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
243             synchronized (readReadyMonitor) {
244                 readReadyMonitor.notifyAll();
245             }
246         }
247     }
248 
249     @Override
250     public IoProcessor<SerialSessionImpl> getProcessor() {
251         return processor;
252     }
253 
254     private class SerialIoProcessor implements IoProcessor<SerialSessionImpl> {
255         public void add(SerialSessionImpl session) {
256             // It's already added when the session is constructed.
257         }
258 
259         public void flush(SerialSessionImpl session) {
260             if (writeWorker == null) {
261                 writeWorker = new WriteWorker();
262                 writeWorker.start();
263             } else {
264                 synchronized (writeMonitor) {
265                     writeMonitor.notifyAll();
266                 }
267             }
268         }
269 
270         public void remove(SerialSessionImpl session) {
271             try {
272                 inputStream.close();
273             } catch (IOException e) {
274                 ExceptionMonitor.getInstance().exceptionCaught(e);
275             }
276             try {
277                 outputStream.close();
278             } catch (IOException e) {
279                 ExceptionMonitor.getInstance().exceptionCaught(e);
280             }
281 
282             try { // Turn flow control off right before close to avoid deadlock
283                 port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
284             } catch (UnsupportedCommOperationException e) {
285                 ExceptionMonitor.getInstance().exceptionCaught(e);
286             }
287 
288             port.close();
289             flush(session);
290             synchronized (readReadyMonitor) {
291                 readReadyMonitor.notifyAll();
292             }
293 
294             serviceListeners.fireSessionDestroyed(SerialSessionImpl.this);
295         }
296 
297         public void updateTrafficControl(SerialSessionImpl session) {
298             throw new UnsupportedOperationException();
299         }
300 
301         public void dispose() {
302             // Nothing to dispose
303         }
304 
305         public boolean isDisposed() {
306             return false;
307         }
308 
309         public boolean isDisposing() {
310             return false;
311         }
312     }
313 }