View Javadoc
1   /*
2    * Copyright 2023 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.bootstrap;
17  
18  import io.netty.util.internal.SystemPropertyUtil;
19  import io.netty.util.internal.logging.InternalLogger;
20  import io.netty.util.internal.logging.InternalLoggerFactory;
21  
22  import java.lang.ref.WeakReference;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Comparator;
27  import java.util.List;
28  import java.util.ServiceLoader;
29  
30  /**
31   * The configurable facade that decides what {@link ChannelInitializerExtension}s to load and where to find them.
32   */
33  abstract class ChannelInitializerExtensions {
34      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializerExtensions.class);
35      private static volatile ChannelInitializerExtensions implementation;
36  
37      private ChannelInitializerExtensions() {
38      }
39  
40      /**
41       * Get the configuration extensions, which is a no-op implementation by default,
42       * or a service-loading implementation if the {@code io.netty.bootstrap.extensions} system property is
43       * {@code serviceload}.
44       */
45      static ChannelInitializerExtensions getExtensions() {
46          ChannelInitializerExtensions impl = implementation;
47          if (impl == null) {
48              synchronized (ChannelInitializerExtensions.class) {
49                  impl = implementation;
50                  if (impl != null) {
51                      return impl;
52                  }
53                  String extensionProp = SystemPropertyUtil.get(ChannelInitializerExtension.EXTENSIONS_SYSTEM_PROPERTY);
54                  logger.debug("-Dio.netty.bootstrap.extensions: {}", extensionProp);
55                  if ("serviceload".equalsIgnoreCase(extensionProp)) {
56                      impl = new ServiceLoadingExtensions(true);
57                  } else if ("log".equalsIgnoreCase(extensionProp)) {
58                      impl = new ServiceLoadingExtensions(false);
59                  } else {
60                      impl = new EmptyExtensions();
61                  }
62                  implementation = impl;
63              }
64          }
65          return impl;
66      }
67  
68      /**
69       * Get the list of available extensions. The list is unmodifiable.
70       */
71      abstract Collection<ChannelInitializerExtension> extensions(ClassLoader cl);
72  
73      private static final class EmptyExtensions extends ChannelInitializerExtensions {
74          @Override
75          Collection<ChannelInitializerExtension> extensions(ClassLoader cl) {
76              return Collections.emptyList();
77          }
78      }
79  
80      private static final class ServiceLoadingExtensions extends ChannelInitializerExtensions {
81          private final boolean loadAndCache;
82  
83          private WeakReference<ClassLoader> classLoader;
84          private Collection<ChannelInitializerExtension> extensions;
85  
86          ServiceLoadingExtensions(boolean loadAndCache) {
87              this.loadAndCache = loadAndCache;
88          }
89  
90          @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
91          @Override
92          synchronized Collection<ChannelInitializerExtension> extensions(ClassLoader cl) {
93              ClassLoader configured = classLoader == null ? null : classLoader.get();
94              if (configured == null || configured != cl) {
95                  Collection<ChannelInitializerExtension> loaded = serviceLoadExtensions(loadAndCache, cl);
96                  classLoader = new WeakReference<ClassLoader>(cl);
97                  extensions = loadAndCache ? loaded : Collections.<ChannelInitializerExtension>emptyList();
98              }
99              return extensions;
100         }
101 
102         private static Collection<ChannelInitializerExtension> serviceLoadExtensions(boolean load, ClassLoader cl) {
103             List<ChannelInitializerExtension> extensions = new ArrayList<ChannelInitializerExtension>();
104 
105             ServiceLoader<ChannelInitializerExtension> loader = ServiceLoader.load(
106                     ChannelInitializerExtension.class, cl);
107             for (ChannelInitializerExtension extension : loader) {
108                 extensions.add(extension);
109             }
110 
111             if (!extensions.isEmpty()) {
112                 Collections.sort(extensions, new Comparator<ChannelInitializerExtension>() {
113                     @Override
114                     public int compare(ChannelInitializerExtension a, ChannelInitializerExtension b) {
115                         return Double.compare(a.priority(), b.priority());
116                     }
117                 });
118                 logger.info("ServiceLoader {}(s) {}: {}", ChannelInitializerExtension.class.getSimpleName(),
119                         load ? "registered" : "detected", extensions);
120                 return Collections.unmodifiableList(extensions);
121             }
122             logger.debug("ServiceLoader {}(s) {}: []", ChannelInitializerExtension.class.getSimpleName(),
123                     load ? "registered" : "detected");
124             return Collections.emptyList();
125         }
126     }
127 }