From 710e77dabe3d68a538c951571564c12727805e51 Mon Sep 17 00:00:00 2001 From: Mike Saavedra Date: Wed, 25 Sep 2013 15:14:02 -0700 Subject: [PATCH] DATAMONGO-764 - Added SSL support to Mongo configuration options. Added support for allowing Mongo clients to use secure SSL connections by introducing the "ssl" property in MongoOptionsFactoryBean that will enable the use of the configured SSLSocketFactory to create SSLSockets. If no custom SSLSocketFactory is configured SSLSocketFactory#getDefault() will be used. We introduce this configuration in a new version of spring-mongo-1.4.xsd. Applied Mike Saavedra's pull request (#75) with the above mentioned extensions. Original pull request: #83. --- .../mongodb/config/MongoParsingUtils.java | 5 +- .../mongodb/core/MongoOptionsFactoryBean.java | 144 ++-- .../main/resources/META-INF/spring.schemas | 3 +- .../data/mongodb/config/spring-mongo-1.4.xsd | 626 ++++++++++++++++++ .../mongodb/config/MongoNamespaceTests.java | 40 +- .../MongoOptionsFactoryBeanUnitTests.java | 25 +- .../config/MongoNamespaceTests-context.xml | 25 +- spring-data-mongodb/template.mf | 1 + 8 files changed, 805 insertions(+), 64 deletions(-) create mode 100644 spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.4.xsd diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index 6613f7066..4ab0b2fc4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import org.w3c.dom.Element; * * @author Mark Pollack * @author Oliver Gierke + * @author Thomas Darimont */ abstract class MongoParsingUtils { @@ -79,6 +80,8 @@ abstract class MongoParsingUtils { setPropertyValue(optionsDefBuilder, optionsElement, "write-timeout", "writeTimeout"); setPropertyValue(optionsDefBuilder, optionsElement, "write-fsync", "writeFsync"); setPropertyValue(optionsDefBuilder, optionsElement, "slave-ok", "slaveOk"); + setPropertyValue(optionsDefBuilder, optionsElement, "ssl", "ssl"); + setPropertyReference(optionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory"); mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); return true; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java index 2271de441..5948a4191 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * Copyright 2010-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,129 +15,146 @@ */ package org.springframework.data.mongodb.core; -import com.mongodb.MongoOptions; +import javax.net.ssl.SSLSocketFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import com.mongodb.MongoOptions; + /** - * A factory bean for construction of a MongoOptions instance + * A factory bean for construction of a {@link MongoOptions} instance. * * @author Graeme Rocher - * @Author Mark Pollack + * @author Mark Pollack + * @author Mike Saavedra + * @author Thomas Darimont */ public class MongoOptionsFactoryBean implements FactoryBean, InitializingBean { - private static final MongoOptions MONGO_OPTIONS = new MongoOptions(); + private final MongoOptions MONGO_OPTIONS = new MongoOptions(); + /** - * number of connections allowed per host will block if run out + * The number of connections allowed per host will block if run out. */ private int connectionsPerHost = MONGO_OPTIONS.connectionsPerHost; /** - * multiplier for connectionsPerHost for # of threads that can block if connectionsPerHost is 10, and - * threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block more than that and an exception will - * be throw + * A multiplier for connectionsPerHost for # of threads that can block a connection. + *

+ * If connectionsPerHost is {@literal 10}, and threadsAllowedToBlockForConnectionMultiplier is {@literal 5}, then + * {@literal 50} threads can block. If more threads try to block an exception will be thrown. */ private int threadsAllowedToBlockForConnectionMultiplier = MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier; /** - * max wait time of a blocking thread for a connection + * Max wait time of a blocking thread for a connection. */ private int maxWaitTime = MONGO_OPTIONS.maxWaitTime; /** - * connect timeout in milliseconds. 0 is default and infinite + * Connect timeout in milliseconds. {@literal 0} is default and means infinite time. */ private int connectTimeout = MONGO_OPTIONS.connectTimeout; /** - * socket timeout. 0 is default and infinite + * The socket timeout. {@literal 0} is default and means infinite time. */ private int socketTimeout = MONGO_OPTIONS.socketTimeout; /** - * This controls whether or not to have socket keep alive turned on (SO_KEEPALIVE). - * - * defaults to false + * This controls whether or not to have socket keep alive turned on (SO_KEEPALIVE). This defaults to {@literal false}. */ public boolean socketKeepAlive = MONGO_OPTIONS.socketKeepAlive; /** - * this controls whether or not on a connect, the system retries automatically + * This controls whether or not the system retries automatically on a failed connect. This defaults to + * {@literal false}. */ private boolean autoConnectRetry = MONGO_OPTIONS.autoConnectRetry; private long maxAutoConnectRetryTime = MONGO_OPTIONS.maxAutoConnectRetryTime; /** - * This specifies the number of servers to wait for on the write operation, and exception raising behavior. - * - * Defaults to 0. + * This specifies the number of servers to wait for on the write operation, and exception raising behavior. This + * defaults to {@literal 0}. */ private int writeNumber; /** - * This controls timeout for write operations in milliseconds. - * - * Defaults to 0 (indefinite). Greater than zero is number of milliseconds to wait. + * This controls timeout for write operations in milliseconds. This defaults to {@literal 0} (indefinite). Greater + * than zero is number of milliseconds to wait. */ private int writeTimeout; /** - * This controls whether or not to fsync. - * - * Defaults to false. + * This controls whether or not to fsync. This defaults to {@literal false}. */ private boolean writeFsync; /** - * Specifies if the driver is allowed to read from secondaries or slaves. - * - * Defaults to false + * Specifies if the driver is allowed to read from secondaries or slaves. This defaults to {@literal false}. */ - @SuppressWarnings("deprecation") - private boolean slaveOk = MONGO_OPTIONS.slaveOk; + @SuppressWarnings("deprecation") private boolean slaveOk = MONGO_OPTIONS.slaveOk; /** - * number of connections allowed per host will block if run out + * This controls SSL support via SSLSocketFactory. This defaults to {@literal false}. + */ + private boolean ssl; + + /** + * Specifies the {@link SSLSocketFactory} to use. This defaults to {@link SSLSocketFactory#getDefault()} + */ + private SSLSocketFactory sslSocketFactory; + + /** + * The maximum number of connections allowed per host until we will block. + * + * @param connectionsPerHost */ public void setConnectionsPerHost(int connectionsPerHost) { this.connectionsPerHost = connectionsPerHost; } /** - * multiplier for connectionsPerHost for # of threads that can block if connectionsPerHost is 10, and - * threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block more than that and an exception will - * be throw + * A multiplier for connectionsPerHost for # of threads that can block a connection. + * + * @see #threadsAllowedToBlockForConnectionMultiplier + * @param threadsAllowedToBlockForConnectionMultiplier */ public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) { this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier; } /** - * max wait time of a blocking thread for a connection + * Max wait time of a blocking thread for a connection. + * + * @param maxWaitTime */ public void setMaxWaitTime(int maxWaitTime) { this.maxWaitTime = maxWaitTime; } /** - * connect timeout in milliseconds. 0 is default and infinite + * The connect timeout in milliseconds. {@literal 0} is default and infinite + * + * @param connectTimeout */ public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } /** - * socket timeout. 0 is default and infinite + * The socket timeout. {@literal 0} is default and infinite. + * + * @param socketTimeout */ public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } /** - * This controls whether or not to have socket keep alive + * This controls whether or not to have socket keep alive. * * @param socketKeepAlive */ @@ -147,12 +164,12 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi /** * This specifies the number of servers to wait for on the write operation, and exception raising behavior. The 'w' - * option to the getlasterror command. Defaults to 0. + * option to the getlasterror command. Defaults to {@literal 0}. *

* * @param writeNumber the number of servers to wait for on the write operation, and exception raising behavior. @@ -164,31 +181,33 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi /** * This controls timeout for write operations in milliseconds. The 'wtimeout' option to the getlasterror command. * - * @param writeTimeout Defaults to 0 (indefinite). Greater than zero is number of milliseconds to wait. + * @param writeTimeout Defaults to {@literal 0} (indefinite). Greater than zero is number of milliseconds to wait. */ public void setWriteTimeout(int writeTimeout) { this.writeTimeout = writeTimeout; } /** - * This controls whether or not to fsync. The 'fsync' option to the getlasterror command. Defaults to false. + * This controls whether or not to fsync. The 'fsync' option to the getlasterror command. Defaults to {@literal false} * - * @param writeFsync to fsync on write (true), otherwise false. + * @param writeFsync to fsync on write (true), otherwise {@literal false}. */ public void setWriteFsync(boolean writeFsync) { this.writeFsync = writeFsync; } /** - * this controls whether or not on a connect, the system retries automatically + * Controls whether or not the system retries automatically, on a failed connect. + * + * @param autoConnectRetry */ public void setAutoConnectRetry(boolean autoConnectRetry) { this.autoConnectRetry = autoConnectRetry; } /** - * The maximum amount of time in millisecons to spend retrying to open connection to the same server. Default is 0, - * which means to use the default 15s if autoConnectRetry is on. + * The maximum amount of time in millisecons to spend retrying to open connection to the same server. This defaults to + * {@literal 0}, which means to use the default {@literal 15s} if {@link #autoConnectRetry} is on. * * @param maxAutoConnectRetryTime the maxAutoConnectRetryTime to set */ @@ -197,7 +216,7 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi } /** - * Specifies if the driver is allowed to read from secondaries or slaves. Defaults to false. + * Specifies if the driver is allowed to read from secondaries or slaves. This defaults to {@literal false}. * * @param slaveOk true if the driver should read from secondaries or slaves. */ @@ -205,8 +224,29 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi this.slaveOk = slaveOk; } + /** + * Specifies if the driver should use an SSL connection to Mongo. This defaults to {@literal false}. + * + * @param ssl true if the driver should use an SSL connection. + */ + public void setSsl(boolean ssl) { + this.ssl = ssl; + } + + /** + * Specifies the SSLSocketFactory to use for creating SSL connections to Mongo. + * + * @param sslSocketFactory the sslSocketFactory to use. + */ + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + + setSsl(sslSocketFactory != null); + this.sslSocketFactory = sslSocketFactory; + } + @SuppressWarnings("deprecation") public void afterPropertiesSet() { + MONGO_OPTIONS.connectionsPerHost = connectionsPerHost; MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier; MONGO_OPTIONS.maxWaitTime = maxWaitTime; @@ -219,6 +259,9 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi MONGO_OPTIONS.w = writeNumber; MONGO_OPTIONS.wtimeout = writeTimeout; MONGO_OPTIONS.fsync = writeFsync; + if (ssl) { + MONGO_OPTIONS.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault()); + } } public MongoOptions getObject() { @@ -232,5 +275,4 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi public boolean isSingleton() { return true; } - } diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas index ebad3c5ac..c5555508e 100644 --- a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas +++ b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas @@ -2,4 +2,5 @@ http\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/sprin http\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.2.xsd=org/springframework/data/mongodb/config/spring-mongo-1.2.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.3.xsd=org/springframework/data/mongodb/config/spring-mongo-1.3.xsd -http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.3.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo-1.4.xsd=org/springframework/data/mongodb/config/spring-mongo-1.4.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.4.xsd diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.4.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.4.xsd new file mode 100644 index 000000000..27d9c86ad --- /dev/null +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.4.xsd @@ -0,0 +1,626 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + The reference to a MongoTemplate. Will default to 'mongoTemplate'. + + + + + + + Enables creation of indexes for queries that get derived from the method name + and thus reference domain class properties. Defaults to false. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + The reference to a MongoTypeMapper to be used by this MappingMongoConverter. + + + + + + + The reference to a MappingContext. Will default to 'mappingContext'. + + + + + + + Disables JSR-303 validation on MongoDB documents before they are saved. By default it is set to false. + + + + + + + + + + Enables abbreviating the field names for domain class properties to the + first character of their camel case names, e.g. fooBar -> fb. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A reference to a custom converter. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java index a11b641e8..96a52bb95 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java @@ -18,6 +18,8 @@ package org.springframework.data.mongodb.config; import static org.junit.Assert.*; import static org.springframework.test.util.ReflectionTestUtils.*; +import javax.net.ssl.SSLSocketFactory; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -41,13 +43,13 @@ import com.mongodb.WriteConcern; * @author Mark Pollack * @author Oliver Gierke * @author Martin Baumgartner + * @author Thomas Darimont */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class MongoNamespaceTests { - @Autowired - private ApplicationContext ctx; + @Autowired private ApplicationContext ctx; @Test public void testMongoSingleton() throws Exception { @@ -59,12 +61,46 @@ public class MongoNamespaceTests { @Test public void testMongoSingletonWithAttributes() throws Exception { + assertTrue(ctx.containsBean("defaultMongo")); MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&defaultMongo"); String host = (String) getField(mfb, "host"); Integer port = (Integer) getField(mfb, "port"); assertEquals("localhost", host); assertEquals(new Integer(27017), port); + + MongoOptions options = (MongoOptions) getField(mfb, "mongoOptions"); + assertFalse("By default socketFactory should not be a SSLSocketFactory", + options.getSocketFactory() instanceof SSLSocketFactory); + } + + /** + * @see DATAMONGO-764 + */ + @Test + public void testMongoSingletonWithSslEnabled() throws Exception { + + assertTrue(ctx.containsBean("mongoSsl")); + MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongoSsl"); + + MongoOptions options = (MongoOptions) getField(mfb, "mongoOptions"); + assertTrue("socketFactory should be a SSLSocketFactory", options.getSocketFactory() instanceof SSLSocketFactory); + } + + /** + * @see DATAMONGO-764 + */ + @Test + public void testMongoSingletonWithSslEnabledAndCustomSslSocketFactory() throws Exception { + + assertTrue(ctx.containsBean("mongoSslWithCustomSslFactory")); + MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongoSslWithCustomSslFactory"); + + SSLSocketFactory customSslSocketFactory = ctx.getBean("customSslSocketFactory", SSLSocketFactory.class); + MongoOptions options = (MongoOptions) getField(mfb, "mongoOptions"); + + assertTrue("socketFactory should be a SSLSocketFactory", options.getSocketFactory() instanceof SSLSocketFactory); + assertSame(customSslSocketFactory, options.getSocketFactory()); } @Test diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java index bb81f3f84..a73845b0d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,20 @@ */ package org.springframework.data.mongodb.core; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import javax.net.ssl.SSLSocketFactory; + import org.junit.Test; import com.mongodb.MongoOptions; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; /** * Unit tests for {@link MongoOptionsFactoryBean}. * * @author Oliver Gierke + * @author Mike Saavedra */ public class MongoOptionsFactoryBeanUnitTests { @@ -41,4 +45,19 @@ public class MongoOptionsFactoryBeanUnitTests { MongoOptions options = bean.getObject(); assertThat(options.maxAutoConnectRetryTime, is(27L)); } + + /** + * @see DATAMONGO-764 + */ + @Test + public void testSslConnection() { + + MongoOptionsFactoryBean bean = new MongoOptionsFactoryBean(); + bean.setSsl(true); + bean.afterPropertiesSet(); + + MongoOptions options = bean.getObject(); + assertNotNull(options.getSocketFactory()); + assertTrue(options.getSocketFactory() instanceof SSLSocketFactory); + } } diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml index 9467bff01..4718bdc05 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml @@ -1,11 +1,13 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:mongo="http://www.springframework.org/schema/data/mongo" + xmlns:context="http://www.springframework.org/schema/context" + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> @@ -38,6 +40,17 @@ + + + + + + + + + + + diff --git a/spring-data-mongodb/template.mf b/spring-data-mongodb/template.mf index e46a19ac9..e8fe82a46 100644 --- a/spring-data-mongodb/template.mf +++ b/spring-data-mongodb/template.mf @@ -13,6 +13,7 @@ Import-Template: javax.annotation.processing.*;version="0", javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, javax.tools.*;version="0", + javax.net.*;version="0", javax.validation.*;version="${validation:[=.=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.bson.*;version="0",