All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cosylab.epics.caj.impl.BroadcastTransport Maven / Gradle / Ivy

Go to download

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>

There is a newer version: 2.4.2
Show newest version
/*
 * 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.impl;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.cosylab.epics.caj.cas.CAJServerContext;
import com.cosylab.epics.caj.impl.reactor.ReactorHandler;

/**
 * CA UDP transport implementation.
 * It receives datagrams from BroadcastConnector registered
 * repeater and sends broadcasts datagrams to given addresses.  
 * @author Matej Sekoranja
 * @version $id$
 */
// TODO buffer out of memory bug (on massive connect)?! 
public class BroadcastTransport implements Transport, ReactorHandler {

	// Get Logger
	private static final Logger logger = Logger.getLogger(BroadcastTransport.class.getName());
	
	/**
	 * Context instance.
	 */
	private CAContext context;

	/**
	 * Corresponding channel.
	 */
	private DatagramChannel channel;

	/**
	 * Cached socket address.
	 */
	private InetSocketAddress socketAddress;

	/**
	 * Connect address.
	 */
	private InetSocketAddress connectAddress;

	/**
	 * Broadcast addresses.
	 */
	private InetSocketAddress[] broadcastAddresses;

	/**
	 * Ignore addresses.
	 */
	private InetSocketAddress[] ignoredAddresses = null;

	/**
	 * Receive buffer.
	 */
	private ByteBuffer receiveBuffer;

	/**
	 * Receive buffer.
	 */
	private ByteBuffer[] receiveBufferArray;

	/**
	 * Remote side transport revision.
	 */
	private short remoteTransportRevision;

	/**
	 * CAS reponse handler.
	 */
	protected ResponseHandler responseHandler = null;

	/**
	 * @param context
	 */
	public BroadcastTransport(CAContext context, ResponseHandler responseHandler, DatagramChannel channel,
							  InetSocketAddress connectAddress, short remoteTransportRevision) {
		this.context = context;
		this.responseHandler = responseHandler;
		this.channel = channel;
		this.connectAddress = connectAddress;
		this.remoteTransportRevision = remoteTransportRevision;
		
		this.broadcastAddresses = getBroadcastAddresses(context.getBroadcastPort());

		socketAddress = (InetSocketAddress)channel.socket().getRemoteSocketAddress();

		// allocate receive buffer
		receiveBuffer = ByteBuffer.allocate(CAConstants.MAX_UDP_RECV);
		receiveBufferArray = new ByteBuffer[] { receiveBuffer };
	}

	public InetSocketAddress[] getBroadcastAddresses(int port) {
		try
		{
			String NIF_CLASSNAME = System.getProperty("CAJ_NIF_CLASSNAME", "com.cosylab.epics.caj.util.nif.InetAddressUtilV6");
			Class clazz = Class.forName(NIF_CLASSNAME);
			Method method = clazz.getMethod("getBroadcastAddresses", new Class[] { int.class });
			InetSocketAddress[] retVal = (InetSocketAddress[])method.invoke(null, new Object[] { Integer.valueOf(context.getBroadcastPort()) });
			//context.getLogger().finer("Using broadcast address(es): " + Arrays.toString(retVal));
			return retVal;
		} catch (Throwable th) {
			// fallback
			context.getLogger().fine("Failed to introspect network interfaces for broadcast addresses, using '255.255.255.255'.");
			return new InetSocketAddress[] { new InetSocketAddress("255.255.255.255", context.getBroadcastPort()) };
		}
	}
	
	/**
	 * Bind UDP socket to the connectAddress.
	 * @param reuseAddress resuse address option.
	 */
	public void bind(boolean reuseAddress) throws SocketException
	{
		DatagramSocket socket = channel.socket();

		socket.setReuseAddress(reuseAddress);
		socket.bind(connectAddress);

		int assignedPort = socket.getLocalPort();

		// Only necessary if connectAddress contains port = 0, but harmless either way
		((CAJServerContext)context).setUdpServerPort(assignedPort);
		context.getLogger().info("Server listening for pv name search on UDP port: " + assignedPort);
	}
	
	/**
	 * Close transport.
	 */
	public void close()
	{
		if (connectAddress != null)
			context.getLogger().finer("UDP connection to " + connectAddress + " closed.");
		context.getReactor().unregisterAndClose(channel);
	}

	/**
	 * Handle IO event.
	 * @see com.cosylab.epics.caj.impl.reactor.ReactorHandler#handleEvent(java.nio.channels.SelectionKey)
	 */
	public void handleEvent(SelectionKey key) {
		if (key.isValid() && key.isReadable())
			processRead();
			
		if (key.isValid() && key.isWritable())
			processWrite();
	}

	/**
	 * Process input (read) IO event.
	 */
	protected void processRead() {

		try
		{
			while (true)
			{
				
				// reset header buffer
				receiveBuffer.clear();

				// read to buffer
				// NOTE: If there are fewer bytes remaining in the buffer
				// than are required to hold the datagram then the remainder
				// of the datagram is silently discarded.
				InetSocketAddress fromAddress = (InetSocketAddress)channel.receive(receiveBuffer);
				
				// check if datagram not available
				// NOTE: If this channel is in non-blocking mode and a datagram is not
				// immediately available then this method immediately returns null.
				if (fromAddress == null)
				 break;

				// check if received from ignore address list
				if (ignoredAddresses != null)
				{
					// we do not care about the port
					InetAddress fromAddressOnly = fromAddress.getAddress();
					for (int i = 0; i < ignoredAddresses.length; i++)
						if (ignoredAddresses[i].getAddress().equals(fromAddressOnly))
							continue;
				}

				context.getLogger().finest("Received " + receiveBuffer.position() + " bytes from " + fromAddress + ".");

				// prepare buffer for reading
				receiveBuffer.flip();

				// invalidate last received sequence
				context.invalidateLastReceivedSequence();

				// handle response				
				while (receiveBuffer.limit() - receiveBuffer.position() >= CAConstants.CA_MESSAGE_HEADER_SIZE)
					responseHandler.handleResponse(fromAddress, this, receiveBufferArray);
 
			}
			
		} catch (IOException ioex) {
			// TODO what to do here
			logger.log(Level.SEVERE, "", ioex);
		}
	}

	/**
	 * Process output (write) IO event.
	 */
	protected void processWrite() {
		// noop (not used for datagrams)
	}

	/**
	 * Send a buffer through the transport.
	 * @param buffer	buffer to send. 
	 */
	protected void send(ByteBuffer buffer) 
	{
		for (int i = 0; i < broadcastAddresses.length; i++)
		{
			try
			{
				// prepare buffer
				buffer.flip();

				//context.getLogger().finest("Sending " + buffer.limit() + " bytes to " + broadcastAddresses[i] + ".");

				channel.send(buffer, broadcastAddresses[i]);
			}
			catch (Throwable ioex) 
			{
				buffer.position(buffer.limit());
				// TODO what to do here
				logger.log(Level.WARNING, "Failed to sent a datagram to:" + broadcastAddresses[i], ioex);
			}
		}
	}
	
	/**
	 * Send a buffer through the transport immediately. 
	 * @param buffer	buffer to send. 
	 * @param address	send address. 
	 * @throws IOException
	 */
	protected void send(ByteBuffer buffer, InetSocketAddress address) throws IOException
	{
		context.getLogger().finest("Sending " + buffer.limit() + " bytes to " + address + ".");
		buffer.flip();
		channel.send(buffer, address);
	}

	/**
	 * @see com.cosylab.epics.caj.impl.Transport#getMinorRevision()
	 */
	public short getMinorRevision() {
		return remoteTransportRevision;
	}

	/**
	 * Send request message(s) immediately.
	 * @see com.cosylab.epics.caj.impl.Transport#submit(com.cosylab.epics.caj.impl.Request)
	 */
	public void submit(Request requestMessage) {
		send(requestMessage.getRequestMessage());
	}

	/**
	 * Send a request message through the transport. 
	 * @param requestMessage	message to send. 
	 * @param address	send address. 
	 * @throws IOException
	 */
	public void send(Request requestMessage, InetSocketAddress address) throws IOException
	{
		send(requestMessage.getRequestMessage(), address);
	}

	/**
	 * @see com.cosylab.epics.caj.impl.Transport#getContext()
	 */
	public CAContext getContext() {
		return context;
	}

	/**
	 * @see com.cosylab.epics.caj.impl.Transport#getPriority()
	 */
	public short getPriority() {
		return CAConstants.CA_DEFAULT_PRIORITY;
	}

	/**
	 * @see com.cosylab.epics.caj.impl.Transport#flush()
	 */
	public boolean flush() {
		// noop since all UDP reqeuests are sent immediately
		return true;
	}

	/**
	 * @see com.cosylab.epics.caj.impl.Transport#getRemoteAddress()
	 */
	public InetSocketAddress getRemoteAddress() {
		return socketAddress;
	}

    /**
     * Get list of broadcast addresses.
     * @return broadcast addresses.
     */
    public InetSocketAddress[] getBroadcastAddresses()
    {
        return broadcastAddresses;
    }

    /**
     * Get list of ignored addresses.
     * @return broadcast addresses.
     */
    public InetSocketAddress[] getIgnoredAddresses()
    {
        return ignoredAddresses;
    }
    
    /**
     * Get connect address.
     * @return connect address.
     */
    public InetSocketAddress getConnectAddress()
    {
        return connectAddress;
    }
    
	/**
	 * Set list of broadcast addresses.
	 * This overrides default DEFAULT_BROADCAST_ADDRESSES list. 
	 * @param addresses list of broadcast addresses, non-null.
	 */
	public void setBroadcastAddresses(InetSocketAddress[] addresses) {
		broadcastAddresses = addresses;
	}

	/**
	 * Set list of broadcast addresses.
	 * @param addresses list of ignored addresses.
	 */
	public void setIgnoredAddresses(InetSocketAddress[] addresses) {
		ignoredAddresses = addresses;
	}

	/**
	 * @return the channel
	 */
	public DatagramChannel getChannel() {
		return channel;
	}

	public String toString() {
		return "BroadcastTransport" + Arrays.toString(broadcastAddresses);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy