diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java index 413bbdf1..876e2ea9 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java @@ -245,7 +245,11 @@ public abstract class AudioBroadcaster implements Listener { if(mBroadcastState != state) { - mLog.info("[" + getStreamName() + "] status: " + state); + if(state == BroadcastState.CONNECTED || state == BroadcastState.DISCONNECTED) + { + mLog.info("[" + getStreamName() + "] status: " + state); + } + mBroadcastState = state; broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE)); diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastState.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastState.java index 5fd6c3f3..9aecadbd 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastState.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastState.java @@ -70,6 +70,11 @@ public enum BroadcastState */ MOUNT_POINT_IN_USE("Mount Point In Use", false), + /** + * Network is unavailable or the server address cannot be resolved + */ + NETWORK_UNAVAILABLE("Network Unavailable", false), + /** * Server is not known or reachable */ diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java index b82e31d2..38ce234e 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java @@ -20,9 +20,14 @@ package io.github.dsheirer.audio.broadcast; import net.miginfocom.swing.MigLayout; -import javax.swing.*; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingConstants; import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; public class BroadcastStatusPanel extends JPanel { @@ -96,7 +101,8 @@ public class BroadcastStatusPanel extends JPanel setBackground(table.getBackground()); setForeground(Color.LIGHT_GRAY); } - else if(state == BroadcastState.INVALID_SETTINGS) + else if(state == BroadcastState.INVALID_SETTINGS || + state == BroadcastState.NETWORK_UNAVAILABLE) { setBackground(Color.YELLOW); setForeground(table.getForeground()); diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java index 1bd767f8..924fd8ec 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java @@ -22,6 +22,7 @@ import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.util.ThreadPool; +import org.apache.mina.core.RuntimeIoException; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; @@ -35,7 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; -import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.URLEncoder; import java.nio.channels.UnresolvedAddressException; @@ -117,70 +117,64 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate { mMetadataQueue.offer(getSong(metadata)); - if(mUpdating.compareAndSet(false, true)) + if(!mUpdating.get() && !mMetadataQueue.isEmpty()) { - String song = mMetadataQueue.poll(); - - while(song != null) + Runnable runnable = new Runnable() { - HttpRequest updateRequest = createUpdateRequest(song); - - if(updateRequest != null) + @Override + public void run() { - ThreadPool.SCHEDULED.schedule(new Runnable() + if(!mMetadataQueue.isEmpty() && mUpdating.compareAndSet(false, true)) { - @Override - public void run() + try { - try - { - ConnectFuture connectFuture = getSocketConnector() - .connect(new InetSocketAddress(mIcecastConfiguration.getHost(), - mIcecastConfiguration.getPort())); - connectFuture.awaitUninterruptibly(); - IoSession session = connectFuture.getSession(); + ConnectFuture connectFuture = getSocketConnector() + .connect(new InetSocketAddress(mIcecastConfiguration.getHost(), + mIcecastConfiguration.getPort())); + connectFuture.awaitUninterruptibly(); + IoSession session = connectFuture.getSession(); - if(session != null) - { - session.write(updateRequest); - } - } - catch(UnresolvedAddressException uae) + if(session != null) { - //Do nothing - the server is temporarily unavailable - } - catch(Exception e) - { - Throwable throwableCause = e.getCause(); + String song = mMetadataQueue.poll(); - if(throwableCause instanceof ConnectException) + while(song != null) { - //Do nothing, the server is unavailable - } - else if(throwableCause instanceof UnresolvedAddressException) - { - //Do nothing - the server is temporarily unavailable - } - else - { - if(!mStackTraceLoggingSuppressed) + HttpRequest updateRequest = createUpdateRequest(song); + + if(updateRequest != null) { - mLog.error("Error sending metadata update. Future errors will " + - "be suppressed", e); - - mStackTraceLoggingSuppressed = true; + session.write(updateRequest); } + + //Fetch next metadata update to send + song = mMetadataQueue.poll(); } + + session.closeNow(); } } - }, 0l, TimeUnit.SECONDS); + catch(UnresolvedAddressException | RuntimeIoException ure) + { + //Do nothing - the server is temporarily unavailable + } + catch(Exception e) + { + Throwable throwableCause = e.getCause(); + + if(!mStackTraceLoggingSuppressed) + { + mLog.error("Error sending metadata update. Future errors will be suppressed", e); + mStackTraceLoggingSuppressed = true; + } + } + + mUpdating.set(false); + } } + }; - //Fetch next metadata update to send - song = mMetadataQueue.poll(); - } - - mUpdating.set(false); + ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS); } } diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java index dc586f1f..8618d6b2 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java @@ -27,15 +27,14 @@ import org.apache.mina.core.RuntimeIoException; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; -import org.apache.mina.core.write.WriteTimeoutException; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.ConnectException; import java.net.InetSocketAddress; +import java.net.SocketException; import java.nio.channels.UnresolvedAddressException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -46,7 +45,8 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster private final static String TERMINATOR = "\r\n"; private final static String SEPARATOR = ":"; private static final long RECONNECT_INTERVAL_MILLISECONDS = 3000; //3 seconds - private static final long CONNECTION_ATTEMPT_TIMEOUT = 5000; //5 seconds + private static final long CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS = 5000; //5 seconds + private static final int WRITE_TIMEOUT_SECONDS = 5; private NioSocketConnector mSocketConnector; private IoSession mStreamingSession = null; @@ -101,7 +101,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster if(mSocketConnector == null) { mSocketConnector = new NioSocketConnector(); - mSocketConnector.setConnectTimeoutCheckInterval(CONNECTION_ATTEMPT_TIMEOUT); + mSocketConnector.getSessionConfig().setWriteTimeout(WRITE_TIMEOUT_SECONDS); // LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class); // loggingFilter.setMessageSentLogLevel(LogLevel.NONE); @@ -127,45 +127,41 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster .connect(new InetSocketAddress(getBroadcastConfiguration().getHost(), getBroadcastConfiguration().getPort())); - boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT, TimeUnit.MILLISECONDS); + boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); - mStreamingSession = future.getSession(); + if(connected) + { + mStreamingSession = future.getSession(); + mConnecting.set(false); + return; + } + } + catch(RuntimeIoException rioe) + { + if(rioe.getCause() instanceof SocketException) + { + setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE); + mConnecting.set(false); + return; + } } catch(UnresolvedAddressException uae) { - //What do you do with this? Nothing. + setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE); + mConnecting.set(false); + return; } - catch(InterruptedException ie) + catch(Exception e) { - //We couldn't connect within the timeout period -- disconnect so that we can try again later - disconnect(); - } - catch(RuntimeIoException rie) - { - Throwable throwableCause = rie.getCause(); - - if(throwableCause instanceof ConnectException) - { - setBroadcastState(BroadcastState.NO_SERVER); - } - else if(throwableCause != null) - { - setBroadcastState(BroadcastState.ERROR); - mLog.debug("[" + getStreamName() + "] failed to connect", rie); - } - else - { - setBroadcastState(BroadcastState.ERROR); - mLog.debug("[" + getStreamName() + "] failed to connect - no exception is available"); - } - - disconnect(); + mLog.error("Error", e); + //Disregard ... we'll disconnect and try again } catch(Throwable t) { - disconnect(); + mLog.error("Throwable error caught", t); } + disconnect(); mConnecting.set(false); } }; @@ -188,6 +184,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster } else { + setBroadcastState(BroadcastState.DISCONNECTED); mLastConnectionAttempt = System.currentTimeMillis(); } } @@ -263,49 +260,12 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { - if(cause instanceof IOException) - { - IOException ioe = (IOException)cause; - - if(ioe.getMessage() != null) - { - String reason = ioe.getMessage(); - - if(connected()) - { - disconnect(); - } - else if(reason.startsWith("Connection reset")) - { - disconnect(); - } - else if(reason.startsWith("Operation timed out")) - { - disconnect(); - } - else - { - setBroadcastState(BroadcastState.ERROR); - disconnect(); - } - } - else if(ioe instanceof WriteTimeoutException) - { - disconnect(); - } - else - { - setBroadcastState(BroadcastState.ERROR); - disconnect(); - mLog.error("[" + getStreamName() + "] Unspecified IO error - streaming halted.", cause); - } - } - else + if(!(cause instanceof IOException)) { mLog.error("[" + getStreamName() + "] Broadcast error", cause); - setBroadcastState(BroadcastState.ERROR); - disconnect(); } + + disconnect(); } @Override