/* * 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; import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * DaapServer * * @author Roger Kapsi */ public abstract class DaapServer<T extends DaapConnection> implements Runnable, LibraryListener { protected static final Logger LOG = LoggerFactory .getLogger(DaapServer.class); /** The Library */ protected final Library library; /** Queue of Library patches */ protected final List<Library> libraryQueue = new ArrayList<Library>(); /** Set of currently active session ids */ protected final Set<SessionId> sessionIds = new HashSet<SessionId>(); /** Set of currently active connections */ protected final List<T> connections = new LinkedList<T>(); /** A DaapConfig instance */ protected DaapConfig config; /** A DaapFilter instance */ protected DaapFilter filter; /** Source for Audio streams */ protected DaapStreamSource streamSource; /** The Authenticator */ protected DaapAuthenticator authenticator; /** Flag for wheather or not the server is running */ protected boolean running = false; public DaapServer(Library library, DaapConfig config) { this.library = library; this.config = config; library.addLibraryListener(this); } /** * Returns the Library of this server * * @return Library */ public Library getLibrary() { return library; } public synchronized void libraryChanged(Library library, Library branch) { if (isRunning() && getNumberOfDaapConnections() > 0) { libraryQueue.add(branch); update(); } } /** * Returns the DaapConfig of this server * * @return DaapConfig of this server */ public DaapConfig getConfig() { return config; } /** * Sets the DaapStreamSource for this server * * @param streamSource * a DaapStreamSource */ public synchronized void setStreamSource(DaapStreamSource streamSource) { this.streamSource = streamSource; } /** * Retrieves the DaapStreamSource of this server * * @return DaapStreamSource or <code>null</code> */ public synchronized DaapStreamSource getStreamSource() { return streamSource; } /** * Sets a DaapFilter for this server * * @param filter * a DaapFilter */ public synchronized void setFilter(DaapFilter filter) { this.filter = filter; } /** * Returns a DaapFilter * * @return a DaapFilter or <code>null</code> */ public synchronized DaapFilter getFilter() { return filter; } public synchronized void setAuthenticator(DaapAuthenticator authenticator) { this.authenticator = authenticator; } /** * Returns the DaapAuthenticator from Library */ public synchronized DaapAuthenticator getAuthenticator() { return authenticator; } /** * Sets the factory for Threads. Servers (NIO) that do not support a * Threaded moddel throw an {@see java.lang.UnsupportedOperationException} * * @param factory a {@link ThreadFactory} */ public synchronized void setThreadFactory(ThreadFactory factory) { throw new UnsupportedOperationException(); } /** * Binds this server to the SocketAddress supplied by DaapConfig * * @throws IOException */ public abstract void bind() throws IOException; /** * Returns <code>true</code> if the server is running. * * @return <code>true</code> if the server is running */ public synchronized boolean isRunning() { return running; } /** * Stops the DAAP Server */ public abstract void stop(); /** * Disconnects all from the server */ public abstract void disconnectAll(); /** * Call this to notify the server that Library has changed */ protected abstract void update(); /** * Returns the number of connections */ public synchronized int getNumberOfDaapConnections() { return getDaapConnections().size(); } /** * Returns the number of streams */ public synchronized int getNumberOfAudioConnections() { return getAudioConnections().size(); } /** * Returns the number of pending connections */ public synchronized int getNumberOfPendingConnections() { return getPendingConnections().size(); } /** * Returns <code>true</code> if sessionId is valid */ protected synchronized boolean isSessionIdValid(SessionId sessionId) { return !SessionId.INVALID.equals(sessionId) && sessionIds.contains(sessionId); } /** * Creates and returns an unique session-id */ protected synchronized SessionId createSessionId() { SessionId sid = SessionId.createSessionId(sessionIds); sessionIds.add(sid); return sid; } protected synchronized void destroySessionId(SessionId sessionId) { sessionIds.remove(sessionId); } /** * Returns <code>true</code> if host with <code>addr</code> is allowed to * connect to this DAAP server. * * @return true host with <code>addr</code> is allowed to connect */ protected synchronized boolean accept(InetAddress addr) { if (filter != null && filter.accept(addr) == false) { if (LOG.isInfoEnabled()) { LOG.info("DaapFilter refused connection from " + addr); } return false; } return true; } /** Adds connection to pending connections pool */ protected synchronized void addPendingConnection(T connection) throws IllegalArgumentException { if (!connection.isUndef()) { throw new IllegalArgumentException(); } connections.add(connection); } /** Returns an unmodifyable list of pending connections */ protected synchronized List<T> getPendingConnections() { List<T> list = new ArrayList<T>(); for (T connection : connections) { if (connection.isUndef()) { list.add(connection); } } return Collections.unmodifiableList(list); } protected synchronized List<T> getConnections() { return Collections.unmodifiableList(new ArrayList<T>(connections)); } /** Returns an unmodifyable list of DAAP connections */ protected synchronized List<T> getDaapConnections() { List<T> list = new ArrayList<T>(); for (T connection : connections) { if (connection.isDaapConnection()) { list.add(connection); } } return Collections.unmodifiableList(list); } /** Returns an unmodifyable List of Audio connections */ protected synchronized List<T> getAudioConnections() { List<T> list = new ArrayList<T>(); for (T connection : connections) { if (connection.isAudioStream()) { list.add(connection); } } return Collections.unmodifiableList(list); } /** Updates connection's state from pending to DAAP or Audio connection */ protected synchronized boolean updateConnection(T connection) { if (connection.isDaapConnection()) { return getDaapConnections().size() < config.getMaxConnections(); } else if (connection.isAudioStream()) { return getAudioConnections().size() < config.getMaxConnections(); } return false; } /** Removes connection */ protected synchronized void removeConnection(DaapConnection connection) throws IllegalStateException { if (!connections.remove(connection)) { throw new IllegalStateException(); } } /** Returns a DAAP connection for the provided sessionId */ protected synchronized T getDaapConnection(SessionId sessionId) { for (T connection : connections) { DaapSession session = connection.getSession(false); if (session != null && connection.isDaapConnection() && sessionId.equals(session.getSessionId())) { return connection; } } return null; } /** Returns an Audio connection for the provided sessionId */ protected synchronized T getAudioConnection(SessionId sessionId) { for (T connection : connections) { DaapSession session = connection.getSession(false); if (session != null && connection.isAudioStream() && sessionId.equals(session.getSessionId())) { return connection; } } return null; } /** Clears internal lists */ protected synchronized void clear() { connections.clear(); sessionIds.clear(); libraryQueue.clear(); } @Override public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("Name: ").append(config.getServerName()).append("\n"); buffer.append("Address: ").append(config.getInetSocketAddress()) .append("\n"); buffer.append("Backlog: ").append(config.getBacklog()).append("\n"); buffer.append("Max connections: ").append(config.getMaxConnections()) .append("\n"); buffer.append("IsRunning: ").append(isRunning()).append("\n"); if (isRunning()) { buffer.append("Connections: ").append(getNumberOfDaapConnections()) .append("\n"); buffer.append("Streams: ").append(getNumberOfAudioConnections()) .append("\n"); } return buffer.toString(); } }