99import java .util .Map ;
1010import java .util .UUID ;
1111import java .util .concurrent .ConcurrentHashMap ;
12+ import java .util .function .BiFunction ;
1213import java .util .function .Function ;
1314
1415import com .mongodb .lang .Nullable ;
3435public final class Conversions {
3536 private static final Logger LOG = LoggerFactory .getLogger (Conversions .class );
3637
37- private static final Map <Class <?>, Map <Class <?>, Function <? , ?>>> CONVERSIONS = new ConcurrentHashMap <>();
38+ private static final Map <Class <?>, Map <Class <?>, BiFunction <?, ClassLoader , ?>>> CONVERSIONS = new ConcurrentHashMap <>();
3839
3940 static {
4041 registerStringConversions ();
@@ -97,10 +98,9 @@ private static void registerStringConversions() {
9798 }
9899 });
99100 register (Class .class , String .class , Class ::getName );
100- register (String .class , Class .class , className -> {
101+ registerWithClassLoader (String .class , Class .class , ( className , classLoader ) -> {
101102 try {
102- // FIXME incorrect classloader
103- return Thread .currentThread ().getContextClassLoader ().loadClass (className );
103+ return classLoader .loadClass (className );
104104 } catch (ClassNotFoundException e ) {
105105 throw new MappingException (e .getMessage (), e );
106106 }
@@ -125,43 +125,40 @@ private static void registerStringConversions() {
125125 }
126126
127127 /**
128- * Register a conversion between two types. For example, to register the conversion of {@link Date} to a {@link Long}, this method
129- * could be invoked as follows:
130- * <code>
131- * register(Date.class, Long.class, Date::getTime);
132- * </code>
128+ * Attempts to convert a value to the given type using the current thread's context ClassLoader.
133129 *
134- * @param source the source type
135- * @param target the target type
136- * @param function the function that performs the conversion. This is often just a method reference.
137- * @param <S> the source type
138- * @param <T> the target type .
130+ * @param value the value to convert
131+ * @param target the target type
132+ * @param <T> the target type
133+ * @return the potentially converted value
134+ * @deprecated Use {@link #convert(Object, Class, ClassLoader)} instead to avoid issues in environments with custom class loading .
139135 */
140- public static <S , T > void register (Class <S > source , Class <T > target , Function <S , T > function ) {
141- register (source , target , function , null );
136+ @ Nullable
137+ @ Deprecated (since = "2.5.3" , forRemoval = true )
138+ public static <T > T convert (@ Nullable Object value , Class <T > target ) {
139+ return convert (value , target , Thread .currentThread ().getContextClassLoader ());
142140 }
143141
144142 /**
145- * Attempts to convert a value to the given type
143+ * Attempts to convert a value to the given type using the specified ClassLoader.
146144 *
147- * @param value the value to convert
148- * @param target the target type
149- * @param <T> the target type
145+ * @param value the value to convert
146+ * @param target the target type
147+ * @param classLoader the ClassLoader to use for class resolution
148+ * @param <T> the target type
150149 * @return the potentially converted value
151150 */
152151 @ SuppressWarnings ({ "unchecked" , "rawtypes" })
153152 @ Nullable
154- public static <T > T convert (@ Nullable Object value , Class <T > target ) {
153+ public static <T > T convert (@ Nullable Object value , Class <T > target , ClassLoader classLoader ) {
155154 if (value == null ) {
156155 return (T ) convertNull (target );
157156 }
158-
159157 final Class <?> fromType = value .getClass ();
160158 if (fromType .equals (target )) {
161159 return (T ) value ;
162160 }
163-
164- final Function function = CONVERSIONS
161+ final BiFunction function = CONVERSIONS
165162 .computeIfAbsent (fromType , (f ) -> new ConcurrentHashMap <>())
166163 .get (target );
167164 if (function == null ) {
@@ -173,7 +170,7 @@ public static <T> T convert(@Nullable Object value, Class<T> target) {
173170 }
174171 return (T ) value ;
175172 }
176- return (T ) function .apply (value );
173+ return (T ) function .apply (value , classLoader );
177174 }
178175
179176 @ Nullable
@@ -187,28 +184,68 @@ private static Object convertNull(Class<?> toType) {
187184 }
188185
189186 /**
190- * Register a conversion between two types. For example, to register the conversion of {@link Date} to a {@link Long}, this method
191- * could be invoked as follows:
192- * <code>
193- * register(Date.class, Long.class, Date::getTime);
194- * </code>
187+ * Register a conversion between two types.
195188 *
196189 * @param source the source type
197190 * @param target the target type
198- * @param function the function that performs the conversion. This is often just a method reference.
199- * @param warning if non-null, this will be the message logged on the WARN level indicating the conversion is taking place.
191+ * @param function the function that performs the conversion
200192 * @param <S> the source type
201- * @param <T> the target type.
193+ * @param <T> the target type
194+ */
195+ public static <S , T > void register (Class <S > source , Class <T > target , Function <S , T > function ) {
196+ register (source , target , function , null );
197+ }
198+
199+ /**
200+ * Register a conversion between two types.
201+ *
202+ * @param source the source type
203+ * @param target the target type
204+ * @param function the function that performs the conversion
205+ * @param warning if non-null, this will be logged on WARN level
206+ * @param <S> the source type
207+ * @param <T> the target type
202208 */
203209 public static <S , T > void register (Class <S > source , Class <T > target , Function <S , T > function ,
204210 @ Nullable String warning ) {
205- final Function <S , T > conversion = warning == null
211+ // Wrap Function as BiFunction (ignore ClassLoader parameter)
212+ BiFunction <S , ClassLoader , T > biFunction = (s , cl ) -> function .apply (s );
213+ registerWithClassLoader (source , target , biFunction , warning );
214+ }
215+
216+ /**
217+ * Register a conversion between two types with ClassLoader support.
218+ *
219+ * @param source the source type
220+ * @param target the target type
221+ * @param function the function that performs the conversion (receives ClassLoader)
222+ * @param <S> the source type
223+ * @param <T> the target type
224+ */
225+ public static <S , T > void registerWithClassLoader (Class <S > source , Class <T > target ,
226+ BiFunction <S , ClassLoader , T > function ) {
227+ registerWithClassLoader (source , target , function , null );
228+ }
229+
230+ /**
231+ * Register a conversion between two types with ClassLoader support.
232+ *
233+ * @param source the source type
234+ * @param target the target type
235+ * @param function the function that performs the conversion (receives ClassLoader)
236+ * @param warning if non-null, this will be logged on WARN level
237+ * @param <S> the source type
238+ * @param <T> the target type
239+ */
240+ public static <S , T > void registerWithClassLoader (Class <S > source , Class <T > target ,
241+ BiFunction <S , ClassLoader , T > function , @ Nullable String warning ) {
242+ final BiFunction <S , ClassLoader , T > conversion = warning == null
206243 ? function
207- : s -> {
244+ : ( s , cl ) -> {
208245 if (LOG .isWarnEnabled ()) {
209246 LOG .warn (warning );
210247 }
211- return function .apply (s );
248+ return function .apply (s , cl );
212249 };
213250 CONVERSIONS .computeIfAbsent (source , (Class <?> c ) -> new HashMap <>())
214251 .put (target , conversion );
0 commit comments