Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions core/src/main/java/dev/morphia/DatastoreImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,13 @@ public class DatastoreImpl implements AdvancedDatastore {
private DatastoreOperations operations;

public DatastoreImpl(MongoClient client, MorphiaConfig config) {
this(client, config, Thread.currentThread().getContextClassLoader());
}

public DatastoreImpl(MongoClient client, MorphiaConfig config, ClassLoader classLoader) {
this.mongoClient = client;
this.database = mongoClient.getDatabase(config.database());
this.mapper = new Mapper(config);
this.mapper = new Mapper(config, classLoader);
this.queryFactory = mapper.getConfig().queryFactory();
importModels();

Expand Down Expand Up @@ -641,7 +645,8 @@ public <T> void refresh(T entity) {
.iterator()
.next();

refreshCodec.decode(new DocumentReader(id), DecoderContext.builder().checkedDiscriminator(true).build());
refreshCodec.decode(new DocumentReader(id, getMapper().getClassLoader()),
DecoderContext.builder().checkedDiscriminator(true).build());
}

@Override
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/dev/morphia/Morphia.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,16 @@ public static Datastore createDatastore(MongoClient mongoClient, MorphiaConfig c
return new DatastoreImpl(mongoClient, config);
}

/**
* Creates a Datastore configured via config file
*
* @param mongoClient the client to use
* @param config the configuration to use
* @param classLoader the classloader to use when scanning for entities and codecs. If null, the default classloader will be used.
* @return a Datastore that you can use to interact with MongoDB
* @since 3.0.0
*/
public static Datastore createDatastore(MongoClient mongoClient, MorphiaConfig config, ClassLoader classLoader) {
return new DatastoreImpl(mongoClient, config, classLoader);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public Aggregation<T> addStage(Stage stage) {
return this;
}

private static class MappingCursor<R> implements MongoCursor<R> {
private class MappingCursor<R> implements MongoCursor<R> {
private final MongoCursor<Document> results;
private final Codec<R> codec;
private final String discriminator;
Expand Down Expand Up @@ -403,7 +403,7 @@ public ServerAddress getServerAddress() {

private R map(Document next) {
next.remove(discriminator);
return codec.decode(new DocumentReader(next), DecoderContext.builder().build());
return codec.decode(new DocumentReader(next, datastore.getMapper().getClassLoader()), DecoderContext.builder().build());
}
}

Expand Down
25 changes: 24 additions & 1 deletion core/src/main/java/dev/morphia/config/MorphiaConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ static MorphiaConfig load() {
return load(MorphiaConfigHelper.MORPHIA_CONFIG_PROPERTIES);
}

/**
* Tries to load a configuration from the default location using the given classloader.
*
* @param classLoader the classloader to use when loading resources from the classpath
* @return the loaded config
*/
static MorphiaConfig load(ClassLoader classLoader) {
return load(MorphiaConfigHelper.MORPHIA_CONFIG_PROPERTIES, classLoader);
}

/**
* Parses and loads the configuration found at the given location
*
Expand All @@ -62,7 +72,20 @@ static MorphiaConfig load() {
* @since 3.0
*/
static MorphiaConfig load(String path) {
List<ConfigSource> configSources = classPathSources(path, currentThread().getContextClassLoader());
return load(path, currentThread().getContextClassLoader());
}

/**
* Parses and loads the configuration found at the given location
*
* @param path the location of the configuration to load. This can be a file path, a classpath resource, a URL, etc.
* @param classLoader the classloader to use when loading resources from the classpath
*
* @return the loaded configuration
* @since 3.0
*/
static MorphiaConfig load(String path, ClassLoader classLoader) {
List<ConfigSource> configSources = classPathSources(path, classLoader);
if (configSources.isEmpty()) {
LoggerFactory.getLogger(MorphiaConfig.class).warn(Sofia.missingConfigFile(path));
return new ManualMorphiaConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
public final class DiscriminatorLookup {
private final Map<String, String> discriminatorClassMap = new ConcurrentHashMap<>();
private final Set<String> packages = new ConcurrentSkipListSet<>();
private final ClassLoader classLoader;

public DiscriminatorLookup(ClassLoader classLoader) {
this.classLoader = classLoader;
}

/**
* Adds a model to the map
Expand All @@ -64,7 +69,7 @@ public void addModel(EntityModel entityModel) {
public Class<?> lookup(String discriminator) {
if (discriminatorClassMap.containsKey(discriminator)) {
try {
return Class.forName(discriminatorClassMap.get(discriminator), true, Thread.currentThread().getContextClassLoader());
return Class.forName(discriminatorClassMap.get(discriminator), true, classLoader);
} catch (ClassNotFoundException e) {
throw new MappingException(e.getMessage(), e);
}
Expand All @@ -85,7 +90,7 @@ public Class<?> lookup(String discriminator) {
private Class<?> getClassForName(String discriminator) {
Class<?> clazz = null;
try {
clazz = Class.forName(discriminator, true, Thread.currentThread().getContextClassLoader());
clazz = Class.forName(discriminator, true, classLoader);
} catch (ClassNotFoundException e) {
// Ignore
}
Expand Down
26 changes: 23 additions & 3 deletions core/src/main/java/dev/morphia/mapping/Mapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class Mapper {
private final MorphiaConfig config;
private final DiscriminatorLookup discriminatorLookup;
private final Object entityRegistrationMonitor = new Object();
private final ClassLoader classLoader;

/**
* Creates a Mapper with the given options.
Expand All @@ -88,8 +89,22 @@ public class Mapper {
*/
@MorphiaInternal
public Mapper(MorphiaConfig config) {
this(config, Thread.currentThread().getContextClassLoader());
}

/**
* Creates a Mapper with the given options and classloader.
*
* @param config the config to use
* @param classLoader the classloader to use when looking up classes by discriminator
* @morphia.internal
* @hidden
*/
@MorphiaInternal
public Mapper(MorphiaConfig config, ClassLoader classLoader) {
this.config = config;
discriminatorLookup = new DiscriminatorLookup();
this.classLoader = classLoader;
discriminatorLookup = new DiscriminatorLookup(classLoader);
}

/**
Expand All @@ -99,7 +114,8 @@ public Mapper(MorphiaConfig config) {
*/
public Mapper(Mapper other) {
config = other.config;
discriminatorLookup = new DiscriminatorLookup();
classLoader = other.classLoader;
discriminatorLookup = new DiscriminatorLookup(other.getClassLoader());
other.mappedEntities.values().forEach(entity -> clone(entity));
listeners.addAll(other.listeners);
}
Expand Down Expand Up @@ -172,6 +188,10 @@ public PropertyModel findIdProperty(Class<?> type) {
return idField;
}

public ClassLoader getClassLoader() {
return classLoader;
}

/**
* Gets the class as defined by any discriminator field
*
Expand Down Expand Up @@ -618,7 +638,7 @@ private List<Class> getClasses(String packageName)
try (ScanResult scanResult = classGraph.scan()) {
for (ClassInfo classInfo : scanResult.getAllClasses()) {
try {
classes.add(Class.forName(classInfo.getName(), true, Thread.currentThread().getContextClassLoader()));
classes.add(Class.forName(classInfo.getName(), true, classLoader));
} catch (Throwable ignored) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ public class ArrayFieldAccessor extends FieldAccessor {

private final TypeData<?> typeData;
private final Class<?> componentType;
private final ClassLoader classLoader;

/**
* Creates the accessor
*
* @param typeData the type data
* @param field the field
*/
public ArrayFieldAccessor(TypeData<?> typeData, Field field) {
public ArrayFieldAccessor(TypeData<?> typeData, Field field, ClassLoader classLoader) {
super(field);
this.typeData = typeData;
componentType = field.getType().getComponentType();
this.classLoader = classLoader;
}

@Override
Expand Down Expand Up @@ -84,6 +86,6 @@ private Object convert(Object o, Class<?> type) {

return newArray;
}
return Conversions.convert(o, type);
return Conversions.convert(o, type, classLoader);
}
}
9 changes: 8 additions & 1 deletion core/src/main/java/dev/morphia/mapping/codec/ClassCodec.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.morphia.mapping.codec;

import dev.morphia.Datastore;
import dev.morphia.mapping.MappingException;

import org.bson.BsonReader;
Expand All @@ -12,10 +13,16 @@
* Defines a codec for Class references
*/
public class ClassCodec implements Codec<Class> {
private final Datastore datastore;

public ClassCodec(Datastore datastore) {
this.datastore = datastore;
}

@Override
public Class decode(BsonReader reader, DecoderContext decoderContext) {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = datastore.getMapper().getClassLoader();
return Class.forName(reader.readString(), true, classLoader);
} catch (ClassNotFoundException e) {
throw new MappingException(e.getMessage(), e);
Expand Down
Loading