
com.cosylab.epics.caj.CAJChannel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jca Show documentation
Show all versions of jca Show documentation
JCA is an EPICS Channel Access library for Java. For more information concerning EPICS or Channel Access please refer to the <a href="http://www.aps.anl.gov/epics">EPICS Web pages</a> or read the <a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/CAref.html">Channel Access manual (3.14)</a>.
<p>This module also includes CAJ, A 100% pure Java implementation of the EPICS Channel Access library.</p>
/*
* Copyright (c) 2004 by Cosylab
*
* The full license specifying the redistribution, modification, usage and other
* rights and obligations is included with the distribution of this project in
* the file "LICENSE-CAJ". If the license is not included visit Cosylab web site,
* .
*
* THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
* IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
* _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
* OR REDISTRIBUTION OF THIS SOFTWARE.
*/
package com.cosylab.epics.caj;
import gov.aps.jca.CAException;
import gov.aps.jca.CAStatus;
import gov.aps.jca.Channel;
import gov.aps.jca.Context;
import gov.aps.jca.Monitor;
import gov.aps.jca.dbr.DBR;
import gov.aps.jca.dbr.DBRFactory;
import gov.aps.jca.dbr.DBRType;
import gov.aps.jca.dbr.Severity;
import gov.aps.jca.event.AccessRightsEvent;
import gov.aps.jca.event.AccessRightsListener;
import gov.aps.jca.event.ConnectionEvent;
import gov.aps.jca.event.ConnectionListener;
import gov.aps.jca.event.GetListener;
import gov.aps.jca.event.MonitorListener;
import gov.aps.jca.event.PutListener;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.cosylab.epics.caj.impl.CAConstants;
import com.cosylab.epics.caj.impl.CATransport;
import com.cosylab.epics.caj.impl.ResponseRequest;
import com.cosylab.epics.caj.impl.Transport;
import com.cosylab.epics.caj.impl.TransportClient;
import com.cosylab.epics.caj.impl.requests.ClearChannelRequest;
import com.cosylab.epics.caj.impl.requests.CreateChannelRequest;
import com.cosylab.epics.caj.impl.requests.EventAddRequest;
import com.cosylab.epics.caj.impl.requests.ReadNotifyRequest;
import com.cosylab.epics.caj.impl.requests.SearchRequest;
import com.cosylab.epics.caj.impl.requests.WriteNotifyRequest;
import com.cosylab.epics.caj.impl.requests.WriteRequest;
/**
* Implementation of CAJ JCA Channel
.
* @author Matej Sekoranja
* @version $id$
*/
public class CAJChannel extends Channel implements TransportClient {
// Get Logger
private static final Logger logger = Logger.getLogger(CAJChannel.class.getName());
/**
* Client channel ID.
*/
protected int channelID;
/**
* Context.
*/
protected CAJContext context;
/**
* Channel name.
*/
protected String name;
/**
* Channel access rights.
*/
protected int accessRights = 0;
/**
* Process priority.
*/
protected short priority;
/**
* Last reported connection status.
*/
protected boolean lastReportedConnectionState = false;
/**
* Connection status.
*/
protected ConnectionState connectionState = ConnectionState.NEVER_CONNECTED;
/**
* Channel data type.
*/
protected DBRType type = DBRType.UNKNOWN;
/**
* Data type element count.
*/
protected int elementCount = 0;
/**
* List of connection listeners.
*/
// TODO consider using weak references
// TODO use them
protected ArrayList connectionListeners = new ArrayList();
/**
* List of access rights listeners.
*/
// TODO consider using weak references
// TODO use them
protected ArrayList accessRightsListeners = new ArrayList();
/**
* Sync. connection (i.e. w/ callback) flag.
*/
protected boolean syncConnection = false;
/**
* IO sequence number.
*/
protected int sequenceNumberIO;
/**
* Map of channels (keys are CIDs).
* Lazy initialization used, since not all channel will have monitors.
*/
// TODO consider using WeakHashMap (and call Channel.destroy() in finalize() method).
protected Map monitors = null;
/**
* List of all channels pending requests.
*/
protected Map responseRequests = new HashMap();
/**
* Number of channel search tries.
*/
protected int searchTries = 0;
/**
* Allow reconnection flag.
*/
protected boolean allowCreation = true;
/**
* Reference counting.
*/
protected int references = 1;
/* ****************** */
/* CA protocol fields */
/* ****************** */
/**
* Server transport.
*/
protected CATransport transport = null;
/**
* Server channel ID.
*/
protected int serverChannelID = 0xFFFFFFFF;
/* ****************** */
/**
* Constructor.
* @param context CAJContext
* @param channelID Client channel ID
* @param name Channel name
* @param listener ConnectionListener
* @param priority Process priority
* @throws CAException JCA Exception
*/
protected CAJChannel(CAJContext context, int channelID, String name,
ConnectionListener listener, short priority) throws CAException
{
this.context = context;
this.channelID = channelID;
this.name = name;
this.priority = priority;
// register before issuing search request
context.registerChannel(this);
// add listener
if (listener != null)
addConnectionListener(listener);
else
{
// add pending request, if sync (i.e. w/o callback)
syncConnection = true;
sequenceNumberIO = context.incrementPendingRequests();
}
// this has to be submitted immediately
initiateSearch();
}
/**
* Create a channel, i.e. submit create channel request to the server.
* This method is called after search is complete.
* sid
, typeCode
, elementCount
might not be
* valid, this depends on protocol revision.
* @param transport CA transport implementation.
* @param sid sid
* @param typeCode type code
* @param elementCount element count
* @return true
if real create channel request needs to be sent to the server.
*/
public synchronized boolean createChannel(CATransport transport, int sid, short typeCode, int elementCount)
{
// do not allow duplicate creation to the same transport
if (!allowCreation)
return false;
allowCreation = false;
// TODO is this really necesarry... 1. priority is to take channel from the existing one,
// so transport must be the same... or until disconnected, i.e. transport is null
// remote address check was already done in context... here reference check is done
// check existing transport
if (this.transport != null && this.transport != transport)
{
disconnectPendingIO(false);
this.transport.release(this);
}
else if (this.transport == transport)
{
// request to sent create request to same transport, ignore
// this happens when server is slower (processing search requests) than client generating it
return false;
}
this.transport = transport;
// revision < v4.4 supply this info already now
if (transport.getMinorRevision() < 4)
{
this.serverChannelID = sid;
this.type = DBRType.forValue(typeCode);
if (this.type == null)
this.type = DBRType.UNKNOWN;
this.elementCount = elementCount;
}
// do not submit CreateChannelRequest here, connection loss while submitting and lock
// on this channel instance may cause deadlock
return true;
}
// this is completely not sycned
public void issueCreateChannelRequest()
{
try
{
// submit (immediately)
new CreateChannelRequest(transport, name, channelID).submit();
}
catch (IOException ioex)
{
createChannelFailed();
}
}
/**
* Cancelation status.
*/
protected volatile boolean canceled = false;
/**
* Called when connecton completed (successfully or not).
* @see com.cosylab.epics.caj.impl.ResponseRequest#cancel()
*/
public synchronized void cancel() {
if (canceled)
return;
canceled = true;
if (syncConnection)
{
syncConnection = false;
context.decrementPendingRequests(sequenceNumberIO);
}
}
/**
* @see com.cosylab.epics.caj.impl.ResponseRequest#timeout()
*/
public void timeout() {
createChannelFailed();
}
/**
* Create channel failed.
*/
public synchronized void createChannelFailed()
{
cancel();
// ... and search again
initiateSearch();
}
/**
* Called when channel created succeeded on the server.
* sid
might not be valid, this depends on protocol revision.
* @param sid sid
* @param typeCode type code
* @param elementCount element count
* @throws IllegalStateException IllegalStateException
*/
public synchronized void connectionCompleted(int sid, short typeCode, int elementCount)
throws IllegalStateException
{
try
{
// do this silently
//checkNotClosedState();
if (connectionState == ConnectionState.CLOSED)
return;
// TODO revise this reconnection...
//else if (connectionState == ConnectionState.CONNECTED)
// throw new IllegalStateException("Channel already connected.");
// revision < v4.1 do not have access rights, grant all
if (transport.getMinorRevision() < 1)
setAccessRights(CAConstants.CA_PROTO_ACCESS_RIGHT_READ |
CAConstants.CA_PROTO_ACCESS_RIGHT_WRITE);
// revision < v4.4 supply this info already now
if (transport.getMinorRevision() >= 4)
serverChannelID = sid;
this.type = DBRType.forValue(typeCode);
if (this.type == null)
this.type = DBRType.UNKNOWN;
this.elementCount = elementCount;
// user might create monitors in listeners, so this has to be done before this can happen
// however, it would not be nice if events would come before connection event is fired
// but this cannot happen since transport (TCP) is serving in this thread
resubscribeSubscriptions();
setConnectionState(ConnectionState.CONNECTED);
}
finally
{
// end connection request
cancel();
}
}
/**
* @see gov.aps.jca.Channel#destroy()
*/
public void destroy() throws CAException, IllegalStateException {
destroy(false);
}
/**
* @see gov.aps.jca.Channel#destroy()
* @param force force destruction regardless of reference count
* @throws CAException JCA Exception
* @throws IllegalStateException illegal state exception
*/
public void destroy(boolean force) throws CAException, IllegalStateException {
if (connectionState == ConnectionState.CLOSED)
throw new IllegalStateException("Channel already destroyed.");
// do destruction via context
context.destroyChannel(this, force);
}
/**
* Increment reference.
*/
public synchronized void acquire() {
references++;
}
/**
* Actual destory method, to be called CAJContext
.
* @param force force destruction regardless of reference count
* @throws CAException JCA Exception
* @throws IllegalStateException method has been invoked at an illegal or inappropriate time
* @throws IOException I/O exception
*/
public synchronized void destroyChannel(boolean force) throws CAException, IllegalStateException, IOException {
if (connectionState == ConnectionState.CLOSED)
throw new IllegalStateException("Channel already destroyed.");
references--;
if (references > 0 && !force)
return;
// stop searching...
context.getChannelSearchManager().unregisterChannel(this);
cancel();
destroyAllMonitors();
disconnectPendingIO(true);
if (connectionState == ConnectionState.CONNECTED)
{
if (transport != null)
{
try
{
// NOTE: this does not submit message immediately, waits for flush to be triggered somehow.. hmmm.
// possible deadlock if sent immediately (since lock on this channel instance is held)
new ClearChannelRequest(transport, channelID, serverChannelID).submit();
}
catch (IOException ioex)
{
// TODO remove?
logger.log(Level.SEVERE, "", ioex);
}
}
// TODO transport is closed immedately,
// so it might happen that ClearChannelRequest is not sent/response received
disconnect(false);
}
else if (transport != null)
{
// unresponsive state, do not forget to release transport
transport.release(this);
transport = null;
}
setConnectionState(ConnectionState.CLOSED);
// unregister
context.unregisterChannel(this);
synchronized (accessRightsListeners)
{
accessRightsListeners.clear();
}
/*
// this makes problem to the queued dispatchers...
synchronized (connectionListeners)
{
connectionListeners.clear();
}
*/
}
/**
* Disconnected notification.
* @param initiateSearch flag to indicate if searching (connect) procedure should be initiated
*/
public synchronized void disconnect(boolean initiateSearch) {
//System.err.println("CHANNEL disconnect");
if (connectionState != ConnectionState.CONNECTED && transport == null)
return;
setConnectionState(ConnectionState.DISCONNECTED);
disconnectPendingIO(false);
// release transport
if (transport != null)
{
transport.release(this);
transport = null;
}
if (initiateSearch)
initiateSearch();
}
/**
* Initiate search (connect) procedure.
*/
public synchronized void initiateSearch()
{
allowCreation = true;
context.getChannelSearchManager().registerChannel(this);
}
/**
* Send search message.
* @param transport transport to be used when sending
* @param buffer buffer to be filled
* @return success status.
*/
public synchronized boolean generateSearchRequestMessage(Transport transport, ByteBuffer buffer)
{
ByteBuffer result = SearchRequest.generateSearchRequestMessage(transport, buffer, name, channelID);
if (result == null)
return false;
if (searchTries < Integer.MAX_VALUE)
searchTries++;
return true;
}
/**
* @see com.cosylab.epics.caj.impl.TransportClient#transportClosed()
*/
public void transportClosed() {
//System.err.println("CHANNEL transportClosed");
disconnect(true);
}
/**
* @see com.cosylab.epics.caj.impl.TransportClient#transportChanged()
*/
public synchronized void transportChanged() {
//System.err.println("CHANNEL transportChanged");
if (connectionState == ConnectionState.DISCONNECTED)
initiateSearch();
}
/**
* @see com.cosylab.epics.caj.impl.TransportClient#transportResponsive(com.cosylab.epics.caj.impl.Transport)
*/
public synchronized void transportResponsive(Transport transport) {
//System.err.println("CHANNEL transportResponsive");
if (connectionState == ConnectionState.DISCONNECTED)
{
/*
// responsive again, stop searching
context.getChannelSearchManager().unregisterChannel(this);
*/
updateSubscriptions();
// reconnect using existing IDs, data
connectionCompleted(serverChannelID, (short)type.getValue(), elementCount);
}
}
/**
* @see com.cosylab.epics.caj.impl.TransportClient#transportUnresponsive()
*/
public synchronized void transportUnresponsive() {
//System.err.println("CHANNEL transportUnresponsive");
if (connectionState == ConnectionState.CONNECTED)
{
// NOTE: 2 types of disconnected state - distinguish them
setConnectionState(ConnectionState.DISCONNECTED);
// ... CA notifies also w/ no access rights callback, although access right are not changed
//transportClosed();
}
}
/**
* @see gov.aps.jca.Channel#getConnectionListeners()
*/
public ConnectionListener[] getConnectionListeners()
throws IllegalStateException {
synchronized (connectionListeners)
{
ConnectionListener[] listeners = new ConnectionListener[connectionListeners.size()];
return (ConnectionListener[])connectionListeners.toArray(listeners);
}
}
/**
* @see gov.aps.jca.Channel#addConnectionListener(gov.aps.jca.event.ConnectionListener)
*/
public void addConnectionListener(ConnectionListener l)
throws CAException, IllegalStateException {
checkNotClosedState();
if (l == null)
throw new IllegalArgumentException("l == null");
synchronized (connectionListeners)
{
if (!connectionListeners.contains(l))
connectionListeners.add(l);
}
}
/**
*
* @see gov.aps.jca.Channel#addConnectionListener(gov.aps.jca.event.ConnectionListener)
*
* @param l ConnectionListener
* @throws CAException JCA Exception
* @throws IllegalStateException if ConnectionListener is null or the channel is in no state to perform this operation (ie destroyed, etc...)
*/
public synchronized void addConnectionListenerAndFireIfConnected(ConnectionListener l)
throws CAException, IllegalStateException {
checkNotClosedState();
if (l == null)
throw new IllegalArgumentException("l == null");
if (connectionState == ConnectionState.CONNECTED)
context.getEventDispatcher().dispatch(new ConnectionEvent(this, true), l);
addConnectionListener(l);
}
/**
* @see gov.aps.jca.Channel#removeConnectionListener(gov.aps.jca.event.ConnectionListener)
*/
public void removeConnectionListener(ConnectionListener l)
throws CAException, IllegalStateException {
checkNotClosedState();
if (l == null)
throw new IllegalArgumentException("l == null");
synchronized (connectionListeners)
{
connectionListeners.remove(l);
}
}
/**
* Set connection state and if changed, notifies listeners.
* @param connectionState state to set.
*/
private synchronized void setConnectionState(ConnectionState connectionState)
{
if (this.connectionState != connectionState)
{
this.connectionState = connectionState;
boolean connectionStatusToReport = (connectionState == ConnectionState.CONNECTED);
if (connectionStatusToReport != lastReportedConnectionState)
{
lastReportedConnectionState = connectionStatusToReport;
synchronized (connectionListeners)
{
context.getEventDispatcher().dispatch(
new ConnectionEvent(this, connectionStatusToReport),
connectionListeners
);
}
}
}
}
/**
* @see gov.aps.jca.Channel#getAccessRightsListeners()
*/
public AccessRightsListener[] getAccessRightsListeners()
throws IllegalStateException {
synchronized (accessRightsListeners)
{
AccessRightsListener[] listeners = new AccessRightsListener[accessRightsListeners.size()];
return (AccessRightsListener[])accessRightsListeners.toArray(listeners);
}
}
/**
* @see gov.aps.jca.Channel#addAccessRightsListener(gov.aps.jca.event.AccessRightsListener)
*/
public void addAccessRightsListener(AccessRightsListener l)
throws CAException, IllegalStateException {
checkNotClosedState();
if (l == null)
throw new IllegalArgumentException("l == null");
synchronized (accessRightsListeners)
{
if (!accessRightsListeners.contains(l))
accessRightsListeners.add(l);
}
}
/**
* @see gov.aps.jca.Channel#removeAccessRightsListener(gov.aps.jca.event.AccessRightsListener)
*/
public void removeAccessRightsListener(AccessRightsListener l)
throws CAException, IllegalStateException {
checkNotClosedState();
if (l == null)
throw new IllegalArgumentException("l == null");
synchronized (accessRightsListeners)
{
accessRightsListeners.remove(l);
}
}
/**
* Set access rights.
* @param accessRights access rights to set
*/
public synchronized void setAccessRights(int accessRights)
{
if (this.accessRights != accessRights)
{
this.accessRights = accessRights;
// do not use access rights accessors since they check state
boolean readAccess = (accessRights & CAConstants.CA_PROTO_ACCESS_RIGHT_READ) != 0;
boolean writeAccess = (accessRights & CAConstants.CA_PROTO_ACCESS_RIGHT_WRITE) != 0;
synchronized (accessRightsListeners)
{
context.getEventDispatcher().dispatch(
new AccessRightsEvent(this, readAccess, writeAccess),
accessRightsListeners
);
}
}
}
/**
* @see gov.aps.jca.Channel#getName()
*/
public String getName() throws IllegalStateException {
return name;
}
/**
* @see gov.aps.jca.Channel#getFieldType()
*/
public synchronized DBRType getFieldType() throws IllegalStateException {
checkState();
return type;
}
/**
* @see gov.aps.jca.Channel#getElementCount()
*/
public synchronized int getElementCount() throws IllegalStateException {
checkState();
return elementCount;
}
/**
* @see gov.aps.jca.Channel#getConnectionState()
*/
public synchronized ConnectionState getConnectionState() throws IllegalStateException {
return connectionState;
}
/**
* NOTE: synchronization guarantees that transport
is non-null
and state == CONNECTED
.
* @see gov.aps.jca.Channel#getHostName()
*/
public synchronized String getHostName() throws IllegalStateException {
connectionRequiredCheck();
return transport.getRemoteAddress().getHostName();
}
/**
* @see gov.aps.jca.Channel#getReadAccess()
*/
public synchronized boolean getReadAccess() throws IllegalStateException {
checkState();
return (accessRights & CAConstants.CA_PROTO_ACCESS_RIGHT_READ) != 0;
}
/**
* @see gov.aps.jca.Channel#getWriteAccess()
*/
public synchronized boolean getWriteAccess() throws IllegalStateException {
checkState();
return (accessRights & CAConstants.CA_PROTO_ACCESS_RIGHT_WRITE) != 0;
}
/**
* @see gov.aps.jca.Channel#put(byte[])
*/
public void put(byte[] value) throws CAException, IllegalStateException {
put(DBRType.BYTE, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(byte[], gov.aps.jca.event.PutListener)
*/
public void put(byte[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.BYTE, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#put(short[])
*/
public void put(short[] value) throws CAException, IllegalStateException {
put(DBRType.SHORT, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(short[], gov.aps.jca.event.PutListener)
*/
public void put(short[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.SHORT, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#put(int[])
*/
public void put(int[] value) throws CAException, IllegalStateException {
put(DBRType.INT, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(int[], gov.aps.jca.event.PutListener)
*/
public void put(int[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.INT, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#put(float[])
*/
public void put(float[] value) throws CAException, IllegalStateException {
put(DBRType.FLOAT, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(float[], gov.aps.jca.event.PutListener)
*/
public void put(float[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.FLOAT, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#put(double[])
*/
public void put(double[] value) throws CAException, IllegalStateException {
put(DBRType.DOUBLE, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(double[], gov.aps.jca.event.PutListener)
*/
public void put(double[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.DOUBLE, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#put(java.lang.String[])
*/
public void put(String[] value) throws CAException, IllegalStateException {
put(DBRType.STRING, value.length, value);
}
/**
* @see gov.aps.jca.Channel#put(java.lang.String[], gov.aps.jca.event.PutListener)
*/
public void put(String[] value, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.STRING, value.length, value, l);
}
/**
* @see gov.aps.jca.Channel#putACKS(gov.aps.jca.dbr.Severity, gov.aps.jca.event.PutListener)
*/
public void putACKS(Severity severity, PutListener l)
throws CAException, IllegalStateException {
put(DBRType.PUT_ACKS, 1, new short[] { (short)severity.getValue()}, l);
}
/**
* @see gov.aps.jca.Channel#putACKS(gov.aps.jca.dbr.Severity)
*/
public void putACKS(Severity severity) throws CAException, IllegalStateException {
put(DBRType.PUT_ACKS, 1, new short[] { (short)severity.getValue()} );
}
/**
* @see gov.aps.jca.Channel#putACKT(boolean, gov.aps.jca.event.PutListener)
*/
public void putACKT(boolean value, PutListener l) throws CAException, IllegalStateException {
put(DBRType.PUT_ACKT, 1, new short[] { value ? (short)1 : (short)0 }, l);
}
/**
* @see gov.aps.jca.Channel#putACKT(boolean)
*/
public void putACKT(boolean value) throws CAException, IllegalStateException {
put(DBRType.PUT_ACKT, 1, new short[] { value ? (short)1 : (short)0 });
}
/**
* @see gov.aps.jca.Channel#get(gov.aps.jca.dbr.DBRType, int)
*/
public DBR get(DBRType type, int count)
throws CAException, IllegalStateException {
connectionRequiredCheck();
// sync get requires predefined count; variable length array not possible
if (count <= 0)
throw new IllegalArgumentException("count <= 0");
if (!getReadAccess())
throw new CAException("No read access rights granted.");
Transport t = getTransport();
if (t != null)
{
try
{
DBR retVal = DBRFactory.create(type, count);
new ReadNotifyRequest(this, null, retVal, t, getServerChannelID(), type.getValue(), count).submit();
return retVal;
}
catch (IOException ioex)
{
throw new CAException("Failed to retrieve value.", ioex);
}
}
else
throw new IllegalStateException("No channel transport available, channel disconnected.");
}
public DBR get(DBR preallocatedDBR, DBRType type, int count)
throws CAException, IllegalStateException {
connectionRequiredCheck();
if (!getReadAccess())
throw new CAException("No read access rights granted.");
Transport t = getTransport();
if (t != null)
{
try
{
DBR retVal = preallocatedDBR; //DBRFactory.create(type, count);
new ReadNotifyRequest(this, null, retVal, t, getServerChannelID(), type.getValue(), count).submit();
return retVal;
}
catch (IOException ioex)
{
throw new CAException("Failed to retrieve value.", ioex);
}
}
else
throw new IllegalStateException("No channel transport available, channel disconnected.");
}
/**
* @see gov.aps.jca.Channel#get(gov.aps.jca.dbr.DBRType, int, gov.aps.jca.event.GetListener)
*/
public void get(DBRType type, int count, GetListener l)
throws CAException, IllegalStateException {
connectionRequiredCheck();
if (!getReadAccess())
throw new CAException("No read access rights granted.");
Transport t = getTransport();
if (t != null)
{
try
{
if (count == 0 && t.getMinorRevision() < 13)
count = getElementCount();
new ReadNotifyRequest(this, l, null, t, getServerChannelID(), type.getValue(), count).submit();
}
catch (IOException ioex)
{
throw new CAException("Failed to retrieve value.", ioex);
}
}
else
throw new IllegalStateException("No channel transport available, channel disconnected.");
}
/**
* Put value.
* @param type DBRType
* @param count count
* @param value value to be put
* @throws CAException JCA Exception
* @throws IllegalStateException No channel transport available, channel disconnected.
*/
public void put(DBRType type, int count, Object value)
throws CAException, IllegalStateException {
connectionRequiredCheck();
if (!getWriteAccess())
throw new CAException("No write access rights granted.");
Transport t = getTransport();
if (t != null)
{
try
{
new WriteRequest(this, t, getServerChannelID(), channelID, type.getValue(), count, value).submit();
}
catch (IOException ioex)
{
throw new CAException("Failed to set value.", ioex);
}
}
else
throw new IllegalStateException("No channel transport available, channel disconnected.");
}
/**
* Put value.
* @param type DBRType
* @param count data count
* @param value value to put
* @param l PutListener
* @throws CAException JCA Exception
* @throws IllegalStateException No channel transport available, channel disconnected.
*/
public void put(DBRType type, int count, Object value, PutListener l)
throws CAException, IllegalStateException {
connectionRequiredCheck();
if (!getWriteAccess())
throw new CAException("No write access rights granted.");
Transport t = getTransport();
if (t != null)
{
try
{
if (l == null)
new WriteRequest(this, t, getServerChannelID(), channelID, type.getValue(), count, value).submit();
else
new WriteNotifyRequest(this, l, t, getServerChannelID(), type.getValue(), count, value).submit();
}
catch (IOException ioex)
{
throw new CAException("Failed to set value.", ioex);
}
}
else
throw new IllegalStateException("No channel transport available, channel disconnected.");
}
/**
* @see gov.aps.jca.Channel#addMonitor(gov.aps.jca.dbr.DBRType, int, int, gov.aps.jca.event.MonitorListener)
*/
public synchronized Monitor addMonitor(
DBRType type,
int count,
int mask,
MonitorListener l)
throws CAException, IllegalStateException {
if (count == 0)
{
Transport t = getTransport();
if (t != null && t.getMinorRevision() < 13)
count = getElementCount();
}
checkNotClosedState();
checkMonitorSize(type, count, context.getMaxArrayBytes());
return new CAJMonitor(context, type, count, this, l, mask);
}
/**
* Get client channel ID.
* @return client channel ID.
*/
public int getChannelID() {
return channelID;
}
/**
* @see gov.aps.jca.Channel#getContext()
*/
public Context getContext() throws IllegalStateException {
return context;
}
/**
* Checks if channel is in connected state,
* if not throws IllegalStateException
if not.
*/
private void connectionRequiredCheck()
{
if (connectionState != ConnectionState.CONNECTED)
throw new IllegalStateException("Channel not connected.");
}
/**
* Checks if channel is in connected or disconnected state,
* if not throws IllegalStateException
if not.
*/
private void checkState()
{
// connectionState is always non-null
if (connectionState != ConnectionState.CONNECTED && connectionState != ConnectionState.DISCONNECTED)
throw new IllegalStateException("Channel not in connected or disconnected state, state = '" + connectionState.getName() + "'.");
}
/**
* Checks if channel is not it closed state.
* if not throws IllegalStateException
if not.
*/
private void checkNotClosedState()
{
if (connectionState == ConnectionState.CLOSED)
throw new IllegalStateException("Channel closed.");
}
/**
* @see gov.aps.jca.Channel#printInfo(java.io.PrintStream)
*/
public synchronized void printInfo(PrintStream out) throws IllegalStateException {
if (connectionState != ConnectionState.CONNECTED)
{
out.println("CHANNEL : " + name);
out.println("TYPE : " + type);
out.println("COUNT : " + elementCount);
out.println("STATE : " + connectionState);
}
else
super.printInfo(out);
}
/**
* Get transport used by this channel.
* @return transport used by this channel.
*/
public synchronized CATransport getTransport() {
return transport;
}
/**
* Get SID.
* @return SID.
*/
public synchronized int getServerChannelID() {
return serverChannelID;
}
/**
* Register monitor.
* @param monitor
*/
void registerMonitor(CAJMonitor monitor)
{
// lazy initialization (double-check sync. pattern)
if (monitors == null)
{
synchronized (this)
{
if (monitors == null)
monitors = new HashMap();
}
}
synchronized (monitors)
{
monitors.put(new Integer(monitor.getSID()), monitor);
}
}
/**
* Unregister monitor.
* @param monitor
*/
void unregisterMonitor(CAJMonitor monitor)
{
if (monitors == null)
return;
synchronized (monitors)
{
monitors.remove(new Integer(monitor.getSID()));
}
}
/**
* Destroy all monitors.
*/
private void destroyAllMonitors() {
if (monitors == null)
return;
CAJMonitor[] monitorsArray;
synchronized (monitors)
{
monitorsArray = new CAJMonitor[monitors.size()];
monitors.values().toArray(monitorsArray);
monitors.clear();
}
for (int i = 0; i < monitorsArray.length; i++)
{
try
{
monitorsArray[i].clear();
}
catch (Throwable th)
{
logger.log(Level.SEVERE, "", th);
}
}
}
/**
* Register a response request.
* @param responseRequest response request to register.
*/
public void registerResponseRequest(ResponseRequest responseRequest)
{
synchronized (responseRequests)
{
responseRequests.put(responseRequest, null);
}
}
/*
* Unregister a response request.
* @param responseRequest response request to unregister.
*/
public void unregisterResponseRequest(ResponseRequest responseRequest)
{
synchronized (responseRequests)
{
responseRequests.remove(responseRequest);
}
}
/**
* Disconnects (destroys) all channels pending IO.
* @param destroy true
if channel is being destroyed.
*/
private void disconnectPendingIO(boolean destroy)
{
CAStatus status;
if (destroy)
status = CAStatus.CHANDESTROY;
else
status = CAStatus.DISCONN;
ResponseRequest[] rrs;
synchronized (responseRequests)
{
rrs = new ResponseRequest[responseRequests.size()];
responseRequests.keySet().toArray(rrs);
}
for (int i = 0; i < rrs.length; i++)
{
try
{
rrs[i].exception(status.getStatusCode(), null);
}
catch (Throwable th)
{
// TODO remove
logger.log(Level.SEVERE, "", th);
}
}
}
/**
* Resubscribe subscriptions.
*/
private void resubscribeSubscriptions()
{
synchronized (responseRequests)
{
// sync get
Transport transport = getTransport();
ResponseRequest[] rrs = new ResponseRequest[responseRequests.size()];
responseRequests.keySet().toArray(rrs);
for (int i = 0; i < rrs.length; i++)
{
try
{
if (rrs[i] instanceof EventAddRequest)
{
EventAddRequest ear = (EventAddRequest)rrs[i];
ear.resubscribeSubscription(transport);
}
}
catch (Throwable th)
{
// TODO remove
logger.log(Level.SEVERE, "", th);
}
}
}
}
/**
* Update subscribtions.
*/
private void updateSubscriptions()
{
synchronized (responseRequests)
{
ResponseRequest[] rrs = new ResponseRequest[responseRequests.size()];
responseRequests.keySet().toArray(rrs);
for (int i = 0; i < rrs.length; i++)
{
try
{
if (rrs[i] instanceof EventAddRequest)
{
EventAddRequest ear = (EventAddRequest)rrs[i];
ear.updateSubscription();
}
}
catch (Throwable th)
{
// TODO remove
logger.log(Level.SEVERE, "", th);
}
}
}
}
/**
* Get number of search tried for this channel.
* @return number of search tried for this channel.
*/
public int getSearchTries() {
return searchTries;
}
/**
* Get process priority.
* @return process priority.
*/
public short getPriority() {
return priority;
}
protected final AtomicReference timerIdRef = new AtomicReference();
public void setTimerId(Object timerId)
{
timerIdRef.set(timerId);
}
public Object getTimerId()
{
return timerIdRef.get();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
return getClass().getSimpleName() + " = { name = " + name + ", connectionState = " + connectionState.getName() + " }";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy