
org.epics.pvmanager.MultiplexedChannelHandler Maven / Gradle / Ivy
/**
* Copyright (C) 2010-14 pvmanager developers. See COPYRIGHT.TXT
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
*/
package org.epics.pvmanager;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Implements a {@link ChannelHandler} on top of a single subscription and
* multiplexes all reads on top of it.
*
* This abstract handler takes care of forwarding the connection and message
* events of a single connection to multiple readers and writers. One needs
* to:
*
* - implement the {@link #connect() } and {@link #disconnect() } function
* to add the protocol specific connection and disconnection logic; the resources
* shared across multiple channels should be left in the datasource
* - every time the connection state changes, call {@link #processConnection(java.lang.Object) },
* which will trigger the proper connection notification mechanism;
* the type chosen as connection payload should be one that stores all the
* information about the channel of communications
* - every time an event is sent, call {@link #processMessage(java.lang.Object) }, which
* will trigger the proper value notification mechanism
* - implement {@link #isConnected(java.lang.Object) } and {@link #isWriteConnected(java.lang.Object) }
* with the logic to extract the connection information from the connection payload
* - use {@link #reportExceptionToAllReadersAndWriters(java.lang.Exception) }
* to report errors
* - implement a set of {@link DataSourceTypeAdapter} that can convert
* the payload to types for pvmanager consumption; the connection payload and
* message payload never leave this handler, only value types created by the
* type adapters
*
*
* @param type of the payload for the connection
* @param type of the payload for each message
* @author carcassi
*/
public abstract class MultiplexedChannelHandler extends ChannelHandler {
private static final Logger log = Logger.getLogger(MultiplexedChannelHandler.class.getName());
private int readUsageCounter = 0;
private int writeUsageCounter = 0;
private boolean connected = false;
private boolean writeConnected = false;
private MessagePayload lastMessage;
private ConnectionPayload connectionPayload;
private Map monitors = new ConcurrentHashMap<>();
private Map, ChannelHandlerWriteSubscription> writeSubscriptions = new ConcurrentHashMap<>();
private boolean processMessageOnDisconnect = true;
private boolean processMessageOnReconnect = true;
private class MonitorHandler {
private final ChannelHandlerReadSubscription subscription;
private DataSourceTypeAdapter typeAdapter;
public MonitorHandler(ChannelHandlerReadSubscription subscription) {
this.subscription = subscription;
}
public final void processConnection(boolean connection) {
subscription.getConnectionWriteFunction().writeValue(connection);
}
public final void processValue(MessagePayload payload) {
if (typeAdapter == null)
return;
// Lock the collector and prepare the new value.
try {
typeAdapter.updateCache(subscription.getValueCache(), getConnectionPayload(), payload);
} catch (RuntimeException e) {
subscription.getExceptionWriteFunction().writeValue(e);
}
}
public final void findTypeAdapter() {
if (getConnectionPayload() == null) {
typeAdapter = null;
} else {
try {
typeAdapter = MultiplexedChannelHandler.this.findTypeAdapter(subscription.getValueCache(), getConnectionPayload());
} catch(RuntimeException ex) {
subscription.getExceptionWriteFunction().writeValue(ex);
}
}
}
}
/**
* Notifies all readers and writers of an error condition.
*
* @param ex the exception to notify
*/
protected synchronized final void reportExceptionToAllReadersAndWriters(Exception ex) {
for (MonitorHandler monitor : monitors.values()) {
monitor.subscription.getExceptionWriteFunction().writeValue(ex);
}
for (ChannelHandlerWriteSubscription subscription : writeSubscriptions.values()) {
subscription.getExceptionWriteFunction().writeValue(ex);
}
}
/**
* Notifies all writers of an error condition.
*
* @param ex the exception to notify
*/
protected synchronized final void reportExceptionToAllWriters(Exception ex) {
for (ChannelHandlerWriteSubscription subscription : writeSubscriptions.values()) {
subscription.getExceptionWriteFunction().writeValue(ex);
}
}
private void reportConnectionStatus(boolean connected) {
for (MonitorHandler monitor : monitors.values()) {
monitor.processConnection(connected);
}
}
private void reportWriteConnectionStatus(boolean writeConnected) {
for (ChannelHandlerWriteSubscription subscription : writeSubscriptions.values()) {
subscription.getConnectionWriteFunction().writeValue(writeConnected);
}
}
/**
* The last processes connection payload.
*
* @return the connection payload or null
*/
protected synchronized final ConnectionPayload getConnectionPayload() {
return connectionPayload;
}
/**
* The last processed message payload.
*
* @return the message payload or null
*/
protected synchronized final MessagePayload getLastMessagePayload() {
return lastMessage;
}
/**
* Process the next connection payload. This should be called whenever
* the connection state has changed.
*
* @param connectionPayload
*/
protected synchronized final void processConnection(ConnectionPayload connectionPayload) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "processConnection for channel {0} connectionPayload {1}", new Object[] {getChannelName(), connectionPayload});
}
this.connectionPayload = connectionPayload;
setConnected(isConnected(connectionPayload));
setWriteConnected(isWriteConnected(connectionPayload));
for (MonitorHandler monitor : monitors.values()) {
monitor.findTypeAdapter();
}
if (isConnected() && lastMessage != null && processMessageOnReconnect) {
processMessage(lastMessage);
}
if (!isConnected() && lastMessage != null && processMessageOnDisconnect) {
processMessage(lastMessage);
}
}
private static DataSourceTypeAdapter, ?> defaultTypeAdapter = new DataSourceTypeAdapter
© 2015 - 2025 Weber Informatics LLC | Privacy Policy