org.firebirdsql.gds.ng.wire.ProtocolCollection Maven / Gradle / Ivy
Show all versions of jaybird Show documentation
/*
* Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program 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
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a source control history command.
*
* All rights reserved.
*/
package org.firebirdsql.gds.ng.wire;
import org.firebirdsql.jaybird.props.AttachmentProperties;
import org.firebirdsql.jaybird.util.PluginLoader;
import java.util.*;
import java.util.stream.Stream;
import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.FB_PROTOCOL_FLAG;
import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.MAXIMUM_SUPPORTED_PROTOCOL_VERSION;
import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.MINIMUM_SUPPORTED_PROTOCOL_VERSION;
import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION10;
/**
* Collection of protocols for a connect request.
*
* In general, {@link #getProtocols(String)} should be used with the {@code enableProtocol} connection property value.
*
*
* @author Mark Rotteveel
* @since 3.0
*/
public final class ProtocolCollection implements Iterable {
private final Map descriptorMap;
private static final ProtocolCollection AVAILABLE_PROTOCOLS;
private static final ProtocolCollection SUPPORTED_PROTOCOLS;
static {
// Load protocol implementation information
Collection availableProtocols = PluginLoader.findPlugins(ProtocolDescriptor.class, List.of(
"org.firebirdsql.gds.ng.wire.version10.Version10Descriptor",
"org.firebirdsql.gds.ng.wire.version11.Version11Descriptor",
"org.firebirdsql.gds.ng.wire.version12.Version12Descriptor",
"org.firebirdsql.gds.ng.wire.version13.Version13Descriptor",
"org.firebirdsql.gds.ng.wire.version15.Version15Descriptor",
"org.firebirdsql.gds.ng.wire.version16.Version16Descriptor",
"org.firebirdsql.gds.ng.wire.version18.Version18Descriptor"));
AVAILABLE_PROTOCOLS = create(availableProtocols);
SUPPORTED_PROTOCOLS = create(availableProtocols.stream()
.filter(p -> MINIMUM_SUPPORTED_PROTOCOL_VERSION <= p.getVersion()
&& p.getVersion() <= MAXIMUM_SUPPORTED_PROTOCOL_VERSION)
.toList());
}
private ProtocolCollection(Map protocolDescriptors) {
// Note: we are not making a defensive copy as the Map should never leak outside this implementation
this.descriptorMap = Collections.unmodifiableMap(protocolDescriptors);
}
@Override
public Iterator iterator() {
return descriptorMap.values().iterator();
}
/**
* @param protocolVersion
* Version of the protocol
* @return ProtocolDescriptor for the specified version, or null if the
* version is not in this ProtocolCollection
*/
public ProtocolDescriptor getProtocolDescriptor(int protocolVersion) {
return descriptorMap.get(protocolVersion);
}
/**
* @return The protocol count
*/
public int getProtocolCount() {
return descriptorMap.size();
}
/**
* Get a list with the protocol versions in this collection.
*
* The returned List is created fresh on every call. Changes to
* the list have no effect on this object.
*
*
* @return Protocol version numbers
*/
public List getProtocolVersions() {
List versions = new ArrayList<>(getProtocolCount());
for (ProtocolDescriptor descriptor : this) {
versions.add(descriptor.getVersion());
}
return versions;
}
/**
* @return stream of the protocol descriptors held by this protocol collection
* @since 6
*/
public Stream stream() {
return descriptorMap.values().stream();
}
/**
* Creates a ProtocolCollection with the specified ProtocolDescriptors.
*
* If {@code descriptors} contains multiple implementations with the same value for
* {@link ProtocolDescriptor#getVersion()}, then the first implementation with the highest value for
* {@link ProtocolDescriptor#getWeight()} will be loaded into the collection.
*
*
* @param descriptors
* Vararg parameter with ProtocolDescriptors
* @return ProtocolCollection
*/
public static ProtocolCollection create(ProtocolDescriptor... descriptors) {
return create(Arrays.asList(descriptors));
}
private static ProtocolCollection create(Collection descriptors) {
Map descriptorMap = new HashMap<>();
for (ProtocolDescriptor descriptor : descriptors) {
ProtocolDescriptor existingDescriptor = descriptorMap.get(descriptor.getVersion());
if (existingDescriptor == null || descriptor.getWeight() > existingDescriptor.getWeight()) {
descriptorMap.put(descriptor.getVersion(), descriptor);
}
}
return new ProtocolCollection(descriptorMap);
}
/**
* Returns the supported ProtocolCollection.
*
* The supported ProtocolCollection is created when this class is loaded by the classloader.
*
*
* The returned collection is a subset of {@link #getAvailableProtocols()}, retaining only the supported protocol
* versions.
*
*
* @return supported ProtocolCollection
* @see ProtocolCollection#create(ProtocolDescriptor...)
* @since 6
*/
public static ProtocolCollection getSupportedProtocols() {
return SUPPORTED_PROTOCOLS;
}
/**
* Returns the available ProtocolCollection.
*
* The available ProtocolCollection is created when this class is loaded by the classloader.
*
*
* This implementation uses the {@link ServiceLoader} to load the collection based on all {@link ProtocolDescriptor}
* implementations found using all the
* {@code /META-INF/services/org.firebirdsql.gds.ng.wire.ProtocolDescriptor} in the classpath. If multiple
* implementations with the same value for {@link ProtocolDescriptor#getVersion()} are found, then the first
* implementation with the highest value for {@link ProtocolDescriptor#getWeight()} will be loaded into the default
* collection.
*
*
* @return available ProtocolCollection
* @see ProtocolCollection#create(ProtocolDescriptor...)
* @since 6
*/
public static ProtocolCollection getAvailableProtocols() {
return AVAILABLE_PROTOCOLS;
}
/**
* Returns the protocol collection consisting of the supported protocol versions and the additional protocol
* versions defined by the {@code enableProtocol} connection string property.
*
* @param enableProtocol
* enable protocol connection property value, see {@link AttachmentProperties#getEnableProtocol()}.
* @return supported protocols and the available additional protocols listed in {@code enableProtocol}
* @since 6
*/
public static ProtocolCollection getProtocols(String enableProtocol) {
if (enableProtocol == null) return getSupportedProtocols();
return switch (enableProtocol.trim()) {
case "" -> getSupportedProtocols();
case "*" -> getAvailableProtocols();
default -> getProtocols0(enableProtocol);
};
}
private static ProtocolCollection getProtocols0(String enableProtocol) {
return create(
Stream.concat(
SUPPORTED_PROTOCOLS.stream(),
Arrays.stream(enableProtocol.split(","))
.map(ProtocolCollection::tryParseInt)
.filter(Objects::nonNull)
.mapToInt(Integer::intValue)
.distinct()
.mapToObj(ProtocolCollection::tryGetProtocolDescriptorUnmaskedAndMasked)
.filter(Objects::nonNull))
.toList());
}
private static ProtocolDescriptor tryGetProtocolDescriptorUnmaskedAndMasked(int version) {
ProtocolDescriptor descriptor = AVAILABLE_PROTOCOLS.getProtocolDescriptor(version);
if (descriptor == null && (version & FB_PROTOCOL_FLAG) != FB_PROTOCOL_FLAG && version != PROTOCOL_VERSION10) {
descriptor = AVAILABLE_PROTOCOLS.getProtocolDescriptor(FB_PROTOCOL_FLAG | version);
}
return descriptor;
}
private static Integer tryParseInt(String s) {
try {
return Integer.valueOf(s.trim());
} catch (NumberFormatException e) {
return null;
}
}
}