17
17
package javax .validation ;
18
18
19
19
20
+ import java .io .IOException ;
21
+ import java .lang .ref .SoftReference ;
20
22
import java .net .URL ;
21
23
import java .net .URLClassLoader ;
24
+ import java .util .Enumeration ;
25
+ import javax .validation .spi .ValidationProvider ;
22
26
23
27
import org .testng .annotations .Test ;
24
28
25
- import static org .testng .AssertJUnit .assertEquals ;
26
- import static org .testng .AssertJUnit .fail ;
29
+ import static org .testng .Assert .assertEquals ;
30
+ import static org .testng .Assert .assertTrue ;
31
+ import static org .testng .Assert .fail ;
27
32
28
33
/**
29
34
* @author Hardy Ferentschik
@@ -41,9 +46,11 @@ public void testCurrentClassLoaderIsUsedInCaseContextClassLoaderCannotLoadServic
41
46
fail ();
42
47
}
43
48
catch ( ValidationException e ) {
49
+ // the custom context URL class loader cannot load the service file, so the exception
50
+ // must be triggered by using the current class loader
44
51
assertEquals (
45
- "Unable to load Bean Validation provider non.existent.ValidationProvider" ,
46
- e . getMessage ()
52
+ e . getMessage () ,
53
+ "Unable to load Bean Validation provider non.existent.ValidationProvider"
47
54
);
48
55
}
49
56
finally {
@@ -61,15 +68,80 @@ public void testCurrentClassLoaderIsUsedInCaseContextClassLoaderIsNull() {
61
68
fail ();
62
69
}
63
70
catch ( ValidationException e ) {
71
+ // context class loader is not. exception must be caused by using the current class loader
64
72
assertEquals (
65
- "Unable to load Bean Validation provider non.existent.ValidationProvider" ,
66
- e . getMessage ()
73
+ e . getMessage () ,
74
+ "Unable to load Bean Validation provider non.existent.ValidationProvider"
67
75
);
68
76
}
69
77
finally {
70
78
Thread .currentThread ().setContextClassLoader ( contextClassLoader );
71
79
}
72
80
}
81
+
82
+ // BVAL-298
83
+ @ Test
84
+ public void testCachedProvidersCanBeGarbageCollected () {
85
+ int LOOP_COUNT = 100 ;
86
+
87
+ ClassLoader contextClassLoader = Thread .currentThread ().getContextClassLoader ();
88
+ try {
89
+ for ( int i = 1 ; i <= LOOP_COUNT ; i ++ ) {
90
+ Thread .currentThread ().setContextClassLoader ( new CustomValidationXmlClassLoader ( "-2" ) );
91
+ Validation .buildDefaultValidatorFactory ();
92
+ }
93
+
94
+ int createdProviders = countInMemoryProviders ();
95
+ assertTrue ( createdProviders > 1 , "There should be cached providers" );
96
+
97
+ try {
98
+ byte [][] buf = new byte [1024 ][];
99
+ for ( int i = 0 ; i < buf .length ; i ++ ) {
100
+ buf [i ] = new byte [10 * 1024 * 1024 ];
101
+ }
102
+ fail ( "The byte array allocation should have triggered a OutOfMemoryError" );
103
+ }
104
+ catch ( OutOfMemoryError ex ) {
105
+ // expected
106
+ }
107
+
108
+ // the VM guarantees that all soft references are cleared before a OutOfMemoryError occurs
109
+ assertEquals ( countInMemoryProviders (), 0 );
110
+ }
111
+ finally {
112
+ Thread .currentThread ().setContextClassLoader ( contextClassLoader );
113
+ }
114
+ }
115
+
116
+ private int countInMemoryProviders () {
117
+ int count = 0 ;
118
+ // we cannot access Validation.DefaultValidationProviderResolver#providersPerClassloader, so we have to
119
+ // indirectly count the providers via DummyValidationProvider#createdValidationProviders
120
+ for ( SoftReference <DummyValidationProvider > ref : DummyValidationProvider .createdValidationProviders ) {
121
+ if ( ref .get () != null ) {
122
+ count ++;
123
+ }
124
+ }
125
+ return count ;
126
+ }
127
+
128
+ public static class CustomValidationXmlClassLoader extends ClassLoader {
129
+ private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider .class .getName ();
130
+ private final String validationXmlSuffix ;
131
+
132
+
133
+ public CustomValidationXmlClassLoader (String suffix ) {
134
+ super ( CustomValidationXmlClassLoader .class .getClassLoader () );
135
+ this .validationXmlSuffix = suffix ;
136
+ }
137
+
138
+ public Enumeration <URL > getResources (String name ) throws IOException {
139
+ if ( SERVICES_FILE .equals ( name ) && validationXmlSuffix != null ) {
140
+ name = name + validationXmlSuffix ;
141
+ }
142
+ return super .getResources ( name );
143
+ }
144
+ }
73
145
}
74
146
75
147
0 commit comments