1717
1818import java .lang .reflect .ParameterizedType ;
1919import java .lang .reflect .Type ;
20- import java .util .Collections ;
2120import java .util .Map ;
2221import java .util .NoSuchElementException ;
2322import java .util .Optional ;
24- import java .util .WeakHashMap ;
2523import javax .inject .Named ;
2624import javax .inject .Singleton ;
2725import org .codehaus .plexus .component .configurator .ComponentConfigurationException ;
4240 * <strong>and</strong> {@link org.immutables.datatype.Data}, otherwise deserialization will fail at
4341 * runtime.
4442 *
43+ * <p>This will not store any references to the datatypes, to allow for sharing across classloaders
44+ * safely (see GH-974 and GH-976).
45+ *
4546 * @author Ashley Scopes
4647 * @since TBC
4748 */
4849@ Named
4950@ Singleton
5051final class ImmutablesDataPlexusConverter extends AbstractBasicConverter {
51- private final Map <Class <Object >, Datatype <Object >> knownDatatypes ;
52-
53- ImmutablesDataPlexusConverter () {
54- // Weak hashmap keys will be deregistered upon classloader destruction safely.
55- knownDatatypes = Collections .synchronizedMap (new WeakHashMap <>());
56- }
57-
5852 @ Override
5953 public boolean canConvert (Class <?> cls ) {
6054 return datatypeFor (cls ).isPresent ();
@@ -81,6 +75,32 @@ public Object fromConfiguration(
8175 return builder .build ();
8276 }
8377
78+ private Optional <Datatype <Object >> datatypeFor (Class <?> cls ) {
79+ if (cls .isPrimitive () || cls .getClassLoader () == null ) {
80+ return Optional .empty ();
81+ }
82+
83+ var loader = cls .getClassLoader ();
84+ var outerClsName = cls .getPackageName () + ".Datatypes_" + cls .getSimpleName ();
85+
86+ try {
87+ var outerCls = loader .loadClass (outerClsName );
88+ var method = outerCls .getMethod ("_" + cls .getSimpleName ());
89+
90+ @ SuppressWarnings ("unchecked" )
91+ var result = (Datatype <Object >) method .invoke (null );
92+
93+ return Optional .of (result );
94+ } catch (ClassNotFoundException ex ) {
95+ return Optional .empty ();
96+ } catch (ReflectiveOperationException ex ) {
97+ throw new IllegalStateException (
98+ "Failed to find datatype for " + cls .getName () + ": " + ex ,
99+ ex
100+ );
101+ }
102+ }
103+
84104 private void consumeChild (
85105 Datatype .Builder <Object > builder ,
86106 PlexusConfiguration child ,
@@ -134,41 +154,6 @@ private void consumeChild(
134154 }
135155 }
136156
137- private Optional <Datatype <Object >> datatypeFor (Class <?> cls ) {
138- if (cls .isPrimitive () || cls .getClassLoader () == null ) {
139- return Optional .empty ();
140- }
141-
142- // Horrible generic voodoo that probably is not safe, but the APIs have conflicting types
143- // and the compiler is not smart enough to help us.
144- @ SuppressWarnings ("unchecked" )
145- var castCls = (Class <Object >) cls ;
146-
147- var datatype = knownDatatypes .computeIfAbsent (castCls , ignored -> {
148- var loader = cls .getClassLoader ();
149- var outerClsName = cls .getPackageName () + ".Datatypes_" + cls .getSimpleName ();
150-
151- try {
152- var outerCls = loader .loadClass (outerClsName );
153- var method = outerCls .getMethod ("_" + cls .getSimpleName ());
154-
155- @ SuppressWarnings ("unchecked" )
156- var result = (Datatype <Object >) method .invoke (null );
157-
158- return result ;
159- } catch (ClassNotFoundException ex ) {
160- return null ;
161- } catch (ReflectiveOperationException ex ) {
162- throw new IllegalStateException (
163- "Failed to find datatype for " + cls .getName () + ": " + ex ,
164- ex
165- );
166- }
167- });
168-
169- return Optional .ofNullable (datatype );
170- }
171-
172157 private static Class <?> rawTypeOf (Type type ) {
173158 return type instanceof ParameterizedType parameterizedType
174159 ? rawTypeOf (parameterizedType .getRawType ())
0 commit comments