Skip to content

Commit 69a1041

Browse files
authored
support additional class loaders for savables (#2888)
* support additional class loaders for savables * avoid allocating new empty lists per call * ignore null classloaders
1 parent 7d11616 commit 69a1041

4 files changed

Lines changed: 66 additions & 10 deletions

File tree

jme3-core/src/main/java/com/jme3/asset/AssetManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import com.jme3.texture.plugins.TGALoader;
4747
import java.io.IOException;
4848
import java.io.InputStream;
49-
import java.util.ArrayList;
49+
import java.util.Collections;
5050
import java.util.EnumSet;
5151
import java.util.List;
5252

@@ -122,7 +122,7 @@ public default void removeClassLoader(ClassLoader loader) {
122122
*/
123123
@Deprecated
124124
public default List<ClassLoader> getClassLoaders() {
125-
return new ArrayList<>();
125+
return Collections.emptyList();
126126
}
127127

128128
/**

jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.lang.reflect.Field;
4141
import java.lang.reflect.InvocationTargetException;
4242
import java.util.ArrayList;
43+
import java.util.Collection;
4344
import java.util.HashMap;
4445
import java.util.List;
4546
import java.util.logging.Level;
@@ -198,9 +199,13 @@ public static Savable fromName(String className)
198199
* @throws ClassNotFoundException thrown if the class name is not in the classpath.
199200
* @throws SecurityException thrown if the filter rejects the class name.
200201
*/
201-
public static Savable fromName(String className, SavableClassFilter classFilter)
202-
throws ClassNotFoundException, IllegalAccessException,
203-
InstantiationException, InvocationTargetException {
202+
public static Savable fromName(String className, SavableClassFilter classFilter)
203+
throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
204+
return fromName(className, classFilter, null);
205+
}
206+
207+
public static Savable fromName(String className, SavableClassFilter classFilter, Collection<ClassLoader> additionalClassLoaders)
208+
throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
204209
className = remapClass(className);
205210
if (classFilter == null) {
206211
throw new NullPointerException("classFilter");
@@ -209,7 +214,7 @@ public static Savable fromName(String className, SavableClassFilter classFilter)
209214
throw new SecurityException("Savable class rejected by filter: " + className);
210215
}
211216

212-
Constructor noArgConstructor = findNoArgConstructor(className);
217+
Constructor noArgConstructor = findNoArgConstructor(className, additionalClassLoaders);
213218
noArgConstructor.setAccessible(true);
214219
try {
215220
return (Savable) noArgConstructor.newInstance();
@@ -226,6 +231,7 @@ public static Savable fromName(String className, SavableClassFilter classFilter)
226231
}
227232
}
228233

234+
229235
/**
230236
* @deprecated use {@link #fromName(java.lang.String)} instead
231237
*/
@@ -240,6 +246,9 @@ public static Savable fromName(String className, List<ClassLoader> loaders) thro
240246
String newClassName = remapClass(className);
241247
synchronized (loaders) {
242248
for (ClassLoader classLoader : loaders) {
249+
if (classLoader == null) {
250+
continue;
251+
}
243252
final Class<?> loadedClass;
244253
try {
245254
loadedClass = classLoader.loadClass(newClassName);
@@ -263,12 +272,35 @@ public static Savable fromName(String className, List<ClassLoader> loaders) thro
263272
* @return the pre-existing constructor (not null)
264273
*/
265274
@SuppressWarnings("unchecked")
266-
private static Constructor findNoArgConstructor(String className)
275+
private static Constructor findNoArgConstructor(String className, Collection<ClassLoader> additionalClassLoaders)
267276
throws ClassNotFoundException, InstantiationException {
268-
Class clazz = Class.forName(className);
277+
Class clazz = null;
278+
279+
try {
280+
clazz = Class.forName(className);
281+
} catch (ClassNotFoundException e) {
282+
if (additionalClassLoaders != null) {
283+
for (ClassLoader classLoader : additionalClassLoaders) {
284+
if (classLoader == null) {
285+
continue;
286+
}
287+
try {
288+
clazz = Class.forName(className, true, classLoader);
289+
break;
290+
} catch (ClassNotFoundException ex) {
291+
// ignore
292+
}
293+
}
294+
}
295+
if (clazz == null) {
296+
throw e;
297+
}
298+
}
299+
269300
if (!SavableClassUtil.isImplementingSavable(clazz)) {
270301
throw new InstantiationException("Class " + className + " does not implement Savable.");
271302
}
303+
272304
Constructor result;
273305
try {
274306
result = clazz.getDeclaredConstructor();

jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public Savable readObject(int id) {
399399
throw new IOException("Invalid J3O object data length: " + dataLength);
400400
}
401401

402-
Savable out = SavableClassUtil.fromName(bco.className, classFilter);
402+
Savable out = SavableClassUtil.fromName(bco.className, classFilter, assetManager == null ? null : assetManager.getClassLoaders());
403403

404404
BinaryInputCapsule cap = new BinaryInputCapsule(this, out, bco);
405405
cap.setContent(dataArray, loc, loc+dataLength);

jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import com.jme3.export.InputCapsule;
3636
import com.jme3.export.Savable;
37+
import com.jme3.export.SavableClassFilter;
3738
import com.jme3.export.SavableClassUtil;
3839
import com.jme3.util.BufferUtils;
3940
import com.jme3.util.IntMap;
@@ -56,6 +57,7 @@
5657
*/
5758
public class DOMInputCapsule implements InputCapsule {
5859
private static final Logger logger = Logger.getLogger(DOMInputCapsule.class .getName());
60+
private SavableClassFilter classFilter = SavableClassFilter.ACCEPT_ALL;
5961

6062
private Document doc;
6163
private Element currentElement;
@@ -85,6 +87,28 @@ public int getSavableVersion(Class<? extends Savable> desiredClass) {
8587
}
8688
}
8789

90+
/**
91+
* Sets the policy used before this importer instantiates classes.
92+
* The default accepts all classes for backward compatibility.
93+
* Use a restrictive filter when loading assets from untrusted sources.
94+
*
95+
* @param classFilter the filter to apply
96+
*/
97+
public void setClassFilter(SavableClassFilter classFilter) {
98+
if (classFilter == null) {
99+
throw new NullPointerException("classFilter");
100+
}
101+
this.classFilter = classFilter;
102+
}
103+
104+
/**
105+
* @return the current class filter
106+
*/
107+
public SavableClassFilter getClassFilter() {
108+
return classFilter;
109+
}
110+
111+
88112
private Element findChildElement(String name) {
89113
if (currentElement == null) {
90114
return null;
@@ -574,7 +598,7 @@ private Savable readSavableFromCurrentElement(Savable defVal) throws IOException
574598

575599
Savable tmp = null;
576600
try {
577-
tmp = SavableClassUtil.fromName(className);
601+
tmp = SavableClassUtil.fromName(className, classFilter, importer.getAssetManager() == null ? null : importer.getAssetManager().getClassLoaders());
578602
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
579603
throw new IOException(e);
580604
}

0 commit comments

Comments
 (0)