11package ru .vyarus .java .generics .resolver .context ;
22
3- import java .lang . reflect . ParameterizedType ;
4- import java . lang . reflect . Type ;
5- import java .lang . reflect . TypeVariable ;
6- import java .util .* ;
3+ import ru . vyarus . java .generics . resolver . util . GenericInfoUtils ;
4+
5+ import java .util . Map ;
6+ import java .util .WeakHashMap ;
77import java .util .concurrent .locks .ReentrantLock ;
88
99/**
1010 * Analyze class hierarchy and produce class hierarchy resolved generics object.
1111 * Resolved generics descriptors are cached.
1212 * <p>Note: when ignore classes used, cache will not work: such descriptors are always resolved.</p>
13+ * <p>Cache may be disabled (e.g. when JRebel used) by using environment variable or system property e.g.:
14+ * {@code System.setProperty(GenericsInfoFactory.CACHE_PROPERTY, 'false')}.
15+ * Property value checked on cache write. To clear current cache state use static method.</p>
1316 *
1417 * @author Vyacheslav Rusakov
1518 * @since 16.10.2014
1619 */
1720public final class GenericsInfoFactory {
1821
22+ /**
23+ * System property or environment variable name to disable cache.
24+ * If value is 'false' - cache disabled, otherwise cache enabled.
25+ */
26+ public static final String CACHE_PROPERTY = GenericsInfoFactory .class .getName () + ".cache" ;
27+
1928 private static final Map <Class <?>, GenericsInfo > CACHE = new WeakHashMap <Class <?>, GenericsInfo >();
2029 // lock will not affect performance for cached descriptors, just to make sure nothing was build two times
2130 private static final ReentrantLock LOCK = new ReentrantLock ();
22- @ SuppressWarnings ("PMD.LooseCoupling" )
23- private static final LinkedHashMap <String , Type > EMPTY_MAP = new LinkedHashMap <String , Type >(0 );
24- private static final String GROOVY_OBJECT = "GroovyObject" ;
2531
2632 private GenericsInfoFactory () {
2733 }
@@ -35,20 +41,23 @@ private GenericsInfoFactory() {
3541 * @return descriptor for class hierarchy generics substitution
3642 */
3743 public static GenericsInfo create (final Class <?> type , final Class <?>... ignoreClasses ) {
38- GenericsInfo descriptor = ignoreClasses .length > 0 ? buildDescriptor (type , ignoreClasses ) : CACHE .get (type );
44+ GenericsInfo descriptor = ignoreClasses .length > 0
45+ ? GenericInfoUtils .create (type , ignoreClasses ) : CACHE .get (type );
3946 if (descriptor == null ) {
4047 LOCK .lock ();
4148 try {
4249 if (CACHE .get (type ) != null ) {
4350 // descriptor could be created while thread wait for lock
4451 descriptor = CACHE .get (type );
4552 } else {
46- descriptor = buildDescriptor (type );
47- // internal check
48- if (CACHE .get (type ) != null ) {
49- throw new IllegalStateException ("Bad concurrency: descriptor already present in cache" );
53+ descriptor = GenericInfoUtils .create (type );
54+ if (isCacheEnabled ()) {
55+ // internal check
56+ if (CACHE .get (type ) != null ) {
57+ throw new IllegalStateException ("Bad concurrency: descriptor already present in cache" );
58+ }
59+ CACHE .put (type , descriptor );
5060 }
51- CACHE .put (type , descriptor );
5261 }
5362 } finally {
5463 LOCK .unlock ();
@@ -57,89 +66,34 @@ public static GenericsInfo create(final Class<?> type, final Class<?>... ignoreC
5766 return descriptor ;
5867 }
5968
60- private static GenericsInfo buildDescriptor (final Class <?> type , final Class <?>... ignoreClasses ) {
61- final Map <Class <?>, LinkedHashMap <String , Type >> generics =
62- new HashMap <Class <?>, LinkedHashMap <String , Type >>();
63- analyzeType (generics , type , Arrays .asList (ignoreClasses ));
64- return new GenericsInfo (type , generics );
65- }
66-
67- private static void analyzeType (final Map <Class <?>, LinkedHashMap <String , Type >> types , final Class <?> type ,
68- final List <Class <?>> ignoreClasses ) {
69- Class <?> supertype = type ;
70- while (true ) {
71- for (Type iface : supertype .getGenericInterfaces ()) {
72- analyzeInterface (types , iface , supertype , ignoreClasses );
73- }
74- final Class next = supertype .getSuperclass ();
75- if (next == null || Object .class == next || ignoreClasses .contains (next )) {
76- break ;
77- }
78- types .put (next , analyzeParent (supertype , types .get (supertype )));
79- supertype = next ;
80- }
81- }
82-
83- private static void analyzeInterface (final Map <Class <?>, LinkedHashMap <String , Type >> types , final Type iface ,
84- final Class <?> supertype , final List <Class <?>> ignoreClasses ) {
85- final Class interfaceType = iface instanceof ParameterizedType
86- ? (Class ) ((ParameterizedType ) iface ).getRawType ()
87- : (Class ) iface ;
88- if (!ignoreClasses .contains (interfaceType )) {
89- if (iface instanceof ParameterizedType ) {
90- final ParameterizedType parametrization = (ParameterizedType ) iface ;
91- final LinkedHashMap <String , Type > generics =
92- resolveGenerics (parametrization , types .get (supertype ));
93-
94- // no generics case and same resolved generics are ok (even if types in different branches of hierarchy)
95- if (types .containsKey (interfaceType ) && !generics .equals (types .get (interfaceType ))) {
96- throw new IllegalStateException (String .format (
97- "Duplicate interface %s declaration in hierarchy: "
98- + "can't properly resolve generics." , interfaceType .getName ()));
99- }
100- types .put (interfaceType , generics );
101- } else if (!GROOVY_OBJECT .equals (interfaceType .getSimpleName ())) {
102- // avoid groovy specific interface (all groovy objects implements it)
103- types .put (interfaceType , EMPTY_MAP );
104- }
105- analyzeType (types , interfaceType , ignoreClasses );
69+ /**
70+ * Clears cached descriptors (already parsed).
71+ * Cache could be completely disabled using system property or environment variable
72+ *
73+ * @see #CACHE_PROPERTY
74+ */
75+ public static void clearCache () {
76+ LOCK .lock ();
77+ try {
78+ CACHE .clear ();
79+ } finally {
80+ LOCK .unlock ();
10681 }
10782 }
10883
109- // LinkedHashMap used instead of usual map to avoid accidental simple map usage (order is important!)
110- @ SuppressWarnings ("PMD.LooseCoupling" )
111- private static LinkedHashMap <String , Type > analyzeParent (final Class type ,
112- final Map <String , Type > rootGenerics ) {
113- LinkedHashMap <String , Type > generics = null ;
114- final Class parent = type .getSuperclass ();
115- if (!type .isInterface () && parent != null && parent != Object .class
116- && type .getGenericSuperclass () instanceof ParameterizedType ) {
117- generics = resolveGenerics ((ParameterizedType ) type .getGenericSuperclass (), rootGenerics );
118- }
119- return generics == null ? EMPTY_MAP : generics ;
84+ /**
85+ * Disables descriptors cache.
86+ */
87+ public static void disableCache () {
88+ System .setProperty (CACHE_PROPERTY , Boolean .FALSE .toString ());
12089 }
12190
122- @ SuppressWarnings ("PMD.LooseCoupling" )
123- private static LinkedHashMap <String , Type > resolveGenerics (final ParameterizedType type ,
124- final Map <String , Type > rootGenerics ) {
125- final LinkedHashMap <String , Type > generics = new LinkedHashMap <String , Type >();
126- final Type [] genericTypes = type .getActualTypeArguments ();
127- final Class interfaceType = (Class ) type .getRawType ();
128- final TypeVariable [] genericNames = interfaceType .getTypeParameters ();
129-
130- final int cnt = genericNames .length ;
131- for (int i = 0 ; i < cnt ; i ++) {
132- final Type genericType = genericTypes [i ];
133- Type resolvedGenericType ;
134- if (genericType instanceof TypeVariable ) {
135- // simple named generics resolved to target types
136- resolvedGenericType = rootGenerics .get (((TypeVariable ) genericType ).getName ());
137- } else {
138- // composite generics passed as is
139- resolvedGenericType = genericType ;
140- }
141- generics .put (genericNames [i ].getName (), resolvedGenericType );
142- }
143- return generics ;
91+ /**
92+ * @return true is cache enabled, false otherwise
93+ */
94+ public static boolean isCacheEnabled () {
95+ final String no = Boolean .FALSE .toString ();
96+ return !no .equals (System .getenv (CACHE_PROPERTY ))
97+ && !no .equals (System .getProperty (CACHE_PROPERTY ));
14498 }
14599}
0 commit comments