All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bff.javampd.server.MPDSocket Maven / Gradle / Ivy

The newest version!
package org.bff.javampd.server;

import org.bff.javampd.MPDException;
import org.bff.javampd.command.MPDCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author bill
 */
public class MPDSocket {
    private static final Logger LOGGER = LoggerFactory.getLogger(MPDSocket.class);

    private Socket socket;
    private BufferedReader reader;

    private final ResponseProperties responseProperties;
    private final ServerProperties serverProperties;
    private final String encoding;
    private String lastError;
    private String version;

    private final String server;
    private final int port;
    private boolean closed;

    private static final int TRIES = 3;

    public MPDSocket(InetAddress server,
                     int port,
                     int timeout) {
        this.server = server.getHostAddress();
        this.port = port;
        this.responseProperties = new ResponseProperties();
        this.serverProperties = new ServerProperties();
        this.encoding = serverProperties.getEncoding();
        connect(timeout);
    }

    /**
     * If MPD is already connected no attempt will be made to connect and the
     * mpdVersion is returned.
     * 

* A timeout of 0 means an infinite wait. * * @param timeout socket timeout, 0 for infinite wait * @throws MPDConnectionException if there is a socked io problem */ private synchronized void connect(int timeout) { connectSocket(timeout); } private void readVersion() { String line; try { line = reader.readLine(); } catch (IOException e) { throw new MPDConnectionException(e); } if (line != null && isResponseOK(line)) { this.version = stripResponse(responseProperties.getOk(), line).trim(); } else { throw new MPDConnectionException("Command from server: " + ((line == null) ? "null" : stripResponse(responseProperties.getError(), line))); } } private void connectSocket(int timeout) { LOGGER.debug("attempting to connect socket to {} with timeout of {}", server, timeout); this.socket = createSocket(); SocketAddress socketAddress = new InetSocketAddress(server, port); try { this.socket.connect(socketAddress, timeout); setReader(new BufferedReader(new InputStreamReader(socket.getInputStream(), encoding))); readVersion(); } catch (Exception ioe) { LOGGER.error("failed to connect socket to {}", server); throw new MPDConnectionException(ioe); } } protected void setReader(BufferedReader reader) { this.reader = reader; } protected Socket createSocket() { return new Socket(); } public synchronized Collection sendCommand(MPDCommand command) { checkConnection(); int count = 0; while (count < TRIES) { try { return sendBytes(convertCommand(command.getCommand(), command.getParams())); } catch (MPDException mpdException) { logCommandError(command, mpdException); throw mpdException; } catch (Exception ex) { logCommandError(command, ex); try { connect(); } catch (Exception exc) { LOGGER.error("Unable to connect to {} on port {}", server, port, exc); } ++count; LOGGER.warn("Retrying command {} for the {} time", command.getCommand(), count); } } LOGGER.error("Unable to send command {} after {} tries", command, TRIES); throw new MPDConnectionException("Unable to send command " + command); } private static void logCommandError(MPDCommand command, Exception se) { LOGGER.error("Error from: {}", command.getCommand(), se); for (String str : command.getParams()) { LOGGER.error("\tparam: {}", str); } } /** * Attempts to connect to MPD with an infinite timeout value. * If MPD is already connected no attempt will be made to connect and the * mpdVersion is returned. */ private synchronized void connect() { connect(0); } private boolean isResponseOK(final String line) { try { if (line.startsWith(responseProperties.getOk()) || line.startsWith(responseProperties.getListOk())) { return true; } } catch (Exception e) { LOGGER.error("Could not determine if response is ok", e); } return false; } private boolean isResponseError(final String line) { if (line.startsWith(responseProperties.getError())) { this.lastError = line.substring(responseProperties.getError().length()).trim(); return true; } else { return false; } } private static String stripResponse(String response, String line) { return line.substring(response.length()); } private static String convertCommand(String command) { return convertCommand(command, new ArrayList<>()); } private static String convertCommand(String command, List params) { StringBuilder sb = new StringBuilder(command); for (String param : params) { param = param.replaceAll("\"", "\\\\\""); sb.append(" \"").append(param).append("\""); } return sb.append("\n").toString(); } public synchronized void sendCommands(List commandList) { StringBuilder sb = new StringBuilder(convertCommand(serverProperties.getStartBulk())); for (MPDCommand command : commandList) { sb.append(convertCommand(command.getCommand(), command.getParams())); } sb.append(convertCommand(serverProperties.getEndBulk())); checkConnection(); try { sendBytes(sb.toString()); String line = reader.readLine(); while (line != null) { if (!isResponseOK(line)) { LOGGER.warn("some command from a command list failed: {}", line); } if (reader.ready()) { line = reader.readLine(); LOGGER.warn("unexpected response line {}", line); } else { line = null; } } } catch (MPDSecurityException se) { LOGGER.error("Response Error from command list", se); throw se; } catch (Exception e) { LOGGER.error("Response Error from command list", e); commandList.forEach(s -> LOGGER.error(s.getCommand())); throw new MPDConnectionException(e.getMessage(), e); } } private List sendBytes(String command) throws IOException { LOGGER.debug("start command: {}", command); List response = new ArrayList<>(); writeToStream(command); String inLine = reader.readLine(); LOGGER.debug("first response line is: {}", inLine); while (inLine != null) { if (isResponseOK(inLine)) { LOGGER.debug("the response was ok"); break; } if (isResponseError(inLine)) { if (lastError.contains("you don't have permission")) { throw new MPDSecurityException(lastError, command); } else { LOGGER.error("Got error from command {}", command); throw new MPDConnectionException(lastError); } } response.add(inLine); inLine = reader.readLine(); } response.forEach(LOGGER::debug); return response; } private void checkConnection() { boolean connected; if (this.closed) { throw new MPDConnectionException("Close has been called on MPD. Create a new MPD."); } if (!socket.isConnected()) { LOGGER.warn("socket hasn't been connected yet"); connected = false; } else { if (!socket.isClosed()) { connected = checkPing(); } else { LOGGER.warn("socket is closed"); connected = false; } } if (!connected) { LOGGER.warn("we've lost connectivity, attempting to connect"); try { connect(); } catch (Exception e) { throw new MPDConnectionException("Connection to server lost: " + e.getMessage(), e); } } } public void close() { this.closed = true; if (!this.socket.isClosed()) { try { this.reader.close(); } catch (IOException e) { throw new MPDConnectionException("Unable to close socket", e); } try { this.socket.close(); } catch (IOException e) { throw new MPDConnectionException("Unable to close socket", e); } } } private void writeToStream(String command) throws IOException { socket.getOutputStream().write(command.getBytes(serverProperties.getEncoding())); } public String getVersion() { return this.version; } private boolean checkPing() { boolean connected = true; try { writeToStream(convertCommand(serverProperties.getPing())); String inLine = reader.readLine(); if (!isResponseOK(inLine)) { connected = false; } } catch (Exception e) { connected = false; LOGGER.error("lost socket connection", e); } return connected; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy