I have just started evaluating Lightstreamer and worked my way through the "HelloWorld" tutorial successfully.  Next I wanted to create a simple "Echo" facility just to ensure I understand what is going on. My understanding (could be wrong) is that if I want to send a message from the client and have it echoed back to me, I need to create a metadata adapter to pass the incoming message to my data adapter, which can then echo it back.  However, the "notifiyUserMessage" method in my metadata adapter never gets fired - so either I have misunderstood the way I am to do this, or I just have a plain old bug I don't understand.
So, firstly, is my understanding correct?  If so, can anyone tell me what I am doing wrong because it is a pretty simple thing.
Here is my metadata code:
public class MetadataAdapter extends LiteralBasedProvider {
   
    private void loadEchoFeed() throws CreditsException {
        if (this.echoFeed == null) {
            try {
                // Get the IMDataAdapter instance to bind it with this
                // Metadata Adapter and send instant messages through it
                this.echoFeed = EchoDataAdapter.feedMap.get(this.adapterSetId);
            } catch (Throwable t) {
                // It can happen if the Messenger Data Adapter jar was not even
                // included in the Adapter Set lib directory (the Messenger
                // Data Adapter could not be included in the Adapter Set as
                // well)
                logError("EchoDataAdapter class was not loaded: " + t);
                throw new CreditsException(0, "No echo feed available",
                        "No echo feed available");
            }
            if (this.echoFeed == null) {
                // The feed is not yet available on the static map, maybe the
                // Echo Data Adapter was not included in the Adapter Set
                logError("EchoDataAdapter not found");
                throw new CreditsException(0, "No echo feed available",
                        "No echo feed available");
            }
        }
    }
    private void handleEchoMessage(String message) throws CreditsException {
        this.echoFeed.sendMessage(message);        
    }
    //------- Public methods--------
    public MetadataAdapter() {
    }
  
    public void init(Map params, File configDir) throws MetadataProviderException  {
        super.init(params, configDir);
        String logConfig = (String)params.get("log_config");
        if (logConfig != null) {
            File logConfigFile = new File(configDir, logConfig);
            String logRefresh = (String)params.get("log_config_refresh_seconds");
            if (logRefresh != null)
                DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000);
            else {
                DOMConfigurator.configure(logConfigFile.getAbsolutePath());
            }
        }
        this.adapterSetId = ((String)params.get("adapters_conf.id"));
    }
    
      
    public void notifyUserMessage(String user, String session, String message) throws NotificationException, CreditsException {
        // Triggered by a client "sendMessage" call.
        // The message encodes an instant message from the client.
        
        if (message == null) {
            logger.warn("Null message received");
            throw new NotificationException("Null message received");
        }             
        
        // Load the echco feeder
        this.loadEchoFeed();
        this.handleEchoMessage(message);
    }    
}
Here is my data adapter code
public class EchoDataAdapter implements SmartDataProvider {
    
    private ItemEventListener listener;
    private final ExecutorService executor; //Used to enqueue the calls to the listener.
    private Logger logger;
  
    public static final ConcurrentHashMap<String, EchoDataAdapter> feedMap =
        new ConcurrentHashMap<String, EchoDataAdapter>();
    
    //------- Private methods--------
    
    //------- Public methods--------
    public EchoDataAdapter() {
        executor = Executors.newSingleThreadExecutor();
    }
    
    public void init(Map params, File configDir) throws DataProviderException   {
        String logConfig = (String) params.get("log_config");
        if (logConfig != null) {
            File logConfigFile = new File(configDir, logConfig);
            String logRefresh = (String) params.get("log_config_refresh_seconds");
            if (logRefresh != null) {
                DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000);
            } else {
                DOMConfigurator.configure(logConfigFile.getAbsolutePath());
            }
        }
        
        // Read the Adapter Set name, which is supplied by the Server as a parameter
        String adapterSetId = (String) params.get("adapters_conf.id");
        // Put a reference to this instance on a static map
        // to be read by the Metadata Adapter
        feedMap.put(adapterSetId, this);
       
    }
    public boolean isSnapshotAvailable(String itemName) throws SubscriptionException  {
        return false;
    }
    public void setListener(ItemEventListener listener) {
        // We will be passed a reference to a listener that we will use to inject the real-time events
        this.listener = listener;
    }
    public void subscribe(String itemName, Object itemHandle, boolean needsIterator) throws SubscriptionException, FailureException  {
        // We must be ready to accept subscription requests. If we receive an "echo" item we will start a thread to
        // handle the data.
        // When the “echo” item is subscribed to by the first user, our Adapter receives that method call and starts a thread 
        // that will generate the real-time data. 
        // If more users subscribe to the “echo” item, the subscribe method is not called anymore. 
        // When the last user unsubscribes from this item, our Adapter is notified through the unsubscribe call
        if (itemName.equals("echo")) {
           // Nothing to do        
        }
    }
    public void subscribe(String itemName, boolean needsIterator) throws SubscriptionException, FailureException  {
    }
    public void unsubscribe(String itemName) throws SubscriptionException, FailureException  {
        // No more users
        if (itemName.equals("echo")) {
          // Nothing to do
        }
    }
    
    public void sendMessage(String message) {
        // Receives a message from the metadata adapter, which we will simply echo back
        this.echoMessage(message);
    }
    private void echoMessage(String message) {
        final HashMap<String, String> echo = new HashMap<String, String>();
        echo.put("message", message);
        
        //If we have a listener create a new Runnable to be used as a task to pass the
        //new update to the listener
        Runnable echoMessage = new Runnable() {
            public void run() {
                // call the update on the listener;
                // in case the listener has just been detached,
                // the listener should detect the case
                listener.smartUpdate(listener, echo, false);
            }
        };
        //We add the task on the executor to pass to the listener the actual status
        executor.execute(echoMessage);
    }
    
}
and my client just does this
        function extractFieldData(event,field) {
                var value;
                if (event.isValueChanged(field)) {
                        value = event.getValue(field);
                }
                return value;
        }
        function handleEcho(event) {
                var eventType = event.item; //This provides the update event name
                var echoed=extractFieldData(event,"message");
                alert("Echo: "+echoed);
        }
        var client = new LightstreamerClient("http://rnsdev:8080","RNS");
        client.connect();
        var echoSub = new Subscription("DISTINCT","echo",new Array("message"));
        echoSub.setDataAdapter("ECHO");
        echoSub.addListener({
                onItemUpdate:handleEcho
        });
        client.subscribe(echoSub);
        client.sendMessage("Will this message get back to me?");