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

com.sshtools.synergy.nio.SshEngineContext Maven / Gradle / Ivy

package com.sshtools.synergy.nio;

/*-
 * #%L
 * Common API
 * %%
 * Copyright (C) 2002 - 2024 JADAPTIVE Limited
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.spi.SelectorProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.sshtools.common.events.EventListener;
import com.sshtools.common.events.EventServiceImplementation;
import com.sshtools.common.util.ByteBufferPool;

/**
 * Each instance of a {@link SshEngine} has a single configuration context.
 */
public class SshEngineContext {

	String product = "SSHD";
	int maximumConnections = -1;
	String tooManyConnectionsText = "Too many connections";

	SshEngine daemon;

	int permanentAcceptThreads = 1;
	int permanentConnectThreads = 1;
	int permanentTransferThreads = 2;
	int maximumChannelsPerThread = 1000;
	int idleServicePeriod = 1;
	int inactivePeriodsPerIdleEvent = 1;
	boolean useDirectByteBuffers = true;
	int bufferPoolArraySize = 65536+4096;
	Map interfacesToBind = new ConcurrentHashMap(8, 0.9f, 1);

	int ipv6WorkaroundPort = 60022;
	String ipv6WorkaroundBindAddress = "127.0.0.1";

	SelectorProvider selectorProvider = SelectorProvider.provider();
	ByteBufferPool bufferPool = null;

	private Map attributes = new HashMap();
	
	SshEngineContext(SshEngine daemon) {
		this.daemon = daemon;
	}

	/**
	 * Set the product name
	 * 
	 * @param String
	 *            product
	 */
	public void setProduct(String product) {
		this.product = product;
	}

	/** Get the product name used to prefix thread names */
	public String getProduct() {
		return product;
	}
	
	/**
	 * Set the maximum number of connections allowed at any one time.
	 * 
	 * @param maximumConnections
	 *            int
	 */
	public void setMaximumConnections(int maximumConnections) {
		this.maximumConnections = maximumConnections;
	}

	/**
	 * Get the maximum number of connections allowed at any one time.
	 * 
	 * @return int
	 */
	public int getMaximumConnections() {
		return maximumConnections;
	}

	/**
	 * Get the text used when disconnecting when the maximum connection threshold has been reached.
	 * @return
	 */
	public String getTooManyConnectionsText() {
		return tooManyConnectionsText;
	}

	/**
	 * Set the text used when disconnecting when the maximum connection threshold has been reached.
	 * @param tooManyConnectionsText
	 */
	public void setTooManyConnectionsText(String tooManyConnectionsText) {
		this.tooManyConnectionsText = tooManyConnectionsText;
	}
	
	/**
	 * Returns a direct buffer pool.
	 * 
	 * @return ByteBufferPool
	 */
	public synchronized ByteBufferPool getBufferPool() {
		if (bufferPool == null)
			bufferPool = new ByteBufferPool(bufferPoolArraySize,
					useDirectByteBuffers);
		return bufferPool;
	}

	/**
	 * Set the SelectorProvider used by the Selector threads.
	 * 
	 * @param selectorProvider
	 *            SelectorProvider
	 */
	public void setSelectorProvider(SelectorProvider selectorProvider) {
		this.selectorProvider = selectorProvider;
	}

	/**
	 * Get the SelectorProvider used to create Selector instances.
	 * 
	 * @return SelectorProvider
	 */
	public SelectorProvider getSelectorProvider() {
		return selectorProvider;
	}

	/**
	 * Get the instance of the SSHD for this context.
	 * 
	 * @return
	 */
	public SshEngine getEngine() {
		return daemon;
	}

	/**
	 * Determine whether the daemon is using direct byte buffers.
	 * 
	 * @return boolean
	 */
	public boolean isUsingDirectBuffers() {
		return useDirectByteBuffers;
	}

	/**
	 * Configure the byte buffer pool to use direct byte buffers.
	 * 
	 * @param useDirectByteBuffers
	 *            boolean
	 */
	public void setUsingDirectBuffers(boolean useDirectByteBuffers) {
		this.useDirectByteBuffers = useDirectByteBuffers;
	}

	/**
	 * Set the size of the byte buffers in the pool. The minimum size is 35000
	 * bytes
	 * 
	 * @param bufferPoolArraySize
	 *            int
	 */
	public void setBufferPoolArraySize(int bufferPoolArraySize) {
		if (bufferPoolArraySize < 35000)
			throw new IllegalArgumentException(
					"The buffer pool must have an array size of at least 35000 bytes (the maximum packet size supported)");
		this.bufferPoolArraySize = bufferPoolArraySize;
	}

	/**
	 * Add an interface and port to the listening socket list and provide the
	 * protocol context.
	 * 
	 * handler for subsequent connections.
	 * 
	 * @param addressToBind
	 *            String
	 * @param portToBind
	 *            int
	 * @param protocolContext
	 * @throws IOException
	 * @throws IOException
	 */
	public ListeningInterface addListeningInterface(String addressToBind,
			int portToBind, ProtocolContextFactory contextFactory, boolean reuseAddress) throws IOException {
		return addListeningInterface(InetAddress.getByName(addressToBind),
				portToBind, contextFactory, reuseAddress);
	}

	/**
	 * Add an interface and port to the listening socket list and provide the
	 * protocol context.
	 * 
	 * @param addressToBind
	 * @param portToBind
	 * @param context
	 * @throws IOException
	 */
	public ListeningInterface addListeningInterface(InetAddress addressToBind,
			int portToBind, ProtocolContextFactory contextFactory, boolean reuseAddress) throws IOException {
		InetSocketAddress ISA = new InetSocketAddress(addressToBind, portToBind);
		ListeningInterface li = new ListeningInterface(ISA, contextFactory);
		li.setSocketOptionReuseAddress(reuseAddress);
		
		interfacesToBind.put(ISA.toString(), li);

		if (daemon.isStarted() && !daemon.isStarting()) {
			if(!daemon.startListeningInterface(li)) {
				throw new IOException("Failed to start interface " + li.getAddressToBind());
			}
		}
		return li;

	}

	/**
	 * Remove a listening interface from the daemon at runtime.
	 * 
	 * @param addressBound
	 * @param portBound
	 */
	public void removeListeningInterface(InetAddress addressBound, int portBound) {
		InetSocketAddress ISA = new InetSocketAddress(addressBound, portBound);
		ListeningInterface li = (ListeningInterface) interfacesToBind.remove(ISA.toString());
		if (li != null) {
			daemon.removeAcceptor(li);
		}

	}

	/**
	 * Remove a listening interface from the daemon at runtime.
	 * 
	 * @param addressBound
	 * @param portBound
	 * @throws UnknownHostException
	 */
	public void removeListeningInterface(String addressBound, int portBound)
			throws UnknownHostException {
		removeListeningInterface(InetAddress.getByName(addressBound), portBound);
	}

	/**
	 * Remove an interface and port from the listening socket list.
	 * 
	 * @param String
	 *            anInterface
	 * @deprecated use {@link removeListeningInterface(String, int)} instead.
	 * @throws IOException
	 */
	public void removeListeningInterface(String anInterface) throws IOException {
		interfacesToBind.remove(anInterface);
	}

	//
	// /**
	// * Gets the protocol context for an interface.
	// *
	// * @param addressToBind String
	// * @param portToBind int
	// * @return ProtocolContext
	// */
	// public ProtocolContext getProtocolContext(String addressToBind, int
	// portToBind) {
	// return (ProtocolContext)interfacesToBind.get(addressToBind + ":" +
	// portToBind);
	// }

	// /**
	// * Get the listening socket list.
	// * @return
	// */
	// public String[] getListeningInterfaces() {
	// String[] interfaces = new String[interfacesToBind.size()];
	// interfacesToBind.keySet().toArray(interfaces);
	// return interfaces;
	// }

	/**
	 * Get the listening socket list.
	 * 
	 * @return InetSocketAddress[]
	 */
	public ListeningInterface[] getListeningInterfaces() {
		return (ListeningInterface[]) interfacesToBind.values().toArray(
				new ListeningInterface[interfacesToBind.size()]);
	}

	/**
	 * Get the number of permanent accept threads.
	 * 
	 * @return int
	 */
	public int getPermanentAcceptThreads() {
		return permanentAcceptThreads;
	}

	/**
	 * Set the number of permanent accept threads.
	 * 

* An accept thread services the asynchronous server socket by processing * requests for connections. Once a connection has been accepted it is then * registered with a transfer thread where all IO is handled. *

* The server maintains this number of permanent threads but will also * dynamically create additional threads if the permanent threads are * overloaded. * * @param permanentAcceptThreads */ public void setPermanentAcceptThreads(int permanentAcceptThreads) { if (permanentAcceptThreads < 1) throw new IllegalArgumentException( "There must be at least one permanent ACCEPT thread"); this.permanentAcceptThreads = permanentAcceptThreads; } /** * Get the number of permanent connect threads. * * @return */ public int getPermanentConnectThreads() { return permanentConnectThreads; } /** * Set the number of permanent connect threads. When existing SSH * connections make outgoing socket connections through port forwarding; the * asynchronous connection process is handled by these threads. Once the * connection has been established the socket is then registered with a * transfer thread where all IO is performed. * * @param permanentConnectThreads */ public void setPermanentConnectThreads(int permanentConnectThreads) { if (permanentConnectThreads < 1) throw new IllegalArgumentException( "There must be at least one permanent CONNECT thread"); this.permanentConnectThreads = permanentConnectThreads; } /** * Get the number of permanent transfer threads. * * @return */ public int getPermanentTransferThreads() { return permanentTransferThreads; } /** * Set the number of permanent transfer threads. Once a socket has either * been accepted or connected, the socket is registered with a transfer * thread. This thread asynchronously performs all the IO for the socket. If * all the permanent threads become fully loaded then additional threads * will be created to handle additional connections and shutdown once they * have no sockets to service. * * @param permanentAcceptThreads */ public void setPermanentTransferThreads(int permanentTransferThreads) { if (permanentTransferThreads < 1) throw new IllegalArgumentException( "There must be at least one permanent TRANSFER thread"); this.permanentTransferThreads = permanentTransferThreads; } /** * Get the maximum number of channels that can be serviced by a single * selector thread. * * @return */ public int getMaximumChannelsPerThread() { return maximumChannelsPerThread; } /** * Set the maximum number of channels that can be serviced by a single * selector thread. Setting this value to 1 effectivley gives you a * thread-per-connection model. * * @param maximumChannelsPerThread */ public void setMaximumChannelsPerThread(int maximumChannelsPerThread) { if (maximumChannelsPerThread < 1) throw new IllegalArgumentException( "You must have at least 1 selector per thread"); this.maximumChannelsPerThread = maximumChannelsPerThread; } /** * Get the time in seconds for each idle period service run. For example if * this setting is 10 (default) then every 10 seconds an idle service run * will be performed. This will process all the current channels on a thread * and evaluate whether an idle event should be fired on the channel. * * @return int */ public int getIdleServiceRunPeriod() { return idleServicePeriod; } /** * * @param idleServicePeriod * int */ public void setIdleServiceRunPeriod(int idleServicePeriod) { this.idleServicePeriod = idleServicePeriod; } /** * To determine whether any channels are idle a service run is performed to * evaluate the state of each channel. Each time a channel is found to be * inactive we flag it. This setting defines how many times it should be * flagged consequentivly before an idle event is fired. * * @return int */ public int getInactiveServiceRunsPerIdleEvent() { return inactivePeriodsPerIdleEvent; } /** * To determine whether any channels are idle a service run is performed to * evaluate the state of each channel. Each time a channel is found to be * inactive we flag it. This setting defines how many times it should be * flagged consequentivly before an idle event is fired. * * @param inactivePeriodsPerIdleEvent * int */ public void setInactiveServiceRunsPerIdleEvent( int inactivePeriodsPerIdleEvent) { this.inactivePeriodsPerIdleEvent = inactivePeriodsPerIdleEvent; } public static void addEventListener(EventListener listener) { EventServiceImplementation.getInstance().addListener(listener); } public static void removeEventListener(EventListener listener) { EventServiceImplementation.getInstance().removeListener(listener); } public int getIpv6WorkaroundPort() { return ipv6WorkaroundPort; } public void setIpv6WorkaroundPort(int ipv6WorkaroundPort) { this.ipv6WorkaroundPort = ipv6WorkaroundPort; } public String getIpv6WorkaroundBindAddress() { return ipv6WorkaroundBindAddress; } public void setIpv6WorkaroundBindAddress(String ipv6WorkaroundBindAddress) { this.ipv6WorkaroundBindAddress = ipv6WorkaroundBindAddress; } public void setAttribute(String name, Object value) { attributes.put(name, value); } @SuppressWarnings("unchecked") public K getAttribute(String name, K defaultValue) { if(!attributes.containsKey(name)) { attributes.put(name, defaultValue); } return (K) attributes.get(name); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy