/**
* Copyright (c) 2011-2014 Exxeleron GmbH
*
* 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 com.exxeleron.qjava;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* The {@link QCallbackConnection}, in addition to {@link QBasicConnection}, provides an internal thread-based mechanism
* for asynchronous subscription.
*
* Methods of {@link QCallbackConnection} are not thread safe.
*/
public class QCallbackConnection extends QBasicConnection {
protected QListener messageListener;
protected Thread listenerThread;
final CopyOnWriteArraySet<QMessagesListener> messagesListeners;
/**
* Initializes a new QCallbackConnection instance.
*
* @param host
* Host of remote q service
* @param port
* Port of remote q service
* @param username
* Username for remote authorization
* @param password
* Password for remote authorization
* @param encoding
* Encoding used for serialization/deserialization of string objects
*/
public QCallbackConnection(final String host, final int port, final String username, final String password, final String encoding) {
super(host, port, username, password, encoding);
this.messagesListeners = new CopyOnWriteArraySet<QMessagesListener>();
}
/**
* Initializes a new QCallbackConnection instance.
*
* @param host
* Host of remote q service
* @param port
* Port of remote q service
* @param username
* Username for remote authorization
* @param password
* Password for remote authorization
*/
public QCallbackConnection(final String host, final int port, final String username, final String password) {
this(host, port, username, password, "ISO-8859-1");
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
if ( connection != null ) {
stopListener();
messagesListeners.clear();
}
super.close();
}
/**
* Spawns a new thread which listens for asynchronous messages from the remote q host. If a messageListener thread
* already exists, nothing happens.
*/
public synchronized void startListener() {
startListener("qJava-listener" + this.toString());
}
/**
* Spawns a new thread which listens for asynchronous messages from the remote q host. If a messageListener thread
* already exists, nothing happens.
*
* @param threadName
* listener thread name
*
*/
public synchronized void startListener( final String threadName ) {
if ( messageListener == null ) {
messageListener = new QListener();
listenerThread = new Thread(messageListener, threadName);
listenerThread.start();
}
}
/**
* Indicates that a messageListener thread should stop. The messageListener thread is stopped after receiving next
* message from the remote q host. If a messageListener doesn't exists, nothing happens.
*/
public synchronized void stopListener() {
if ( messageListener != null ) {
messageListener.running = false;
messageListener = null;
try {
listenerThread.join(500);
} catch ( final InterruptedException e ) {
// ignore
}
}
}
/**
* Registers messageListener so that it will receive {@link QMessage} when data from kdb+ has been received.
*
* @param listener
* a {@link QMessagesListener} to be registered
*/
public void addMessagesListener( final QMessagesListener listener ) {
messagesListeners.add(listener);
}
/**
* Unregisters messageListener so that it will no longer receive {@link QMessage}.
*
* @param listener
* a {@link QMessagesListener} to be unregistered
*/
public void removeMessagesListener( final QMessagesListener listener ) {
messagesListeners.remove(listener);
}
/**
* Support for reporting incoming messages from kdb+ services.
*
* @param message
* a message to be distributed among messages listeners
*/
protected void fireMessageReceivedEvent( final QMessage message ) {
for ( final QMessagesListener listener : messagesListeners ) {
listener.messageReceived(message);
}
}
/**
* Support for reporting incoming messages from kdb+ services.
*
* @param message
* a message to be distributed among messages listeners
*/
protected void fireErrorReceivedEvent( final QErrorMessage message ) {
for ( final QMessagesListener listener : messagesListeners ) {
listener.errorReceived(message);
}
}
class QListener implements Runnable {
boolean running = true;
public void run() {
while ( running && isConnected() ) {
try {
final QMessage message = reader.read(false);
fireMessageReceivedEvent(message);
} catch ( final QException e ) {
fireErrorReceivedEvent(new QErrorMessage(e));
} catch ( final Exception e ) {
fireErrorReceivedEvent(new QErrorMessage(e));
running = false;
break;
}
}
}
}
}