/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.remote.client;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.remote.Transaction;
import org.apache.nifi.remote.TransferDirection;
import org.apache.nifi.remote.client.http.HttpClient;
import org.apache.nifi.remote.client.socket.SocketClient;
import org.apache.nifi.remote.exception.HandshakeException;
import org.apache.nifi.remote.exception.PortNotRunningException;
import org.apache.nifi.remote.exception.ProtocolException;
import org.apache.nifi.remote.exception.UnknownPortException;
import org.apache.nifi.remote.protocol.DataPacket;
import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol;
import org.apache.nifi.remote.protocol.http.HttpProxy;
import org.apache.nifi.security.util.KeyStoreUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* <p>
* The SiteToSiteClient provides a mechanism for sending data to a remote
* instance of NiFi (or NiFi cluster) and retrieving data from a remote instance
* of NiFi (or NiFi cluster).
* </p>
*
* <p>
* When configuring the client via the {@link SiteToSiteClient.Builder}, the
* Builder must be provided the URL of the remote NiFi instance. If the URL
* points to a standalone instance of NiFi, all interaction will take place with
* that instance of NiFi. However, if the URL points to the NiFi Cluster Manager
* of a cluster, the client will automatically handle load balancing the
* transactions across the different nodes in the cluster.
* </p>
*
* <p>
* The SiteToSiteClient provides a {@link Transaction} through which all
* interaction with the remote instance takes place. After data has been
* exchanged or it is determined that no data is available, the Transaction can
* then be canceled (via the {@link Transaction#cancel(String)} method) or can
* be completed (via the {@link Transaction#complete(boolean)} method).
* </p>
*
* <p>
* An instance of SiteToSiteClient can be obtained by constructing a new
* instance of the {@link SiteToSiteClient.Builder} class, calling the
* appropriate methods to configured the client as desired, and then calling the
* {@link SiteToSiteClient.Builder#build() build()} method.
* </p>
*
* <p>
* The SiteToSiteClient itself is immutable once constructed and is thread-safe.
* Many threads can share access to the same client. However, the
* {@link Transaction} that is created by the client is not thread safe and
* should not be shared among threads.
* </p>
*/
public interface SiteToSiteClient extends Closeable {
/**
* <p>
* Creates a new Transaction that can be used to either send data to a
* remote NiFi instance or receive data from a remote NiFi instance,
* depending on the value passed for the {@code direction} argument.
* </p>
*
* <p>
* <b>Note:</b> If all of the nodes are penalized (See
* {@link Builder#nodePenalizationPeriod(long, TimeUnit)}), then this method
* will return <code>null</code>.
* </p>
*
* @param direction specifies which direction the data should be
* transferred. A value of {@link TransferDirection#SEND} indicates that
* this Transaction will send data to the remote instance; a value of
* {@link TransferDirection#RECEIVE} indicates that this Transaction will be
* used to receive data from the remote instance.
*
* @return a Transaction to use for sending or receiving data, or
* <code>null</code> if all nodes are penalized.
* @throws org.apache.nifi.remote.exception.HandshakeException he
* @throws org.apache.nifi.remote.exception.PortNotRunningException pnre
* @throws IOException ioe
* @throws org.apache.nifi.remote.exception.UnknownPortException upe
*/
Transaction createTransaction(TransferDirection direction) throws HandshakeException, PortNotRunningException, ProtocolException, UnknownPortException, IOException;
/**
* <p>
* In order to determine whether the server is configured for secure
* communications, the client may have to query the server's RESTful
* interface. Doing so could result in an IOException.
* </p>
*
* @return {@code true} if site-to-site communications with the remote
* instance are secure, {@code false} if site-to-site communications with
* the remote instance are not secure. Whether or not communications are
* secure depends on the server, not the client
* @throws IOException if unable to query the remote instance's RESTful
* interface or if the remote instance is not configured to allow
* site-to-site communications
*/
boolean isSecure() throws IOException;
/**
*
* @return the configuration object that was built by the Builder
*/
SiteToSiteClientConfig getConfig();
/**
* <p>
* The Builder is the mechanism by which all configuration is passed to the
* SiteToSiteClient. Once constructed, the SiteToSiteClient cannot be
* reconfigured (i.e., it is immutable). If a change in configuration should
* be desired, the client should be {@link Closeable#close() closed} and a
* new client created.
* </p>
*/
public static class Builder implements Serializable {
private static final long serialVersionUID = -4954962284343090219L;
private Set<String> urls;
private long timeoutNanos = TimeUnit.SECONDS.toNanos(30);
private long penalizationNanos = TimeUnit.SECONDS.toNanos(3);
private long idleExpirationNanos = TimeUnit.SECONDS.toNanos(30L);
private SSLContext sslContext;
private String keystoreFilename;
private String keystorePass;
private KeystoreType keystoreType;
private String truststoreFilename;
private String truststorePass;
private KeystoreType truststoreType;
private EventReporter eventReporter = EventReporter.NO_OP;
private File peerPersistenceFile;
private boolean useCompression;
private String portName;
private String portIdentifier;
private int batchCount;
private long batchSize;
private long batchNanos;
private InetAddress localAddress;
private SiteToSiteTransportProtocol transportProtocol = SiteToSiteTransportProtocol.RAW;
private HttpProxy httpProxy;
/**
* Populates the builder with values from the provided config
*
* @param config to start with
* @return the builder
*/
public Builder fromConfig(final SiteToSiteClientConfig config) {
this.urls = config.getUrls();
this.timeoutNanos = config.getTimeout(TimeUnit.NANOSECONDS);
this.penalizationNanos = config.getPenalizationPeriod(TimeUnit.NANOSECONDS);
this.idleExpirationNanos = config.getIdleConnectionExpiration(TimeUnit.NANOSECONDS);
this.sslContext = config.getSslContext();
this.keystoreFilename = config.getKeystoreFilename();
this.keystorePass = config.getKeystorePassword();
this.keystoreType = config.getKeystoreType();
this.truststoreFilename = config.getTruststoreFilename();
this.truststorePass = config.getTruststorePassword();
this.truststoreType = config.getTruststoreType();
this.eventReporter = config.getEventReporter();
this.peerPersistenceFile = config.getPeerPersistenceFile();
this.useCompression = config.isUseCompression();
this.transportProtocol = config.getTransportProtocol();
this.portName = config.getPortName();
this.portIdentifier = config.getPortIdentifier();
this.batchCount = config.getPreferredBatchCount();
this.batchSize = config.getPreferredBatchSize();
this.batchNanos = config.getPreferredBatchDuration(TimeUnit.NANOSECONDS);
this.localAddress = config.getLocalAddress();
this.httpProxy = config.getHttpProxy();
return this;
}
/**
* <p>Specifies the URL of the remote NiFi instance.</p>
* <p>If this URL points to a NiFi node in a NiFi cluster, data transfer to and from
* nodes will be automatically load balanced across the different nodes.</p>
*
* <p>For better connectivity with a NiFi cluster, use {@link #urls(Set)} instead.</p>
*
* @param url url of remote instance
* @return the builder
*/
public Builder url(final String url) {
final Set<String> urls = new LinkedHashSet<>();
if (url != null && url.length() > 0) {
urls.add(url);
}
this.urls = urls;
return this;
}
/**
* <p>
* Specifies the local address to use when communicating with the remote NiFi instance.
* </p>
*
* @param localAddress the local address to use, or <code>null</code> to use <code>anyLocal</code> address.
* @return the builder
*/
public Builder localAddress(final InetAddress localAddress) {
this.localAddress = localAddress;
return this;
}
/**
* <p>
* Specifies the URLs of the remote NiFi instance.
* </p>
* <p>
* If this URL points to a NiFi node in a NiFi cluster, data transfer to and from
* nodes will be automatically load balanced across the different nodes.
* </p>
*
* <p>
* Multiple urls provide better connectivity with a NiFi cluster, able to connect
* to the target cluster at long as one of the specified urls is accessible.
* </p>
*
* @param urls urls of remote instance
* @return the builder
*/
public Builder urls(final Set<String> urls) {
this.urls = urls;
return this;
}
/**
* Specifies the communications timeouts to use when interacting with
* the remote instances. The default value is 30 seconds.
*
* @param timeout to use when interacting with remote instances
* @param unit unit of time over which to interpret the given timeout
* @return the builder
*/
public Builder timeout(final long timeout, final TimeUnit unit) {
this.timeoutNanos = unit.toNanos(timeout);
return this;
}
/**
* Specifies the amount of time that a connection can remain idle in the
* connection pool before it is "expired" and shutdown. The default
* value is 30 seconds.
*
* @param timeout to use when interacting with remote instances
* @param unit unit of time over which to interpret the given timeout
* @return the builder
*/
public Builder idleExpiration(final long timeout, final TimeUnit unit) {
this.idleExpirationNanos = unit.toNanos(timeout);
return this;
}
/**
* If there is a problem communicating with a node (i.e., any node in
* the remote NiFi cluster or the remote instance of NiFi if it is
* standalone), specifies how long the client should wait before
* attempting to communicate with that node again. While a particular
* node is penalized, all other nodes in the remote cluster (if any)
* will still be available for communication. The default value is 3
* seconds.
*
* @param period time to wait between communication attempts
* @param unit over which to evaluate the given period
* @return the builder
*/
public Builder nodePenalizationPeriod(final long period, final TimeUnit unit) {
this.penalizationNanos = unit.toNanos(period);
return this;
}
/**
* Specifies the SSL Context to use when communicating with the remote
* NiFi instance(s). If not specified, communications will not be
* secure. The remote instance of NiFi always determines whether or not
* Site-to-Site communications are secure (i.e., the client will always
* use secure or non-secure communications, depending on what the server
* dictates). <b>Note:</b> The SSLContext provided by this method will be
* ignored if using a Serializable Configuration (see {@link #buildSerializableConfig()}).
* If a Serializable Configuration is required and communications are to be
* secure, the {@link #keystoreFilename(String)}, {@link #keystorePass(String)},
* {@link #keystoreType}, {@link #truststoreFilename}, {@link #truststorePass(String)},
* and {@link #truststoreType(KeystoreType)} methods must be used instead.
*
* @param sslContext the context
* @return the builder
*/
public Builder sslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}
/**
* @return the filename to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
*/
public String getKeystoreFilename() {
return keystoreFilename;
}
/**
* Sets the filename to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
*
* @param keystoreFilename the filename to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
* @return the builder
*/
public Builder keystoreFilename(final String keystoreFilename) {
this.keystoreFilename = keystoreFilename;
return this;
}
/**
* @return the password to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
*/
public String getKeystorePass() {
return keystorePass;
}
/**
* Sets the password to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
*
* @param keystorePass the password to use for the Keystore in order to communicate securely
* with the remote instance of NiFi
* @return the builder
*/
public Builder keystorePass(final String keystorePass) {
this.keystorePass = keystorePass;
return this;
}
/**
* @return the type of Keystore to use in order to communicate securely
* with the remote instance of NiFi
*/
public KeystoreType getKeystoreType() {
return keystoreType;
}
/**
* Sets the type of the Keystore to use in order to communicate securely
* with the remote instance of NiFi
*
* @param keystoreType the type of the Keystore to use in order to communicate securely
* with the remote instance of NiFi
* @return the builder
*/
public Builder keystoreType(final KeystoreType keystoreType) {
this.keystoreType = keystoreType;
return this;
}
/**
* @return the filename to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
*/
public String getTruststoreFilename() {
return truststoreFilename;
}
/**
* Sets the filename to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
*
* @param truststoreFilename the filename to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
* @return the builder
*/
public Builder truststoreFilename(final String truststoreFilename) {
this.truststoreFilename = truststoreFilename;
return this;
}
/**
* @return the password to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
*/
public String getTruststorePass() {
return truststorePass;
}
/**
* Sets the password to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
*
* @param truststorePass the filename to use for the Truststore in order to communicate securely
* with the remote instance of NiFi
*/
public Builder truststorePass(final String truststorePass) {
this.truststorePass = truststorePass;
return this;
}
/**
* @return the type of the Truststore to use in order to communicate securely
* with the remote instance of NiFi
*/
public KeystoreType getTruststoreType() {
return truststoreType;
}
/**
* Sets the password type of the Truststore to use in order to communicate securely
* with the remote instance of NiFi
*
* @param truststoreType the type of the Truststore to use in order to communicate securely
* with the remote instance of NiFi
* @return the builder
*/
public Builder truststoreType(final KeystoreType truststoreType) {
this.truststoreType = truststoreType;
return this;
}
/**
* Provides an EventReporter that can be used by the client in order to
* report any events that could be of interest when communicating with
* the remote instance. The EventReporter provided must be threadsafe.
*
* @param eventReporter reporter
* @return the builder
*/
public Builder eventReporter(final EventReporter eventReporter) {
this.eventReporter = eventReporter;
return this;
}
/**
* Specifies a file that the client can write to in order to persist the
* list of nodes in the remote cluster and recover the list of nodes
* upon restart. This allows the client to function if the remote
* Cluster Manager is unavailable, even after a restart of the client
* software. If not specified, the list of nodes will not be persisted
* and a failure of the Cluster Manager will result in not being able to
* communicate with the remote instance if a new client is created.
*
* @param peerPersistenceFile file
* @return the builder
*/
public Builder peerPersistenceFile(final File peerPersistenceFile) {
this.peerPersistenceFile = peerPersistenceFile;
return this;
}
/**
* Specifies whether or not data should be compressed before being
* transferred to or from the remote instance.
*
* @param compress true if should compress
* @return the builder
*/
public Builder useCompression(final boolean compress) {
this.useCompression = compress;
return this;
}
/**
* Specifies the protocol to use for site to site data transport.
* @param transportProtocol transport protocol
* @return the builder
*/
public Builder transportProtocol(final SiteToSiteTransportProtocol transportProtocol) {
this.transportProtocol = transportProtocol;
return this;
}
/**
* Specifies the name of the port to communicate with. Either the port
* name or the port identifier must be specified.
*
* @param portName name of port
* @return the builder
*/
public Builder portName(final String portName) {
this.portName = portName;
return this;
}
/**
* Specifies the unique identifier of the port to communicate with. If
* it is known, this is preferred over providing the port name, as the
* port name may change.
*
* @param portIdentifier identifier of port
* @return the builder
*/
public Builder portIdentifier(final String portIdentifier) {
this.portIdentifier = portIdentifier;
return this;
}
/**
* When pulling data from a NiFi instance, the sender chooses how large
* a Transaction is. However, the client has the ability to request a
* particular batch size/duration. This method specifies the preferred
* number of {@link DataPacket}s to include in a Transaction.
*
* @param count client preferred batch size
* @return the builder
*/
public Builder requestBatchCount(final int count) {
this.batchCount = count;
return this;
}
/**
* When pulling data from a NiFi instance, the sender chooses how large
* a Transaction is. However, the client has the ability to request a
* particular batch size/duration. This method specifies the preferred
* number of bytes to include in a Transaction.
*
* @param bytes client preferred batch size
* @return the builder
*/
public Builder requestBatchSize(final long bytes) {
this.batchSize = bytes;
return this;
}
/**
* When pulling data from a NiFi instance, the sender chooses how large
* a Transaction is. However, the client has the ability to request a
* particular batch size/duration. This method specifies the preferred
* amount of time that a Transaction should span.
*
* @param value client preferred batch duration
* @param unit client preferred batch duration unit
* @return the builder
*/
public Builder requestBatchDuration(final long value, final TimeUnit unit) {
this.batchNanos = unit.toNanos(value);
return this;
}
/**
* @return a {@link SiteToSiteClientConfig} for the configured values
* but does not create a SiteToSiteClient
*/
public SiteToSiteClientConfig buildConfig() {
return new StandardSiteToSiteClientConfig(this);
}
/**
* @return a new SiteToSiteClient that can be used to send and receive
* data with remote instances of NiFi
*
* @throws IllegalStateException if either the url is not set or neither
* the port name nor port identifier is set,
* or if the transport protocol is not supported.
*/
public SiteToSiteClient build() {
if (urls == null) {
throw new IllegalStateException("Must specify URL to build Site-to-Site client");
}
if (portName == null && portIdentifier == null) {
throw new IllegalStateException("Must specify either Port Name or Port Identifier to build Site-to-Site client");
}
switch (transportProtocol){
case RAW:
return new SocketClient(buildConfig());
case HTTP:
return new HttpClient(buildConfig());
default:
throw new IllegalStateException("Transport protocol '" + transportProtocol + "' is not supported.");
}
}
/**
* @return the configured URL for the remote NiFi instance
*/
public String getUrl() {
if (urls != null && urls.size() > 0) {
return urls.iterator().next();
}
return null;
}
/**
* @param timeUnit unit over which to interpret the timeout
* @return the communications timeout
*/
public long getTimeout(final TimeUnit timeUnit) {
return timeUnit.convert(timeoutNanos, TimeUnit.NANOSECONDS);
}
/**
* @param timeUnit unit over which to interpret the time
* @return the amount of of time that a connection can remain idle in
* the connection pool before being shutdown
*/
public long getIdleConnectionExpiration(final TimeUnit timeUnit) {
return timeUnit.convert(idleExpirationNanos, TimeUnit.NANOSECONDS);
}
/**
* @param timeUnit unit of reported time
* @return the amount of time that a particular node will be ignored
* after a communications error with that node occurs
*/
public long getPenalizationPeriod(TimeUnit timeUnit) {
return timeUnit.convert(penalizationNanos, TimeUnit.NANOSECONDS);
}
/**
* @return the SSL Context that is configured for this builder
*/
public SSLContext getSslContext() {
return sslContext;
}
/**
* @return the EventReporter that is to be used by clients to report
* events
*/
public EventReporter getEventReporter() {
return eventReporter;
}
/**
* @return the file that is to be used for persisting the nodes of a
* remote cluster, if any
*/
public File getPeerPersistenceFile() {
return peerPersistenceFile;
}
/**
* @return a boolean indicating whether or not compression will be used
* to transfer data to and from the remote instance
*/
public boolean isUseCompression() {
return useCompression;
}
/**
* @return the transport protocol to use, defaults to RAW
*/
public SiteToSiteTransportProtocol getTransportProtocol(){
return transportProtocol;
}
/**
* @return the name of the port that the client is to communicate with
*/
public String getPortName() {
return portName;
}
/**
* @return the identifier of the port that the client is to communicate
* with
*/
public String getPortIdentifier() {
return portIdentifier;
}
/**
* Specify a HTTP proxy information to use with HTTP protocol of Site-to-Site communication.
* @param httpProxy HTTP proxy information
* @return the builder
*/
public Builder httpProxy(final HttpProxy httpProxy) {
this.httpProxy = httpProxy;
return this;
}
public HttpProxy getHttpProxy() {
return httpProxy;
}
}
class StandardSiteToSiteClientConfig implements SiteToSiteClientConfig, Serializable {
private static final long serialVersionUID = 1L;
// This Set instance has to be initialized here to be serialized via Kryo.
private final Set<String> urls = new LinkedHashSet<>();
private final long timeoutNanos;
private final long penalizationNanos;
private final long idleExpirationNanos;
private final SSLContext sslContext;
private final String keystoreFilename;
private final String keystorePass;
private final KeystoreType keystoreType;
private final String truststoreFilename;
private final String truststorePass;
private final KeystoreType truststoreType;
private final EventReporter eventReporter;
private final File peerPersistenceFile;
private final boolean useCompression;
private final SiteToSiteTransportProtocol transportProtocol;
private final String portName;
private final String portIdentifier;
private final int batchCount;
private final long batchSize;
private final long batchNanos;
private final HttpProxy httpProxy;
private final InetAddress localAddress;
// some serialization frameworks require a default constructor
private StandardSiteToSiteClientConfig() {
this.timeoutNanos = 0;
this.penalizationNanos = 0;
this.idleExpirationNanos = 0;
this.sslContext = null;
this.keystoreFilename = null;
this.keystorePass = null;
this.keystoreType = null;
this.truststoreFilename = null;
this.truststorePass = null;
this.truststoreType = null;
this.eventReporter = null;
this.peerPersistenceFile = null;
this.useCompression = false;
this.portName = null;
this.portIdentifier = null;
this.batchCount = 0;
this.batchSize = 0;
this.batchNanos = 0;
this.transportProtocol = null;
this.httpProxy = null;
this.localAddress = null;
}
private StandardSiteToSiteClientConfig(final SiteToSiteClient.Builder builder) {
if (builder.urls != null) {
this.urls.addAll(builder.urls);
}
this.timeoutNanos = builder.timeoutNanos;
this.penalizationNanos = builder.penalizationNanos;
this.idleExpirationNanos = builder.idleExpirationNanos;
this.sslContext = builder.sslContext;
this.keystoreFilename = builder.keystoreFilename;
this.keystorePass = builder.keystorePass;
this.keystoreType = builder.keystoreType;
this.truststoreFilename = builder.truststoreFilename;
this.truststorePass = builder.truststorePass;
this.truststoreType = builder.truststoreType;
this.eventReporter = builder.eventReporter;
this.peerPersistenceFile = builder.peerPersistenceFile;
this.useCompression = builder.useCompression;
this.portName = builder.portName;
this.portIdentifier = builder.portIdentifier;
this.batchCount = builder.batchCount;
this.batchSize = builder.batchSize;
this.batchNanos = builder.batchNanos;
this.transportProtocol = builder.getTransportProtocol();
this.httpProxy = builder.getHttpProxy();
this.localAddress = builder.localAddress;
}
@Override
public boolean isUseCompression() {
return useCompression;
}
@Override
public String getUrl() {
if (urls != null && urls.size() > 0) {
return urls.iterator().next();
}
return null;
}
@Override
public Set<String> getUrls() {
return urls;
}
@Override
public long getTimeout(final TimeUnit timeUnit) {
return timeUnit.convert(timeoutNanos, TimeUnit.NANOSECONDS);
}
@Override
public long getIdleConnectionExpiration(final TimeUnit timeUnit) {
return timeUnit.convert(idleExpirationNanos, TimeUnit.NANOSECONDS);
}
@Override
public SSLContext getSslContext() {
if (sslContext != null) {
return sslContext;
}
final KeyManagerFactory keyManagerFactory;
if (keystoreFilename != null && keystorePass != null && keystoreType != null) {
try {
// prepare the keystore
final KeyStore keyStore = KeyStoreUtils.getKeyStore(getKeystoreType().name());
try (final InputStream keyStoreStream = new FileInputStream(new File(getKeystoreFilename()))) {
keyStore.load(keyStoreStream, keystorePass.toCharArray());
}
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePass.toCharArray());
} catch (final Exception e) {
throw new IllegalStateException("Failed to load Keystore", e);
}
} else {
keyManagerFactory = null;
}
final TrustManagerFactory trustManagerFactory;
if (truststoreFilename != null && truststorePass != null && truststoreType != null) {
try {
// prepare the truststore
final KeyStore trustStore = KeyStoreUtils.getTrustStore(getTruststoreType().name());
try (final InputStream trustStoreStream = new FileInputStream(new File(getTruststoreFilename()))) {
trustStore.load(trustStoreStream, truststorePass.toCharArray());
}
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
} catch (final Exception e) {
throw new IllegalStateException("Failed to load Truststore", e);
}
} else {
trustManagerFactory = null;
}
if (keyManagerFactory != null && trustManagerFactory != null) {
try {
// initialize the ssl context
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
sslContext.getDefaultSSLParameters().setNeedClientAuth(true);
return sslContext;
} catch (final Exception e) {
throw new IllegalStateException("Created keystore and truststore but failed to initialize SSLContext", e);
}
} else {
return null;
}
}
@Override
public String getPortName() {
return portName;
}
@Override
public String getPortIdentifier() {
return portIdentifier;
}
@Override
public long getPenalizationPeriod(final TimeUnit timeUnit) {
return timeUnit.convert(penalizationNanos, TimeUnit.NANOSECONDS);
}
@Override
public File getPeerPersistenceFile() {
return peerPersistenceFile;
}
@Override
public EventReporter getEventReporter() {
return eventReporter;
}
@Override
public long getPreferredBatchDuration(final TimeUnit timeUnit) {
return timeUnit.convert(batchNanos, TimeUnit.NANOSECONDS);
}
@Override
public long getPreferredBatchSize() {
return batchSize;
}
@Override
public int getPreferredBatchCount() {
return batchCount;
}
@Override
public String getKeystoreFilename() {
return keystoreFilename;
}
@Override
public String getKeystorePassword() {
return keystorePass;
}
@Override
public KeystoreType getKeystoreType() {
return keystoreType;
}
@Override
public String getTruststoreFilename() {
return truststoreFilename;
}
@Override
public String getTruststorePassword() {
return truststorePass;
}
@Override
public KeystoreType getTruststoreType() {
return truststoreType;
}
@Override
public SiteToSiteTransportProtocol getTransportProtocol() {
return transportProtocol;
}
@Override
public HttpProxy getHttpProxy() {
return httpProxy;
}
@Override
public InetAddress getLocalAddress() {
return localAddress;
}
}
}