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    *   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, 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.netty.util;
18  
19  import io.netty.util.internal.ObjectUtil;
20  import io.netty.util.internal.PlatformDependent;
21  import io.netty.util.internal.SystemPropertyUtil;
22  import io.netty.util.internal.logging.InternalLogger;
23  import io.netty.util.internal.logging.InternalLoggerFactory;
24  
25  import java.lang.reflect.Constructor;
26  import java.security.AccessController;
27  import java.security.PrivilegedAction;
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 = ObjectUtil.checkNotNull(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.DEFAULT_SAMPLING_INTERVAL);
66      }
67  
68      /**
69       * @deprecated Use {@link #newResourceLeakDetector(Class, int)} instead.
70       * <p>
71       * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class.
72       *
73       * @param resource the resource class used to initialize the {@link ResourceLeakDetector}
74       * @param samplingInterval the interval on which sampling takes place
75       * @param maxActive This is deprecated and will be ignored.
76       * @param <T> the type of the resource class
77       * @return a new instance of {@link ResourceLeakDetector}
78       */
79      @Deprecated
80      public abstract <T> ResourceLeakDetector<T> newResourceLeakDetector(
81              Class<T> resource, int samplingInterval, long maxActive);
82  
83      /**
84       * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class.
85       *
86       * @param resource the resource class used to initialize the {@link ResourceLeakDetector}
87       * @param samplingInterval the interval on which sampling takes place
88       * @param <T> the type of the resource class
89       * @return a new instance of {@link ResourceLeakDetector}
90       */
91      @SuppressWarnings("deprecation")
92      public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval) {
93          return newResourceLeakDetector(resource, ResourceLeakDetector.DEFAULT_SAMPLING_INTERVAL, Long.MAX_VALUE);
94      }
95  
96      /**
97       * Default implementation that loads custom leak detector via system property
98       */
99      private static final class DefaultResourceLeakDetectorFactory extends ResourceLeakDetectorFactory {
100         private final Constructor<?> obsoleteCustomClassConstructor;
101         private final Constructor<?> customClassConstructor;
102 
103         DefaultResourceLeakDetectorFactory() {
104             String customLeakDetector;
105             try {
106                 customLeakDetector = AccessController.doPrivileged(new PrivilegedAction<String>() {
107                     @Override
108                     public String run() {
109                         return SystemPropertyUtil.get("io.netty.customResourceLeakDetector");
110                     }
111                 });
112             } catch (Throwable cause) {
113                 logger.error("Could not access System property: io.netty.customResourceLeakDetector", cause);
114                 customLeakDetector = null;
115             }
116             if (customLeakDetector == null) {
117                 obsoleteCustomClassConstructor = customClassConstructor = null;
118             } else {
119                 obsoleteCustomClassConstructor = obsoleteCustomClassConstructor(customLeakDetector);
120                 customClassConstructor = customClassConstructor(customLeakDetector);
121             }
122         }
123 
124         private static Constructor<?> obsoleteCustomClassConstructor(String customLeakDetector) {
125             try {
126                 final Class<?> detectorClass = Class.forName(customLeakDetector, true,
127                         PlatformDependent.getSystemClassLoader());
128 
129                 if (ResourceLeakDetector.class.isAssignableFrom(detectorClass)) {
130                     return detectorClass.getConstructor(Class.class, int.class, long.class);
131                 } else {
132                     logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector);
133                 }
134             } catch (Throwable t) {
135                 logger.error("Could not load custom resource leak detector class provided: {}",
136                         customLeakDetector, t);
137             }
138             return null;
139         }
140 
141         private static Constructor<?> customClassConstructor(String customLeakDetector) {
142             try {
143                 final Class<?> detectorClass = Class.forName(customLeakDetector, true,
144                         PlatformDependent.getSystemClassLoader());
145 
146                 if (ResourceLeakDetector.class.isAssignableFrom(detectorClass)) {
147                     return detectorClass.getConstructor(Class.class, int.class);
148                 } else {
149                     logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector);
150                 }
151             } catch (Throwable t) {
152                 logger.error("Could not load custom resource leak detector class provided: {}",
153                         customLeakDetector, t);
154             }
155             return null;
156         }
157 
158         @SuppressWarnings("deprecation")
159         @Override
160         public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval,
161                                                                    long maxActive) {
162             if (obsoleteCustomClassConstructor != null) {
163                 try {
164                     @SuppressWarnings("unchecked")
165                     ResourceLeakDetector<T> leakDetector =
166                             (ResourceLeakDetector<T>) obsoleteCustomClassConstructor.newInstance(
167                                     resource, samplingInterval, maxActive);
168                     logger.debug("Loaded custom ResourceLeakDetector: {}",
169                             obsoleteCustomClassConstructor.getDeclaringClass().getName());
170                     return leakDetector;
171                 } catch (Throwable t) {
172                     logger.error(
173                             "Could not load custom resource leak detector provided: {} with the given resource: {}",
174                             obsoleteCustomClassConstructor.getDeclaringClass().getName(), resource, t);
175                 }
176             }
177 
178             ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<T>(resource, samplingInterval,
179                                                                                        maxActive);
180             logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector);
181             return resourceLeakDetector;
182         }
183 
184         @Override
185         public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval) {
186             if (customClassConstructor != null) {
187                 try {
188                     @SuppressWarnings("unchecked")
189                     ResourceLeakDetector<T> leakDetector =
190                             (ResourceLeakDetector<T>) customClassConstructor.newInstance(resource, samplingInterval);
191                     logger.debug("Loaded custom ResourceLeakDetector: {}",
192                             customClassConstructor.getDeclaringClass().getName());
193                     return leakDetector;
194                 } catch (Throwable t) {
195                     logger.error(
196                             "Could not load custom resource leak detector provided: {} with the given resource: {}",
197                             customClassConstructor.getDeclaringClass().getName(), resource, t);
198                 }
199             }
200 
201             ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<T>(resource, samplingInterval);
202             logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector);
203             return resourceLeakDetector;
204         }
205     }
206 }