Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ The `pull-request.yml` workflow runs on every PR and is the gating check:
| Error | Cause | Fix |
|-------|-------|-----|
| `Cannot connect to MongoDB` in tests | Docker not running / Testcontainers can't pull image | Start Docker daemon; or use `-Dmongodb=local` with a local `mongod` |
| `No MorphiaConfig found` | Missing `morphia-config.properties` on classpath | Add the file to `src/test/resources/`, or call `Morphia.createDatastore(client, new ManualMorphiaConfig())` |
| `No MorphiaConfig found` | Missing `morphia-config.properties` on classpath | Add the file to `src/test/resources/`, or call `Morphia.createDatastore(client, dev.morphia.config.MorphiaConfig.load())` |
| `MappingException: No usable constructor` | Entity class has no no-arg constructor accessible to Morphia | Add a `protected` or `public` no-arg constructor |
| `ClassCastException` with proxies | Lazy-loading proxy and `instanceof` check clash | Check `MorphiaInternals.proxyClassesPresent()` before using proxies, or disable lazy loading |
| Tests skipped with `SkipException` | MongoDB version below minimum for a feature | Pass a higher `-Dmongodb=` version |
Expand Down
34 changes: 34 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@
</goals>
<phase>generate-test-resources</phase>
</execution>
<execution>
<id>morphia-annotations-asm</id>
<goals>
<goal>morphia-annotation-node</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
<plugin>
Expand Down Expand Up @@ -171,6 +178,27 @@
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus.gizmo</groupId>
<artifactId>gizmo</artifactId>
<version>1.9.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-jdt</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand All @@ -180,6 +208,12 @@
<artifactId>spotbugs-annotations</artifactId>
</dependency>

<dependency>
<groupId>org.jboss.windup.decompiler</groupId>
<artifactId>decompiler-fernflower</artifactId>
<version>6.3.9.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
Expand Down
205 changes: 180 additions & 25 deletions core/src/main/java/dev/morphia/config/ManualMorphiaConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import org.bson.codecs.configuration.CodecProvider;

import static dev.morphia.config.MorphiaConfigHelper.dumpConfigurationFile;
import static dev.morphia.mapping.DateStorage.UTC;
import static dev.morphia.mapping.DiscriminatorFunction.className;
import static dev.morphia.mapping.DiscriminatorFunction.simpleName;
import static dev.morphia.mapping.NamingStrategy.camelCase;
import static dev.morphia.mapping.NamingStrategy.identity;
Expand All @@ -32,40 +34,40 @@
@MorphiaInternal
@SuppressWarnings("removal")
public class ManualMorphiaConfig implements MorphiaConfig {
Boolean applyCaps;
Boolean applyDocumentValidations;
Boolean applyIndexes;

Optional<CodecProvider> codecProvider;

NamingStrategy collectionNaming;

String database;

DateStorage dateStorage;
DiscriminatorFunction discriminator;
String discriminatorKey;
Boolean enablePolymorphicQueries;
Boolean ignoreFinals;
MapperType mapper;
List<String> packages;
PropertyDiscovery propertyDiscovery;
List<PropertyAnnotationProvider<?>> propertyAnnotationProviders;
NamingStrategy propertyNaming;
QueryFactory queryFactory;
Boolean storeEmpties;
Boolean storeNulls;
private Boolean applyCaps;
private Boolean applyDocumentValidations;
private Boolean applyIndexes;

private Optional<CodecProvider> codecProvider;

private NamingStrategy collectionNaming;

private String database;

private DateStorage dateStorage;
private DiscriminatorFunction discriminator;
private String discriminatorKey;
private Boolean enablePolymorphicQueries;
private Boolean ignoreFinals;
private MapperType mapper;
private List<String> packages;
private PropertyDiscovery propertyDiscovery;
private List<PropertyAnnotationProvider<?>> propertyAnnotationProviders;
private NamingStrategy propertyNaming;
private QueryFactory queryFactory;
private Boolean storeEmpties;
private Boolean storeNulls;

/**
* @hidden
*/
public ManualMorphiaConfig() {
protected ManualMorphiaConfig() {
}

/**
* @hidden
*/
public ManualMorphiaConfig(MorphiaConfig base) {
protected ManualMorphiaConfig(MorphiaConfig base) {
applyCaps = base.applyCaps();
applyDocumentValidations = base.applyDocumentValidations();
applyIndexes = base.applyIndexes();
Expand Down Expand Up @@ -122,90 +124,243 @@ public Boolean applyCaps() {
return orDefault(applyCaps, FALSE);
}

@Override
public MorphiaConfig applyCaps(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.applyCaps = value;
return newConfig;
}

@Override
public Boolean applyDocumentValidations() {
return orDefault(applyDocumentValidations, FALSE);
}

@Override
public MorphiaConfig applyDocumentValidations(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.applyDocumentValidations = value;
return newConfig;
}

@Override
public Boolean applyIndexes() {
return orDefault(applyIndexes, FALSE);
}

@Override
public MorphiaConfig applyIndexes(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.applyIndexes = value;
return newConfig;
}

@Override
public Optional<CodecProvider> codecProvider() {
return orDefault(codecProvider, Optional.empty());
}

@Override
public MorphiaConfig codecProvider(CodecProvider value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.codecProvider = Optional.of(value);
return newConfig;
}

@Override
public NamingStrategy collectionNaming() {
return orDefault(collectionNaming, camelCase());
}

@Override
public MorphiaConfig collectionNaming(NamingStrategy value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.collectionNaming = value;
return newConfig;
}

@Override
public MorphiaConfig database(String value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.database = value;
return newConfig;
}

@Override
public DateStorage dateStorage() {
return orDefault(dateStorage, UTC);
}

@Override
public MorphiaConfig dateStorage(DateStorage value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.dateStorage = value;
return newConfig;
}

@Override
public DiscriminatorFunction discriminator() {
return orDefault(discriminator, simpleName());
}

@Override
public MorphiaConfig discriminator(DiscriminatorFunction value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.discriminator = value;
return newConfig;
}

@Override
public String discriminatorKey() {
return orDefault(discriminatorKey, "_t");
}

@Override
public MorphiaConfig discriminatorKey(String value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.discriminatorKey = value;
return newConfig;
}

@Override
public Boolean enablePolymorphicQueries() {
return orDefault(enablePolymorphicQueries, FALSE);
}

@Override
public MorphiaConfig enablePolymorphicQueries(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.enablePolymorphicQueries = value;
return newConfig;
}

@Override
public Boolean ignoreFinals() {
return orDefault(ignoreFinals, FALSE);
}

@Override
public MorphiaConfig ignoreFinals(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.ignoreFinals = value;
return newConfig;
}

@Override
public MorphiaConfig legacy() {
ManualMorphiaConfig newConfig = new ManualMorphiaConfig(this);
newConfig.dateStorage = DateStorage.SYSTEM_DEFAULT;
newConfig.discriminatorKey = "className";
newConfig.discriminator = className();
newConfig.collectionNaming = identity();
newConfig.propertyNaming = identity();
return newConfig;
}

@Override
public MapperType mapper() {
return orDefault(mapper, MapperType.LEGACY);
}

@Override
public MorphiaConfig mapper(MapperType value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.mapper = value;
return newConfig;
}

@Override
public List<String> packages() {
return orDefault(packages, List.of());
}

@Override
public MorphiaConfig packages(List<String> value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.packages = value;
return newConfig;
}

@Override
public List<PropertyAnnotationProvider<?>> propertyAnnotationProviders() {
return orDefault(propertyAnnotationProviders, List.of(new MorphiaPropertyAnnotationProvider()));
}

@Override
public MorphiaConfig propertyAnnotationProviders(List<PropertyAnnotationProvider<?>> list) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.propertyAnnotationProviders = list;
if (list.isEmpty() || list.stream().noneMatch(p -> p instanceof MorphiaPropertyAnnotationProvider)) {
newConfig.propertyAnnotationProviders.add(new MorphiaPropertyAnnotationProvider());
}
return newConfig;
}

@Override
public PropertyDiscovery propertyDiscovery() {
return orDefault(propertyDiscovery, FIELDS);
}

@Override
public MorphiaConfig propertyDiscovery(PropertyDiscovery value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.propertyDiscovery = value;
return newConfig;
}

@Override
public NamingStrategy propertyNaming() {
return orDefault(propertyNaming, identity());
}

@Override
public MorphiaConfig propertyNaming(NamingStrategy value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.propertyNaming = value;
return newConfig;
}

@Override
public QueryFactory queryFactory() {
return orDefault(queryFactory, new DefaultQueryFactory());
}

@Override
public MorphiaConfig queryFactory(QueryFactory value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.queryFactory = value;
return newConfig;
}

@Override
public Boolean storeEmpties() {
return orDefault(storeEmpties, FALSE);
}

@Override
public MorphiaConfig storeEmpties(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.storeEmpties = value;
return newConfig;
}

@Override
public Boolean storeNulls() {
return orDefault(storeNulls, FALSE);
}

@Override
public MorphiaConfig storeNulls(Boolean value) {
var newConfig = new ManualMorphiaConfig(this);
newConfig.storeNulls = value;
return newConfig;
}

@Override
public String toConfigFormat(boolean showComplete) {
return dumpConfigurationFile(this, showComplete);
}

protected <T> T orDefault(@Nullable T localValue, T defaultValue) {
return localValue != null ? localValue : defaultValue;
}
Expand Down
Loading
Loading