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

com.aoindustries.aoserv.master.DaemonHandler Maven / Gradle / Ivy

/*
 * aoserv-master - Master server for the AOServ Platform.
 * Copyright (C) 2001-2013, 2015, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-master.
 *
 * aoserv-master 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.
 *
 * aoserv-master 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-master.  If not, see .
 */
package com.aoindustries.aoserv.master;

import com.aoindustries.aoserv.client.linux.Server;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.aoserv.daemon.client.AOServDaemonConnector;
import com.aoindustries.dbc.DatabaseAccess;
import com.aoindustries.dbc.DatabaseConnection;
import com.aoindustries.io.AOPool;
import com.aoindustries.net.HostAddress;
import com.aoindustries.net.InetAddress;
import com.aoindustries.net.Port;
import java.io.IOException;
import java.security.SecureRandom;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * The AOServDaemonHandler handles all the accesses to the daemons.
 *
 * @author  AO Industries, Inc.
 */
final public class DaemonHandler {

	private DaemonHandler() {
	}

	/**
	 * The amount of time before a daemon will be accessed again once
	 * flagged as unavailable.
	 */
	public static final int DAEMON_RETRY_DELAY=5*1000; // Used to be 60*1000

	private static final Map connectors = new HashMap<>();

	public static int getDaemonConcurrency() {
		int total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getConcurrency();
		}
		return total;
	}

	public static int getDaemonConnections() {
		int total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getConnectionCount();
		}
		return total;
	}

	public static HostAddress getDaemonConnectAddress(DatabaseAccess database, int linuxServer) throws IOException, SQLException {
		HostAddress address = database.executeObjectQuery(
			ObjectFactories.hostAddressFactory,
			"select daemon_connect_address from linux.\"Server\" where server=?",
			linuxServer
		);
		if(address!=null) return address;
		InetAddress ip = database.executeObjectQuery(ObjectFactories.inetAddressFactory,
			"select\n"
			+ "  host(ia.\"inetAddress\")\n"
			+ "from\n"
			+ "  linux.\"Server\" ao,\n"
			+ "  net.\"Bind\" nb,\n"
			+ "  net.\"IpAddress\" ia\n"
			+ "where\n"
			+ "  ao.server=?\n"
			+ "  and ao.daemon_connect_bind=nb.id\n"
			+ "  and nb.\"ipAddress\"=ia.id",
			linuxServer
		);
		if(ip==null) throw new SQLException("Unable to find daemon IP address for Server: "+linuxServer);
		if(ip.isUnspecified()) {
			ip = database.executeObjectQuery(ObjectFactories.inetAddressFactory,
				"select\n"
				+ "  host(ia.\"inetAddress\")\n"
				+ "from\n"
				+ "  linux.\"Server\" ao,\n"
				+ "  net.\"Bind\" nb,\n"
				+ "  linux.\"Server\" ao2,\n"
				+ "  net.\"Device\" nd,\n"
				+ "  net.\"IpAddress\" ia\n"
				+ "where\n"
				+ "  ao.server=?\n"
				+ "  and ao.daemon_connect_bind=nb.id\n"
				+ "  and nb.server=ao2.server\n"
				+ "  and ao2.server=nd.server\n"
				+ "  and ao2.\"daemonDeviceId\"=nd.\"deviceId\"\n"
				+ "  and nd.id=ia.device\n"
				+ "  and not ia.\"isAlias\"\n"
				+ "limit 1",
				linuxServer
			);
			if(ip==null) throw new SQLException("Unable to find daemon IP address for Server: "+linuxServer);
		}
		return HostAddress.valueOf(ip);
	}

	public static Port getDaemonConnectorPort(DatabaseAccess database, int linuxServer) throws IOException, SQLException {
		return database.executeObjectQuery(
			ObjectFactories.portFactory,
			"select\n"
			+ "  nb.port,\n"
			+ "  nb.net_protocol\n"
			+ "from\n"
			+ "  linux.\"Server\" ao,\n"
			+ "  net.\"Bind\" nb\n"
			+ "where\n"
			+ "  ao.server=?\n"
			+ "  and ao.daemon_connect_bind=nb.id",
			linuxServer
		);
	}

	public static String getDaemonConnectorProtocol(DatabaseAccess database, int linuxServer) throws IOException, SQLException {
		return database.executeStringQuery(
			"select\n"
			+ "  nb.app_protocol\n"
			+ "from\n"
			+ "  linux.\"Server\" ao,\n"
			+ "  net.\"Bind\" nb\n"
			+ "where\n"
			+ "  ao.server=?\n"
			+ "  and ao.daemon_connect_bind=nb.id",
			linuxServer
		);
	}

	public static int getDaemonConnectorPoolSize(DatabaseAccess database, int linuxServer) throws IOException, SQLException {
		return database.executeIntQuery(
			"select\n"
			+ "  pool_size\n"
			+ "from\n"
			+ "  linux.\"Server\"\n"
			+ "where\n"
			+ "  server=?",
			linuxServer
		);
	}

	public static AOServDaemonConnector getDaemonConnector(DatabaseAccess database, int linuxServer) throws IOException, SQLException {
		Integer I = linuxServer;
		synchronized(DaemonHandler.class) {
			AOServDaemonConnector O=connectors.get(I);
			if(O!=null) return O;
			AOServDaemonConnector conn = AOServDaemonConnector.getConnector(
				getDaemonConnectAddress(database, linuxServer),
				MasterConfiguration.getLocalIp(),
				getDaemonConnectorPort(database, linuxServer),
				getDaemonConnectorProtocol(database, linuxServer),
				MasterConfiguration.getDaemonKey(database, linuxServer),
				getDaemonConnectorPoolSize(database, linuxServer),
				AOPool.DEFAULT_MAX_CONNECTION_AGE,
				MasterConfiguration.getSSLTruststorePath(),
				MasterConfiguration.getSSLTruststorePassword()
			);
			connectors.put(I, conn);
			return conn;
		}
	}

	public static int getDaemonConnects() {
		int total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getConnects();
		}
		return total;
	}

	public static int getDaemonCount() {
		return connectors.size();
	}

	public static int getDaemonMaxConcurrency() {
		int total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getMaxConcurrency();
		}
		return total;
	}

	public static int getDaemonPoolSize() {
		int total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getPoolSize();
		}
		return total;
	}

	public static long getDaemonTotalTime() {
		long total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getTotalTime();
		}
		return total;
	}

	public static long getDaemonTransactions() {
		long total=0;
		Iterator I=connectors.keySet().iterator();
		while(I.hasNext()) {
			Integer key=I.next();
			total+=connectors.get(key).getTransactionCount();
		}
		return total;
	}

	private static final Map downDaemons = new HashMap<>();

	public static void invalidateTable(Table.TableID tableID) {
		if(
			tableID==Table.TableID.AO_SERVERS
			|| tableID==Table.TableID.IP_ADDRESSES
			|| tableID==Table.TableID.NET_BINDS
		) {
			synchronized(DaemonHandler.class) {
				connectors.clear();
			}
		}
	}

	/**
	 * The availability of daemons is maintained to avoid repeatedly trying to access
	 * a daemon that is not responding while other daemons could be used.
	 */
	public static boolean isDaemonAvailable(int linuxServer) {
		Integer I = linuxServer;
		synchronized(downDaemons) {
			Long O=downDaemons.get(I);
			if(O!=null) {
				long downTime=System.currentTimeMillis() - O;
				if(downTime<0) {
					downDaemons.remove(I);
					return true;
				}
				if(downTime recentKeys = new HashMap<>();
	private static long lastKeyCleanTime = -1;

	/**
	 * @param connectAddress Overridden connect address or null to use the default
	 */
	public static Server.DaemonAccess grantDaemonAccess(
		DatabaseConnection conn,
		int linuxServer,
		HostAddress connectAddress,
		int daemonCommandCode,
		String param1,
		String param2,
		String param3,
		String param4
	) throws IOException, SQLException {
		long key;
		synchronized(recentKeys) {
			long currentTime=System.currentTimeMillis();
			if(lastKeyCleanTime==-1) lastKeyCleanTime=currentTime;
			else {
				long timeSince=currentTime-lastKeyCleanTime;
				if(timeSince<0 || timeSince>=(5L*60*1000)) {
					// Clean up the entries over one hour old
					Iterator I=recentKeys.keySet().iterator();
					while(I.hasNext()) {
						Long keyObj=I.next();
						long time = recentKeys.get(keyObj);
						timeSince=currentTime-time;
						if(timeSince<0 || timeSince>=(60L*60*1000)) {
							I.remove();
						}
					}
					lastKeyCleanTime=currentTime;
				}
			}

			// Generate the key
			SecureRandom secureRandom = MasterServer.getSecureRandom();
			while(true) {
				key=secureRandom.nextLong();
				Long L = key;
				if(!recentKeys.containsKey(L)) {
					recentKeys.put(L, System.currentTimeMillis());
					break;
				}
			}
		}

		// Send the key to the daemon
		AOServDaemonConnector daemonConnector = getDaemonConnector(conn, linuxServer);
		conn.releaseConnection();
		daemonConnector.grantDaemonAccess(key, daemonCommandCode, param1, param2, param3, param4);

		return new Server.DaemonAccess(
			getDaemonConnectorProtocol(conn, linuxServer),
			connectAddress!=null ? connectAddress : getDaemonConnectAddress(conn, linuxServer),
			getDaemonConnectorPort(conn, linuxServer),
			key
		);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy