/*
* 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
* 26-Aug-05 removed potential null pointer exception in removeNode()
* 30-Sep-06 modified to comply with new NetUtil version
* 08-Apr-08 fixes potential locking problem in messageReceived
* 11-Jan-09 ensuring command names begin with a leading slash
* to ensure compatibility with different scsynth versions
*/
package de.sciss.jcollider;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.sciss.net.OSCClient;
import de.sciss.net.OSCMessage;
import de.sciss.net.OSCListener;
/**
* Despite the name, the <code>OSCMultiResponder</code>
* mimics the SClang counter part only superficially.
* It absorbs the whole <code>OSCResponder</code> class
* and is based on the <code>NetUtil</code> OSC library.
* <p>
* While the super class <code>OSCReceiver</code> allows
* only a coarse message filtering, using the simple
* <code>OSCListener</code> interface, the <code>OSCMultiResponder</code>
* maintains a map of OSC command names and listeners
* (<code>OSCResponderNode</code>s) who wish to be
* informed about only this particular type of messages.
* <p>
* When a new node is added using the <code>addNode</code>
* method, the static list of all multi responders is searched
* for the given server address. If it exists, the corresponding
* multi responder is used, otherwise a new multi responder is
* created. Likewise, when <code>removeNode</code> is called,
* the multi responder checks if all nodes have been removed,
* and if so will terminate the OSC receiver.
* <p>
* To keep the responder permanently active, the server creates
* a multi responder for its address upon instantiation.
*
* @author Hanns Holger Rutz
* @version 0.34, 11-Jan-10
*/
public class OSCMultiResponder
// extends OSCReceiver
implements OSCListener
{
// private static final Map mapServerToMulti = new HashMap();
private final List allNodes = new ArrayList();
// private final Map mapAddrToCmds = new HashMap();
private final Map mapCmdToNodes = new HashMap();
// private final SocketAddress addr;
private static final boolean debug = false;
// private final boolean alwaysListening;
// private final Runnable startOrStop;
private final OSCClient c;
private OSCResponderNode[] resps = new OSCResponderNode[ 2 ];
private final Object sync = new Object();
/**
* Creates a new responder for the given
* <code>OSCClient</code>. This is done by the server to
* create a permanent listener. Users should
* use the <code>OSCResponderNode</code> class
* instead.
*
* @param c the client to who's receiver we should listen
*
* @see OSCResponderNode
*/
// protected OSCMultiResponder( InetSocketAddress addr, boolean alwaysListening )
protected OSCMultiResponder( final OSCClient c )
throws IOException
{
// super( DatagramChannel.open(), addr );
this.c = c;
// this.addr = addr;
// this.alwaysListening = alwaysListening;
// mapServerToMulti.put( s, this );
// if( alwaysListening ) {
//if( debug ) System.err.println( "OSCMultiResponder( addr = " + this.addr +"; hash = "+hashCode() + " ): startListening" );
// c.start();
c.addOSCListener( this );
// startOrStop = null;
// } else {
// final OSCMultiResponder enc_this = this;
// startOrStop = new Runnable() {
// public void run()
// {
// synchronized( allNodes ) {
// if( allNodes.isEmpty() ) {
//if( debug ) System.err.println( "OSCMultiResponder( addr = " + enc_this.addr +"; hash = "+enc_this.hashCode() + " ): startListening" );
// c.start();
// c.addOSCListener( enc_this );
// } else {
// c.removeOSCListener( enc_this );
//if( debug ) System.err.println( "OSCMultiResponder( addr = " + enc_this.addr +"; hash = " + enc_this.hashCode() + " ): stopListening" );
// try {
// c.stop();
// }
// catch( IOException e1 ) {
// e1.printStackTrace( System.err );
// }
// }
// }
// }
// };
// }
}
//private static DatagramChannel createChannel()
//throws IOException
//{
// final DatagramChannel dch = DatagramChannel.open();
//// dch.socket().bind( new InetSocketAddress( "127.0.0.1", 0 ));
// dch.socket().bind( new InetSocketAddress( InetAddress.getLocalHost(), 0 ));
// return dch;
//}
// private static DatagramChannel createChannel( SocketAddress addr )
// throws IOException
// {
// DatagramChannel dch = DatagramChannel.open();
//// dch.connect( addr );
// dch.socket().bind( null );
// return dch;
// }
// protected static OSCMultiResponder addNode( Server s, OSCResponderNode node )
// throws IOException
// {
// OSCMultiResponder resp;
//
// synchronized( mapServerToMulti ) {
// resp = (OSCMultiResponder) mapServerToMulti.get( s );
//// if( resp == null ) {
//// resp = new OSCMultiResponder( s );
//// }
// }
// resp.addNode( node );
// return resp;
// }
protected Object getSync()
{
return sync;
}
protected void addNode( OSCResponderNode node )
throws IOException
{
List specialNodes;
synchronized( sync ) {
// if( allNodes.isEmpty() && !alwaysListening ) {
// if( EventQueue.isDispatchThread() ) {
// startOrStop.run();
// } else {
// EventQueue.invokeLater( startOrStop );
// }
// }
allNodes.add( node );
specialNodes = (List) mapCmdToNodes.get( node.getCommandName() );
if( specialNodes == null ) {
specialNodes = new ArrayList( 4 );
mapCmdToNodes.put( node.getCommandName(), specialNodes );
}
specialNodes.add( node );
}
}
protected void removeNode( OSCResponderNode node )
// throws IOException
{
final List specialNodes;
synchronized( sync ) {
specialNodes = (List) mapCmdToNodes.get( node.getCommandName() );
if( specialNodes != null ) {
specialNodes.remove( node );
allNodes.remove( node );
// for( int i = 0; i < resps.length; i++ ) {
// resps[ i ] = null; // clear references
// }
if( allNodes.isEmpty() ) {
mapCmdToNodes.clear();
// if( !alwaysListening ) {
// if( EventQueue.isDispatchThread() ) {
// startOrStop.run();
// } else {
// EventQueue.invokeLater( startOrStop );
// }
// }
}
}
}
}
protected void dispose()
{
synchronized( sync ) {
c.removeOSCListener( this );
// mapAddrsToMultis.remove( this );
allNodes.clear();
mapCmdToNodes.clear();
// if( resps.length > 0 ) resps = new OSCResponderNode[ 0 ];
// resps = null;
if( debug ) System.err.println( "OSCMultiResponder( client = " + c +"; hash = " + hashCode() + " ): dispose" );
c.dispose();
}
// try {
// stopListening();
// getChannel().close();
// }
// catch( IOException e1 ) {
// e1.printStackTrace( System.err );
// }
}
// // true if o is an OSCResponder
// // with the same address and command name
// public boolean equals( Object o )
// {
// if( o instanceof OSCResponder ) {
// final OSCResponder resp = (OSCResponder) o;
// return( this.cmdName.equals( o.cmdName ) && this.addr.equals( o.addr ));
// } else {
// return false;
// }
// }
//
// // [...] so as to maintain the general contract for the hashCode method,
// // which states that equal objects must have equal hash codes.
// public int hashCode()
// {
// return( addr.hashCode() ^ cmdName.hashCode() );
// }
// ------------ OSCListener interface ------------
public void messageReceived( OSCMessage msg, SocketAddress sender, long time )
{
final List specialNodes;
final int numResps;
final String cmdNameTmp = msg.getName();
final String cmdName = (cmdNameTmp.charAt( 0 ) == '/') ? cmdNameTmp : "/" + cmdNameTmp;
synchronized( sync ) {
specialNodes = (List) mapCmdToNodes.get( cmdName );
if( specialNodes == null ) return;
numResps = specialNodes.size();
resps = (OSCResponderNode[]) specialNodes.toArray( resps );
}
for( int i = 0; i < numResps; i++ ) {
try {
resps[ i ].messageReceived( msg, sender, time );
}
catch( Exception e ) {
e.printStackTrace( Server.getPrintStream() );
}
resps[ i ] = null;
}
}
}