1+ /*
2+ * Copyright 2025 The gRPC Authors
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+ package io .grpc ;
18+
19+ import static com .google .common .base .Preconditions .checkNotNull ;
20+
21+ import java .util .logging .Level ;
22+ import java .util .logging .Logger ;
23+
24+ /**
25+ * Utilities for working with {@link ChildChannelConfigurer}.
26+ *
27+ * @since 1.79.0
28+ */
29+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/12574" )
30+ public final class ChildChannelConfigurers {
31+ private static final Logger logger = Logger .getLogger (ChildChannelConfigurers .class .getName ());
32+
33+ // Singleton no-op instance to avoid object churn
34+ private static final ChildChannelConfigurer NO_OP = builder -> {
35+ };
36+
37+ private ChildChannelConfigurers () { // Prevent instantiation
38+ }
39+
40+ /**
41+ * Returns a configurer that does nothing.
42+ * Useful as a default value to avoid null checks in internal code.
43+ */
44+ public static ChildChannelConfigurer noOp () {
45+ return NO_OP ;
46+ }
47+
48+ /**
49+ * Returns a configurer that applies all the given configurers in sequence.
50+ *
51+ * <p>If any configurer in the chain throws an exception, the remaining ones are skipped
52+ * (unless wrapped in {@link #safe(ChildChannelConfigurer)}).
53+ *
54+ * @param configurers the configurers to apply in order. Null elements are ignored.
55+ */
56+ public static ChildChannelConfigurer compose (ChildChannelConfigurer ... configurers ) {
57+ checkNotNull (configurers , "configurers" );
58+ return builder -> {
59+ for (ChildChannelConfigurer configurer : configurers ) {
60+ if (configurer != null ) {
61+ configurer .accept (builder );
62+ }
63+ }
64+ };
65+ }
66+
67+ /**
68+ * Returns a configurer that applies the delegate but catches and logs any exceptions.
69+ *
70+ * <p>This prevents a buggy configurer (e.g., one that fails metric setup) from crashing
71+ * the critical path of channel creation.
72+ *
73+ * @param delegate the configurer to wrap.
74+ */
75+ public static ChildChannelConfigurer safe (ChildChannelConfigurer delegate ) {
76+ checkNotNull (delegate , "delegate" );
77+ return builder -> {
78+ try {
79+ delegate .accept (builder );
80+ } catch (Exception e ) {
81+ logger .log (Level .WARNING , "Failed to apply child channel configuration" , e );
82+ }
83+ };
84+ }
85+
86+ /**
87+ * Returns a configurer that applies the delegate only if the given condition is true.
88+ *
89+ * <p>Useful for applying interceptors only in specific environments (e.g., Debug/Test).
90+ *
91+ * @param condition true to apply the delegate, false to do nothing.
92+ * @param delegate the configurer to apply if condition is true.
93+ */
94+ public static ChildChannelConfigurer conditional (boolean condition ,
95+ ChildChannelConfigurer delegate ) {
96+ checkNotNull (delegate , "delegate" );
97+ return condition ? delegate : NO_OP ;
98+ }
99+ }
0 commit comments