2

Is it me, or are the MongoDb drivers and Spring-Shell deeply incompatible? To start, I'm not talking about the Spring-Data-Mongo stuff, I'm talking about the actual java client that the MongoDb folks put out.

My Pom is as follows:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-starter</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-sync</artifactId>
            <version>3.11.0</version>
        </dependency>
    </dependencies>

If I try to use the MongoDb client from the Spring shell, I consistenty get noclassdeffound errors all over the place. A simplified bare bones shell method is as follows:

import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

import java.util.Date;

import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

@ShellComponent
public class AuditCommands {

    @ShellMethod("Just testing here")
    public int cube(int number)
    {
        return number*number*number;
    }

    @ShellMethod("Sends a test document to mongo")
    public void mgo()
    {
        System.out.println("Hello there.  Doing some mongo stuff");


        //MongoClient mongoClient = MongoClients.create();
        MongoClient mongoClient = MongoClients.create("mongodb://whateversite:12345");

        // New up a registry to automatically handle pojos
        CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().automatic(true).build()));

        // Grep database instance
        MongoDatabase database = mongoClient.getDatabase("MyDb");
        database = database.withCodecRegistry(pojoCodecRegistry);

        MongoCollection<Audit> collection = database.getCollection("MyCollection", Audit.class);

        Audit audit = new Audit();
        audit.setAuditId(1);
        audit.setAuditTypeId(5);
        audit.setCreatedOn(new Date());
        audit.setMessage("Making mongo great again..");

        collection.insertOne(audit);

        System.out.println("Done..!!..");
    }
}

I receive the following error if I try to execute the "mgo" ShellMethod in my example I get the following error.

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-09-02 18:10:54.634 ERROR 18848 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    com.mongodb.client.internal.MongoClientImpl.<init>(MongoClientImpl.java:67)

The following method did not exist:

    com.mongodb.MongoClientSettings.getAutoEncryptionSettings()Lcom/mongodb/AutoEncryptionSettings;

The method's class, com.mongodb.MongoClientSettings, is available from the following locations:

    jar:file:/C:/Users/xxxxx/.m2/repository/org/mongodb/mongodb-driver-core/3.8.2/mongodb-driver-core-3.8.2.jar!/com/mongodb/MongoClientSettings.class

It was loaded from the following location:

    file:/C:/Users/xxxxx/.m2/repository/org/mongodb/mongodb-driver-core/3.8.2/mongodb-driver-core-3.8.2.jar


Action:

Correct the classpath of your application so that it contains a single, compatible version of com.mongodb.MongoClientSettings


Process finished with exit code 1

If I remove Spring-Shell and Spring-Boot, that MongoDb code works fine.

So what gives here? Am I missing some essential point here or is this stuff essentially broken? I'm not a Java/Spring native, so I'm sure it won't come as a surprise when I say that connecting to Mongo and throwing a couple of documents around comes off muuuuuuch cleaner in C#, Python, and node. (And yes I know I can use spring-data-mongo, but that just seems like a really opinionated API for someone coming from a different language background)

Marcus
  • 166
  • 6

1 Answers1

2

Okay, I'm going to answer my own question here because I've learned a little more about this.

I wound up giving up on trying to make spring boot exclude all the mongodb dependencies that I didn't want and didn't ask for. So this wasn't really a spring-shell issue. I wound up just matching the version of the driver that spring boot is using in my own pom.

As in..

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.8.2</version>
</dependency>

Once you do that, you get a bunch "connecting to localhost:27017...." issues. You can fix this by excluding the ludicrous MongoAutoConfigure that spring defaults to. More specifically, you have to go with a parameterized SpringBootApplication annotation like this:

@SpringBootApplication(exclude = MongoAutoConfiguration.class)
public class Main {
    public static void main(String[] args)
    {
        SpringApplication.run(Main.class);
    }
}

As I mentioned earlier, I'm not a Java native, so my opinions are heavily flavored by the frameworks I grew up on. But.. The idea that spring just automatically tries to connect to a potentially networked resource is completely asinine to me. It's one thing if I'm actually trying to use spring-mongodb and it's super opinionated pattern, but I'm not in my case. The equivalent would be if I pulled the Dapper assemblies from NuGet and they tried to log into the nearest local instance of sql server just for the heck of it. Very sketchy value proposition at best, and surface area for some sort of creative exploit at worst. I just don't see what "I" get out of this behavior.

Marcus
  • 166
  • 6