/* * Digital Audio Access Protocol (DAAP) Library * Copyright (C) 2004-2010 Roger Kapsi * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ardverk.daap.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.util.concurrent.ThreadFactory; import org.ardverk.daap.DaapConfig; import org.ardverk.daap.DaapConnection; import org.ardverk.daap.DaapServer; import org.ardverk.daap.Library; import org.ardverk.daap.SessionId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This DAAP server is written with the classical I/O and multible Threads. * * @author Roger Kapsi */ public class DaapServerBIO extends DaapServer<DaapConnectionBIO> { private static final Logger LOG = LoggerFactory.getLogger(DaapServerBIO.class); private volatile ThreadFactory threadFactory = new DaapThreadFactory("DaapConnectionThread"); private ServerSocket ssocket; /** * Creates a new DAAP server with Library and {@see SimpleConfig} * * @param library * a Library */ public DaapServerBIO(Library library) { this(library, new DaapConfig()); } /** * Creates a new DAAP server with Library and DaapConfig * * @param library * a Library * @param config * a DaapConfig */ public DaapServerBIO(Library library, DaapConfig config) { super(library, config); } /** * Sets the DaapThreadFactory for this DAAP server * * @param fectory * a DaapThreadFactory */ public synchronized void setThreadFactory(ThreadFactory factory) { if (factory == null) { threadFactory = new DaapThreadFactory("DaapConnectionThread"); } else { threadFactory = factory; } } /** * Binds this server to the SocketAddress supplied by DaapConfig * * @throws IOException */ public synchronized void bind() throws IOException { if (isRunning()) return; SocketAddress bindAddr = config.getInetSocketAddress(); int backlog = config.getBacklog(); ssocket = new ServerSocket(); ssocket.bind(bindAddr, backlog); if (LOG.isInfoEnabled()) { LOG.info("DaapServerBIO bound to " + bindAddr); } } /** * Stops the DAAP Server */ public synchronized void stop() { if (!isRunning()) return; running = false; try { if (ssocket != null) ssocket.close(); } catch (IOException err) { LOG.error("IOException", err); } disconnectAll(); } /** * Disconnects all DAAP and Stream connections */ public synchronized void disconnectAll() { for (DaapConnectionBIO connection : connections) { connection.disconnect(); } clear(); } /** * Call this to notify the server that Library has changed */ protected synchronized void update() { for (DaapConnectionBIO conn : connections) { for (Library library : libraryQueue) { conn.enqueueLibrary(library); } try { conn.update(); } catch (IOException err) { LOG.error("IOException", err); } } libraryQueue.clear(); } /** * The run loop */ @Override public void run() { running = true; try { while (running) { Socket socket = ssocket.accept(); try { synchronized (this) { if (running && accept(socket.getInetAddress())) { socket.setSoTimeout(DaapConnection.TIMEOUT); DaapConnectionBIO connection = new DaapConnectionBIO(this, socket); addPendingConnection(connection); Thread connThread = threadFactory.newThread(connection); connThread.start(); } else { socket.close(); } } } catch (IOException err) { LOG.error("IOException", err); try { socket.close(); } catch (IOException ignored) { } } Thread.sleep(100); } } catch (InterruptedException err) { LOG.error("InterruptedException", err); // throw new RuntimeException(err); } catch (SocketException err) { if (running) { LOG.error("SocketException", err); } // throw new RuntimeException(err); } catch (IOException err) { LOG.error("IOException", err); // throw new RuntimeException(err); } finally { stop(); } } /* Make them accessible for classes in this package */ protected synchronized DaapConnectionBIO getAudioConnection( SessionId sessionId) { return super.getAudioConnection(sessionId); } /* Make them accessible for classes in this package */ protected synchronized DaapConnectionBIO getDaapConnection( SessionId sessionId) { return super.getDaapConnection(sessionId); } /* Make them accessible for classes in this package */ protected synchronized boolean isSessionIdValid(SessionId sessionId) { return super.isSessionIdValid(sessionId); } /* Make them accessible for classes in this package */ protected synchronized void removeConnection(DaapConnection connection) { super.removeConnection(connection); } /* Make them accessible for classes in this package */ protected synchronized boolean updateConnection(DaapConnectionBIO connection) { if (!isRunning()) { return false; } return super.updateConnection(connection); } }