io.helidon.dbclient.DbClientServiceBase Maven / Gradle / Ivy
Show all versions of helidon-dbclient Show documentation
/*
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.dbclient;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import io.helidon.common.Builder;
import io.helidon.common.config.Config;
/**
* A base implementation of a client service that supports configuration
* of execution based on a statement name pattern and statement types.
*/
public abstract class DbClientServiceBase implements DbClientService {
private final Predicate predicate;
/**
* Create a new instance based on the builder base each implementation must extend.
*
* @param builder builder to configure predicate to use
*/
protected DbClientServiceBase(BuilderBase, ?> builder) {
this.predicate = builder.predicate();
}
@Override
public final DbClientServiceContext statement(DbClientServiceContext context) {
if (predicate.test(context)) {
return apply(context);
}
return context;
}
/**
* This method is only invoked if the predicate for this service
* was passed.
*
* @param context db client invocation context
* @return single with the new context (or the same one if not modified)
* @see #statement(io.helidon.dbclient.DbClientServiceContext)
*/
protected abstract DbClientServiceContext apply(DbClientServiceContext context);
/**
* A base class for builders of {@link DbClientServiceBase}.
*
* @param type of the builder extending this class
* @param Type of the built {@link DbClientServiceBase} instance
*/
public abstract static class BuilderBase, T extends DbClientServiceBase>
implements Builder {
private static final Predicate YES = it -> true;
private static final Predicate NO = it -> false;
// we can filter by statement name
private final Set statementNames = new LinkedHashSet<>();
// and statement type
private final Set statementTypes = EnumSet.noneOf(DbStatementType.class);
private Predicate predicate;
private boolean enabled = true;
/**
* Default constructor.
*/
protected BuilderBase() {
}
/**
* Configure this client service from config.
*
* Supported keys:
*
* Database Client Service configuration options
*
* key
* default value
* description
*
*
* statement-names
*
* An array of statement name patterns to apply this service for. If undefined, service
* would be executed for all statements.
* See {@link #statementNames(String...)} and {@link java.util.regex.Pattern}
*
*
* statement-types
*
* An array of statement types to apply this service for. If undefined, service
* would be executed for all statements.
* See {@link #statementTypes(io.helidon.dbclient.DbStatementType...)}.
*
*
* enabled
* {@code true}
* Whether this client service is enabled. See {@link #enabled(boolean)}
*
*
*
* @param config configuration on the node of this service
* @return updated builder instance
*/
public B config(Config config) {
config.get("statement-names").asList(String.class).ifPresent(this::statementNames);
config.get("statement-types").asNodeList()
.ifPresent(nodes -> statementTypes(nodes.stream()
.map(cfg -> DbStatementType.valueOf(cfg.asString().get()))
.toList()));
config.get("enabled").asBoolean().ifPresent(this::enabled);
return identity();
}
/**
* Configure a predicate whose result will be used to decide whether to
* trigger this service or not.
*
* When a predicate is explicitly configured, {@link #statementNames(String...)}
* and {@link #statementTypes(io.helidon.dbclient.DbStatementType...)} is ignored.
*
* @param predicate predicate that should return {@code true} to enable this
* service, or {@code false} to disable it
* @return updated builder instance
*/
public B statementPredicate(Predicate predicate) {
this.predicate = predicate;
return identity();
}
/**
* Configure statement types this service will be triggered for.
* If an explicit {@link #statementPredicate(java.util.function.Predicate)} is configured,
* this method is ignored.
*
* @param types types that trigger this service
* @return updated builder instance
*/
public B statementTypes(DbStatementType... types) {
return statementTypes(List.of(types));
}
/**
* Configure statement name patterns this service will be triggered for.
* If an explicit {@link #statementPredicate(java.util.function.Predicate)} is configured,
* this method is ignored.
*
* @param names name patterns (as in {@link java.util.regex.Pattern}) that trigger this service
* @return updated builder instance
*/
public B statementNames(String... names) {
return statementNames(List.of(names));
}
/**
* Configure whether this service is enabled or not.
*
* @param enabled whether to enable this service or disable it, {@code true} by default
*/
public void enabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Configures statement types from configuration.
*
* @param types types to add for this service
* @return updated builder instance
*/
protected B statementTypes(List types) {
this.statementTypes.addAll(types);
return identity();
}
/**
* Configures statement name patterns from configuration.
*
* @param names names to add for this service
* @return updated builder instance
*/
protected B statementNames(List names) {
this.statementNames.addAll(names);
return identity();
}
/**
* Set of statement name patterns.
*
* @return configured statement names
*/
protected Set statementNames() {
return statementNames;
}
/**
* Set of statement types.
*
* @return configured statement types
*/
protected Set statementTypes() {
return statementTypes;
}
/**
* Predicate used to build a client service.
*
* The predicate always returns {@code false} if service is disabled.
*
* The predicate is obtained from the configured predicate using
* {@link #statementPredicate(java.util.function.Predicate)},
* if none is configured, it is created from configured statement types and statement names.
* If none are configured, the predicate just returns {@code true}.
*
* @return predicate to check whether this service should be invoked for current statement context
*/
protected Predicate predicate() {
if (!enabled) {
return NO;
}
if (null != predicate) {
return predicate;
}
List namePatterns = statementNames.stream()
.map(Pattern::compile)
.toList();
Set types = EnumSet.copyOf(statementTypes);
Predicate namePredicate;
Predicate typePredicate;
if (namePatterns.isEmpty()) {
namePredicate = YES;
} else {
namePredicate = it -> {
String statementName = it.statementName();
for (Pattern namePattern : namePatterns) {
if (namePattern.matcher(statementName).matches()) {
return true;
}
}
return false;
};
}
if (types.isEmpty()) {
typePredicate = YES;
} else {
typePredicate = it -> types.contains(it.statementType());
}
return context -> namePredicate.test(context) && typePredicate.test(context);
}
}
}