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

is.codion.common.rmi.server.DefaultServerConfiguration Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of Codion.
 *
 * Codion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Codion 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Codion.  If not, see .
 *
 * Copyright (c) 2020 - 2024, Björn Darri Sigurðsson.
 */
package is.codion.common.rmi.server;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.function.Supplier;

import static is.codion.common.Text.nullOrEmpty;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Objects.requireNonNull;

/**
 * Configuration values for a {@link AbstractServer}.
 */
final class DefaultServerConfiguration implements ServerConfiguration {

	private static final Logger LOG = LoggerFactory.getLogger(DefaultServerConfiguration.class);

	private final int port;
	private final int registryPort;
	private final Collection auxiliaryServerFactoryClassNames;
	private final int adminPort;
	private final boolean sslEnabled;
	private final Supplier serverNameSupplier;
	private final RMIClientSocketFactory rmiClientSocketFactory;
	private final RMIServerSocketFactory rmiServerSocketFactory;
	private final String objectInputFilterFactoryClassName;
	private final int connectionMaintenanceInterval;
	private final int connectionLimit;

	private String serverName;

	DefaultServerConfiguration(DefaultServerConfiguration.DefaultBuilder builder) {
		this.port = builder.serverPort;
		this.registryPort = builder.registryPort;
		this.auxiliaryServerFactoryClassNames = unmodifiableCollection(builder.auxiliaryServerFactoryClassNames);
		this.adminPort = builder.serverAdminPort;
		this.sslEnabled = builder.sslEnabled;
		this.serverName = builder.serverName;
		this.serverNameSupplier = builder.serverNameSupplier == null ? () -> serverName : builder.serverNameSupplier;
		this.rmiClientSocketFactory = builder.rmiClientSocketFactory;
		this.rmiServerSocketFactory = builder.rmiServerSocketFactory;
		this.objectInputFilterFactoryClassName = builder.objectInputFilterFactoryClassName;
		this.connectionMaintenanceInterval = builder.connectionMaintenanceInterval;
		this.connectionLimit = builder.connectionLimit;
	}

	@Override
	public String serverName() {
		if (serverName == null) {
			serverName = serverNameSupplier.get();
		}

		return serverName;
	}

	@Override
	public int port() {
		return port;
	}

	@Override
	public int registryPort() {
		return registryPort;
	}

	@Override
	public int adminPort() {
		return adminPort;
	}

	@Override
	public Collection auxiliaryServerFactoryClassNames() {
		return auxiliaryServerFactoryClassNames;
	}

	@Override
	public boolean sslEnabled() {
		return sslEnabled;
	}

	@Override
	public Optional rmiClientSocketFactory() {
		return Optional.ofNullable(rmiClientSocketFactory);
	}

	@Override
	public Optional rmiServerSocketFactory() {
		return Optional.ofNullable(rmiServerSocketFactory);
	}

	@Override
	public Optional objectInputFilterFactoryClassName() {
		return Optional.ofNullable(objectInputFilterFactoryClassName);
	}

	@Override
	public int connectionMaintenanceInterval() {
		return connectionMaintenanceInterval;
	}

	@Override
	public int connectionLimit() {
		return connectionLimit;
	}

	static final class DefaultBuilder implements Builder {

		static {
			resolveClasspathKeyStore();
		}

		private final int serverPort;
		private final int registryPort;
		private final Collection auxiliaryServerFactoryClassNames = new HashSet<>();
		private int serverAdminPort;
		private boolean sslEnabled = true;
		private String serverName;
		private Supplier serverNameSupplier;
		private RMIClientSocketFactory rmiClientSocketFactory = new SslRMIClientSocketFactory();
		private RMIServerSocketFactory rmiServerSocketFactory = new SslRMIServerSocketFactory();
		private String objectInputFilterFactoryClassName;
		private Integer connectionMaintenanceInterval = DEFAULT_CONNECTION_MAINTENANCE_INTERVAL;
		private int connectionLimit = -1;

		DefaultBuilder(int serverPort, int registryPort) {
			this.serverPort = serverPort;
			this.registryPort = registryPort;
		}

		@Override
		public DefaultBuilder adminPort(int adminPort) {
			this.serverAdminPort = adminPort;
			return this;
		}

		@Override
		public DefaultBuilder serverName(Supplier serverNameSupplier) {
			this.serverNameSupplier = requireNonNull(serverNameSupplier);
			return this;
		}

		@Override
		public DefaultBuilder serverName(String serverName) {
			this.serverName = requireNonNull(serverName);
			return this;
		}

		@Override
		public DefaultBuilder auxiliaryServerFactoryClassNames(Collection auxiliaryServerFactoryClassNames) {
			this.auxiliaryServerFactoryClassNames.addAll(requireNonNull(auxiliaryServerFactoryClassNames));
			return this;
		}

		@Override
		public DefaultBuilder sslEnabled(boolean sslEnabled) {
			this.sslEnabled = sslEnabled;
			if (sslEnabled) {
				rmiClientSocketFactory(new SslRMIClientSocketFactory());
				rmiServerSocketFactory(new SslRMIServerSocketFactory());
			}
			else {
				rmiClientSocketFactory(null);
				rmiServerSocketFactory(null);
			}
			return this;
		}

		@Override
		public DefaultBuilder rmiClientSocketFactory(RMIClientSocketFactory rmiClientSocketFactory) {
			this.rmiClientSocketFactory = rmiClientSocketFactory;
			return this;
		}

		@Override
		public DefaultBuilder rmiServerSocketFactory(RMIServerSocketFactory rmiServerSocketFactory) {
			this.rmiServerSocketFactory = rmiServerSocketFactory;
			return this;
		}

		@Override
		public DefaultBuilder objectInputFilterFactoryClassName(String objectInputFilterFactoryClassName) {
			this.objectInputFilterFactoryClassName = objectInputFilterFactoryClassName;
			return this;
		}

		@Override
		public DefaultBuilder connectionMaintenanceInterval(int connectionMaintenanceInterval) {
			this.connectionMaintenanceInterval = connectionMaintenanceInterval;
			return this;
		}

		@Override
		public DefaultBuilder connectionLimit(int connectionLimit) {
			this.connectionLimit = connectionLimit;
			return this;
		}

		@Override
		public ServerConfiguration build() {
			return new DefaultServerConfiguration(this);
		}

		private static synchronized void resolveClasspathKeyStore() {
			String keystore = CLASSPATH_KEYSTORE.get();
			if (nullOrEmpty(keystore)) {
				LOG.warn("No classpath key store specified via {}", CLASSPATH_KEYSTORE.propertyName());
				return;
			}
			if (KEYSTORE.isNotNull()) {
				throw new IllegalStateException("Classpath keystore (" + keystore + ") can not be specified when "
								+ JAVAX_NET_KEYSTORE + " is already set to " + KEYSTORE.get());
			}
			try (InputStream inputStream = DefaultServerConfiguration.class.getClassLoader().getResourceAsStream(keystore)) {
				if (inputStream == null) {
					LOG.warn("Specified key store not found on classpath: {}", keystore);
					return;
				}
				File file = File.createTempFile("serverKeyStore", "tmp");
				Files.write(file.toPath(), readBytes(inputStream));
				file.deleteOnExit();

				KEYSTORE.set(file.getPath());
				LOG.info("Classpath key store {} written to file {} and set as {}",
								CLASSPATH_KEYSTORE.propertyName(), file, JAVAX_NET_KEYSTORE);
			}
			catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		private static byte[] readBytes(InputStream stream) throws IOException {
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			byte[] buffer = new byte[8192];
			int line;
			while ((line = stream.read(buffer)) != -1) {
				os.write(buffer, 0, line);
			}
			os.flush();

			return os.toByteArray();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy