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

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

There is a newer version: 1.91.8
Show newest version
/*
 * 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.account.Account;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.aoserv.master.dns.DnsService;
import com.aoindustries.collections.IntArrayList;
import com.aoindustries.collections.IntCollection;
import com.aoindustries.collections.IntList;
import com.aoindustries.collections.SortedArrayList;
import com.aoindustries.dbc.DatabaseAccess;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * In the request lifecycle, table invalidations occur after the database connection has been committed
 * and released.  This ensures that all data is available for the processes that react to the table
 * updates.  For efficiency, each host and account will only be notified once per table per
 * request.
 *
 * @author  AO Industries, Inc.
 */
// TODO: This should use HashSet instead of SortedArrayList
final public class InvalidateList {

	/**
	 * The invalidate list is used as part of the error logging, so it is not
	 * logged to the ticket system.
	 */
	private static final Logger logger = Logger.getLogger(InvalidateList.class.getName());

	/** Copy once to avoid repeated copies. */
	final private static Table.TableID[] tableIDs = Table.TableID.values();
	// TODO: Unused 2018-11-18: final private static int numTables = tableIDs.length;

	// TODO: Unused 2018-11-18: final private static String[] tableNames=new String[numTables];

	/**
	 * Indicates that all hosts or account.Account should receive the invalidate signal.
	 */
	public static final List allAccounts = Collections.unmodifiableList(new ArrayList<>());
	public static final IntList allHosts = new IntArrayList();

	private final Map> hostLists = new EnumMap<>(Table.TableID.class);
	private final Map> accountLists = new EnumMap<>(Table.TableID.class);

	/**
	 * Resets back to default state.
	*/
	public void reset() {
		hostLists.clear();
		accountLists.clear();
	}

	public void addTable(
		DatabaseAccess conn,
		Table.TableID tableID,
		Account.Name account,
		int host,
		boolean recurse
	) throws IOException, SQLException {
		addTable(
			conn,
			tableID,
			getAccountCollection(account),
			getHostCollection(host),
			recurse
		);
	}

	public void addTable(
		DatabaseAccess conn,
		Table.TableID tableID,
		Collection accounts,
		int host,
		boolean recurse
	) throws IOException, SQLException {
		addTable(
			conn,
			tableID,
			accounts,
			getHostCollection(host),
			recurse
		);
	}

	public void addTable(
		DatabaseAccess conn,
		Table.TableID tableID,
		Account.Name account,
		IntCollection hosts,
		boolean recurse
	) throws IOException, SQLException {
		addTable(
			conn,
			tableID,
			getAccountCollection(account),
			hosts,
			recurse
		);
	}

	public void addTable(
		DatabaseAccess conn,
		Table.TableID tableID,
		Collection accounts,
		IntCollection hosts,
		boolean recurse
	) throws IOException, SQLException {
		// TODO: Unused 2018-11-18: if(tableNames[tableID.ordinal()]==null) tableNames[tableID.ordinal()]=TableHandler.getTableName(conn, tableID);

		// Add to the account lists
		if(accounts==null || accounts==allAccounts) {
			accountLists.put(tableID, allAccounts);
		} else {
			if(!accounts.isEmpty()) {
				List SV = accountLists.get(tableID);
				// TODO: Just use HashSet here
				if(SV == null) accountLists.put(tableID, SV = new SortedArrayList<>());
				for(Account.Name account : accounts) {
					if(account == null) logger.log(Level.WARNING, null, new RuntimeException("Warning: account is null"));
					else if(!SV.contains(account)) SV.add(account);
				}
			}
		}

		// Add to the host lists
		if(hosts==null || hosts==allHosts) {
			hostLists.put(tableID, allHosts);
		} else if(!hosts.isEmpty()) {
			List SV = hostLists.get(tableID);
			// TODO: Just use HashSet here
			if(SV == null) hostLists.put(tableID, SV = new SortedArrayList<>());
			for(Integer id : hosts) {
				if(id == null) logger.log(Level.WARNING, null, new RuntimeException("Warning: id is null"));
				else if(!SV.contains(id)) SV.add(id);
			}
		}

		// Recursively invalidate those tables who's filters might have been effected
		if(recurse) {
			switch(tableID) {
				case AO_SERVERS :
					addTable(conn, Table.TableID.FIREWALLD_ZONES,       accounts, hosts, true);
					addTable(conn, Table.TableID.LINUX_SERVER_ACCOUNTS, accounts, hosts, true);
					addTable(conn, Table.TableID.LINUX_SERVER_GROUPS,   accounts, hosts, true);
					addTable(conn, Table.TableID.MYSQL_SERVERS,         accounts, hosts, true);
					addTable(conn, Table.TableID.POSTGRES_SERVERS,      accounts, hosts, true);
					break;
				case BUSINESS_SERVERS :
					addTable(conn, Table.TableID.SERVERS, accounts, hosts, true);
					break;
				case BUSINESSES :
					addTable(conn, Table.TableID.BUSINESS_PROFILES, accounts, hosts, true);
					break;
				case CYRUS_IMAPD_BINDS :
					addTable(conn, Table.TableID.CYRUS_IMAPD_SERVERS, accounts, hosts, false);
					break;
				case CYRUS_IMAPD_SERVERS :
					addTable(conn, Table.TableID.CYRUS_IMAPD_BINDS, accounts, hosts, false);
					break;
				case EMAIL_DOMAINS :
					addTable(conn, Table.TableID.EMAIL_ADDRESSES,   accounts, hosts, true);
					addTable(conn, Table.TableID.MAJORDOMO_SERVERS, accounts, hosts, true);
					break;
				case FAILOVER_FILE_REPLICATIONS :
					addTable(conn, Table.TableID.SERVERS,      accounts, hosts, true);
					addTable(conn, Table.TableID.NET_DEVICES,  accounts, hosts, true);
					addTable(conn, Table.TableID.IP_ADDRESSES, accounts, hosts, true);
					addTable(conn, Table.TableID.NET_BINDS,    accounts, hosts, true);
					break;
				case IP_REPUTATION_LIMITER_SETS:
					// Sets are only visible when used by at least one limiter in the same server farm
					addTable(conn, Table.TableID.IP_REPUTATION_SETS,         accounts, hosts, true);
					addTable(conn, Table.TableID.IP_REPUTATION_SET_HOSTS,    accounts, hosts, true);
					addTable(conn, Table.TableID.IP_REPUTATION_SET_NETWORKS, accounts, hosts, true);
					break;
				case HTTPD_BINDS :
					addTable(conn, Table.TableID.IP_ADDRESSES, accounts, hosts, true);
					addTable(conn, Table.TableID.NET_BINDS,    accounts, hosts, false);
					break;
				case HTTPD_SITE_BINDS :
					addTable(conn, Table.TableID.HTTPD_BINDS,             accounts, hosts, true);
					addTable(conn, Table.TableID.HTTPD_SITE_BIND_HEADERS, accounts, hosts, false);
					addTable(conn, Table.TableID.RewriteRule,             accounts, hosts, false);
					break;
				case HTTPD_TOMCAT_SITES :
					addTable(conn, Table.TableID.HTTPD_TOMCAT_SITE_JK_MOUNTS, accounts, hosts, false);
					break;
				case IP_ADDRESSES :
					addTable(conn, Table.TableID.IpAddressMonitoring, accounts, hosts, false);
					break;
				case LINUX_ACCOUNTS :
					addTable(conn, Table.TableID.FTP_GUEST_USERS, accounts, hosts, true);
					addTable(conn, Table.TableID.USERNAMES,       accounts, hosts, true);
					break;
				case LINUX_SERVER_ACCOUNTS :
					addTable(conn, Table.TableID.LINUX_ACCOUNTS,       accounts, hosts, true);
					addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, accounts, hosts, true);
					break;
				case LINUX_SERVER_GROUPS :
					addTable(conn, Table.TableID.EMAIL_LISTS,          accounts, hosts, true);
					addTable(conn, Table.TableID.LINUX_GROUPS,         accounts, hosts, true);
					addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, accounts, hosts, true);
					break;
				case MAJORDOMO_SERVERS :
					addTable(conn, Table.TableID.MAJORDOMO_LISTS, accounts, hosts, true);
					break;
				case MYSQL_SERVER_USERS :
					addTable(conn, Table.TableID.MYSQL_USERS, accounts, hosts, true);
					break;
				case MYSQL_SERVERS :
					addTable(conn, Table.TableID.NET_BINDS,          accounts, hosts, true);
					addTable(conn, Table.TableID.MYSQL_DATABASES,    accounts, hosts, true);
					addTable(conn, Table.TableID.MYSQL_SERVER_USERS, accounts, hosts, true);
					break;
				case NET_BINDS :
					addTable(conn, Table.TableID.HTTPD_BINDS,              accounts, hosts, false);
					addTable(conn, Table.TableID.NET_BIND_FIREWALLD_ZONES, accounts, hosts, false);
					break;
				case NET_BIND_FIREWALLD_ZONES :
					// Presence of "public" firewalld zone determines compatibility "open_firewall" for clients
					// version <= 1.80.2
					addTable(conn, Table.TableID.NET_BINDS, accounts, hosts, false);
					break;
				case NET_DEVICES :
					addTable(conn, Table.TableID.IP_ADDRESSES, accounts, hosts, true);
					break;
				case NOTICE_LOG :
					addTable(conn, Table.TableID.NoticeLogBalance, accounts, hosts, false);
					break;
				case NoticeLogBalance :
					// Added for compatibility "balance" for pre-1.83.0 clients
					addTable(conn, Table.TableID.NOTICE_LOG, accounts, hosts, false);
					break;
				case PACKAGE_DEFINITIONS :
					addTable(conn, Table.TableID.PACKAGE_DEFINITION_LIMITS, accounts, hosts, true);
					break;
				case PACKAGES :
					addTable(conn, Table.TableID.PACKAGE_DEFINITIONS, accounts, hosts, true);
					break;
				case POSTGRES_SERVER_USERS :
					addTable(conn, Table.TableID.POSTGRES_USERS, accounts, hosts, true);
					break;
				case POSTGRES_SERVERS :
					addTable(conn, Table.TableID.NET_BINDS,             accounts, hosts, true);
					addTable(conn, Table.TableID.POSTGRES_DATABASES,    accounts, hosts, true);
					addTable(conn, Table.TableID.POSTGRES_SERVER_USERS, accounts, hosts, true);
					break;
				case SENDMAIL_BINDS :
					addTable(conn, Table.TableID.SENDMAIL_SERVERS, accounts, hosts, false);
					break;
				case SENDMAIL_SERVERS :
					addTable(conn, Table.TableID.SENDMAIL_BINDS, accounts, hosts, false);
					break;
				case SERVERS :
					addTable(conn, Table.TableID.AO_SERVERS,      accounts, hosts, true);
					addTable(conn, Table.TableID.IP_ADDRESSES,    accounts, hosts, true);
					addTable(conn, Table.TableID.NET_DEVICES,     accounts, hosts, true);
					addTable(conn, Table.TableID.VIRTUAL_SERVERS, accounts, hosts, true);
					break;
				case SSL_CERTIFICATES :
					addTable(conn, Table.TableID.SSL_CERTIFICATE_NAMES,      accounts, hosts, false);
					addTable(conn, Table.TableID.SSL_CERTIFICATE_OTHER_USES, accounts, hosts, false);
					break;
				case USERNAMES :
					addTable(conn, Table.TableID.BUSINESS_ADMINISTRATORS, accounts, hosts, true);
					break;
				case VIRTUAL_SERVERS :
					addTable(conn, Table.TableID.VIRTUAL_DISKS, accounts, hosts, true);
					break;
				case WhoisHistoryAccount :
					addTable(conn, Table.TableID.WhoisHistory, accounts, hosts, false);
					break;
			}
		}
	}

	public List getAffectedAccounts(Table.TableID tableID) {
		List SV=accountLists.get(tableID);
		if(SV != null || hostLists.containsKey(tableID)) {
			return (SV == null) ? allAccounts : SV;
		} else {
			return null;
		}
	}

	public List getAffectedHosts(Table.TableID tableID) {
		List SV=hostLists.get(tableID);
		if(SV != null || accountLists.containsKey(tableID)) {
			return (SV == null) ? allHosts : SV;
		} else {
			return null;
		}
	}

	public void invalidateMasterCaches() {
		for(Table.TableID tableID : tableIDs) {
			if(hostLists.containsKey(tableID) || accountLists.containsKey(tableID)) {
				AccountHandler.invalidateTable(tableID);
				CvsHandler.invalidateTable(tableID);
				DaemonHandler.invalidateTable(tableID);
				// TODO: Have each service register to receive invalidation signals
				try {
					MasterServer.getService(DnsService.class).invalidateTable(tableID);
				} catch(NoServiceException e) {
					// OK when running batch credit card processing from command line
				}
				EmailHandler.invalidateTable(tableID);
				WebHandler.invalidateTable(tableID);
				LinuxAccountHandler.invalidateTable(tableID);
				MasterServer.invalidateTable(tableID);
				MysqlHandler.invalidateTable(tableID);
				PackageHandler.invalidateTable(tableID);
				PostgresqlHandler.invalidateTable(tableID);
				NetHostHandler.invalidateTable(tableID);
				TableHandler.invalidateTable(tableID);
				AccountUserHandler.invalidateTable(tableID);
			}
		}
	}

	public boolean isInvalid(Table.TableID tableID) {
		return hostLists.containsKey(tableID) || accountLists.containsKey(tableID);
	}

	public static Collection getAccountCollection(Account.Name ... accounts) {
		if(accounts.length == 0) return Collections.emptyList();
		Collection coll = new ArrayList<>(accounts.length);
		Collections.addAll(coll, accounts);
		return coll;
	}

	public static IntCollection getHostCollection(int ... hosts) throws IOException, SQLException {
		if(hosts.length == 0) return new IntArrayList(0);
		IntCollection coll = new IntArrayList(hosts.length);
		for(int host : hosts) coll.add(host);
		return coll;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy