Skip to content

Commit

Permalink
Merge pull request #80 from epics-base/cas_nameserver
Browse files Browse the repository at this point in the history
CAS hook to allow building a CA name server
  • Loading branch information
shroffk authored Jun 17, 2024
2 parents 0ab4e4e + 3ef6119 commit bca12fd
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 16 deletions.
15 changes: 14 additions & 1 deletion src/core/com/cosylab/epics/caj/cas/handlers/SearchResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ protected void internalHandleResponse(
}

if (completion == ProcessVariableExistanceCompletion.EXISTS_HERE ||
completion == ProcessVariableExistanceCompletion.DOES_NOT_EXIST_HERE)
completion == ProcessVariableExistanceCompletion.DOES_NOT_EXIST_HERE ||
completion.doesExistElsewhere())
{
searchResponse(responseFrom, dataType, dataCount, parameter1, completion);
}
Expand Down Expand Up @@ -123,6 +124,18 @@ private void searchResponse(InetSocketAddress responseFrom, short dataType, int
context.getLogger().log(Level.WARNING, "Failed to send back search response to: " + responseFrom, th);
}
}
else if (completion.doesExistElsewhere())
{
// send back
try
{
// TODO prepend version header (if context.getLastReceivedSequenceNumber() != 0)
SearchRequest searchRequest = new SearchRequest(context.getBroadcastTransport(), completion.getOtherAddress(), (short)dataCount, cid);
context.getBroadcastTransport().send(searchRequest, responseFrom);
} catch (Throwable th) {
context.getLogger().log(Level.WARNING, "Failed to send back search response to: " + responseFrom, th);
}
}
else if (completion == ProcessVariableExistanceCompletion.DOES_NOT_EXIST_HERE)
{
// check if not found reply is expected - allowed only when not broadcast
Expand Down
35 changes: 30 additions & 5 deletions src/core/com/cosylab/epics/caj/cas/requests/SearchRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
package com.cosylab.epics.caj.cas.requests;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.cosylab.epics.caj.cas.CAJServerContext;
import com.cosylab.epics.caj.impl.CAConstants;
Expand All @@ -31,10 +34,25 @@
*/
public class SearchRequest extends AbstractCARequest {

private static final Logger logger = Logger.getLogger(SearchRequest.class.getName());

/**
* @param transport Transport to use
* @param clientMinorVersion
* @param cid Channel ID
*/
public SearchRequest(Transport transport, short clientMinorVersion, int cid)
{
this(transport, null, clientMinorVersion, cid);
}

/**
* @param transport
* @param transport Transport to use
* @param other_address Optional other address to report, null to use this transport
* @param clientMinorVersion
* @param cid Channel ID
*/
public SearchRequest(Transport transport, short clientMinorVersion, int cid) {
public SearchRequest(Transport transport, InetSocketAddress other_address, short clientMinorVersion, int cid) {
super(transport);

// add minor version payload (aligned by 8)
Expand All @@ -43,15 +61,22 @@ public SearchRequest(Transport transport, short clientMinorVersion, int cid) {

// set server IP address
int serverIP = 0xFFFFFFFF;
int port = other_address == null
? transport.getContext().getServerPort()
: other_address.getPort();

if (clientMinorVersion >= 8)
{
InetAddress serverAddress = ((CAJServerContext)(transport.getContext())).getServerInetAddress();
InetAddress serverAddress = other_address == null
? ((CAJServerContext)(transport.getContext())).getServerInetAddress()
: other_address.getAddress();
if (serverAddress != null && !serverAddress.isAnyLocalAddress())
serverIP = InetAddressUtil.ipv4AddressToInt(serverAddress);
logger.log(Level.FINE, "Replying to search with " + serverAddress + ":" + port);
}

requestMessage = insertCAHeader(transport, requestMessage,
(short)6, (short)8, (short)transport.getContext().getServerPort(), 0,
(short)6, (short)8, (short)port, 0,
serverIP, cid);

requestMessage.putShort(CAConstants.CAS_MINOR_PROTOCOL_REVISION);
Expand Down
50 changes: 40 additions & 10 deletions src/core/gov/aps/jca/cas/ProcessVariableExistanceCompletion.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,70 @@

package gov.aps.jca.cas;

import gov.aps.jca.Enum;
import java.net.InetSocketAddress;

/**
* Process variable existance completion enum class.
* Process variable existance completion class.
* @author Matej Sekoranja (matej.sekoranja@cosylab.com)
* @version $Id: ProcessVariableExistanceCompletion.java,v 1.1 2006-03-06 17:16:04 msekoranja Exp $
*/
public final class ProcessVariableExistanceCompletion extends Enum
public final class ProcessVariableExistanceCompletion
{

private final String name;
private final InetSocketAddress addr;

/**
* Process variable exists.
*/
public static final ProcessVariableExistanceCompletion EXISTS_HERE = new ProcessVariableExistanceCompletion("EXISTS_HERE");
public static final ProcessVariableExistanceCompletion EXISTS_HERE = new ProcessVariableExistanceCompletion("EXISTS_HERE", null);

/**
* Process variable exists at a different address, not this CA server.
*/
public static ProcessVariableExistanceCompletion EXISTS_ELSEWHERE(final InetSocketAddress addr)
{
return new ProcessVariableExistanceCompletion("EXISTS_ELSEWHERE", addr);
}

/** @return Does the PV exist elsewhere? */
public boolean doesExistElsewhere()
{
return addr != null;
}

/** @return Other address where PV does exist or null */
public InetSocketAddress getOtherAddress()
{
return addr;
}

/**
* Process variable does not exist.
*/
public static final ProcessVariableExistanceCompletion DOES_NOT_EXIST_HERE = new ProcessVariableExistanceCompletion("DOES_NOT_EXIST_HERE");
public static final ProcessVariableExistanceCompletion DOES_NOT_EXIST_HERE = new ProcessVariableExistanceCompletion("DOES_NOT_EXIST_HERE", null);

/**
* Deffered result (asynchronous operation),
* <code>ProcessVariableExistanceCompletionCallback.processVariableExistanceTestCompleted()</code> callback method method should be called to return completion.
*/
public static final ProcessVariableExistanceCompletion ASYNC_COMPLETION = new ProcessVariableExistanceCompletion("ASYNC_COMPLETION");
public static final ProcessVariableExistanceCompletion ASYNC_COMPLETION = new ProcessVariableExistanceCompletion("ASYNC_COMPLETION", null);

/**
* Creates a new PV existance completion.
* Contructor is <code>protected</code> to deny creation of unsupported types.
* @param name name of the completion.
* @param addr Address if exists elsewhere, otherwise null
*/
protected ProcessVariableExistanceCompletion(String name) {
super(name);
protected ProcessVariableExistanceCompletion(final String name, final InetSocketAddress addr) {
this.name = name;
this.addr = addr;
}

@Override
public String toString()
{
if (doesExistElsewhere())
return name + "@" + addr;
return name;
}

}
93 changes: 93 additions & 0 deletions test/com/cosylab/epics/caj/cas/test/CANameServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.cosylab.epics.caj.cas.test;

import java.net.InetSocketAddress;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.HashMap;
import java.util.Map;
import gov.aps.jca.CAException;
import gov.aps.jca.CAStatus;
import gov.aps.jca.CAStatusException;
import gov.aps.jca.JCALibrary;
import gov.aps.jca.cas.ProcessVariable;
import gov.aps.jca.cas.ProcessVariableAttachCallback;
import gov.aps.jca.cas.ProcessVariableEventCallback;
import gov.aps.jca.cas.ProcessVariableExistanceCallback;
import gov.aps.jca.cas.ProcessVariableExistanceCompletion;
import gov.aps.jca.cas.Server;
import gov.aps.jca.cas.ServerContext;

/** Example for a Channel Access name server
*
* A name server is a CA server that listens to PV searches.
* It does not, however, host any PVs.
* Instead, it has a list of _other_ CA servers like IOCs
* which host the PVs, and the CA name server then provides
* the _other_ CA server's address.
*
* The main purpose is reduction of CA search traffic.
* Instead of having clients broadcast their requests,
* they can directly contact the CA name server.
*
* Example usage:
*
* In one terminal,
* export EPICS_CA_SERVER_PORT=9876
* then run an IOC database with a record named "ramp".
*
* In another terminal,
* caget ramp
* will not be able to connect.
*
* Now run this CANameServer on the same host,
* and try `caget ramp` again.
* It will reach the name server, which replies with 127.0.0.1, port 9876
* to the seach request and client can then reach the IOC.
*/
public class CANameServer
{
public static void main(String[] args) throws Exception
{ // Log as much as possible
final Logger logger = Logger.getLogger("");
logger.setLevel(Level.ALL);
for (Handler handler : logger.getHandlers())
handler.setLevel(Level.ALL);

// Example for a name server database using some hardcoded values.
final Map<String, InetSocketAddress> names = new HashMap<>();
names.put("ramp", new InetSocketAddress("127.0.0.1", 9876));
names.put("Fred", new InetSocketAddress("1.2.3.4", 4242));
names.put("Jane", new InetSocketAddress("5.6.7.8", 5757));

JCALibrary jca = JCALibrary.getInstance();
Server server = new Server()
{
@Override
public ProcessVariableExistanceCompletion processVariableExistanceTest(String name,
InetSocketAddress client,
ProcessVariableExistanceCallback callback)
throws CAException, IllegalArgumentException, IllegalStateException
{
System.out.println("Client " + client + " searches for '" + name + "'");
final InetSocketAddress addr = names.get(name);
if (addr != null)
return ProcessVariableExistanceCompletion.EXISTS_ELSEWHERE(addr);
else
return ProcessVariableExistanceCompletion.DOES_NOT_EXIST_HERE;
}

@Override
public ProcessVariable processVariableAttach(String name,
ProcessVariableEventCallback event,
ProcessVariableAttachCallback attach)
throws CAStatusException, IllegalArgumentException, IllegalStateException
{
throw new CAStatusException(CAStatus.NOSUPPORT, "not supported");
}
};

final ServerContext context = jca.createServerContext(JCALibrary.CHANNEL_ACCESS_SERVER_JAVA, server);
context.run(0);
}
}

0 comments on commit bca12fd

Please sign in to comment.