2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.velux.internal.bridge.slip.io;
15 import java.io.DataInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.concurrent.Callable;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.Future;
23 import java.util.concurrent.RejectedExecutionException;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
30 * This is an extension of {@link java.io.DataInputStream}, which adds timeouts to receive operation.
32 * A data input stream lets an application read primitive Java data
33 * types from an underlying input stream in a machine-independent
34 * way. An application uses a data output stream to write data that
35 * can later be read by a data input stream.
37 * For an in-depth discussion, see:
38 * https://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-inputstream-with-a-timeout
40 * @author Guenther Schreiner - Initial contribution.
43 class DataInputStreamWithTimeout extends DataInputStream {
46 * ***************************
47 * ***** Private Objects *****
51 * Executor for asynchronous read command
53 ExecutorService executor = Executors.newFixedThreadPool(2);
56 * Creates a DataInputStreamWithTimeout that uses the specified
57 * underlying DataInputStream.
59 * @param in the specified input stream
61 public DataInputStreamWithTimeout(InputStream in) {
66 * Reads up to <code>len</code> bytes of data from the contained
67 * input stream into an array of bytes. An attempt is made to read
68 * as many as <code>len</code> bytes, but a smaller number may be read,
69 * possibly zero. The number of bytes actually read is returned as an
73 * This method blocks until input data is available, end of file is
74 * detected, or an exception is thrown <B>until</B> the given timeout.
77 * If <code>len</code> is zero, then no bytes are read and
78 * <code>0</code> is returned; otherwise, there is an attempt to read at
79 * least one byte. If no byte is available because the stream is at end of
80 * file, the value <code>-1</code> is returned; otherwise, at least one
81 * byte is read and stored into <code>b</code>.
84 * The first byte read is stored into element <code>b[off]</code>, the
85 * next one into <code>b[off+1]</code>, and so on. The number of bytes read
86 * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
87 * bytes actually read; these bytes will be stored in elements
88 * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
89 * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
90 * <code>b[off+len-1]</code> unaffected.
93 * In every case, elements <code>b[0]</code> through
94 * <code>b[off]</code> and elements <code>b[off+len]</code> through
95 * <code>b[b.length-1]</code> are unaffected.
97 * @param b the buffer into which the data is read.
98 * @param off the start offset in the destination array <code>b</code>
99 * @param len the maximum number of bytes read.
100 * @param timeoutMSecs the maximum duration of this read before throwing a TimeoutException.
101 * @return the total number of bytes read into the buffer, or
102 * <code>-1</code> if there is no more data because the end
103 * of the stream has been reached.
104 * @exception NullPointerException If <code>b</code> is <code>null</code>.
105 * @exception IndexOutOfBoundsException If <code>off</code> is negative,
106 * <code>len</code> is negative, or <code>len</code> is greater than
107 * <code>b.length - off</code>
108 * @exception IOException if the first byte cannot be read for any reason
109 * other than end of file, the stream has been closed and the underlying
110 * input stream does not support reading after close, or another I/O
111 * error occurs. Additionally it will occur when the timeout happens.
112 * @see java.io.DataInputStream#read
114 public synchronized int read(byte b[], int off, int len, int timeoutMSecs) throws IOException {
115 // Definition of Method which encapsulates the Read of data
116 Callable<Integer> readTask = new Callable<Integer>() {
118 public Integer call() throws IOException {
119 return in.read(b, off, len);
123 Future<Integer> future = executor.submit(readTask);
124 return future.get(timeoutMSecs, TimeUnit.MILLISECONDS);
125 } catch (RejectedExecutionException e) {
126 throw new IOException("executor failed", e);
127 } catch (ExecutionException e) {
128 throw new IOException("execution failed", e);
129 } catch (InterruptedException e) {
130 throw new IOException("read interrupted", e);
131 } catch (TimeoutException e) {
132 throw new IOException("read timeout", e);