/*
* OSCResponderNode.java
* JCollider
*
* Copyright (c) 2004-2010 Hanns Holger Rutz. All rights reserved.
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either
* version 2, june 1991 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License (gpl.txt) along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de , or visit http://www.sciss.de/jcollider
*
*
* JCollider is closely modelled after SuperCollider Language,
* often exhibiting a direct translation from Smalltalk to Java.
* SCLang is a software originally developed by James McCartney,
* which has become an Open Source project.
* See http://www.audiosynth.com/ for details.
*
*
* Changelog:
* 04-Aug-05 created
* 30-Sep-06 modified to comply with new NetUtil version
*/
package de.sciss.jcollider;
import java.io.IOException;
import java.net.SocketAddress;
import de.sciss.net.OSCListener;
import de.sciss.net.OSCMessage;
/**
* Similar operation as the SClang class
* of the same name, but slightly different implementation,
* based on the new <code>OSCMultiResponder</code> class
* and <code>NetUtil</code>.
* <P>
* <B>As of v0.29</B> the creator signature had to be changed to use a <code>Server</code> instead
* of a network address, unfortunately (a side effect of using <code>OSCClient</code> in <code>Server</code>
* and <code>OSCMultiResponder</code>). So you may need to update old code.
*
* @author Hanns Holger Rutz
* @version 0.36, 08-Oct-09
*/
public class OSCResponderNode
implements OSCListener // , Runnable
{
// private final InetSocketAddress addr;
// private final Server server;
private final String cmdName;
private final Action action;
private volatile boolean removeWhenDone = false;
private volatile boolean listening = false;
// private final List collMessages = Collections.synchronizedList( new ArrayList() ); // elements = IncomingMessage instances
private final Object sync;
private final OSCMultiResponder multi;
/**
* Creates a new responder node to listen
* for messages coming in from the given server.
* Filters out messages different from
* the specified command name. Upon reception,
* the provided OSCListener is invoked.
* <p>
* After creating the responder, the <code>add</code>
* method has to be called separately to actually start
* the listening process.
*
* @param s server of incoming messages
* @param cmdName name of the OSC command at whose
* arrival the action is invoked
* @param action the action's <code>messageReceived</code>
* method is called upon message reception.
* Note that just as specified in the
* <code>OSCListener</code> interface, the action
* should not assume to be in any particular thread.
* The current implementation calls the action in the
* OSC listening thread, but this is not guaranteed.
* Calls to Swing components should be deferred appropriately.
*
* @see Server
* @see #add()
*/
public OSCResponderNode( final Server s, String cmdName, final OSCListener action )
{
this( s, cmdName, new Action() {
public void respond( OSCResponderNode node, OSCMessage msg, long time )
{
action.messageReceived( msg, s.getAddr(), time );
}
});
}
/**
* Creates a new responder node to listen
* for messages coming in from the given server.
* Filters out messages different from
* the specified command name. Upon reception,
* the provided action is invoked.
* <p>
* After creating the responder, the <code>add</code>
* method has to be called separately to actually start
* the listening process.
*
* @param s server of incoming messages
* @param cmdName name of the OSC command at whose
* arrival the action is invoked
* @param action the action's <code>respond</code>
* method is called upon message reception.
* Note that just as specified in the
* <code>OSCListener</code> interface, the action
* should not assume to be in any particular thread.
* The current implementation calls the action in the
* OSC listening thread, but this is not guaranteed.
* Calls to Swing components should be deferred appropriately.
*
* @see Server
* @see #add()
*/
public OSCResponderNode( Server s, String cmdName, Action action )
{
this.cmdName = cmdName;
this.action = action;
multi = s.getMultiResponder();
sync = multi.getSync();
}
/**
* Queries the name which is
* used as the message filter
*
* @return the name of the OSC command to which this
* responder listens
*/
public String getCommandName()
{
return cmdName;
}
/**
* Adds the node to the list of actively listening nodes.
* If you are uncertain about the node's state, check
* <code>isListening</code> first, since this method will
* throw an <code>IllegalStateException</code> if you try
* to add it twice.
*
* @return the responder node (for convenience)
*
* @see #remove()
* @see #isListening()
*
* @throws IllegalStateException if the node has already been added
*/
public OSCResponderNode add()
throws IOException
{
synchronized( sync ) {
if( listening ) throw new IllegalStateException( "OSCResponderNode.add() : duplicate call" );
multi.addNode( this );
listening = true;
return this;
}
}
/**
* Queries the node's state.
*
* @return <code>true</code> if the node is active
* (was added), <code>false</code> otherwise
* (newly created node or removed)
*/
public boolean isListening()
{
synchronized( sync ) {
return( listening );
}
}
/**
* Tags the node to remove itself after the next
* unfiltered message arrived. If the node shall
* receive exactly one message, a clean code must
* call this method before calling the <code>add</code>
* method.
*
* @return the responder node (for convenience)
*/
public OSCResponderNode removeWhenDone()
{
removeWhenDone = true;
return this;
}
/**
* This method is called as part of
* the implementation of the <code>OSCListener</code>
* interface. It dispatches the message to
* the action. If <code>removeWhenDone</code>
* was called, it will remove the node after
* the action returns.
*
* @see #removeWhenDone()
*/
public void messageReceived( OSCMessage msg, SocketAddress sender, long time )
{
if( listening ) {
try {
action.respond( this, msg, time );
}
catch( Exception e1 ) {
e1.printStackTrace( Server.getPrintStream() );
}
if( removeWhenDone ) {
remove(); // OSCMultiResponder will take care of thread issues
}
}
}
/**
* Removes the node from the list of actively
* listening nodes. If the node was already removed,
* this method does nothing.
*
* @return the responder node (for convenience)
* @throws IOException if there was a problem
*
* @see #add()
*/
public OSCResponderNode remove()
// throws IOException
{
synchronized( sync ) {
listening = false;
multi.removeNode( this );
return this;
}
}
// ----------- internal classes -----------
public interface Action {
public void respond( OSCResponderNode node, OSCMessage msg, long time );
}
// private static class IncomingMessage
// {
// private final OSCMessage msg;
// private final SocketAddress sender;
// private final long time;
//
// private IncomingMessage( OSCMessage msg, SocketAddress sender, long time )
// {
// this.msg = msg;
// this.sender = sender;
// this.time = time;
// }
// }
}