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

com.cosylab.epics.caj.cas.CASAcceptor 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.10
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.cas;

import gov.aps.jca.CAException;

import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.cosylab.epics.caj.impl.CAContext;
import com.cosylab.epics.caj.impl.reactor.ReactorHandler;
import com.cosylab.epics.caj.impl.reactor.lf.LeaderFollowersHandler;

/**
 * Channel Access Server TCP acceptor.
 * @author Matej Sekoranja
 * @version $id$
 */
public class CASAcceptor implements ReactorHandler {

	// Get Logger
	private static final Logger logger = Logger.getLogger(CASAcceptor.class.getName());
	
	/**
	 * Context instance.
	 */
	private CAContext context;
	
	/**
	 * Bind server socket address.
	 */
	private InetSocketAddress bindAddress = null;

	/**
	 * Server socket channel.
	 */
	private ServerSocketChannel serverSocketChannel = null;
	
	/**
	 * @param context
	 */
	public CASAcceptor(CAContext context, int port) throws CAException {
		this.context = context;
		
		int assignedPort = initialize(port);
		
		// update port, if dynamically assigned port is used
		if (assignedPort != port)
			((CAJServerContext)context).setServerPort(assignedPort);
	}

	/**
	 * 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.isAcceptable())
		{
			try
			{
				ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel(); 
				SocketChannel socket = serverSocketChannel.accept();
				
				SocketAddress address = socket.socket().getRemoteSocketAddress();
				context.getLogger().finer("Accepted connection from CA client: " + address);
				
				// use non-blocking channel (no need for soTimeout)			
				socket.configureBlocking(false);
			
				// enable TCP_NODELAY (disable Nagle's algorithm)
				socket.socket().setTcpNoDelay(true);
				
				// enable TCP_KEEPALIVE
				socket.socket().setKeepAlive(true);
			
				// TODO tune buffer sizes?! Win32 defaults are 8k, which is OK
				//socket.socket().setReceiveBufferSize();
				//socket.socket().setSendBufferSize();

				// create transport
				CASTransport transport = new CASTransport(context, socket);
				ReactorHandler handler = transport;
				if (context.getLeaderFollowersThreadPool() != null)
				    handler = new LeaderFollowersHandler(context.getReactor(), handler, context.getLeaderFollowersThreadPool());
				
				// register to reactor
				context.getReactor().register(socket, SelectionKey.OP_READ, handler);
				
				context.getLogger().finer("Serving to CA client: " + address);

			} 
			catch (Throwable th)
			{
				logger.log(Level.SEVERE, "", th);
			}
		}
	}

	/**
	 * Initialize connection acception. 
	 * @return port where server is listening
	 */
	private int initialize(int port) throws CAException
	{
		// specified bind address
		bindAddress = new InetSocketAddress(port);

		int tryCount = 0;
		while (true)
		{
			tryCount++;
			
			try
			{
				context.getLogger().finer("Creating acceptor to " + bindAddress + ".");
	
				serverSocketChannel = ServerSocketChannel.open();
				serverSocketChannel.socket().bind(bindAddress);
				serverSocketChannel.configureBlocking(false);
				
				// update bind address, if dynamically port selection was used
				if (bindAddress.getPort() == 0)
				{
					bindAddress = new InetSocketAddress(serverSocketChannel.socket().getLocalPort());
					context.getLogger().info("Using dynamically assigned TCP port " + bindAddress.getPort() + ".");
				}
				
				// create handler
				ReactorHandler handler = this;
				if (context.getLeaderFollowersThreadPool() != null)
				    handler = new LeaderFollowersHandler(context.getReactor(), handler, context.getLeaderFollowersThreadPool());
				
				// register to reactor
				context.getReactor().register(serverSocketChannel, SelectionKey.OP_ACCEPT, handler);
	
				// rise level if port is assigned dynamically
				context.getLogger().finer("Accepting connections at " + bindAddress + ".");

				// all OK, return
				return bindAddress.getPort();
			}
			catch (BindException be)
			{
				// failed to bind to specified bind address,
				// try to get port dynamically, but only once
				if (tryCount == 1)
				{
					context.getLogger().info("Configured TCP port " + port + " is unavailable, trying to assign it dynamically.");
					bindAddress = new InetSocketAddress(0);
				}
				else
				{
					throw new CAException("Failed to create acceptor to " + bindAddress, be);
				}				
			}
			catch (Throwable th)
			{
				throw new CAException("Failed to create acceptor to " + bindAddress, th);
			}
		}
		
	}
	
	/**
	 * Bind socket address.
	 * @return bind socket address, null if not binded.
	 */
	public InetAddress getBindAddress() 
	{
		return (serverSocketChannel != null) ? 
				serverSocketChannel.socket().getInetAddress() : null;
	}
	
	/**
	 * Destroy acceptor (stop listening).
	 */
	public void destroy()
	{
		if (serverSocketChannel != null)
		{
			context.getLogger().finer("Stopped accepting connections at " + bindAddress + ".");

			context.getReactor().unregisterAndClose(serverSocketChannel);
		}
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy