Reduce dependency on commons-io and commons-codec (#10614)

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2021-04-30 16:53:44 +02:00 committed by GitHub
parent 02b4943a11
commit e6d8dfb7e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 175 additions and 124 deletions

View File

@ -23,7 +23,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.openhab.binding.allplay.internal.AllPlayBindingConstants; import org.openhab.binding.allplay.internal.AllPlayBindingConstants;
import org.openhab.binding.allplay.internal.AllPlayBindingProperties; import org.openhab.binding.allplay.internal.AllPlayBindingProperties;
import org.openhab.core.common.ThreadPoolManager; import org.openhab.core.common.ThreadPoolManager;
@ -538,7 +537,7 @@ public class AllPlayHandler extends BaseThingHandler
private byte[] getRawDataFromUrl(String urlString) throws Exception { private byte[] getRawDataFromUrl(String urlString) throws Exception {
URL url = new URL(urlString); URL url = new URL(urlString);
URLConnection connection = url.openConnection(); URLConnection connection = url.openConnection();
return IOUtils.toByteArray(connection.getInputStream()); return connection.getInputStream().readAllBytes();
} }
private int convertPercentToAbsoluteVolume(PercentType percentVolume) throws SpeakerException { private int convertPercentToAbsoluteVolume(PercentType percentVolume) throws SpeakerException {

View File

@ -30,7 +30,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler; import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler;
@ -411,10 +410,16 @@ public class BlueGigaBridgeHandler extends AbstractBluetoothBridgeHandler<BlueGi
// Ignore all as RXTX seems to send arbitrary exceptions when BlueGiga module is detached // Ignore all as RXTX seems to send arbitrary exceptions when BlueGiga module is detached
} finally { } finally {
outputStream.ifPresent(output -> { outputStream.ifPresent(output -> {
IOUtils.closeQuietly(output); try {
output.close();
} catch (IOException e) {
}
}); });
inputStream.ifPresent(input -> { inputStream.ifPresent(input -> {
IOUtils.closeQuietly(input); try {
input.close();
} catch (IOException e) {
}
}); });
sp.close(); sp.close();
logger.debug("Closed serial port."); logger.debug("Closed serial port.");

View File

@ -12,13 +12,13 @@
*/ */
package org.openhab.binding.bluetooth.bluegiga.internal; package org.openhab.binding.bluetooth.bluegiga.internal;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureCommand; import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -96,7 +96,7 @@ public class BlueGigaSerialHandler {
// Wait BlueGiga controller have stopped all activity // Wait BlueGiga controller have stopped all activity
Thread.sleep(100); Thread.sleep(100);
logger.trace("Bytes available: {}", inputStream.available()); logger.trace("Bytes available: {}", inputStream.available());
IOUtils.skipFully(inputStream, inputStream.available()); skipFully(inputStream, inputStream.available());
} catch (InterruptedException e) { } catch (InterruptedException e) {
close = true; close = true;
} catch (IOException e) { } catch (IOException e) {
@ -105,6 +105,28 @@ public class BlueGigaSerialHandler {
logger.trace("Flush done"); logger.trace("Flush done");
} }
private void skipFully(final InputStream input, final long toSkip) throws IOException {
if (toSkip < 0) {
throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip);
}
long remain = toSkip;
final byte[] byteArray = new byte[8192];
while (remain > 0) {
final long n = input.read(byteArray, 0, (int) Math.min(remain, byteArray.length));
if (n < 0) { // EOF
break;
}
remain -= n;
}
long skipped = toSkip - remain;
if (skipped != toSkip) {
throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped);
}
}
/** /**
* Requests parser thread to shutdown. Waits forever while the parser thread is getting shut down. * Requests parser thread to shutdown. Waits forever while the parser thread is getting shut down.
*/ */
@ -123,8 +145,14 @@ public class BlueGigaSerialHandler {
parserThread.interrupt(); parserThread.interrupt();
// Give a fair chance to shutdown nicely // Give a fair chance to shutdown nicely
Thread.sleep(50); Thread.sleep(50);
IOUtils.closeQuietly(outputStream); try {
IOUtils.closeQuietly(inputStream); outputStream.close();
} catch (IOException e) {
}
try {
inputStream.close();
} catch (IOException e) {
}
parserThread.join(0); parserThread.join(0);
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.warn("Interrupted in packet parser thread shutdown join."); logger.warn("Interrupted in packet parser thread shutdown join.");

View File

@ -33,7 +33,6 @@ import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate; import javax.xml.stream.util.StreamReaderDelegate;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
@ -310,7 +309,8 @@ public class DenonMarantzHttpConnector extends DenonMarantzConnector {
XMLInputFactory xif = XMLInputFactory.newInstance(); XMLInputFactory xif = XMLInputFactory.newInstance();
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader xsr = xif.createXMLStreamReader(IOUtils.toInputStream(result)); XMLStreamReader xsr = xif
.createXMLStreamReader(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8)));
xsr = new PropertyRenamerDelegate(xsr); xsr = new PropertyRenamerDelegate(xsr);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -344,7 +344,8 @@ public class DenonMarantzHttpConnector extends DenonMarantzConnector {
JAXBContext jcResponse = JAXBContext.newInstance(response); JAXBContext jcResponse = JAXBContext.newInstance(response);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T obj = (T) jcResponse.createUnmarshaller().unmarshal(IOUtils.toInputStream(result)); T obj = (T) jcResponse.createUnmarshaller()
.unmarshal(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8)));
return obj; return obj;
} }

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl; package org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -42,7 +43,6 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openhab.binding.digitalstrom.internal.lib.config.Config; import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager; import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
@ -459,7 +459,7 @@ public class HttpTransportImpl implements HttpTransport {
correctedPath = correctedPath + "/"; correctedPath = correctedPath + "/";
} }
} }
InputStream certInputStream = IOUtils.toInputStream(cert); InputStream certInputStream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
X509Certificate trustedCert; X509Certificate trustedCert;
try { try {
trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509") trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
@ -488,7 +488,7 @@ public class HttpTransportImpl implements HttpTransport {
private SSLSocketFactory generateSSLContextFromPEMCertString(String pemCert) { private SSLSocketFactory generateSSLContextFromPEMCertString(String pemCert) {
if (pemCert != null && !pemCert.isBlank() && pemCert.startsWith(BEGIN_CERT)) { if (pemCert != null && !pemCert.isBlank() && pemCert.startsWith(BEGIN_CERT)) {
try { try {
InputStream certInputStream = IOUtils.toInputStream(pemCert); InputStream certInputStream = new ByteArrayInputStream(pemCert.getBytes(StandardCharsets.UTF_8));
final X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509") final X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(certInputStream); .generateCertificate(certInputStream);

View File

@ -16,7 +16,9 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
@ -25,7 +27,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import org.apache.commons.io.IOUtils;
import org.openhab.binding.ihc.internal.ws.datatypes.WSControllerState; import org.openhab.binding.ihc.internal.ws.datatypes.WSControllerState;
import org.openhab.binding.ihc.internal.ws.datatypes.WSFile; import org.openhab.binding.ihc.internal.ws.datatypes.WSFile;
import org.openhab.binding.ihc.internal.ws.datatypes.WSLoginResult; import org.openhab.binding.ihc.internal.ws.datatypes.WSLoginResult;
@ -325,10 +326,13 @@ public class IhcClient {
} }
byte[] decodedBytes = Base64.getDecoder().decode(byteStream.toString()); byte[] decodedBytes = Base64.getDecoder().decode(byteStream.toString());
logger.debug("File size after base64 encoding: {} bytes", decodedBytes.length); logger.debug("File size after base64 encoding: {} bytes", decodedBytes.length);
try (GZIPInputStream gzis = new GZIPInputStream(new ByteArrayInputStream(decodedBytes))) { try (GZIPInputStream gzis = new GZIPInputStream(new ByteArrayInputStream(decodedBytes));
try (InputStreamReader in = new InputStreamReader(gzis, "ISO-8859-1")) { InputStreamReader reader = new InputStreamReader(gzis, StandardCharsets.ISO_8859_1);
return IOUtils.toByteArray(in, "ISO-8859-1"); ByteArrayOutputStream baos = new ByteArrayOutputStream();
} OutputStreamWriter writer = new OutputStreamWriter(baos)) {
reader.transferTo(writer);
writer.flush();
return baos.toByteArray();
} }
} }
} catch (IOException | IllegalArgumentException e) { } catch (IOException | IllegalArgumentException e) {

View File

@ -14,6 +14,7 @@ package org.openhab.binding.ihc.internal.ws.projectfile;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -24,7 +25,6 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.FileUtils;
import org.openhab.binding.ihc.internal.ws.datatypes.WSProjectInfo; import org.openhab.binding.ihc.internal.ws.datatypes.WSProjectInfo;
import org.openhab.binding.ihc.internal.ws.exeptions.IhcExecption; import org.openhab.binding.ihc.internal.ws.exeptions.IhcExecption;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -76,7 +76,10 @@ public class ProjectFileUtils {
*/ */
public static void saveToFile(String filePath, byte[] data) throws IhcExecption { public static void saveToFile(String filePath, byte[] data) throws IhcExecption {
try { try {
FileUtils.writeByteArrayToFile(new File(filePath), data); try (FileOutputStream stream = new FileOutputStream(filePath)) {
stream.write(data);
stream.flush();
}
} catch (IOException e) { } catch (IOException e) {
throw new IhcExecption(e); throw new IhcExecption(e);
} }

View File

@ -36,4 +36,13 @@
</plugins> </plugins>
</build> </build>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project> </project>

View File

@ -19,6 +19,11 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency> <dependency>
<groupId>io.reactivex</groupId> <groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId> <artifactId>rxjava</artifactId>

View File

@ -19,7 +19,6 @@ import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.Arrays; import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.openhab.core.util.HexUtils; import org.openhab.core.util.HexUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -182,11 +181,8 @@ public class OnkyoAlbumArt {
try { try {
URL url = new URL(albumArtUrl); URL url = new URL(albumArtUrl);
URLConnection connection = url.openConnection(); URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream(); try (InputStream inputStream = connection.getInputStream()) {
try { return inputStream.readAllBytes();
return IOUtils.toByteArray(inputStream);
} finally {
IOUtils.closeQuietly(inputStream);
} }
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
logger.warn("Album Art download failed from url '{}', reason {}", albumArtUrl, e.getMessage()); logger.warn("Album Art download failed from url '{}', reason {}", albumArtUrl, e.getMessage());

View File

@ -24,7 +24,6 @@ import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import org.apache.commons.io.IOUtils;
import org.openhab.binding.onkyo.internal.eiscp.EiscpCommand; import org.openhab.binding.onkyo.internal.eiscp.EiscpCommand;
import org.openhab.binding.onkyo.internal.eiscp.EiscpException; import org.openhab.binding.onkyo.internal.eiscp.EiscpException;
import org.openhab.binding.onkyo.internal.eiscp.EiscpMessage; import org.openhab.binding.onkyo.internal.eiscp.EiscpMessage;
@ -222,17 +221,26 @@ public class OnkyoConnection {
logger.debug("closed connection tester!"); logger.debug("closed connection tester!");
} }
if (inStream != null) { if (inStream != null) {
IOUtils.closeQuietly(inStream); try {
inStream.close();
} catch (IOException e) {
}
inStream = null; inStream = null;
logger.debug("closed input stream!"); logger.debug("closed input stream!");
} }
if (outStream != null) { if (outStream != null) {
IOUtils.closeQuietly(outStream); try {
outStream.close();
} catch (IOException e) {
}
outStream = null; outStream = null;
logger.debug("closed output stream!"); logger.debug("closed output stream!");
} }
if (eiscpSocket != null) { if (eiscpSocket != null) {
IOUtils.closeQuietly(eiscpSocket); try {
eiscpSocket.close();
} catch (IOException e) {
}
eiscpSocket = null; eiscpSocket = null;
logger.debug("closed socket!"); logger.debug("closed socket!");
} }

View File

@ -19,7 +19,6 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sonos.internal.handler.ZonePlayerHandler; import org.openhab.binding.sonos.internal.handler.ZonePlayerHandler;
@ -118,7 +117,10 @@ public class SonosAudioSink implements AudioSink {
logger.warn("We do not have any callback url, so Sonos cannot play the audio stream!"); logger.warn("We do not have any callback url, so Sonos cannot play the audio stream!");
} }
} else { } else {
IOUtils.closeQuietly(audioStream); try {
audioStream.close();
} catch (IOException e) {
}
throw new UnsupportedAudioStreamException( throw new UnsupportedAudioStreamException(
"Sonos can only handle FixedLengthAudioStreams and URLAudioStreams.", audioStream.getClass()); "Sonos can only handle FixedLengthAudioStreams and URLAudioStreams.", audioStream.getClass());
// Instead of throwing an exception, we could ourselves try to wrap it into a // Instead of throwing an exception, we could ourselves try to wrap it into a

View File

@ -13,6 +13,7 @@
package org.openhab.io.neeo.internal; package org.openhab.io.neeo.internal;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -22,7 +23,6 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -136,7 +136,8 @@ public abstract class AbstractServlet extends HttpServlet implements AutoCloseab
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
req.getReader().mark(150000); req.getReader().mark(150000);
logger.debug("doPost: {} with {}", getFullURL(req), IOUtils.toString(req.getReader())); logger.debug("doPost: {} with {}", getFullURL(req),
new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
req.getReader().reset(); req.getReader().reset();
} }

View File

@ -21,7 +21,6 @@ import java.util.Objects;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -58,8 +57,7 @@ public class HttpResponse {
httpReason = response.getStatusInfo().getReasonPhrase(); httpReason = response.getStatusInfo().getReasonPhrase();
if (response.hasEntity()) { if (response.hasEntity()) {
InputStream is = response.readEntity(InputStream.class); contents = response.readEntity(InputStream.class).readAllBytes();
contents = IOUtils.toByteArray(is);
} else { } else {
contents = null; contents = null;
} }

View File

@ -13,6 +13,7 @@
package org.openhab.io.neeo.internal.servletservices; package org.openhab.io.neeo.internal.servletservices;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -20,7 +21,6 @@ import java.util.Objects;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.io.neeo.NeeoService; import org.openhab.io.neeo.NeeoService;
@ -155,7 +155,8 @@ public class ThingDashboardService extends DefaultServletService {
NeeoUtil.write(resp, gson.toJson(ReturnStatus.SUCCESS)); NeeoUtil.write(resp, gson.toJson(ReturnStatus.SUCCESS));
} else if (paths[0].equalsIgnoreCase("restoredevice")) { } else if (paths[0].equalsIgnoreCase("restoredevice")) {
final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader())); final NeeoThingUID uid = new NeeoThingUID(
new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
context.getDefinitions().remove(uid); context.getDefinitions().remove(uid);
final NeeoDevice device = context.getDefinitions().getDevice(uid); final NeeoDevice device = context.getDefinitions().getDevice(uid);
if (device == null) { if (device == null) {
@ -164,7 +165,8 @@ public class ThingDashboardService extends DefaultServletService {
NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device))); NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device)));
} }
} else if (paths[0].equalsIgnoreCase("refreshdevice")) { } else if (paths[0].equalsIgnoreCase("refreshdevice")) {
final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader())); final NeeoThingUID uid = new NeeoThingUID(
new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
final NeeoDevice device = context.getDefinitions().getDevice(uid); final NeeoDevice device = context.getDefinitions().getDevice(uid);
if (device == null) { if (device == null) {
NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device no longer exists in openHAB!"))); NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device no longer exists in openHAB!")));
@ -172,12 +174,14 @@ public class ThingDashboardService extends DefaultServletService {
NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device))); NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device)));
} }
} else if (paths[0].equalsIgnoreCase("deletedevice")) { } else if (paths[0].equalsIgnoreCase("deletedevice")) {
final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader())); final NeeoThingUID uid = new NeeoThingUID(
new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
final boolean deleted = context.getDefinitions().remove(uid); final boolean deleted = context.getDefinitions().remove(uid);
NeeoUtil.write(resp, gson.toJson(new ReturnStatus( NeeoUtil.write(resp, gson.toJson(new ReturnStatus(
deleted ? null : "Device " + uid + " was not found (possibly already deleted?)"))); deleted ? null : "Device " + uid + " was not found (possibly already deleted?)")));
} else if (paths[0].equalsIgnoreCase("exportrules")) { } else if (paths[0].equalsIgnoreCase("exportrules")) {
final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader())); final NeeoThingUID uid = new NeeoThingUID(
new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
final NeeoDevice device = context.getDefinitions().getDevice(uid); final NeeoDevice device = context.getDefinitions().getDevice(uid);
if (device == null) { if (device == null) {
NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device " + uid + " was not found"))); NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device " + uid + " was not found")));
@ -228,7 +232,7 @@ public class ThingDashboardService extends DefaultServletService {
resp.setContentType("text/plain"); resp.setContentType("text/plain");
resp.setHeader("Content-disposition", "attachment; filename=\"" + device.getName() + ".rules\""); resp.setHeader("Content-disposition", "attachment; filename=\"" + device.getName() + ".rules\"");
IOUtils.write(sb, resp.getOutputStream()); resp.getOutputStream().write(sb.toString().getBytes(StandardCharsets.UTF_8));
} }
/** /**

View File

@ -19,11 +19,15 @@ import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -82,13 +86,26 @@ public class MapTransformationServiceTest {
public void setUp() throws IOException { public void setUp() throws IOException {
processor = new TestableMapTransformationService(); processor = new TestableMapTransformationService();
processor.activate(bundleContext); processor.activate(bundleContext);
FileUtils.copyDirectory(new File(SRC_FOLDER), new File(CONFIG_FOLDER)); copyDirectory(SRC_FOLDER, CONFIG_FOLDER);
} }
@AfterEach @AfterEach
public void tearDown() throws IOException { public void tearDown() throws IOException {
processor.deactivate(); processor.deactivate();
FileUtils.deleteDirectory(new File(CONFIG_FOLDER));
try (Stream<Path> walk = Files.walk(Path.of(CONFIG_FOLDER))) {
walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
}
}
private void copyDirectory(String from, String to) throws IOException {
Files.walk(Paths.get(from)).forEach(fromPath -> {
Path toPath = Paths.get(to, fromPath.toString().substring(from.length()));
try {
Files.copy(fromPath, toPath);
} catch (IOException e) {
}
});
} }
@Test @Test

View File

@ -15,12 +15,12 @@ package org.openhab.voice.mactts.internal;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.audio.AudioException; import org.openhab.core.audio.AudioException;
import org.openhab.core.audio.AudioFormat; import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioStream; import org.openhab.core.audio.AudioStream;
@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory;
* @author Kelly Davis * @author Kelly Davis
*/ */
@Component @Component
@NonNullByDefault
public class MacTTSService implements TTSService { public class MacTTSService implements TTSService {
private final Logger logger = LoggerFactory.getLogger(MacTTSService.class); private final Logger logger = LoggerFactory.getLogger(MacTTSService.class);
@ -51,34 +52,30 @@ public class MacTTSService implements TTSService {
/** /**
* Set of supported audio formats * Set of supported audio formats
*/ */
private final Set<AudioFormat> audioFormats = initAudioFormats(); private final Set<AudioFormat> audioFormats = Set.of(
new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, null, (long) 44100));
@Override @Override
public Set<Voice> getAvailableVoices() { public Set<Voice> getAvailableVoices() {
return this.voices; return voices;
} }
@Override @Override
public Set<AudioFormat> getSupportedFormats() { public Set<AudioFormat> getSupportedFormats() {
return this.audioFormats; return audioFormats;
} }
@Override @Override
public AudioStream synthesize(String text, Voice voice, AudioFormat requestedFormat) throws TTSException { public AudioStream synthesize(String text, Voice voice, AudioFormat requestedFormat) throws TTSException {
// Validate arguments // Validate arguments
if ((null == text) || text.isEmpty()) { if (text.isEmpty()) {
throw new TTSException("The passed text is null or empty"); throw new TTSException("The passed text is null or empty");
} }
if (!this.voices.contains(voice)) { if (!voices.contains(voice)) {
throw new TTSException("The passed voice is unsupported"); throw new TTSException("The passed voice is unsupported");
} }
boolean isAudioFormatSupported = false; boolean isAudioFormatSupported = audioFormats.stream()
for (AudioFormat currentAudioFormat : this.audioFormats) { .anyMatch(audioFormat -> audioFormat.isCompatible(requestedFormat));
if (currentAudioFormat.isCompatible(requestedFormat)) {
isAudioFormatSupported = true;
break;
}
}
if (!isAudioFormatSupported) { if (!isAudioFormatSupported) {
throw new TTSException("The passed AudioFormat is unsupported"); throw new TTSException("The passed AudioFormat is unsupported");
} }
@ -96,35 +93,16 @@ public class MacTTSService implements TTSService {
* @return The voices of this instance * @return The voices of this instance
*/ */
private final Set<Voice> initVoices() { private final Set<Voice> initVoices() {
Set<Voice> voices = new HashSet<>();
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try { try {
Process process = Runtime.getRuntime().exec("say -v ?"); Process process = Runtime.getRuntime().exec("say -v ?");
inputStreamReader = new InputStreamReader(process.getInputStream()); try (InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
return bufferedReader.lines().map(MacTTSVoice::new).collect(Collectors.toSet());
String nextLine;
while ((nextLine = bufferedReader.readLine()) != null) {
voices.add(new MacTTSVoice(nextLine));
} }
} catch (IOException e) { } catch (IOException e) {
logger.error("Error while executing the 'say -v ?' command: {}", e.getMessage()); logger.error("Error while executing the 'say -v ?' command: {}", e.getMessage());
} finally { return Set.of();
IOUtils.closeQuietly(bufferedReader);
} }
return voices;
}
/**
* Initializes this.audioFormats
*
* @return The audio formats of this instance
*/
private final Set<AudioFormat> initAudioFormats() {
AudioFormat audioFormat = new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16,
null, (long) 44100);
return Collections.singleton(audioFormat);
} }
@Override @Override
@ -133,7 +111,7 @@ public class MacTTSService implements TTSService {
} }
@Override @Override
public String getLabel(Locale locale) { public String getLabel(@Nullable Locale locale) {
return "macOS TTS"; return "macOS TTS";
} }
} }

View File

@ -19,7 +19,6 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -39,17 +38,12 @@ public class MacTTSVoiceTest {
@Test @Test
public void testConstructor() throws IOException { public void testConstructor() throws IOException {
assumeTrue("Mac OS X".equals(System.getProperty("os.name"))); assumeTrue("Mac OS X".equals(System.getProperty("os.name")));
BufferedReader bufferedReader = null; Process process = Runtime.getRuntime().exec("say -v ?");
try { try (InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
Process process = Runtime.getRuntime().exec("say -v ?"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader);
String nextLine = bufferedReader.readLine(); String nextLine = bufferedReader.readLine();
MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine); MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine);
assertNotNull(voiceMacOS, "The MacTTSVoice(String) constructor failed"); assertNotNull(voiceMacOS, "The MacTTSVoice(String) constructor failed");
} finally {
IOUtils.closeQuietly(bufferedReader);
} }
} }
@ -59,17 +53,12 @@ public class MacTTSVoiceTest {
@Test @Test
public void getUIDTest() throws IOException { public void getUIDTest() throws IOException {
assumeTrue("Mac OS X".equals(System.getProperty("os.name"))); assumeTrue("Mac OS X".equals(System.getProperty("os.name")));
BufferedReader bufferedReader = null; Process process = Runtime.getRuntime().exec("say -v ?");
try { try (InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
Process process = Runtime.getRuntime().exec("say -v ?"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader);
String nextLine = bufferedReader.readLine(); String nextLine = bufferedReader.readLine();
MacTTSVoice macTTSVoice = new MacTTSVoice(nextLine); MacTTSVoice macTTSVoice = new MacTTSVoice(nextLine);
assertTrue(0 == macTTSVoice.getUID().indexOf("mactts:"), "The VoiceMacOS UID has an incorrect format"); assertTrue(0 == macTTSVoice.getUID().indexOf("mactts:"), "The VoiceMacOS UID has an incorrect format");
} finally {
IOUtils.closeQuietly(bufferedReader);
} }
} }
@ -79,17 +68,12 @@ public class MacTTSVoiceTest {
@Test @Test
public void getLabelTest() throws IOException { public void getLabelTest() throws IOException {
assumeTrue("Mac OS X".equals(System.getProperty("os.name"))); assumeTrue("Mac OS X".equals(System.getProperty("os.name")));
BufferedReader bufferedReader = null; Process process = Runtime.getRuntime().exec("say -v ?");
try { try (InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
Process process = Runtime.getRuntime().exec("say -v ?"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader);
String nextLine = bufferedReader.readLine(); String nextLine = bufferedReader.readLine();
MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine); MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine);
assertNotNull(voiceMacOS.getLabel(), "The MacTTSVoice label has an incorrect format"); assertNotNull(voiceMacOS.getLabel(), "The MacTTSVoice label has an incorrect format");
} finally {
IOUtils.closeQuietly(bufferedReader);
} }
} }
@ -99,17 +83,12 @@ public class MacTTSVoiceTest {
@Test @Test
public void getLocaleTest() throws IOException { public void getLocaleTest() throws IOException {
assumeTrue("Mac OS X" == System.getProperty("os.name")); assumeTrue("Mac OS X" == System.getProperty("os.name"));
BufferedReader bufferedReader = null; Process process = Runtime.getRuntime().exec("say -v ?");
try { try (InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
Process process = Runtime.getRuntime().exec("say -v ?"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader);
String nextLine = bufferedReader.readLine(); String nextLine = bufferedReader.readLine();
MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine); MacTTSVoice voiceMacOS = new MacTTSVoice(nextLine);
assertNotNull(voiceMacOS.getLocale(), "The MacTTSVoice locale has an incorrect format"); assertNotNull(voiceMacOS.getLocale(), "The MacTTSVoice locale has an incorrect format");
} finally {
IOUtils.closeQuietly(bufferedReader);
} }
} }
} }

View File

@ -34,7 +34,7 @@
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.5</version> <version>2.8.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -13,13 +13,13 @@
package org.openhab.voice.marytts.internal; package org.openhab.voice.marytts.internal;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.SequenceInputStream; import java.io.SequenceInputStream;
import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioInputStream;
import org.apache.commons.io.IOUtils;
import org.openhab.core.audio.AudioException; import org.openhab.core.audio.AudioException;
import org.openhab.core.audio.AudioFormat; import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioSource; import org.openhab.core.audio.AudioSource;
@ -54,12 +54,23 @@ class MaryTTSAudioStream extends FixedLengthAudioStream {
* @throws IOException * @throws IOException
*/ */
public MaryTTSAudioStream(AudioInputStream inputStream, AudioFormat audioFormat) throws IOException { public MaryTTSAudioStream(AudioInputStream inputStream, AudioFormat audioFormat) throws IOException {
rawAudio = IOUtils.toByteArray(inputStream); // The length of an AudioInputStream is expressed in sample frames, not bytes so readAllBytes() cannot be used.
rawAudio = inputStreamToBytes(inputStream);
this.length = rawAudio.length + 36; this.length = rawAudio.length + 36;
this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio)); this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
this.audioFormat = audioFormat; this.audioFormat = audioFormat;
} }
private byte[] inputStreamToBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
int n = 0;
byte[] buffer = new byte[4096];
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
@Override @Override
public AudioFormat getFormat() { public AudioFormat getFormat() {
return this.audioFormat; return this.audioFormat;
@ -146,7 +157,10 @@ class MaryTTSAudioStream extends FixedLengthAudioStream {
@Override @Override
public synchronized void reset() throws IOException { public synchronized void reset() throws IOException {
IOUtils.closeQuietly(inputStream); try {
inputStream.close();
} catch (IOException e) {
}
this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio)); this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
} }