/* * 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.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An abstract base class for DaapConnections. * * @author Roger Kapsi */ public abstract class DaapConnection { // private static final Logger LOG = // LoggerFactory.getLogger(DaapConnection.class); private static final Logger LOG = LoggerFactory .getLogger(DaapConnection.class); public static final int TIMEOUT = 3000; public static final int LIBRARY_TIMEOUT = 30000; protected static enum ConnectionType { /** Undef type of connection */ UNDEF, /** A DAAP connection */ DAAP, /** An audio stream */ AUDIO; } protected final DaapResponseWriter writer; protected DaapServer<?> server; protected DaapSession session; protected ConnectionType type = ConnectionType.UNDEF; protected int protocolVersion = DaapUtil.NULL; protected String nonce; protected List<Library> libraryQueue; protected boolean locked = false; public DaapConnection(DaapServer<?> server) { this.server = server; writer = new DaapResponseWriter(); libraryQueue = new LinkedList<Library>(); } /** * Call this to initiate an update which causes the client to update to the * current Library revision. * * @throws IOException */ public abstract void update() throws IOException; /** * Writes data from the queue to the Channel/OutputStream * * @throws IOException * @return true if keepAlive */ public boolean write() throws IOException { if (writer.write()) { if (isAudioStream()) { return false; } } return true; } /** * Called when a DaapConnection is beeing removed from the connection pool. */ public void close() { writer.clear(); if (session != null) { session.invalidate(); session = null; } } /** * Returns the DaapServer to which this DaapConnection is associated to. * * @return the associated DaapServer */ public DaapServer<?> getServer() { return server; } /** * Sets the type of this connection. Either UNDEF, DAAP or an AUDIO stream * * @param type * the type of this connection */ protected void setConnectionType(ConnectionType type) { this.type = type; } /** * Sets the protocol version of this connection */ protected void setProtocolVersion(int protocolVersion) { this.protocolVersion = protocolVersion; } /** * Returns <code>true</code> if this connection is an audio stream. * * @return true if this is an audio stream */ public boolean isAudioStream() { return ConnectionType.AUDIO.equals(type); } /** * Returns <code>true</code> if this connection is a DAAP connection * (handles Requests/Respones). * * @return true if this is a DAAP connection */ public boolean isDaapConnection() { return ConnectionType.DAAP.equals(type); } /** * Returns <code>true</code> if the type of this connection is currently * indetermined. * * @return <code>true</code> if connection is indetermined */ public boolean isUndef() { return ConnectionType.UNDEF.equals(type); } /** * Returns the protocol version of this connection or DaapUtil.UNDEF_VALUE * if it is currently unknown * * @return protocol version of this connection */ public int getProtocolVersion() { return protocolVersion; } /** * Creates if nessesary a new DaapSession and returns it. * * @param create * if true creates a new Session object if necessary * @return a DaapSession object or null if create is false and no * DaapSession object existed */ public DaapSession getSession(boolean create) { if (session == null && create) { session = new DaapSession(server.createSessionId()); } return session; } /** * */ public SessionId getSessionId(boolean create) { SessionId sessionId = SessionId.INVALID; DaapSession session = getSession(create); if (session != null) { sessionId = session.getSessionId(); } return sessionId; } /** * Clears the library queue */ public void clearLibraryQueue() { synchronized (libraryQueue) { libraryQueue.clear(); } } /** * Adds library to the library queue */ public void enqueueLibrary(Library library) { synchronized (libraryQueue) { libraryQueue.add(library); } } /** * Returns the first item from the library queue */ protected Library getFirstInQueue() { synchronized (libraryQueue) { if (!libraryQueue.isEmpty()) { return libraryQueue.get(0); } return server.getLibrary(); } } /** * Returns the last item from the library queue */ protected Library getLastInQueue() { synchronized (libraryQueue) { if (!libraryQueue.isEmpty()) { return libraryQueue.get(libraryQueue.size() - 1); } return server.getLibrary(); } } /** * Returns the next item from the library queue */ public Library nextLibrary(DaapRequest request) { synchronized (libraryQueue) { int delta = request.getDelta(); if (delta == DaapUtil.NULL || libraryQueue.isEmpty()) { return server.getLibrary(); } Library first = libraryQueue.get(0); if (first.getRevision() != request.getRevisionNumber()) { if (LOG.isErrorEnabled()) { LOG.error("Client doesn't request the current revision: " + delta + "/" + request.getRevisionNumber() + "/" + first.getRevision()); } clearLibraryQueue(); return null; } else if (delta > first.getRevision()) { if (LOG.isErrorEnabled()) { LOG.error("Client is ahead of us: " + delta + "/" + first.getRevision()); } clearLibraryQueue(); return null; } else if (first.getRevision() == delta) { libraryQueue.remove(0); if (libraryQueue.isEmpty()) { return server.getLibrary(); } else { if (request.getRevisionNumber() != delta) { return nextLibrary(request); } else { return nextLibrary(new DaapRequest(this, getSessionId(false), request .getRevisionNumber() + 1, delta)); } } } else { return first; } } } /** * Locks the connection * * @see #update() * @see DaapRequestProcessor#processUpdateRequest(DaapRequest) */ protected synchronized void lock() { locked = true; } /** * Unlocks the connection * * @see #update() * @see DaapRequestProcessor#processUpdateRequest(DaapRequest) */ protected synchronized void unlock() { locked = false; } /** * Returns true if connection is marked as locked * * @see #update() * @see DaapRequestProcessor#processUpdateRequest(DaapRequest) */ protected synchronized boolean isLocked() { return locked; } /** * */ synchronized String getNonce() { return nonce; } /** * */ synchronized String createNonce() { nonce = DaapUtil.nonce(); return nonce; } }