View Javadoc
1   /*
2    * Copyright 2016 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  
17  package io.netty5.util;
18  
19  import io.netty5.util.internal.ObjectUtil;
20  import io.netty5.util.internal.PlatformDependent;
21  import io.netty5.util.internal.SystemPropertyUtil;
22  import io.netty5.util.internal.logging.InternalLogger;
23  import io.netty5.util.internal.logging.InternalLoggerFactory;
24  
25  import java.lang.reflect.Constructor;
26  
27  import static java.util.Objects.requireNonNull;
28  
29  /**
30   * This static factory should be used to load {@link ResourceLeakDetector}s as needed
31   */
32  public abstract class ResourceLeakDetectorFactory {
33      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetectorFactory.class);
34  
35      private static volatile ResourceLeakDetectorFactory factoryInstance = new DefaultResourceLeakDetectorFactory();
36  
37      /**
38       * Get the singleton instance of this factory class.
39       *
40       * @return the current {@link ResourceLeakDetectorFactory}
41       */
42      public static ResourceLeakDetectorFactory instance() {
43          return factoryInstance;
44      }
45  
46      /**
47       * Set the factory's singleton instance. This has to be called before the static initializer of the
48       * {@link ResourceLeakDetector} is called by all the callers of this factory. That is, before initializing a
49       * Netty Bootstrap.
50       *
51       * @param factory the instance that will become the current {@link ResourceLeakDetectorFactory}'s singleton
52       */
53      public static void setResourceLeakDetectorFactory(ResourceLeakDetectorFactory factory) {
54          factoryInstance = requireNonNull(factory, "factory");
55      }
56  
57      /**
58       * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class.
59       *
60       * @param resource the resource class used to initialize the {@link ResourceLeakDetector}
61       * @param <T> the type of the resource class
62       * @return a new instance of {@link ResourceLeakDetector}
63       */
64      public final <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource) {
65          return newResourceLeakDetector(resource, ResourceLeakDetector.SAMPLING_INTERVAL);
66      }
67  
68      /**
69       * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class.
70       *
71       * @param resource the resource class used to initialize the {@link ResourceLeakDetector}
72       * @param samplingInterval the interval on which sampling takes place
73       * @param <T> the type of the resource class
74       * @return a new instance of {@link ResourceLeakDetector}
75       */
76      public abstract <T> ResourceLeakDetector<T> newResourceLeakDetector(
77              Class<T> resource, int samplingInterval);
78  
79      /**
80       * Default implementation that loads custom leak detector via system property
81       */
82      private static final class DefaultResourceLeakDetectorFactory extends ResourceLeakDetectorFactory {
83          private final Constructor<?> customClassConstructor;
84  
85          DefaultResourceLeakDetectorFactory() {
86              String customLeakDetector;
87              try {
88                  customLeakDetector = SystemPropertyUtil.get("io.netty5.customResourceLeakDetector");
89              } catch (Throwable cause) {
90                  logger.error("Could not access System property: io.netty5.customResourceLeakDetector", cause);
91                  customLeakDetector = null;
92              }
93              if (customLeakDetector == null) {
94                  customClassConstructor = null;
95              } else {
96                  customClassConstructor = customClassConstructor(customLeakDetector);
97              }
98          }
99  
100         private static Constructor<?> customClassConstructor(String customLeakDetector) {
101             try {
102                 final Class<?> detectorClass = Class.forName(customLeakDetector, true,
103                         PlatformDependent.getSystemClassLoader());
104 
105                 if (ResourceLeakDetector.class.isAssignableFrom(detectorClass)) {
106                     return detectorClass.getConstructor(Class.class, int.class);
107                 } else {
108                     logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector);
109                 }
110             } catch (Throwable t) {
111                 logger.error("Could not load custom resource leak detector class provided: {}",
112                         customLeakDetector, t);
113             }
114             return null;
115         }
116 
117         @Override
118         public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval) {
119             ObjectUtil.checkPositive(samplingInterval, "samplingInterval");
120             if (customClassConstructor != null) {
121                 try {
122                     @SuppressWarnings("unchecked")
123                     ResourceLeakDetector<T> leakDetector =
124                             (ResourceLeakDetector<T>) customClassConstructor.newInstance(resource, samplingInterval);
125                     logger.debug("Loaded custom ResourceLeakDetector: {}",
126                             customClassConstructor.getDeclaringClass().getName());
127                     return leakDetector;
128                 } catch (Throwable t) {
129                     logger.error(
130                             "Could not load custom resource leak detector provided: {} with the given resource: {}",
131                             customClassConstructor.getDeclaringClass().getName(), resource, t);
132                 }
133             }
134 
135             ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<>(resource, samplingInterval);
136             logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector);
137             return resourceLeakDetector;
138         }
139     }
140 }