/*
* 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;
}
}