You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2020/11/30 21:14:23 UTC
[nifi] branch main updated: NIFI-8054: Updated ReflectionUtils to
use a WeakHashMap for the mapping of annotations to methods with that
annotation. This way,
the ReflectionUtils class will not hold a reference to Classes that are no
longer referenced elsewhere. (#4694)
This is an automated email from the ASF dual-hosted git repository.
bbende pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new aaa1452 NIFI-8054: Updated ReflectionUtils to use a WeakHashMap for the mapping of annotations to methods with that annotation. This way, the ReflectionUtils class will not hold a reference to Classes that are no longer referenced elsewhere. (#4694)
aaa1452 is described below
commit aaa1452d041ff34f3647004e73a1f78f59c561d4
Author: markap14 <ma...@hotmail.com>
AuthorDate: Mon Nov 30 16:14:12 2020 -0500
NIFI-8054: Updated ReflectionUtils to use a WeakHashMap for the mapping of annotations to methods with that annotation. This way, the ReflectionUtils class will not hold a reference to Classes that are no longer referenced elsewhere. (#4694)
---
.../org/apache/nifi/util/ClassAnnotationPair.java | 61 --------------------
.../java/org/apache/nifi/util/ReflectionUtils.java | 67 ++++++++++++++++++----
2 files changed, 57 insertions(+), 71 deletions(-)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ClassAnnotationPair.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ClassAnnotationPair.java
deleted file mode 100644
index 44ffa70..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ClassAnnotationPair.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.nifi.util;
-
-import java.lang.annotation.Annotation;
-import java.util.Arrays;
-
-public class ClassAnnotationPair {
- private final Class<?> clazz;
- private final Class<? extends Annotation>[] annotations;
-
- public ClassAnnotationPair(final Class<?> clazz, final Class<? extends Annotation>[] annotations) {
- this.clazz = clazz;
- this.annotations = annotations;
- }
-
- public Class<?> getDeclaredClass() {
- return clazz;
- }
-
- public Class<? extends Annotation>[] getAnnotations() {
- return annotations;
- }
-
- @Override
- public int hashCode() {
- return 41 + 47 * clazz.hashCode() + 47 * Arrays.hashCode(annotations);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj == null) {
- return false;
- }
-
- if (!(obj instanceof ClassAnnotationPair)) {
- return false;
- }
-
- final ClassAnnotationPair other = (ClassAnnotationPair) obj;
- return clazz == other.clazz && Arrays.equals(annotations, other.annotations);
- }
-}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ReflectionUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ReflectionUtils.java
index 5f163e6..420fe08 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ReflectionUtils.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/ReflectionUtils.java
@@ -27,16 +27,16 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
public class ReflectionUtils {
private final static Logger LOG = LoggerFactory.getLogger(ReflectionUtils.class);
- private static ConcurrentMap<ClassAnnotationPair, List<Method>> annotationCache = new ConcurrentHashMap<>();
+ private static Map<Class<?>, Map<Annotations, List<Method>>> annotationCache = new WeakHashMap<>();
/**
* Invokes all methods on the given instance that have been annotated with the given Annotation. If the signature of the method that is defined in <code>instance</code> uses 1 or more parameters,
@@ -158,19 +158,32 @@ public class ReflectionUtils {
return isSuccess;
}
- private static List<Method> findMethodsWithAnnotations(final Class<?> clazz, final Class<? extends Annotation>[] annotations) {
+ private static List<Method> findMethodsWithAnnotations(final Class<?> clazz, final Class<? extends Annotation>[] annotationClasses) {
// We use a cache here to store a mapping of Class & Annotation[] to those methods that contain the annotation.
// This is done because discovering this using Reflection is fairly expensive (can take up to tens of milliseconds on laptop).
// While this may not seem like much time, consider deleting a Process Group with thousands of Processors or instantiating
// a Template with thousands of Processors. This can add up to several seconds very easily.
- final ClassAnnotationPair pair = new ClassAnnotationPair(clazz, annotations);
- List<Method> methods = annotationCache.get(pair);
- if (methods != null) {
- return methods;
+ final Annotations annotations = new Annotations(annotationClasses);
+
+ synchronized (annotationCache) {
+ final Map<Annotations, List<Method>> innerMap = annotationCache.get(clazz);
+ if (innerMap != null) {
+ final List<Method> methods = innerMap.get(annotations);
+ if (methods != null) {
+ return methods;
+ }
+ }
+ }
+
+ // The methods to invoke have not been cached. Discover them via reflection.
+ final List<Method> methods = discoverMethodsWithAnnotations(clazz, annotationClasses);
+
+ // Store the discovered methods in our cache so that they are available next time.
+ synchronized (annotationCache) {
+ final Map<Annotations, List<Method>> innerMap = annotationCache.computeIfAbsent(clazz, key -> new ConcurrentHashMap<>());
+ innerMap.putIfAbsent(annotations, methods);
}
- methods = discoverMethodsWithAnnotations(clazz, annotations);
- annotationCache.putIfAbsent(pair, methods);
return methods;
}
@@ -305,4 +318,38 @@ public class ReflectionUtils {
return false;
}
}
+
+ private static class Annotations {
+ private final Class<? extends Annotation>[] array;
+
+ public Annotations(final Class<? extends Annotation>[] array) {
+ this.array = array;
+ }
+
+ public Class<? extends Annotation>[] getArray() {
+ return array;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(array);
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Annotations)) {
+ return false;
+ }
+
+ final Annotations otherAnnotations = (Annotations) other;
+ return Arrays.equals(this.array, otherAnnotations.array);
+ }
+ }
}