/*
* Copyright (c) 2015-2016, Christoph Engelbert (aka noctarius) and
* contributors. All rights reserved.
*
* Licensed 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 com.noctarius.tengi.core.config;
import com.noctarius.tengi.core.connection.HandshakeHandler;
import com.noctarius.tengi.core.connection.Transport;
import com.noctarius.tengi.core.impl.Validate;
import com.noctarius.tengi.core.serialization.marshaller.Marshaller;
import com.noctarius.tengi.core.serialization.marshaller.MarshallerFilter;
import com.noctarius.tengi.core.serialization.marshaller.MarshallerReader;
import com.noctarius.tengi.core.serialization.marshaller.MarshallerWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.noctarius.tengi.core.serialization.marshaller.Marshaller.marshaller;
/**
* <p>The <tt>ConfigurationBuilder</tt> is designed to create an immutable instance of the
* {@link com.noctarius.tengi.core.config.Configuration} interface. It supports a fluent
* configuration style and collects all necessary properties or uses some meaningful
* defaults.</p>
* <p>The internal design of the <tt>ConfigurationBuilder</tt> is not thread-safe and
* therefore it is not recommended to use if from multiple threads concurrently.</p>
*/
public class ConfigurationBuilder {
protected final Set<MarshallerConfiguration> marshallers = new HashSet<>();
protected final List<Transport> transports = new ArrayList<>();
protected final Map<Transport, Integer> transportPorts = new HashMap<>();
protected boolean sslEnabled = false;
protected boolean gzipEnabled = false;
protected boolean snappyEnabled = false;
protected HandshakeHandler handshakeHandler = null;
/**
* Configures a new {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} and
* a corresponding {@link com.noctarius.tengi.core.serialization.marshaller.MarshallerFilter}
* to be injected into the internal serializer framework.
*
* @param marshallerFilter the <tt>MarshallerFilter</tt> to be bound
* @param marshaller the <tt>Marshaller</tt> to be bound
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder addMarshaller(MarshallerFilter marshallerFilter, Marshaller marshaller) {
marshallers.add(new MarshallerConfiguration(marshallerFilter, marshaller));
return this;
}
/**
* <p>Configures a new {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} and
* a corresponding {@link com.noctarius.tengi.core.serialization.marshaller.MarshallerFilter}
* to be injected into the internal serializer framework. This method supports Java 8 Lambdas and
* the bound <tt>Marshaller</tt> is internally created by the given
* {@link com.noctarius.tengi.core.serialization.marshaller.MarshallerReader} and
* {@link com.noctarius.tengi.core.serialization.marshaller.MarshallerWriter}.</p>
* <p>The given <tt>marshallerId</tt> will be written to the byte-stream to identify
* the created <tt>Marshaller</tt>.</p>
*
* @param marshallerFilter the <tt>MarshallerFilter</tt> to be bound
* @param marshallerId the id for the generated <tt>Marshaller</tt>
* @param reader the <tt>MarshallerReader</tt> to be delegated to for un-marshalling
* @param writer the <tt>MarshallerWriter</tt> to be delegated to for marshalling
* @param <O> the value type to be marshalled and un-marshalled
* @param <I> the type of the marshaller ID
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public <O, I> ConfigurationBuilder addMarshaller(MarshallerFilter marshallerFilter, I marshallerId, //
MarshallerReader<O> reader, MarshallerWriter<O> writer) {
marshallers.add(new MarshallerConfiguration(marshallerFilter, marshaller(marshallerId, reader, writer)));
return this;
}
/**
* <p>Adds a {@link com.noctarius.tengi.core.connection.Transport} to the possible transports.
* The transport will be added to the current index of the internal list. Transport will later
* be connected in this very order.</p>
* <p>If the same transport is added multiple times to the configuration it will be tried multiple
* times as well. There is no duplication check and duplicates are fully supported.</p>
*
* @param transport the <tt>Transport to be added</tt>
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder addTransport(Transport transport) {
transports.add(transport);
return this;
}
/**
* <p>Adds one or more {@link com.noctarius.tengi.core.connection.Transport}s to the possible transports.
* The transports will be added to the current index of the internal list. Transport will later
* be connected in this very order.</p>
* <p>If the same transport is added multiple times to the configuration it will be tried multiple
* times as well. There is no duplication check and duplicates are fully supported.</p>
*
* @param transports the <tt>Transport</tt>s to be added
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder addTransport(Transport... transports) {
this.transports.addAll(Arrays.asList(transports));
return this;
}
/**
* Configures an explicit mapping of a port (for example TCP or UDP) to the given
* {@link com.noctarius.tengi.core.connection.Transport}. Any previously configured
* port for this very transport is overridden. Any configuration also overrides the
* default port from the transport. The port must be between 1 and 65535, restriction
* from the operating system might apply and define a different range of usable port
* numbers.
*
* @param transport the <tt>Transport</tt> to bind to the port
* @param port the port to bind the <tt>Transport</tt> to
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder transportPort(Transport transport, int port) {
Validate.greaterOrEqual("port", 1, port);
Validate.lowerOrEqual("port", 65535, port);
transportPorts.put(transport, port);
return this;
}
/**
* Defines if the {@link com.noctarius.tengi.core.connection.Transport}s should use SSL.
* This only applies to transports that support SSL encrypted connection. Please refer to
* any <tt>Transport</tt> documentation to find out if SSL is supported or not. Calling this
* method multiple times will override any previously set value.
*
* @param sslEnabled true to enable SSL, false to disable it
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder ssl(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
return this;
}
/**
* Defines if the {@link com.noctarius.tengi.core.connection.Transport}s should use GZIP compression.
* This only applies to transports that support GZIP compression connection. Please refer to
* any <tt>Transport</tt> documentation to find out if GZIP compression is supported or not.
* Calling this method multiple times will override any previously set value.
*
* @param gzipEnabled true to enable GZIP compression, false to disable it
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder gzip(boolean gzipEnabled) {
this.gzipEnabled = gzipEnabled;
return this;
}
/**
* Defines if the {@link com.noctarius.tengi.core.connection.Transport}s should use Snappy compression.
* This only applies to transports that support Snappy compression connection. Please refer to
* any <tt>Transport</tt> documentation to find out if Snappy compression is supported or not.
* Calling this method multiple times will override any previously set value.
*
* @param snappyEnabled true to enable Snappy compression, false to disable it
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder snappy(boolean snappyEnabled) {
this.snappyEnabled = snappyEnabled;
return this;
}
/**
* Defines the {@link com.noctarius.tengi.core.connection.HandshakeHandler} instance
* to verify, accept or deny new connection handshakes. On client-side additional information can be
* extracted from the handshake response retrieved from the server.
*
* @param handshakeHandler the <tt>HandshakeHandler</tt> instance to be configured
* @return this instance of the <tt>ConfigurationBuilder</tt> for fluent programing style
*/
public ConfigurationBuilder handshakeHandler(HandshakeHandler handshakeHandler) {
this.handshakeHandler = handshakeHandler;
return this;
}
/**
* Build the {@link com.noctarius.tengi.core.config.Configuration} instance with any values currently
* set in this <tt>ConfigurationBuilder</tt> instance. The created configuration is immutable and
* can't be changed afterwards.
*
* @return an immutable <tt>Configuration</tt> instance bound to the prior configured settings
*/
public Configuration build() {
return new ConfigurationImpl( //
marshallers, transports, transportPorts, sslEnabled, gzipEnabled, snappyEnabled, handshakeHandler);
}
protected static class ConfigurationImpl
implements Configuration {
private final Set<MarshallerConfiguration> marshallers;
private final List<Transport> transports;
private final Map<Transport, Integer> transportPorts;
private final boolean sslEnabled;
private boolean gzipEnabled = false;
private boolean snappyEnabled = false;
private final HandshakeHandler handshakeHandler;
protected ConfigurationImpl(Set<MarshallerConfiguration> marshallers, List<Transport> transports,
Map<Transport, Integer> transportPorts, boolean sslEnabled, boolean gzipEnabled,
boolean snappyEnabled, HandshakeHandler handshakeHandler) {
this.marshallers = Collections.unmodifiableSet(new HashSet<>(marshallers));
this.transports = Collections.unmodifiableList(new ArrayList<>(transports));
this.transportPorts = Collections.unmodifiableMap(new HashMap<>(transportPorts));
this.sslEnabled = sslEnabled;
this.gzipEnabled = gzipEnabled;
this.snappyEnabled = snappyEnabled;
this.handshakeHandler = handshakeHandler;
}
@Override
public Set<MarshallerConfiguration> getMarshallers() {
return marshallers;
}
@Override
public List<Transport> getTransports() {
return transports;
}
@Override
public Map<Transport, Integer> getTransportPorts() {
return transportPorts;
}
@Override
public int getTransportPort(Transport transport) {
Integer port = transportPorts.get(transport);
if (port != null) {
return port;
}
return transport.getDefaultPort();
}
@Override
public boolean isSslEnabled() {
return sslEnabled;
}
@Override
public boolean isGzipEnabled() {
return gzipEnabled;
}
@Override
public boolean isSnappyEnabled() {
return snappyEnabled;
}
@Override
public HandshakeHandler getHandshakeHandler() {
return handshakeHandler;
}
}
}