Compare commits

..

574 Commits

Author SHA1 Message Date
Spring Buildmaster
d861fecdb8 DATAMONGO-981 - Release version 1.6.0.M1. 2014-07-10 10:38:55 -07:00
Oliver Gierke
f280e23095 DATAMONGO-981 - Prepare 1.6.0.M1 (Evans M1). 2014-07-10 19:28:34 +02:00
Oliver Gierke
ed0e1d92c0 DATAMONGO-981 - Updated changelog. 2014-07-10 17:14:24 +02:00
Christoph Strobl
d82fc22659 DATAMONGO-944 - Add support for $currentDate to Update.
Added currentDate and currentTimestamp to Update.

Original pull request: #200.
2014-07-10 15:13:59 +02:00
Thomas Darimont
6616d6788c DATAMONGO-975 - Add support for extracting date/time components from a field projection.
We added some extract-methods to ProjectionOperationBuilder to be able to extract date / time components from projected fields.

Original pull request: #204.
2014-07-10 12:45:17 +02:00
Christoph Strobl
322a7cf033 DATAMONGO-969 - Fixed nested id handling in SpringDataMongodbSerializer.
SpringDataMongodbSerializer now defensively triggers mapping of the DBObject created by the default serializer. This asserts that ids buried in nested structures like { "_id" : { "$in" : ["x", "y"] } } are converted correctly.

Original pull request: #202.
2014-07-09 21:48:02 +02:00
Christoph Strobl
0f487c10ba DATAMONGO-983 - Remove links to forum.spring.io.
Replace forum links with those to stackoverflow.

Original Pull Request: #205
2014-07-09 21:21:04 +02:00
Christoph Strobl
11417144bd DATAMONGO-980 - Use meta annotations from commons for @Score.
We now use Spring Data Commons' @ReadOnlyProperty to meta-annotate @Score to mark it as read-only property.

Original pull request: #201.
Related tickets: DATACMNS-534.
2014-07-09 14:51:21 +02:00
Christoph Strobl
dafc59b163 DATAMONGO-972 - Querydsl integration now handles references correctly.
SpringDataMongodbSerializer now overrides the necessary methods to create the appropriate DBRef objects when serializing data via Querydsl.

We currently disable the test case as it the fix taking effect requires Querydsl 3.4.1 which unfortunately breaks Java 6 compatibility. We include the fix nonetheless to allow users on Java 7 to potentially use the latest Querydsl.

Original pull request: #203.
Related tickets: querydsl/querydsl#803.
2014-07-09 13:47:04 +02:00
Oliver Gierke
566f9a80c4 DATAMONGO-982 - Added build profiles to build against next MongoDB driver versions.
Added build profile for MongoDB Java driver versions 2.12.3-SNAPSHOT and 3.0.0-SNAPSHOT. Added another property to be able to build manifests correctly as the snapshot versions aren't valid OSGi versions.

Adapted MongoExceptionTranslator to convert the new Exceptions being thrown for server timeouts and the deprecated values we currently handle.
2014-07-08 17:36:01 +02:00
Christoph Strobl
89a42c5648 DATAMONGO-976 - Add support for reading $meta projection on textScore into document.
We introduced @TextScore that can be used to mark a property to take { $meta : “textScore” }. In contrast to @Transient the value will be considered when reading documents.
The value can and will only get picked up if the score field is retrieved from the store.

Original pull request: #198.
2014-07-07 11:43:43 +02:00
Christoph Strobl
83ffbb00e8 DATAMONGO-850 - Add support for full text search via $text
Using TextQuery and TextCriteria allows creation of queries using $text $search.

{ $meta : “textScore” } can be included using TextQuery.includeScore. As the fieldname used for textScore must not be fixed to “score” one can use the overload taking the fieldname to override the default.

Original pull request: #198.
2014-07-07 11:39:47 +02:00
Christoph Strobl
84913cecab DATAMONGO-937 - Add support for creating text index.
We now support creating text index based on information gathered on domain types.

Using @TextIndexed marks properties to be considered for the full text index. Use the weight attribute to influence document scoring during search operations.

Please note that using @TextIndexed on entity properties forces all properties of any sub document to be considered as part of the text index. Any set weight will in that case be propagated to the siblings taking the most recent weight information into account, which means that a the weight attribute can be overridden for properties in sub documents.

The setting the index default language can be done via @Document(language) while @Language can be used to define the language_override field.

As text search is disabled by default for mongodb 2.4 we use a jUnit ClassRule to restrict integration tests potentially creating text index (as the entities for testing are found in the classpath) to only be executed in when a 2.6+ mongodb server is present.

For usage hints please see section 6.3.4 (Text Indexes) of reference manual.

Original pull request: #198.
2014-07-07 11:30:08 +02:00
Christoph Strobl
998bb09a92 DATAMONGO-978 - Derived delete query should pass on type information.
We now pass on type information for derived delete queries to the according delete operation. This propagates the information correctly to the according Before and After events.

Before this change the type would have been set to null in case of non collection like method return type.

Original pull request: #199.
2014-07-03 14:16:57 +02:00
Oliver Gierke
cd68a8db54 DATAMONGO-977 - Removed reflective detection of Spring 4 in DBRef proxy creation.
After the Spring 4 upgrade we can now directly use the Objenesis infrastructure of it.
2014-07-02 09:23:19 +02:00
Oliver Gierke
df8477d180 DATAMONGO-955 - Updated changelog. 2014-06-30 10:57:12 +02:00
Christoph Strobl
244fbae0ce DATAMONGO-962 - Cycle guard should respect full path.
We now check on intersections of given path and existing to not only check types and contained property names but also properties full path which must not be present in already traversed paths.

Additionally we’ll now catch any CyclicPropertyReferenceExceptions on the root level to prevent cycle detection interfering with application startup.

Original pull request: #197.
2014-06-27 19:25:26 +02:00
Oliver Gierke
19e08a52c0 DATAMONGO-970 - MongoTemplate.remove(…) now correctly builds query for DBObjects.
If a DBObject was handed into MongoTemplate.remove(…) we previously failed to look up the id value to create a by-id-query. This commit adds explicit handling of DBObjects by looking up their _id field to obtain the id value.
2014-06-27 16:12:50 +02:00
Thomas Darimont
6389b1bb73 DATAMONGO-954 - Add support for system variables in aggregation operations.
Add assumes for appropriate MongoDB version.
2014-06-26 14:19:24 +02:00
Thomas Darimont
cadcbf6106 DATAMONGO-954 - Add support for system variables in aggregation operations.
We now support referring to system variables like for instance $$ROOT or $$CURRENT from within aggregation framework pipeline projection and group expressions.

Original pull request: #190.
2014-06-25 15:53:44 +02:00
Christoph Strobl
118f007ca6 DATAMONGO-963 - @CompoundIndex should treat expireAfterSeconds correctly.
We added an additional check on the fields used as key, so that TTL is ignored for CompoundIndex with more than one field (which effectively renders it useless on @CompoundIndex at all).

Prior to this change potentially invalid index structures would have been created for e.g. @CompoundIndex(def = "{'foo': 1, 'bar': 1}", expireAfterSeconds=10) leading to MongoDB not being able to clean up the indexes (logs: "ERROR: key for ttl index can only have 1 field")

This fix is related to https://jira.mongodb.org/browse/SERVER-10075.

Original pull request: #196.
2014-06-25 15:12:32 +02:00
Christoph Strobl
cbb32bd29d DATAMONGO-950 - Add support for limiting the query result in the query derivation mechanism.
When deriving the query from its method name we check for the limit set on the PartTree to pass this on to the created query. PagedExecution not takes the overall limit into account, skips a query execution entirely (if the Pageable is out of scope completely) or alters the query limits accordingly.

Note, that there has been significant rework of this compared to the pull request to avoid new API in Query and extensive changes in MongoTemplate's QueryCursorPreparer.

Original pull request: #191.
2014-06-25 14:58:59 +02:00
Thomas Darimont
9858dcd740 DATAMONGO-960 - Allow to pass options to the Aggregation Pipeline.
We introduced AggregationOptions abstraction to conveniently construct option objects that can be handed to an Aggregation via the new Aggregation.withOptions(...) factory method. For more details, see the Builder class' JavaDoc.

Note that we exposed the "rawResults" in AggregationResults and put a null guard in MongoTemplate aggregate in order to support the "explain" option.

Original pull request: #195.
2014-06-25 13:25:57 +02:00
Thomas Darimont
1fb76d135b DATAMONGO-953 - Add equals(…)/hashCode()/toString() to Update.
We now use the underlying updateObject to implement appropriate equals(…)/hashCode() and toString() methods.

Original pull request: #192.
2014-06-25 12:40:30 +02:00
Oliver Gierke
bb62c8b2f1 DATAMONGO-958 - Switch to FieldNamingStrategy SPI in Spring Data Commons. 2014-06-20 21:27:24 +02:00
Christoph Strobl
2cbe7bf885 DATAMONGO-952 - Derived queries should consider field specification in @Query.
PartTreeMongoQuery now explicitly check the presence of a manually defined field spec on the query method and creates a new Query if so.

Original pull request: #188.
2014-06-18 12:53:14 +02:00
Christoph Strobl
6043f6b74d DATAMONGO-949 - CycleGuard should only match properties in word boundaries.
We modified the regular expression used for cycle detection to match on the exact property name within the inspected path using word boundaries. This fix prevents sub sequences of an existing property (like ‘sub’ would have matched ‘substr’) from being matched.

Along the way we fixed the (false) assertion in one of the tests, as we create the +1 cycle reference index before actually breaking the operation.
2014-06-18 08:32:30 +02:00
Christoph Strobl
ef1366592a DATAMONGO-948 - Sort should be taken as is when no type information available.
Object type mapping for sort is skipped in the case no type information is present when executing query using mongo template.
2014-06-18 08:27:57 +02:00
Thomas Darimont
01cf9fb8f3 DATAMONGO-938 - Apply QueryMapper in MongoTemplate.mapReduce(…).
Previously MongoTemplate.mapReduce(...) didn't translate nested objects, e.g. GeoCommand, within the given query. That could lead to exceptions during query serialization. We now pass the query and sort object of the given Query through the QueryMapper to avoid such problems.

Original pull request: #184.
2014-06-18 08:27:54 +02:00
Thomas Darimont
285c406d5d DATAMONGO-745 - Added test cases for custom query with $in and pageable parameter.
Added test cases to verify that this works.

Original pull request: #186.
2014-06-18 07:49:06 +02:00
Oliver Gierke
ad29e52a57 DATAMONGO-936 - After release cleanups. 2014-05-20 19:54:03 +02:00
Spring Buildmaster
3cfe207c83 DATAMONGO-936 - Prepare next development iteration. 2014-05-20 09:35:38 -07:00
Spring Buildmaster
c7e65cbc40 DATAMONGO-936 - Release version 1.5.0.RELEASE. 2014-05-20 09:35:35 -07:00
Oliver Gierke
b8e02efb04 DATAMONGO-936 - Prepare 1.5 GA.
Removed obsolete readme.txt.
2014-05-20 17:14:04 +02:00
Oliver Gierke
c7f20fb836 DATAMONGO-936 - Prepare 1.5 GA. 2014-05-20 16:57:34 +02:00
Oliver Gierke
28a0202ef4 DATAMONGO-936 - Updated changelog. 2014-05-20 16:26:20 +02:00
Christoph Strobl
164e947045 DATAMONGO-926 - Avoid StackOverflowError while resolving index structures.
We now guard cyclic non transient, non DBRef property references while inspecting domain types for potentially index structures. To do so we check on the properties path and owning type to determine potential cycles. If found we log a warn message pointing to path, entity and property potentially causing problems and skip processing for this path.

Original pull request: #180.
2014-05-19 19:09:40 +02:00
Christoph Strobl
7e65c0c87d DATAMONGO-367 - Nested @Indexed should not trigger creation of separate collection.
The issue has been solved along with DATAMONGO-888 (Pull Request: #162). We have created additional tests to explicitly check it has truly been fixed.
2014-05-19 17:19:28 +02:00
Christoph Strobl
9c1f753f17 DATAMONGO-929 - Use property path for keys of Indexed and CompoundIndex.
Index creation failed for @Indexed and @CompoundIndex as the resolved dotPath was not used for creation. We now not only resolve the dotPath but also use it within the key for index definition. In case of a nested compound index the key definition is enhanced by the provided path.

When leaving the key definition empty for nested compound index we'll create an index for the whole nested document. Trying to create a compound index on root level not providing key information leads to InvalidDataApiUsageException.

Original pull request: #179.
2014-05-19 17:14:36 +02:00
Oliver Gierke
0f821eb52d DATAMONGO-925, DATAMONGO-928 - Polishing.
Only reject attribute setup if abbreviation is activated and a custom strategy is configured. Additional test cases for the rejection case and a custom, over-configuration (explicitly setting abbreviation to false, which is the default anyway).

Related pull request: #177.
2014-05-19 17:07:50 +02:00
Ryan Tenney
e3aadd63ab DATAMONGO-928 - Removed explicit default value for abbreviate-field-names from namespace XSD.
The default for boolean attributes leaks into the evaluation of XML namespace attributes which causes us being unable to detect whether two attributes have been set in a conflicting way.

Fix the documentation on the field-naming-strategy-ref attribute.

Original pull request: #183.
Related pull request: #177.
Related ticket: DATAMONGO-925.
2014-05-19 16:44:03 +02:00
Christoph Strobl
aa06d520df DATAMONGO-647 - Added test case to show that field names are mapped correctly.
Additional test added to check if the issue has truly been resolved by DATAMONGO-888.

Original pull request: #181.
Related pull Request: #162.
Related ticket: DATAMONGO-888.
2014-05-19 14:38:06 +02:00
Oliver Gierke
4fa1d4ba97 DATAMONGO-919 - After release cleanups. 2014-05-02 15:45:06 +02:00
Spring Buildmaster
ba9f11b345 DATAMONGO-919 - Prepare next development iteration. 2014-05-02 05:58:16 -07:00
Spring Buildmaster
4777dd2e5e DATAMONGO-919 - Release version 1.5.0.RC1. 2014-05-02 05:58:13 -07:00
Oliver Gierke
64b4591b72 DATAMONGO-919 - Prepare 1.5.0.RC1.
Upgraded to Spring Data Parent 1.4.0.RC1 and Spring Data Commons 1.8.0.RC1. Switched to milestone repository.
2014-05-02 14:50:48 +02:00
Oliver Gierke
bac5961fd8 DATAMONGO-919 - Updated changelog. 2014-05-02 14:50:48 +02:00
Thomas Darimont
b3cac862d8 DATAMONGO-924 - Improve aggregation field reference resolving.
Previously we didn't support referring to aliased fields defined in former stages of an aggregation pipeline. We now also consider field aliases during field reference lookup.

Original pull request: #176.
2014-05-02 14:46:13 +02:00
Oliver Gierke
b8ab2ad539 DATAMNOG-919 - Forward port changelog entries from bugfix branch. 2014-05-02 12:48:18 +02:00
Christoph Strobl
618d5bd5e9 DATAMONGO-919 - Prepare Release 1.5.0.RC1
Upgrade AspectJ Maven plugin to recent version which enables building the module on java 8 using AspectJ 1.8.
2014-05-01 21:02:01 +02:00
Oliver Gierke
096c3278b3 DATAMONGO-92 - Upgraded to MongoDB Java driver 2.12.1. 2014-04-30 08:51:18 +02:00
Kim Toms
c9d9976c22 DATAMONGO-920 - Improve debug message for delete events in AbstractMongoEventListener.
Adjusted debug message to reflect the actual operation.

Original pull request: #95.
2014-04-29 15:52:12 +02:00
Thomas Darimont
65497f93d4 DATAMONGO-827 - Allow index creation use mongodb auto generated names.
We now support letting MongoDB generate index names by introducing the attribute "useGeneratedName" to the @Indexed, @GeoSpatialIndex, @CompoundIndex annotations.
2014-04-28 19:58:56 +02:00
Christoph Strobl
af8a53bef6 DATAMONGO-909 - Compound index on inherited class should be created correctly.
With the overhaul of the index creation done in DATAMONGO-899 the CompoundIndex annotation is not longer just looked up at the concrete type but rather all its interfaces and super classes. So we just added an additional test to verify this behaviour.
2014-04-28 19:58:56 +02:00
Oliver Gierke
916b856e97 DATAMONGO-899 - Polished API of new index creation abstractions.
Removed the introduction of the IndexDefinition being collection aware again. The collection an index is created in is now held in the IndexDefinitionHolder. This is mostly due to the fact that the IndexDefinition implementations can be used with MongoTemplate and the index opoerations take a collection alongside the index definition.

Made the IndexResolver API package protected so that we can further change it going forward. We should think about deprecating the collectionName attributes on index annotations as it doesn't make too much sense to manually configure the collection name for the indexes as the collection is predefined through the domain type setting here. This would allow us to remove the entire collection handling code inside the IndexResolver implementation.

Turned IndexDefinitionHolder into a value object.

Original pull request: #168.
2014-04-28 19:58:50 +02:00
Christoph Strobl
7848da63f2 DATAMONGO-899 - Ensure proper creation of index structures for nested entities.
Index creation did not consider the properties path when creating the index. This lead to broken index creation when nesting entities that might require index structures.

Off now index creation traverses the entities property path for all top level entities (namely those holding the @Document annotation) and creates the index using the full property path.

This is a breaking change as having an entity to carry the @Document annotation has not been required by now.

Original Pull Request: #168
2014-04-28 16:50:26 +02:00
Christoph Strobl
f359a1d31a DATAMONGO-847 - Allow usage of Query within an Update clause.
In case we detect Query within a value used for an Update value we map the query itself to build the expression to use. This allows to form query statements for e.g. $pull using the same API as for the query itself.

Update update = new Update().pull("list", query(where("value").in("foo", "bar")));

Original Pull Request: #172.
2014-04-28 13:30:13 +02:00
Thomas Darimont
72d645feae DATAMONGO-917 - Improve Spring 4.0 framework version detection to avoid NullPointerExceptions.
We now check for the presence of DefaultParameterNameDiscoverer in order to determine if we are running with a Spring version later than 4.0 since this avoids potential NullPointerExceptions in cases where the package version information is not available e.g. in cases where the application was bundled into an uberjar e.g. via the maven-shade-plugin.

Original pull request: #173.
2014-04-28 13:21:09 +02:00
Thomas Darimont
72fe382bba DATAMONGO-913 - Improve DBRef handling in for LazyLoadingProxies.
We now use the captured DBRef of a given LazyLoadingProxy in MappingMongoConverter.toDBRef(..) in order to avoid a new DBRef creation that would fail for the proxy.

Original pull request: #174.
2014-04-28 13:07:57 +02:00
Thomas Darimont
d25e840cf5 DATAMONGO-914 - Improve resolving of lazy-loading proxies for classes that override equals(…)/hashCode().
We now properly resolve lazy-loading proxies for @DBRef's when an overridden equals or hash code method is called with Spring 4. We fall back to our old Objenesis proxy generation in order to circumvent the default handling for overridden hashcCode() and equals(…) methods in CglibAopProxies generated by Spring 4.

If we detect that we run with Spring 4 we use the repacked Objenesis that is included in Spring 4. Previously the generated proxy used some generic hashCode() or equals(…) logic that did not trigger a proper lazy loading in such cases.

Original pull request: #171.
2014-04-23 09:29:33 +02:00
Thomas Darimont
df1775572a DATAMONGO-912 - Consider custom conversions in all stages of an aggregation pipeline.
We now consider custom mongo conversions in all stages of an aggregation pipeline. Previously we did this only for the first stage and returned object basically unmapped in later stages. We now pass the root AggregationOperationContext on to nested ExposedFieldsAggregationOperationContexts so that those can delegate any mongo Mapping to the root context.

Original pull request: #170.
2014-04-23 09:00:56 +02:00
Christoph Strobl
86670cd49f DATAMONGO-893 - Converter must not write "_class" information for know types.
We now actively pass on property type information to MetadataBackedField to ensure type hints get picked up correctly when converting a value to the according DBObject.

This has to be done as the fix for DATAMONGO-812 enforced proper writing of _class information for Updates, which caused trouble when querying documents by nested (complex) properties using an 'in' clause.

Original pull request: #169.
2014-04-15 17:36:11 +02:00
Christoph Strobl
31a4bf906e DATAMONGO-892 - Reject nested MappingMongoConverter declarations in XML.
Mapping information is potentially required by multiple instances and thus must not be registered as nested bean. We now actively check for such an invalid scenario and explicitly reject it.

Original pull request: #165.
2014-04-15 09:11:12 +02:00
Christoph Strobl
599291e8b7 DATAMONGO-897 - Fixed potential NullPointerException in QueryMapper.
If an association property points to an interface not containing the id property QueryMapper threw a NullPointerException in isAssociationConversionNecessary(…) as the lookup of the id property fails. 

We now check for the presence of an id property on the target type and check for assignability to indicated the need for conversion (usually in case when developers use raw ids in their update clauses, not the actual target instance.

Original pull request: #164.
2014-04-15 09:11:00 +02:00
Thomas Darimont
f5a04fb9fb DATAMONGO-908 - Support for nested field references in group operations.
We now allow referring to nested field expressions if the root segment of the nested field expression was exposed in earlier stages of the aggregation pipeline.

Original pull request: #167.
2014-04-15 07:55:57 +02:00
Oliver Gierke
88558b67c3 DATAMONGO-866 - Polishing for new field naming strategy configuration support.
Use the camel case split logic from Spring Data Commons (introduced for DATACMNS-486) in a common CamelCaseSplittingFieldNamingStrategy super class.

MappingMongoConverterParser now also rejects the configuration if both abbreviate-field-names and field-naming-strategy-ref are configured.
2014-04-10 18:36:37 +02:00
Ryan Tenney
d52cb255e0 DATAMONGO-866 - Add means to configure field naming strategy. 2014-04-10 18:03:55 +02:00
Oliver Gierke
6c214cbc37 DATAMONGO-910 - Upgraded to latest MongoDB Java driver 2.12.0.
Removed special mongo-osgi property as the driver version is now a valid OSGi version number.
2014-04-10 16:55:22 +02:00
Jeff Yemin
01012c1448 DATAMONGO-895, DATAMONGO-896 - Assert compatibility with latest MongoDB Java driver.
Upgrade next MongoDB driver version to 2.12.0. Strong upgrade coming in a subsequent commit to make sure we can backport the compatibility checks to the bugfix branch without forcing users into a driver upgrade.

Relaxing error message comparison in assertion so that it still matches against the message returned by MongoDB 2.6. When comparing the value of the version field, compare against a Long rather than an Integer, since the version field generated is a Long. This allows the test to pass against the upcoming 2.12.0 release of the Java driver, which has a stricter implementation of BasisDBObject.equals(…).

Original pull requests: #159, #160.
2014-04-10 15:57:45 +02:00
Christoph Strobl
4c7befb910 DATAMONGO-888 - Sorting now considers mapping information.
We now pipe the DBObject containing sorting information for queries through the QueryMapper to make sure potential field mappings are applied.

Original Pull Request: #162.
2014-04-10 15:43:51 +02:00
Christoph Strobl
b62669ec8f DATAMONGO-907 - Assert compatibility with mongodb 2.6.
Fix test to only check on parts of the expected error message common in both 2.4 and 2.6.

Original Pull Request: #166.
2014-04-10 13:33:34 +02:00
Oliver Gierke
0fb74caf9b DATAMONGO-905 - Removed obsolete dependency to CGLib from cross-store support.
Also we now optionally depend on the HIbernate JPA API JAR so that using other persistence providers doesn'T cause an API JAR duplication.
2014-04-09 12:12:29 +02:00
Oliver Gierke
9623dac01f DATAMONGO-901 - Fixed regression of not registering type predicting post processor.
The changes for DATAMONGO-843 introduced a regression by skipping the registration of the RepositoryInterfaceAwareBeanPostProcessor. This can cause the wiring of repository bean definitions to fail depending on in which order the bean definitions get instantiated.

This change reintroduces the registration and adds an explicit test case for it.
2014-04-02 17:58:36 +02:00
Spring Buildmaster
695b27968c DATAMONGO-859 - Prepare next development iteration. 2014-03-31 17:10:40 +02:00
Spring Buildmaster
22933e4493 DATAMONGO-859 - Release version 1.5.0.M1. 2014-03-31 08:04:03 -07:00
Thomas Darimont
40aa6bbdd5 DATAMONGO-859 - Prepare release 1.5 M1.
Updated readme.md and mongodb.xml to reflect recent version. Updated Spring Data Commons and Spring Data Build versions in pom.xml. Update pom.xml to use release repository. Updated docbkx to use recent Spring Data Commons version. Updated changelog to reflect changes and releases.

Original pull request: #161.
2014-03-31 16:47:15 +02:00
Christoph Strobl
5e43f5846a DATAMONGO-471 - Add support for $each when using $addToSet.
Additionally to Update.addToSet(String, Object) the method 'addToSet(String)' has been introduced, returning a builder to allow the creation of $addToSet command for either single value, or multiple values using $each.

Using value:
new Update().addToSet("key").value("spring");

Using each:
new Update().addToSet("key").each("spring", "data", "mongodb");

Original Pull Request: #157.
2014-03-31 15:25:14 +02:00
Thomas Darimont
2cfd4781bc DATAMONGO-884 - Improved handling for Object methods in LazyLoadingInterceptor.
We now handle invocations of equals(…)/hashCode()/toString()  methods that are not overridden with custom proxy aware logic. This avoids potentially NullPointerExceptions and makes it easier to debug code that deals with proxies (due to a proper toString representation of a proxy).

Original pull request: #158.
2014-03-31 15:19:30 +02:00
Thomas Darimont
031ab0c07b DATACMNS-482 - Fix compiler error due to changes in SD Commons.
Fixed a compiler that got introduced by making the geospatial types in Spring Data Commons serializable.
2014-03-31 15:14:08 +02:00
Thomas Darimont
10f69f6623 DATAMONGO-884 - Fix potential NullPointerException for lazy DBRefs.
We now initialize the proxy in case an Object-method is called that is overridden in the traget class. Removed the additional check for initialization and to-DBRef-methods as they're repeated in the target method.

Original pull requests: #152, #153.
2014-03-27 17:57:39 +01:00
Thomas Darimont
d7b03915a7 DATAMONGO-858 - Revised rendering of geo spatial structures.
Switched back to the old style of rendering (as in 1.4.x) of DBObjects when they are used as values in persistent domain objects, adjusted the GeoConverters accordingly. In order to render geo structures correctly when they are used within a query we now wrap them in a GeoCommand that triggers a different Shape rendering.

We now render the metric that was used in the Distance definition of the radius of a Circle or Sphere.
2014-03-27 13:18:55 +01:00
Oliver Gierke
ed55d48a53 DATAMONGO-858 - Polishing.
Moved to use the newly introduced geo types from Spring Data Commons. Adde deprecation warning suppression everywhere else.

Adapted Sonargraph architecture description file and split up namespace registration into repository specific stuff and everything else.
2014-03-27 13:18:55 +01:00
Thomas Darimont
d5ed4e0ac2 DATAMONGO-858 - Add support for common geospatial structures.
Backed the geo spatial structures of SD MongoDB by the new geo spatial structures in SD commons. Deprecated the MongoDB geo spatial types to make users aware that we're going to remove them in one of the following development iterations. Added custom conversions for basic geo spatial types.

We deliberately choose not to let Circle extends CMNS geo.Circle since it would break clients that use the legacy Circle API (getRadius() returns a Distance in CMNS where as it returns a plain double in Mongo).
2014-03-27 13:18:55 +01:00
Oliver Gierke
75194730e9 DATAMONGO-887 - Added unit tests to verify TreeMaps can be converted. 2014-03-27 08:58:53 +01:00
Oliver Gierke
a09183d2eb DATAMONGO-880 - Minor polishing in lazy-loading area.
Took the change to add @since tags to the types introduced for lazy loading. Polished JavaDoc where necessary. Removed methods solely existing for testing purposes and use reflection in tests to minimize the API being published.
2014-03-20 09:27:37 +01:00
Thomas Darimont
45dd3cd988 DATAMONGO-880 - Improved handling of persistence of lazy-loaded DBRefs.
Added LazyLoadingProxy interface that will be implemented by every LazyLoading-proxy that is created by the DefaultDbRefResolver. Clients can now cast those proxies to this interface and call it's methods initialize a proxy explicitly or to get the referenced DBRef if possible.

We now keep a reference to the DBRef that lead to the creation of a LazyLoadingProxy in order to be able to reuse it in case one assigns the proxy to a field that should be a DBRef. This avoids unnecessary conversion.

Previously saving of proxies wasn't possible since the mapping infrastructure did not know how to extract the entity information from the proxy. We now either store the DBRef backed by the proxy directly or we initialize the proxy first and use the result of LazyLoadingProxy.initialize().

Original pull request: #151.
2014-03-20 09:26:08 +01:00
Oliver Gierke
b24e34c360 DATAMONGO-883 - Adapted to changes in auditing config in Spring Data MongoDB. 2014-03-18 20:10:43 +01:00
Oliver Gierke
fa9b5efdab DATAMONGO-882 - Adapted to removal of obsolete generics in BeanWrapper. 2014-03-18 20:08:22 +01:00
Oliver Gierke
8f2ced8ada DATAMONGO-881 - Allow custom conversions to override default conversions.
User provided converters are now registered *after* the default converters to make sure they enjoy precedence over the default ones. 

This is achieved by inverting the order of converters after the conversions have been registered. This is necessary as the registration order for convertible pairs is different from the one of the converters. For the pairs, earlier registered instances take precedence, while for the actual converter instances, instances registered later trump ones registered before.
2014-03-18 09:32:28 +01:00
Oliver Gierke
ff92cf1429 DATAMONGO-566 - Polishing.
Inlined a few methods to reduce the number of indirections. Added a bit of missing JavaDoc here and there. StringBasedMongoQuery now prevents a manually defined query from being marked as both count and delete query.

Polished test cases a little.

Original pull request: #147.
2014-03-17 17:51:54 +01:00
Christoph Strobl
ba48290a3e DATAMONGO-566 - Add support for derived delete-by queries.
Using keywords remove or delete in derived query, or setting @Query(delete=true) removes documents matching the query. If the return type is assignable to Number, the total number of affected documents is returned. In case the return type is collection like the query is executed against the store in first place. All documents included in the resulting collection are deleted in a subsequent call.

Additionally findAllAndRemove(…) methods have been added to MongoTemplate.

Original pull request: #147.
2014-03-17 17:51:08 +01:00
Oliver Gierke
70e5efd0d9 DATAMONGO-877 - Added guard against null-package in AbstractMappingConfiguration.
AbstractMappingConfiguration.getMappingBasePackage() now quards against a null package returned for the configuration class. This can happen if the class resides in the default package.
2014-03-10 12:47:26 +01:00
Oliver Gierke
4eae229bff DATAMONGO-876 - Adapt to API changes introduced for better property access config.
Adapted usage of BeanWrapper as the property access is now solely defined via the PersistentProperty. Adapted MongoPersistentEntityIndexCreator to lookup annotations via PersistentProperty instead of the backing field. Removed code from BasicMongoPersistentProperty which is now already implemented in the Spring Data Commons types.
2014-03-07 14:38:23 +01:00
Oliver Gierke
47f0607c49 DATAMONGO-809 - Polishing.
Added ticket references to test cases in GridFsTemplateIntegratinoTests.
2014-03-07 08:30:43 +01:00
Martin Baumgartner
753e794194 DATAMONGO-809 - Filename is now optional when storing files to GridFS.
Added method overloads to GridFsOperations and GridFsTemplate to store files without a filename given.

Original pull request: #119.
2014-03-07 08:30:43 +01:00
Thomas Darimont
d27bec8ed5 DATAMONGO-773 - Verify that @DBRef fields can be included in query.
Added test cases to verify that projection search with included @DBRef fields works as expected.

Original pull request: #142.
2014-03-06 13:34:17 +01:00
Christoph Strobl
c63f7f75dc DATAMONGO-868 - MongoTemplate.findAndModify(…) increases version if not handled manually.
MongoTemplate.findAndModify(…) increments the version property in case it's not manually set in the Update object given.

Original Pull Request: #141.
2014-03-06 11:51:13 +01:00
Christoph Strobl
84040518cf DATAMONGO-863 - UpdateMapper doesn't convert raw DBObjects anymore.
UpdateMapper now only performs simple conversion if it encounters a DBObject, instead of deep inspection on keywords used. This allows to use custom clauses nested in Update for operations not directly supported.

Original Pull Request: #138.
2014-03-06 11:45:35 +01:00
Christoph Strobl
c66b9a538c DATAMONGO-821 - Fixed handling of keyword expressions for DBRefs.
Query Mapper skips DBRef conversion in case the given source value is a nested DBObject. This allows to directly use mongodb operators wrapped in DBObject on association properties.

Original Pull Request: #139.
2014-03-06 11:22:39 +01:00
Oliver Gierke
a0c6b9aa64 DATAMONGO-843 - Register default MongoMappingContext for auditing.
The MongoAuditingRegistrar now also register a fallback MongoMappingContext in case none is present in the BeanDefinitionRegistry.
2014-03-06 09:10:31 +01:00
Thomas Darimont
9370c1ee01 DATAMONGO-843 - Improvements in auditing configuration.
Repositories now declare a fallback MappingContext in case none is configured explicitly to make sure @EnableMongoAuditing also works without an explicit MappingContext bean defined.

AuditingEntityListener is now referring to the IsNewAwareAuditingHandler via an intermediate ObjectFactory to prevent the downstream dependencies from being instantiated eagerly at listener init time.

This is to prevent circular initialization dependencies as Spring accesses ApplicationEventListener beans very early in the container lifecycle to check whether they might be interested in a certain even and just dropped immediately afterwards.

Changed BeanNames.MAPPING_CONTEXT constant to mongoMappingContext to let the XML configuration be consistent with AbstractMongoConfiguration.mongoMappingContext().
2014-03-05 19:50:27 +01:00
Oliver Gierke
2839e9491f DATAMONGO-871 - Add support for arrays as query method return types.
Changed AbstractMongoQuery to potentially convert all query execution results using the DefaultConversionService in case the query result doesn't match the expected return value.

This allows arrays to be returned for collection queries as the conversion service cam transparently convert between collections and arrays.
2014-03-05 10:03:09 +01:00
Oliver Gierke
6963f9e07a DATAMONGO-870 - Added support for sliced query execution.
Added support for Slice as return type for query methods. The execution will expand the requested page size by one to read one more element than actually requested. If that additional element is returned, it will considered to be an indicator for whether a next slice is available.

Related issues: DATACMNS-397.
2014-03-04 17:37:26 +01:00
Thomas Darimont
8dd08a36a0 DATAMONGO-865 - Adjust test dependencies to avoid ClassNotFoundException during test runs.
Added jul-to-slf4j dependency to avoid exceptions being logged during test runs.

Original pull request: #135.
2014-03-04 09:43:49 +01:00
Christoph Strobl
a908e89ef7 DATAMONGO-829 - NearQuery should not default 'num' to zero.
NearQuery now ignores query.getLimit() equal to zero, when adding Query to NearQuery. This has to be done as limit is defaulted to zero within Query which then results in unintended propagation of the parameter.

In case 'num' should be explicitly set to zero one might use 'NearQuery.num(0)' as an alternative to the query approach.

Introduced 'null' check for 'NearQuery.query(Query)' and 'NearQuery.with(Pageable)' along the way.

Original Pull Request: #133
2014-03-03 15:24:09 +01:00
Christoph Strobl
5ace4032ed DATAMONGO-862 - Fixed handling of unmapped paths for updates.
UpdateMapper uses key instead of cleaned property path when not directly pointing to a property.

Original pull request: #132.
2014-02-27 16:55:54 +01:00
Oliver Gierke
621b299f6f DATAMONGO-833 - Add support for reading EnumSets and EnumMaps.
Switched to use Spring Data Commons' CollectionFactory that is capable of creating EnumSets and EnumMaps. Added unit test inspired by pull request #113 for EnumSets and an additional one for EnumMaps.

Slightly refactored the algorithm for reading maps to prevent repeated type lookups.

Related pull request: #113.
2014-02-26 05:35:55 +01:00
Spring Buildmaster
a2628d1b74 DATAMONGO-854 - Prepare next development iteration. 2014-02-24 15:31:41 +01:00
Spring Buildmaster
294616432d DATAMONGO-854 - Release version 1.4.0 RELEASE. 2014-02-24 06:25:32 -08:00
Christoph Strobl
47dd512f95 DATAMONGO-854 - Prepare 1.4.0.RELEASE.
Update artifact version in readme for release and snapshot.
Use commons 1.7.0 resources in docbkx.
Update changelog.
Update version information in notice and readme.

Original pull request: #130.
2014-02-24 15:11:40 +01:00
Thomas Darimont
f16e8d85e5 DATAMONGO-856 - Documentation update.
Removed outdated why-spring-data-doc. Removed CouchDB reference from requirements document. Fixed some typos. Added missing opening <para>-element. Fixed rendering of author information. Added copyright and product name information.

Original pull request: #128.
2014-02-24 11:43:58 +01:00
Christoph Strobl
eb03ae61f2 DATAMONGO-856 - Documentation updates.
Updated references from springsource.org to spring.io. Updated references to mongodb.org. Update vendor to Pivotal Software, Inc. Update required/recommended versions. Update CustomConversions configuration section. Added missing section id's. Fixed some typos. Added missing JavaDoc.

Original pull request: #128.
2014-02-24 11:42:59 +01:00
Christoph Strobl
5be66a3fee DATAMONGO-853 - Update does not allow null keys anymore.
Added check for blank / null keys when adding key to Update.

Original pull request: #129.
2014-02-24 10:51:48 +01:00
Thomas Darimont
d88e4c0e3e DATAMONGO-468 - Verify that one can use a domain object in DbRef field updates.
Added test case to demonstrate that using a domain object as a value for a DbRef field update is already supported.

Original pull request: #127.
2014-02-21 14:03:35 +01:00
Christoph Strobl
57d1449008 DATAMONGO-852 - Update keeps track of fields to be modified.
Update holds a set of fields that modifications are registered for. This information is used to determine if a modification is registered for the version field of a versioned entity. The change was introduced since the present solution did not correctly find the version property correctly within the DBObject resulting from the mapped update.

In case version property is already included in Update automatic version update via $inc is will be skipped.

Original pull request: #126
2014-02-21 13:57:58 +01:00
Oliver Gierke
8d00a0d926 DATAMONGO-404 - Polishing of DBRef creation in Update clauses.
Refactored the internals of UpdateMapper to simplify the code a little. Removed the special converter in favor of handling the mapped key generation directly. This can be removed again, once DATACMNS-444 is fixed.

MetadataBackedField.getPath(String) now also rejects PersistentPropertyPaths the refer to anything else but the id property in case it traverses an association.

Changed MetadataBackedField to return the association property in calls to ….getProperty() as it is the PersistentProperty to hand to the mapping infrastructure for object conversion.

Changed MappingMongoConverter to also check, whether the given source object handed into DBRef creation is of the ID type and simply use that for DBRef creation. This allows creating DBRefs from ids as well.
2014-02-19 21:13:01 +01:00
Oliver Gierke
e3fa844488 DATAMONGO-854 - Upgraded to Spring Data Commons snapshots.
Upgraded to snapshots of Spring Data Commons and the build parent.
2014-02-19 20:02:34 +01:00
Thomas Darimont
58bee75a6b DATAMONGO-404 - Fixed Update.pull(…) handling to work with DBRefs.
We now support pointing to DBRef-mapped properties in Update.pull(…) and also allow to refer to the id of the DBRef to avoid having to create an instance of the entity.
2014-02-19 18:00:49 +01:00
Oliver Gierke
a402395f5c DATAMONGO-849 - Fixed invalid class reference in readme.
Minor sample code optimizations to use MongoClient instead of Mongo. Fixed repository URL.
2014-02-17 16:38:25 +01:00
Oliver Gierke
9d5f8f3ba0 DATAMONGO-848 - Added tweaks to be compatible with Java driver 2.12.
Added build profile to be able to build against next Mongo Java driver version (2.12.0-rc0) currently. Tweaked Bundlor version replacements to allow binding non-OSGi compatible Mongo driver versions.

Exception translator now handles newly introduced MongoServerSelectionException which the driver throws to indicate it can't connect to a MongoDB instance as of driver version 2.12. GridFsTemplate now uses an empty query object instead of null to indicate that no query should be used. 

Adapted test cases to be able to deal with the slightly changed representation of serverUsed in command results (2.12 removed leading slash).
2014-02-17 12:42:19 +01:00
Christoph Strobl
7ebf953063 DATAMONGO-354 - Update.pushAll(…) now supports multiple values.
Update.pushAll(…) now is a multiFieldOperation which allows to send values for different fields within one command.

Original Pull Request: #122.
2014-02-17 11:45:15 +01:00
Christoph Strobl
617ebe0ca7 DATAMONGO-828 - Fixed version checks for updates in MongoTemplate.
Added inspection of the query object to check if the update should only apply to a given version. If so and no documents have been updated we still throw an OptimisticLockingException. For all other cases - like UpdateFirst - zero affected documents is fine.

Original Pull Request: #121.
2014-02-17 11:30:38 +01:00
Christoph Strobl
7f76789664 DATAMONGO-410 - Added test case to show that UpdateMapper considers custom converter.
Original pull request: #124.
2014-02-17 11:03:52 +01:00
Oliver Gierke
81e5919ace DATAMONGO-812 - Added assumptions to not break tests on old MongoDB versions. 2014-02-11 18:50:09 +01:00
Oliver Gierke
efd74956dc DATAMONGO-812 - Polishing.
Changed convertToMongoType(…) to forward type hints to recursive calls to make sure type information is written if a TypeInformation was provided initially. Make sure that UpdateMapper hands in an initial type hint to the converter to make sure type information gets written.

Changed the signature of QueryMapper.getMappedObjectForField(…) to allow customizing the entire entry being added to the result. This is in preparation of more advanced mappings that might have to customize the mapped key.

Fixed newly introduced test cases in MongoTemplateTests.

Original pull request: #112.
2014-02-11 17:39:08 +01:00
Christoph Strobl
49eee40f7e DATAMONGO-812 - Add support for $push $each since $pushAll is deprecated.
$pushAll has been deprecated in MongoDB 2.4. Instead of calling pushAll one can use push in combination with each. The abstraction for pushAll will remain in code for now but may be removed in a subsequent version.

Original pull request: #112.
2014-02-11 17:39:07 +01:00
Thomas Darimont
8e93b844c7 DATAMONGO-830 - Prevent NullPointerException during cache warmup in CustomConversions.
We now use a ConcurrentHashMap to cache the results of custom read target lookups in order to avoid having to traverse the readingPairs for every lookup. The use of ConcurrentHashMap should also prevent potentially NullPointerExceptions from being thrown if custom conversions are initialized in heavily threaded environments.

Original pull request: #117.
2014-02-10 18:49:01 +01:00
Thomas Darimont
3e64432f1a DATAMONGO-842 - Improve documentation in GridFS section.
Rephrased wording for better understanding.

Original pull request: #120.
2014-02-10 10:30:57 +01:00
Thomas Darimont
88c968ad36 DATAMONGO-840 - Improve support for nested field references in SpEL expressions within Projections.
We now correctly add a compound expression that represents a field reference to the previous operation arguments if necessary.

Original pull request: #118.
2014-02-10 10:18:14 +01:00
Thomas Darimont
99eefe0773 DATAMONGO-838 - Cannot refer to expression based field in group operation.
Previously we didn't set a proper target value for the generated expression field. As a potential fix we just use the alias as the target field.

Original pull request: #116.
2014-02-10 09:59:15 +01:00
Oliver Gierke
3d4569be14 DATAMONGO-826 - Remove obsolete milestone repository. 2014-02-09 14:33:27 +01:00
Spring Buildmaster
57455c4a26 DATAMONGO-826 - Prepare next development iteration. 2014-01-29 06:04:53 -08:00
Spring Buildmaster
f9e20d12b2 DATAMONGO-826 - Release version 1.4.0.RC1. 2014-01-29 06:04:50 -08:00
Thomas Darimont
4d6152c65e DATAMONGO-826 - Prepare release 1.4.0.RC1.
Updated project metadata (changelog, notice, readme) and bumped versions.
Fixed broken links in the documentation.
2014-01-29 14:42:52 +01:00
Thomas Darimont
d81cc53c12 DATAMONGO-837 - Upgrade MongoDB java driver to 2.11.4.
Upgraded the MongoDB java driver from 2.11.3 (current) to the recommended bug fix version 2.11.4.
2014-01-29 13:27:46 +01:00
Oliver Gierke
af4b84ea43 DATAMONGO-835 - Code cleanups. 2014-01-28 12:46:36 +01:00
Thomas Darimont
f9110828bc DATAMONGO-407 - Fixed query mapping for updates using collection references.
When an update clause contained a collection element reference (….$.…) we failed to write the type information of the target value object as the key was not translated into a correct property path correctly. We now strip the reference literals and re-apply them when the mapped key is generated.
2014-01-27 19:39:00 +01:00
Christoph Strobl
f301837be5 DATAMONGO-807 - findAndModify(…) now retains type information.
Using findAndUpdate(…) did not retain type information when used to update a whole nested type instead of single fields within the type. We now use the UpdateMapper instead of QueryMapper in doFindAndModify(…).

Original pull request: #110.
2014-01-27 18:12:44 +01:00
Martin Baumgartner
4d29d937eb DATAMONGO-823 - Add bucket attribute to <mongo:gridFsTemplate />.
Original pull request: #114.
2014-01-25 22:05:20 +01:00
Thomas Darimont
86c11bc614 DATAMONGO-790 - Ensure compatibility with Spring Framework 4.0.
The GenericApplicationContext doesn't refresh automatically in Spring 4.x we thus we have to call refresh manually. Also the test for the custom application listener registration fails on 4.0, so I adapted it to run on both versions.
2014-01-24 18:53:42 +01:00
Oliver Gierke
be34b4e503 DATAMONGO-822 - Enable CDI repositories to be instantiated eagerly.
From the CDI extension we now use the callback newly introduced in Spring Data Commons to enable it to trigger eager initialization.

See also: DATACMNS-416.
2014-01-24 18:53:35 +01:00
Thomas Darimont
ebfa2c5689 DATAMONGO-686 - Fixed potential race-condition in QueryMapper.
We now create a new a new DBObject in QueryMapper#getMappedValue() instead of replacing them in the original objects in order to prevent the original query object being manipulated.

Added test case to verify that the original DBObject is not manipulated.

Original pull request: #111.
2014-01-23 13:05:31 +01:00
Oliver Gierke
b245ef2d9e DATAMONGO-826 - Polished poms.
Removed Spring dependency versions and fixed repository declarations.
2014-01-23 12:57:41 +01:00
Christoph Strobl
5ef40d54bc DATAMONGO-811 - UpdateFirst and updateMulti should increase version.
Version of documents are increased when updated via MongoTemplate.updateFirst and MongoTemplate.updateMulti just as it is done when calling MongoTemplate.save(...).

Original pull request #109
2014-01-22 11:03:23 +01:00
Laurent Canet
c679dba438 DATAMONGO-778 - Improved support for geospatial indexing with @GeoSpatialIndexed.
We now support to create geospatial indices of type 2D sphere and geoHaystack using the @GeospatialIndexed annotation on fields.

Original pull request #82, #104.
2014-01-14 15:18:36 +01:00
Thomas Darimont
fd6e4000b5 DATAMONGO-816 - Improve query handling in MongoTemplate.executeQuery().
We now process the given query with the queryMapper before passing it on to the executeQueryInternal(…) in order to deal with potentially required query modifications, e.g. enum value conversions.

Original pull request: #108.
2014-01-14 14:56:00 +01:00
Thomas Darimont
c12a27a8f8 DATAMONGO-805 - Excluding DBRef field in a query causes a MappingException.
Previously we tried to convert all DBRef associations into appropriate DBRef structures even if they were to be ignored. We now ignore excluded properties in DBRef associations correctly.

Original pull request: #102.
2014-01-14 14:32:10 +01:00
Oliver Gierke
df2184f204 DATAMONGO-824 - Added contribution guidelines.
Linked to the actual contribution guidelines maintained in the Spring Data Build project.
2014-01-14 13:43:37 +01:00
Oliver Gierke
e9c8644d23 DATACMNS-414 - Adapted to removed generics in AuditingHandler API. 2014-01-12 20:05:45 +01:00
Thomas Darimont
c730b8f479 DATAMONGO-813 - Improve handling for non-existing resources in GridFSTemplate.
We now return null for a non-existing resource instead of throwing a NPE.

Original pull request: #106.
2013-12-13 15:19:01 +01:00
Thomas Darimont
f3b31fc467 DATAMONGO-808 - Improve ServerAddressPropertyEditor to support IPv6 addresses.
Improved parsing of ServerAddress to be able to handle IPv6 addresses correctly. We now use the actor ServerAddress(InetAddress) to be able to pass an IPv6 address. The constructor which takes a String as the hostname can't deal with IPv6 addresses directly because it tries to extract a port at the wrong location of such an address.

This change should not change the behavior too much, since the constructor ServerAddress(String, int) already calls InetAddress.getByName(...) internally.

Original pull request: #103.
2013-12-13 14:59:12 +01:00
Oliver Gierke
f778b2554c DATAMONGO-799 - Refer to next version of build parent and Spring Data Commons. 2013-12-09 17:24:42 +01:00
Oliver Gierke
9d292f64b9 DATAMONGO-806 - Fixed invalid rendering of id field references.
Tweaked the rendering of projection operations to always use the field based reference lookup to make sure the reference gets rendered aliased. Moved value calculation logic into FieldReference.

Original pull request: #101.
2013-12-09 16:42:29 +01:00
Thomas Darimont
8ab038f83c DATAMONGO-806 - No property _id found for type com.entity.User.
Added test case to reproduce the issue.
2013-12-09 16:42:29 +01:00
Martin Baumgartner
689552c28e DATAMONGO-726 - Fixed classname references in namespace XSDs.
Original pull request: #100.
2013-12-09 09:59:51 +01:00
Thomas Darimont
9ea9912b23 DATAMONGO-799 - Fix failing test in MongoTemplateTests on MongoDB 2.5.x.
Generalized exception message matching to reflect the changed exception message in MongoDB 2.5.x that also works with previous versions of MongoDB.

Original pull request: #97.
2013-12-04 12:19:12 +01:00
Thomas Darimont
a952ce5d2b DATAMONGO-804 - Fix default annotation attribute value for repositoryImplementationPostfix().
Changed repositoryImplementationPostfix() in EnableMongoRepositories to "Impl" to be consistent with other EnableXXXRepositories annotations. Note that this change is of rather documenting nature, as the defaulting of the configuration is applied in DefaultRepositoryConfiguration.getImplementationPostfix() in Spring Data Commons.

Original pull request: #99.
2013-12-04 12:13:14 +01:00
Oliver Gierke
b88d960893 DATAMONGO-802 - Changed return type of AbstractMongoConfiguration.mongoDbFactroy().
Changed the return type of said method to MongoDbFactory to allow subclasses to return a completely different implementation class.
2013-12-04 12:13:01 +01:00
Spring Buildmaster
e44d1f5f9a DATAMONGO-799 - Prepare next development iteration. 2013-11-19 04:19:12 -08:00
Spring Buildmaster
2b5e2361a8 DATAMONGO-799 - Release version 1.4.0.M1. 2013-11-19 04:19:10 -08:00
Thomas Darimont
5737f2d19d DATAMONGO-801 - Prepare release 1.4.0 M1.
Updated project metadata and bumped versions.
2013-11-19 13:12:06 +01:00
Oliver Gierke
60494a6904 DATAMONGO-800 - Improved AuditingIntegrationTests.
Added a tiny Thread.sleep(…) to make sure the assertion works on fast machines. If the operations after the first step all happen within a millisecond, it will fail.
2013-11-18 13:50:42 +01:00
Oliver Gierke
ceb561e3e4 DATAMONGO-792 - Polishing of JavaConfig support for auditing.
Original pull request: #94.
2013-11-12 11:57:25 +01:00
Thomas Darimont
e2d0220cea DATAMONGO-792 - Add support to configure auditing via JavaConfig.
Introduces the necessary infrastructure in form of MongoAuditingRegistrar to configure auditing with MongoDB via Annotation config. The MongoDB auditing feature can be enabled by annotating a configuration class with the EnableMongoAuditing annotation. Added section to reference documentation.

Original pull request: #94.
2013-11-12 11:50:46 +01:00
Oliver Gierke
ea33e8b8c6 DATAMONGO-348 - Improved interceptor for lazy-loaded DBRefs.
We're now routing calls to methods declared on Object into the proxy to not accidentally resolve the lazy loading proxy on access of methods like toString() etc.
2013-11-08 19:19:16 +01:00
Oliver Gierke
506b6a2e85 DATAMONGO-795 - More predictable behavior in CustomConversions.
The target type lookup previously was unpredictable in cases two converters were registered for the same source type. We now use LinkedHashMaps to register the converters and also make sure that we prefer manually registered converters over the default ones.

Related pull request: #96.
2013-11-07 15:11:50 +01:00
Thomas Darimont
7c0eee9e09 DATAMONGO-789 - Support login via different authentication database.
MongoDbUtils now supports to perform the authentication against a dedicated authenticationDatabase - if no authenticationDatabase is given explicitly then the given regular database will be used. The authentication database can be configured via the authentication-dbname attribute of the db-factory element in xml config or by overriding the getAuthenticationDatabaseName() method of AbstractMongoConfiguration.

Original pull request: #92.
2013-11-06 11:59:48 +01:00
Thomas Darimont
332e5eb715 DATAMONGO-791 - Added newAggregation(…) overloads to accept a List.
Aggregations can now be constructed from a list of AggregateOperations. This simplifies the usage in cases where one has to conditionally in- or exclude AggregateOperations from an AggregationPipeline.

Original pull request: #93.
2013-11-06 11:00:31 +01:00
Oliver Gierke
39ee9b56e2 DATAMONGO-793 - Adapt test cases to new initialization model of repositories.
Moved tests for nested repositories to a separate package to prevent initialization of repositories that are not meant to be instantiated actually.
2013-11-05 17:29:05 +01:00
Thomas Darimont
8fb390ee88 DATAMONGO-348 - Added serialization support for lazy-loading for DBRefs. 2013-11-05 15:22:19 +01:00
Oliver Gierke
df1c4496dc DATAMONGO-348 - Polishing of lazy loading implementation.
Extracted DelegatingDbRefResolver and associates from MappingMongoConverter. Let MongoDbFactory expose PersistenceExceptionTranslator only to prevent invalid dependency to core package. Renamed DbRefResolveCallback to ResolverCallback. Removed AbstractDbRefResolver and moved the functionality implemented there (triggering of exception translation) into the DbRefResolver.

MappingMongoConverter now uses a slightly extended version of DbRefResolver so that we can essentially replace the MongoDbFactory dependency with the DbRefResolver one.

Added support for Objenesis based lazy-loading proxies to support domain classes without a default constructor. Explicitly check for Spring 4 being present as with it the default ProxyFactory already supports that out of the box.

Added missing JavaDoc and assertions. A lot of cleanups and removal of deprecation warnings in test cases.
2013-11-04 20:21:10 +01:00
Thomas Darimont
b808fd3003 DATAMONGO-348 - Add support for lazy loading of DbRefs.
Introduced DbRefResolver interface in order to be able to abstract how a DbRef is resolved that is used in MappingMongoConverter#doWithAssociations. The present behaviour was to resolve a DbRef eagerly. This functionality is now implemented by EagerDbRefResolver. In order to support lazy loading we have to provide some means to define the desired loading behaviour. This can now be done via the "lazy"-Attribute on @DbRef which defaults to false.
If the attribute is set to true the LazyDbRefResolver is used to create a Proxy that eagerly loads the required data on demand when one of the (non-Object) proxy methods is called. MongoDbFactory now exposes a MongoExceptionTranslator that is now used by the MappingMongoConverter and MongoTemplate.

Introduced a DelegatingDbRefResolver that can delegate to a DbRefResolveCallback in order  to perform the actual DbRef resolution.
We now use cglib-proxies if necessary if the referenced association is a concrete class.

Added unit tests for lazy loading of interface types, concrete collection types and concrete domain types. Exposed state from LazyLoadingInterceptor for better testability.

Added unit tests for lazy loading of classes with custom PersistenceConstructor.
Moved integration tests for PersonRepository into its own test class.
2013-11-04 20:15:24 +01:00
Oliver Gierke
ed12298271 DATAMONGO-788 - Polishing.
Slightly changed the way the the simple reference rendering for projections is implemented. Introduced an isAliased() method on Field to be able to determine whether the field reference has been renamed explicitly.

Original pull request: #90.
2013-10-31 14:40:09 +00:00
Thomas Darimont
682798325b DATAMONGO-788 - Projection operations do not render synthetic fields properly.
Introduced boolean isSynthetic() attribute to FieldReference to determine if the given reference points to a synthetic field, as this controls whether we render a simple include (1) or a concrete reference ($fieldName) E.g. isSynthetic() would be true for a field reference to _id.

Original pull request: #90.
2013-10-31 14:39:48 +00:00
Oliver Gierke
0e69021486 DATAMONGO-787 - Upgraded to Spring Framework 3.2.4.
Upgraded to Spring Data Commons 1.3.0.BUILD-SNAPSHOT to benefit from upgrade to Spring 3.2.4 Added a workaround in the code introduced for DATAMONGO-774 which now runs into SPR-11031.
2013-10-25 20:57:21 +02:00
Komi Serge Innocent
ae7e24f1b6 DATAMONGO-746 - Creating an IndexInfo now also works with Doubles.
DefaultIndexOperations is now able to detect that in contrast to what's currently documented in the MongoDB reference documentation, it apparently returns double values for the index direction.

Original pull request: #67.
2013-10-25 10:48:57 +02:00
Thomas Darimont
94d4fa613c DATAMONGO-780 - Add support for nested repositories.
Support for considering nested repository interfaces can now be configured on the EnableMongoRepositories annotation via the considerNestedRepositories property.

Original pull request: #87.
2013-10-24 14:56:02 +02:00
Max Leuthäuser
39c9593b39 DATAMONGO-782 - Fixed typo in documentation.
Original pull request: #86.
2013-10-21 17:33:49 +02:00
Oliver Gierke
6e5f3661a8 DATAMONGO-774 - Some polishing in the reference documentation. 2013-10-17 15:54:05 +02:00
Thomas Darimont
2bd78e0bf0 DATAMONGO-774 - Added usage examples for SpEL expressions in projections to the reference documentation.
Original pull request: #81.
2013-10-17 15:54:05 +02:00
Oliver Gierke
dd59cdc59a DATAMONGO-774 - A round of Jürgenization for SpEL support in aggregation framework support.
Introduced dedicated spel package and extracted value objects to encapsulate and express information about the node transformation in a more semantical way.

Moved a lot of the logic contained in the SpelExpressionTransformer into the value objects for cohesiveness and testability. Updated Sonargraph architecture model to reflect the new packages we've introduced.

Original pull request: #81.
2013-10-17 15:54:01 +02:00
Thomas Darimont
7e471e2301 DATAMONGO-774 - Support SpEL expressions to define projection operations in the aggregation framework.
ProjectionOperations can now be built using SpEL expressions as this significantly shortens the code needed to express the project, especially for slightly more complex mathematical expressions.

Projection now has an ….andExpression(…) method that takes a SpEL expression and optional arguments that can be referred to via their index, i.e. a SpEl expression "5 + [0]" can be expanded using ….andExpression("5 + [0]", 7).… so that the projection can be prepared and dynamically get values bound.

Original pull request: #81.
2013-10-17 15:43:29 +02:00
Oliver Gierke
0871a43831 DATAMONGO-764 - Polished JavaDoc in MongoOptionsFactoryBean. 2013-10-15 14:04:09 +02:00
Mike Saavedra
710e77dabe 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.
2013-10-15 14:03:36 +02:00
Oliver Gierke
9c996617e8 DATAMONGO-630 - Polishing in UpdateTests. 2013-10-13 15:23:11 +02:00
Becca Gaspard
eebd49ab8d DATAMONGO-630 - Support $setOnInsert update operator.
Original pull request: #70.
2013-10-13 15:13:12 +02:00
Oliver Gierke
fb979b1734 DATAMONGO-752 - Improved keyword detection in QueryMapper.
The check for keywords in QueryMapper now selectively decides between checks for a nested keyword (DBObject) object and the check for a simple key. This allows the usage of criteria values starting with $ (e.g. { 'myvalue' : '$334' }) without the value being considered a keyword and thus erroneously triggering a potential conversion of the value.

Moved more logic for a keyword into the Keyword value object.
2013-10-13 13:48:00 +02:00
Oliver Gierke
b5c88938e0 DATAMONGO-534 - GridFsTemplate supports queries with sorting.
Upgraded to Mongo Java Driver 2.11.3 to be able to forward the sorting options contained in a Query to eventually be able to return sorted results when querying for files.
2013-10-13 12:41:06 +02:00
Oliver Gierke
4027770701 DATAMONGO-777 - Upgraded to Mongo Java Driver 2.11.3.
Upgraded to latest mongo Java Driver release to benefit from new API introduced (required to fix DATAMONGO-534). Adapted test cases to API changes in DuplicateKey.
2013-10-13 12:41:06 +02:00
Oliver Gierke
a120ce2bb1 DATAMONGO-776 - Improvement in TypeBasedAggregationOperationContext.
TypeBasedAggregationOperationContext's getReference(…) doesn't use a PropertyPath anymore to avoid running the camel case property resolution.
2013-10-10 14:03:25 +02:00
Thomas Darimont
a5d40a049d DATAMONGO-769 - Improve support for arithmetic operators in AggregationFramework.
We now support the usage of field references in arithmetic projection operations.

Original pull request: #80.
2013-10-10 11:09:04 +02:00
Thomas Darimont
f0f12d5296 DATAMONGO-771 - Fix raw JSON string handling in MongoTemplate.insert(…).
We now support insertion of JSON objects as plain strings via MongoTemplate.insert(…).

Original pull request: #79.
2013-10-08 12:46:03 +02:00
Oliver Gierke
24e06cf219 DATAMONGO-761 - Fix path key lookup for non-properties in SpringDataMongoDBSerializer.
In our Querydsl MongodbSerializer implementation we now only inspect the MongoPersistentProperty for a field name if the given path is really a property path. Previously we tried to always resolve a persistent property even if the given path was an array index path, a map key or the like.
2013-10-08 12:37:18 +02:00
Oliver Gierke
1b83ff0382 DATAMONGO-766 - Support for nested field references through @Field.
The MappingMongoConverter now supports reading and writing nested fields from and to documents by using @Field(…) with a path expression, e.g. @Field("a.b"). We now correctly create the nested objects and also reuse the previously created intermediates when populating further properties.

Not that this might cause the need to define field ordering explicitly as later properties might override the values set using a path reference in @Field.
2013-10-08 12:23:24 +02:00
Thomas Darimont
fe41202f96 DATAMONGO-770 - Add support for IgnoreCase in query derivation.
We now support IgnoreCase and AllIgnoreCase in predicate expression for derived queries.

Original pull request: #78.
2013-10-07 17:13:33 +02:00
Thomas Darimont
78235b4799 DATAMONGO-768 - Improve documentation of how to use @PersistenceConstructor.
Added an additional section to chapter 7.3 that describes the parameter value binding when the @PersistenceConstructor annotation including a small usage example. Added a concrete example for the @Value annotation that uses SpEL.

Original pull request: #77.
2013-10-01 14:04:34 +02:00
Thomas Darimont
51ece4353b DATAMONGO-759 - Improved rendering of GroupOperation.
GroupOperation gets the _id field now rendered as null if no group fields were added to the operation. Previously it was rendered as empty document (i.e. { }). While this was technically correct as well, we're now closer to what the MongoDB reference documentation describes.

Original pull request: #73.
2013-09-30 19:20:05 +02:00
Thomas Darimont
51bab838b0 DATAMONGO-757 - Align output of projection operation with MongoDB defaults.
Adjusted FieldProjection to generate an appropriate representation of included / excluded fields (namely :1 for included and :0 for excluded).
Polished guards to handle only _id is allowed to be excluded (DATAMONGO-758).

Original pull request: #76.
2013-09-27 12:56:01 +02:00
Thomas Darimont
361f9daa45 DATAMONGO-753 - Add support for nested field references in aggregation operations.
Aggregation pipelines now correctly handle nested field references in aggregation operations. We introduced FieldsExposingAggregationOperation to mark AggregationOperations that change the set of exposed fields available for processing by later AggregationOperations. Extracted context state out of AggregationOperation to ExposedFieldsAggregationContext for better separation of concerns. Modified toDbObject(…) in Aggregation to only replace the aggregation context when the current AggregationOperation is a FieldExposingAggregationOperation.

Original pull request: #74.
2013-09-26 14:29:04 +02:00
Thomas Darimont
56b23a6dbe DATAMONGO-758 - Current mongodb Versions only support to explicitly exclude the _id property in a projection.
Added guard to FieldProjection.from within ProjectionOption to prevent users from excluding fields other than "_id".
2013-09-24 16:38:20 +02:00
Thomas Darimont
9e15c17e26 DATAMONGO-760 - Add ability to override CRUD methods using @Query.
Annotated @Query with @QueryAnnotation to mark it as a custom query annotation so that one can redeclare a repository CRUD method and let it execute the annotated query instead of triggering the generic implementation.

Original pull request: #71.
2013-09-24 16:09:28 +02:00
Thomas Darimont
a3c77a43b6 DATAMONGO-740 - Prepare for next iteration.
Updated pom.xml to refer to the latest SD Commons Version 1.7.0.BUILD-SNAPSHOT.
2013-09-18 13:50:02 +02:00
Oliver Gierke
55169e2e11 DATAMONGO-740 - Updated readme after 1.4.0.RELEASE release. 2013-09-09 16:10:55 -07:00
Spring Buildmaster
24672e6bdd DATAMONGO-740 - Prepare next development iteration. 2013-09-09 15:56:23 -07:00
Spring Buildmaster
1a46abfbb9 DATAMONGO-740 - Release version 1.3.0.RELEASE. 2013-09-09 15:56:20 -07:00
Thomas Darimont
61284228dd DATAMONGO-740 - Prepare 1.3.0 RELEASE. 2013-09-09 15:50:04 -07:00
Thomas Darimont
8cb92de1ee DATAMONGO-445 - Allow to skip unnecessary elements in NearQuery.
Added support for skipping elements for NearQuery in MongoTemplate. As mongodb currently (2.4.4) doesn't support he skipping of elements in geoNear-Queries we skip the unnecessary elements ourselves. We use the limit & skip information from the given query or an explicitly passed Pageable.

Original pull request: #64.
2013-08-22 09:20:19 +02:00
Oliver Gierke
5d3cc3fa04 DATAMONGO-743 - Added DBObjectToStringConverter.
Added a reading converter to dump DBObject instances into a String directly to enable executing queries against MongoDB into a String version of the query result without marshaling the DBObject into a Map first.
2013-08-22 08:55:07 +02:00
Thomas Darimont
c0b99740dc DATAMONGO-742 - Document CDI integration in reference documentation.
Added chapter for CDI Integration under the new chapter Miscellaneous.

Original pull request: #63.
2013-08-13 12:20:58 +02:00
Randy Watler
595bbd3aa7 DATAMONGO-737 - Register TransactionSynchronization holder once per Mongo instance.
Original pull request: #62.
2013-08-12 09:51:02 +02:00
Chuong Ngo
5d2fc31164 DATAMONGO-738 - Allow to pass collectionName along with entityClass as parameter to update methods in MongoTemplate.
Added appropriate overloaded methods to MongoOperations and MongoTemplate. Applied pull request from Chuong Ngo <chuong.h.ngo.ctr@mail.mil>.
Original pull request: #57.
2013-08-09 15:30:00 +02:00
Thomas Darimont
a9dc0fae69 DATAMONGO-725 - Improve configurability and documentation of TypeMapper on MappingMongoConverter.
Added new attribute type-mapper-ref to the mapping-converter element in spring-mongo-1.3.xsd in order to support the configuration of custom-type-mappers. Removed the unsupported attributes "mongo-ref" and "mongo-template-ref" from the mapping-converter element in spring-mongo-1.3.xsd because they are not considered anymore.

Updated MappingMongoConverterParser to be aware of the new attribute. Added examples for configuring a custom MongoTypeMapper the usage of @TypeAlias to the reference documentation.

Original pull request: #61.
2013-08-09 13:44:53 +02:00
Thomas Darimont
0605c7b753 DATAMONGO-507 - Reject incorrect usage of Criteria#not().
Added a guard to Criteria#(and|or|nor)Operator to prevent wrapping $and, $or or $nor expressions in a $not expression as mongodb currently doesn't support this. Added test case to CriteriaTests to verify that not() works as specified.

Original pull request: #60.
2013-08-09 12:23:25 +02:00
Thomas Darimont
21352a8829 DATAMONGO-602 - Added test case to MongoTemplate tests to verify that querying by BigInteger ids work.
Original pull request: #59.
2013-08-07 18:58:32 +02:00
Oliver Gierke
58e1d2dbd9 DATAMONGO-741 - Fixed check for nested property references in aggregation framework.
Fixed using the actual field reference instead of the field name on resolving. Added equals(…) and hashCode() methods to value objects. Added unit tests for TypeBasedAggregationOperationContext.
2013-08-07 10:21:48 +02:00
Spring Buildmaster
4f7821e3c2 DATAMONGO-732 - Prepare next development iteration. 2013-08-05 08:48:22 -07:00
Spring Buildmaster
9dd866e34a DATAMONGO-732 - Release version 1.3.0.RC1. 2013-08-05 08:48:15 -07:00
Oliver Gierke
def6079795 DATAMONGO-732 - Prepare 1.3.0.RC1.
Updated changelog, notice and readme. Upgraded to Spring Data Build parent 1.1.1.RELEASE. Upgraded to Spring Data Commons RC1. Switched to milestone repository. Updated links to the parts of the Spring Data Commons reference documentation.
2013-08-05 17:33:58 +02:00
Thomas Darimont
f3f537c1a6 DATAMONGO-586 - Adjusted examples in reference documentation.
Modified formatting and moved the detailed descriptions below the example code.
Added example for arithmetic operations in projection operations.
2013-08-05 17:20:37 +02:00
Thomas Darimont
ad44db386b DATAMONGO-586 - Adjusted examples in reference documentation.
Changed examples in reference documentation to reflect the new DSL style.
2013-08-05 14:59:15 +02:00
Thomas Darimont
bcc3bf61b6 DATAMONGO-586 - Add initial support for arithmetic expressions.
Added test cases to ProjectionOperationUnitTests. Adjusted DSL for GroupOperation to be similar to ProjectionOperation. Introduced GroupOperationBuilder to GroupOperation to be able to define an alias for the current GroupOperation. Adjusted test cases in AggregationTests to the new DSL style accordingly. Added test cases to GroupOperationUnitTests for push and addToSet.
2013-08-05 14:58:10 +02:00
Oliver Gierke
1a28a294d1 DATAMONGO-586 - A bit of Jürgenization on ProjectionOperations. 2013-08-05 14:58:09 +02:00
Thomas Darimont
14623a3655 DATAMONGO-586 - Add initial support for arithmetic expressions.
ProjectionOperationBuilder now implements AggregationOperation in order to be able support aliased as well as non alias projection operation expressions. Added test case for arithmetic operations to AggregationTests. Added Product domain class to be able to demonstrate some meaningful arithmetic operations. Applied changes from code review. Added internal private remove method to ProjectionOperation to allow previous operation to support aliasing.
2013-08-05 14:56:45 +02:00
Thomas Darimont
6dcaa31897 DATAMONGO-586 - Added chapter for Aggregation Framework support to the reference documentation.
Added chapter to mongodb.xml.
Added myself to the authors list in index.xml
2013-08-05 14:56:45 +02:00
Oliver Gierke
e57fe346c0 DATAMONGO-586 - Next round of polishing.
Refined the way the aggregation pipeline gets rendered into a DBObject. More tests. Added $avg and shortcut methods to GroupOperations. Fixed ProjectionOperation to use 1 for implicit references. Made ProjectionOperation publicly visible. Added automatic result unwrapping. API consistency, tests, JavaDoc polish.
2013-08-05 14:55:36 +02:00
Thomas Darimont
7dd94949d5 DATAMONGO-586 - Initial support for automatic field reference resolution.
Added automatic field reference resolution which removes the need to have in depth knowledge on how aggregation steps structures the output.
Introduced AggregateOperationContext abstraction to hold the information of available fields for an aggregation step.
Introduced ContextConsumingAggregateOperation and ContextProducingAggregateOperation abstractions to be able to distinguish operations.
Updates test cases to reflect the API changes.
2013-08-05 14:55:35 +02:00
Oliver Gierke
966f971bee DATAMONGO-586 - Some refactorings in the integration test. 2013-08-05 14:55:34 +02:00
Thomas Darimont
aa23c579e8 DATAMONGO-586 - Added test case for complex aggregation framework use case.
Added Zipcode sample dataset from 10gen. Allow Projections to be used in conjunction with GroupOperations. Integration & Refactoring of github contribution by Tobias Trelle and Sebastian Herold. Switched from builder-style to static factory based DSL construction of aggregation specifications. Introduced embedded DSL for convenient construction of aggregation specifications. Added test cases based on mongodb aggregation framework examples. Added more test cases, additional java doc. Added test case for unwind operation (returnFiveMostCommonLikes) in AggregationTests. Other test cases should now also run in CI environment, due to deterministic result ordering. Adjusted write concern to ensure persistence of sample data.

Introduced TypedAggregation which holds type information of the input type of an aggregation. Cleaned up aggregate methods on MongoOperations. Removed HasToDBObject interface. Cleaned up constructors for Aggregation and TypedAggregation.
2013-08-05 14:49:48 +02:00
Oliver Gierke
6b634d08ce DATAMONGO-586 - Yet another round of polish.
Added Apache license headers where missing. Removed separate package. Reduced visibility of ReferenceUtil as it's internal use only. Fixed formatting.
2013-08-05 14:49:25 +02:00
Sebastian Herold
b7b61405f9 DATAMONGO-586 - Further enhancements to Aggregation API.
Fixed parameter names in comments. Add static factory method. Implement basic aggregation operation join point. Implement match operation. Extracted ReferenceUtil. Created starting point of $group operation with _id field definition and $addToSet fields.
2013-08-05 14:49:24 +02:00
Oliver Gierke
4d65aa7207 DATAMONGO-586 - First round of polish.
Fixed or added copyright headers where necessary. Added Tobias as author where necessary. Added @since tags to newly introduced classes and methods. Documented non-nullability of parameters. Polished test cases a bit.
2013-08-05 14:49:24 +02:00
Tobias Trelle
c129c706a3 DATAMONGO-586 - Initial commit for support of the aggregation framework.
Fluent interface for AggregationPipeline, tests. Added type safe versions for aggregation operations $match and $sort. Not null assertions + auto-prefix field in $unwind operation. Type safe impl for projections (first version). Support for $add and $substract in projection.
2013-08-05 14:49:24 +02:00
Johno Crawford
7823385ac7 DATAMONGO-544 - Added support for TTL collection via indexing annotation.
Added expireAfterSeconds attribute to @Indexed and @CompoundIndex annotations. Adapted MongoPersistentEntityIndexCreator to evaluate the attribute and configure the index about to be created if the attribute was configured to something non default.

Original pull request: #55.
2013-07-31 09:17:50 +02:00
Oliver Gierke
21fcfe11c2 DATAMONGO-731 - Adapted API changes for Parameters in Spring Data Commons.
Adapted API changes introduced for DATACMNS-350.
2013-07-30 15:18:25 +02:00
Thomas Darimont
bfe33a446c DATAMONGO-728 - Added missing package-info files. 2013-07-23 16:32:38 +02:00
Thomas Darimont
9be50316c3 DATAMONGO-709 - Added support for restricting results by document types.
Added restrict(…) method to the Query API that generates appropriate filter criteria to restrict the result to certain types only. 

Type restrictions in query expressions are now applied in QueryMapper via a MongoTypeMapper based on information passed in through the query object in a "special" key. Exposed MongoTypeMapper in MongoConverter and MappingMongoConverter. Merged DefaultTypeMapper and DefaultMongoTypeMapper.

Original pull request: #53.
2013-07-23 15:14:09 +02:00
Thomas Darimont
30513267af DATAMONGO-721 - Updates retain type information for collections properties.
Added test case to demonstrate that the fix for DATACMNS-345 will propagate into Spring Data MongoDB.

Original pull request: #52.
2013-07-16 13:39:03 +02:00
Oliver Gierke
d3d480e79b DATAMONGO-724 - Adapted test case to show type information written by converters gets regarded. 2013-07-16 11:51:50 +02:00
Oliver Gierke
c39ad1bbc4 DATAMONGO-724 - Added test case to show registered converters work.
Added test case that shows that if a custom converter doesn't write type information on its own, the managed type can't be used in polymorphic scenarios. Direct type mappings still work as expected.
2013-07-16 10:09:55 +02:00
Oliver Gierke
fcdc29df49 DATAMONGO-723 - Cleand up a few misnamed test cases.
Reactivated test cases that were name Test instead of Tests by accident.
2013-07-12 18:44:13 +02:00
Oliver Gierke
de7120d8dd DATAMONGO-717 - Forward port of test case.
Added a testcase to show the lifecycle callbacks are invoked in the appropriate order.
2013-07-10 23:07:08 +02:00
Thomas Darimont
84df02ae38 DATAMONGO-685 - ServerInfo should return used hostname reported by MongoDB.
Added test case getHostNameShouldReturnServerNameReportedByMongo() to MongoMonitorIntegrationTests. Modified MongoMonitorIntegrationTests to use common mongo-infrastructure configuration. ServerInfo.getHostName() is now derived from serverStatus.serverUsed.

Original pull request: #51.
2013-07-10 14:30:51 +02:00
Thomas Darimont
d6c5907940 DATAMONGO-702 - Allow usage of property names in field specifications.
MongoTemplate now translates property names used in a Query's field specification into the according field names. Refactored delegation in various doFind(…) methods and polished JavaDoc.

Original pull request: #50.
2013-07-09 11:26:20 +02:00
Thomas Darimont
b2fe54c0a1 DATAMONGO-392 - Updates now retain type information.
Extracted mongo type conversion in QueryMapper into delegateConvertToMongoType(…). Introduced QueryMapper subclass UpdateQueryMapper to retain type information in delegateConvertToMongoType(…). Added test case updatesShouldRetainTypeInformation to MongoTemplateTests.

Original pull request: #49.
2013-07-08 15:07:10 +02:00
Oliver Gierke
47a198c688 DATAMONGO-540 - Fixed test case.
Apparently the test case was not working on MongoDB instances in version 2.2.
2013-07-08 14:16:07 +02:00
Thomas Darimont
5d9dbda03b DATAMONGO-688 - Improve detection of id properties.
Added support for precedence of explicit id property mapping over implicit property mappings. Changed BasicMongoPersistentProperty.getFieldName() to return the mongo _id field name only for the "effective" id property considering the owner entity if already set). Added some test cases for all possible cases to MongoMappingContextUnitTests.

Original pull request: #48.
2013-07-08 14:16:00 +02:00
Thomas Darimont
36d52862bc DATAMONGO-671 - Improve test case to not fail on fast machines. 2013-07-05 11:57:15 +02:00
Thomas Darimont
0afbf6fe19 DATAMONGO-540 - Added test case to show findOne(…) works after upsert. 2013-07-05 11:55:37 +02:00
Oliver Gierke
b0bf8cb718 DATAMONGO-714 - Updated formatter to latest changes. 2013-07-05 11:51:20 +02:00
Thomas Darimont
567a8d9d5b DATAMONGO-713 - Fixed some spelling errors in README.md. 2013-07-05 10:29:35 +02:00
Oliver Gierke
ceef18d7a4 DATAMONGO-671 - Added integration tests to show lookups by date are working. 2013-07-03 17:48:04 +02:00
Thomas Darimont
4f57712f12 DATAMONGO-693 - More robust handling of host and replica set config in MongoFactoryBean.
MongoFactoryBean now considers empty strings for the replicaPair property as not set at all. The ServerAdressPropertyEditor also returns null as value for empty text strings. Deprecated setter for replica pair on MongoFactoryBean.
2013-07-02 16:31:02 +02:00
Oliver Gierke
478396c503 DATAMONGO-675 - MongoTemplate now maps Update in findAndModify(…).
The Update object handed to ….findAndModify(…) is now handed through the QueryMapper before being executed.
2013-07-01 08:28:40 +02:00
Oliver Gierke
aa80d1ad0a DATAMONGO-706 - Fixed DBRef conversion for nested keywords. 2013-06-29 13:07:34 +02:00
Oliver Gierke
fd28ab4d33 DATAMONGO-705 - Fixed handling of DBRefs in QueryMapper.
QueryMapper now converts values to become DBRefs correctly in getMappedKeyword(…). Added an exclusion path for the value handling in case we have an $exists keyword.
2013-06-25 19:25:13 +02:00
Andrew Duncan
187c80dfcc DATAMONGO-701 - Improve performance of starts-with and ends-with queries.
This changes the starts-with regex to the prefixed form using ^ to better make use of any index on the queried field. Also changes ending-with queries to use the $ anchor.
2013-06-25 17:56:07 +02:00
Ivan Sopov
389a3ac066 DATAMONGO-704 - Removed references to SimpleMongoConverter from JavaDoc.
Removing SimpleMongoConverter references from javadocs In commit 2832b524d3 MappingMongoConverter was made default instead of SimpleMongoConverter. Also SimpleMongoConverter was completely removed between 1.0.0.M3 and 1.0.0.M4 releases. This is an update for JavaDocs, that still reference SimpleMongoConverter as the default MongoConverter.
2013-06-25 17:31:32 +02:00
Oliver Gierke
297bd3e3dd DATAMONGO-690 - Upgraded to Spring Data Commons snapshots. 2013-06-24 13:39:13 +02:00
Oliver Gierke
b11fba3321 DATAMONGO-694 - Added test case to show overriding accusers works. 2013-06-14 12:44:24 +02:00
Spring Buildmaster
3c68671d86 DATAMONGO-690 - Prepare next development iteration. 2013-06-04 11:37:17 -07:00
Spring Buildmaster
b171f4192d DATAMONGO-690 - Release version 1.3.0.M1. 2013-06-04 11:37:13 -07:00
Oliver Gierke
21a1ce985c DATAMONGO-690 - Prepare 1.3 M1 release.
Upgraded to Spring Data Build 1.1.RELEASE, Spring Data Commons 1.6 M1. Switched to milestone repository. Updated changelog. Refer to latest docs from Spring Data Commons.
2013-06-04 20:07:43 +02:00
Oliver Gierke
97caba50bf DATAMONGO-680, DATAMONGO-681 - Expose MongoOperations.exists(…).
We're now exposing dedicated exists(…) methods on MongoOperations which simply looks up a cursor and inspects it for the presence of at least one element. SimpleMongoRepository implementation now also uses this optimized exists check.
2013-05-24 09:39:25 +02:00
A. B. M. Kowser Patwary
818f739d5a DATAMONGO-676 - SimpleMongoRepository now consistently uses EntityInformation.
SimpleMongoRepository.findOne(…) & ….delete(…) now use entityInformation.getCollectionName() to resolve the collection name to interact with. This allows global customizations of the collection name on repositories.
2013-05-24 09:39:07 +02:00
Oliver Gierke
a44c1fdd2d DATAMONGO-683 - QueryMapper now handles default ID names it mapping metadata missing.
Changed the implementation so that _id is considered an id field if no metadata is present. Heavily refactored QueryMapper internals so that the conversion code is more readable.
2013-05-24 09:16:57 +02:00
Oliver Gierke
6b35ca80d4 DATAMONGO-679 - Fixed saving plain JSON strings in MongoTemplate.
MongoTemplate.doSave(…) didn't handle plain JSON strings correctly. It now correctly passes the marshaled object to the underlying method.
2013-05-23 20:40:16 +02:00
Patryk Wąsik
23b276745c DATAMONGO-677 - QueryMapper now handles DBRefs in Maps correctly.
QueryMapper now handle Map with DBRef value, which is needed to process an update in MongoTemplate.doUpdate(…) to save versioned document correctly.
2013-05-23 20:10:18 +02:00
Oliver Gierke
be0092d3f5 DATAMONGO-682 - Performance improvement for mapping hotspots.
This commit includes the MongoDB specific parts for the mapping subsystem performance improvements. Reworked PerformanceTest to output more reasonable numbers.

Heavily inspired by Patryk Wasik's contribution at https://github.com/SpringSource/spring-data-mongodb/pull/37.

GitHub PR: #37
2013-05-23 19:45:09 +02:00
Oliver Gierke
f36792d419 DATAMONGO-569 - Improved AbstractMongoConfiguration.
AbstractMongoConfiguration doesn't expose a Mongo instance anymore until you explicitly make it one by annotating the implementation method in the configuration sub lass with @Bean.

Removed the custom call to MongoMappingContext.initialize() as Spring call the lifecycle method for us anyway.
2013-05-17 19:50:35 +02:00
Oliver Gierke
31393db6ff DATACMNS-379 - Adapt to changes in Spring Data Commons. 2013-05-13 09:29:04 +02:00
Martin Baumgartner
8b50af07ce DATAMONGO-545 - Add BeforeDeleteEvent and AfterDeleteEvent.
Added events for before and after deletion of documents. Polished logging code in AbstractMongoEventListener.
2013-04-29 15:45:14 +02:00
Oliver Gierke
0eb315a758 DATAMONGO-667 - Removed deprecated types and added further deprecations.
Removed Sort in favor of Sort in Spring Data Commons. Deprecated Order in favor of Direction in Spring Data Commons. Changed implementation to use the types from Spring Data Commons.
2013-04-29 14:02:10 +02:00
Oliver Gierke
976f5dd0e3 DATAMONGO-666 - Fixed architecture violation caused by exception class.
Moved MongoDataIntegrityViolationException into core package to break up package cycle. Updated SOnargraph architecture description to capture issues more closely.
2013-04-29 13:04:24 +02:00
Philipp Schneider
f614364918 DATAMONGO-554 - Add background attribute to @Indexed and @CompoundIndex.
@Indexed and @CompoundIndex now carry a background flag that can be used to enable background indexing. Updated MongoPersistentEntityIndexCreator to consider the flag and hand it to the ensureIndex(…) call.
2013-04-29 11:18:44 +02:00
Philipp Schneider
38a86033be DATAMONGO-663 - Added equals(…) and hashCode() to Field. 2013-04-26 15:37:51 +02:00
Patryk Wąsik
d11c20d548 DATAMONGO-657 - Support DBRef for maps
Added capability to write DBRef instances for map values. Reading had been supported before but writing wasn't.
2013-04-26 14:44:31 +02:00
Oliver Gierke
3e2387ae6b Fix path to repo to make sure snapshot build sees all repositories. 2013-04-26 14:44:19 +02:00
Oliver Gierke
e8a1caec53 Fixed pom.xml to refer to correct snapshot repo. 2013-04-18 13:33:28 +02:00
Oliver Gierke
44c0b14018 DATAMONGO-658 - Major overhaul of README.md.
Simplified repository usage. Added JavaConfig sample. Use Markdown syntax consistently.
2013-04-18 10:38:17 +02:00
Arthur Loder
3daf3fc95b DATAMONGO-658 - Fixes in README.md. 2013-04-18 10:20:05 +02:00
Oliver Gierke
bd3aac8342 DATAMONGO-658 - Pull forward changes made to README in 1.2.x branch. 2013-04-18 10:11:06 +02:00
Oliver Gierke
94f697da10 DATAMONGO-656 - Fixed potential NPE in MongoTemplate. 2013-04-17 10:25:01 +02:00
Oliver Gierke
0cdec56a27 DATAMONGO-651 - MongoTemplate now throws Mongo-specific exception with WriteResult.
If the WriteResultChecking is set to EXCEPTION on a MongoTemplate, we now throw a Mongo-specific exception that captures both the WriteResult and MongoActionOperation for further evaluation.
2013-04-15 17:03:35 +02:00
Patryk Wąsik
9d83331f9f DATAMONGO-652 - Added support for $elemMatch and $ positional operator. 2013-04-12 16:13:58 +02:00
Oliver Gierke
071cd1647f DATAMONGO-571 - Fixed setting null values during update of versioned entities.
In case of updating a versioned object,the Update object is now constructed from plain key value pairs, not using $set anymore. This will correctly set the null values in the updated document.
2013-04-11 12:01:38 +02:00
Oliver Gierke
92af5aa345 DATAMONGO-650 - Added snapshot repository to resolve Spring Data Commons. 2013-04-11 10:57:04 +02:00
Oliver Gierke
c07ad0fdf6 DATAMONGO-648 - Replaced XSD ids with XSD strings.
This change allows usage of Spring Data MongoDB XML namespace elements with <bean /> element using a profile. This scenario creates the case of e.g. two <mongo:db-factory /> declarations in the same XML file.
2013-04-10 20:33:31 +02:00
Oliver Gierke
04e0f5c4a7 DATAMONGO-642 - MongoChangeSetPersister now considers mapped collection.
So far the change set persister has used the plain domain type name to persist data. We now consider the collection name defined by the object mapping (through @Document(collection = "…")).
2013-04-02 11:42:36 +02:00
Oliver Gierke
133975fb44 DATAMONGO-641 - Fixed potential NullPointerException in MongoLog4jAppender.
Log4j appender now only closes Mongo instance if available.
2013-04-02 10:56:34 +02:00
Oliver Gierke
9a372a57e0 DATAMONGO-641 - Reformatting to prepare fix. 2013-04-02 10:53:25 +02:00
Oliver Gierke
e67644094a DATAMONGO-628 - Allow dependency of config layer to GridFS.
Updated Sonargraph architecture description to allow a dependency form the configuration layer to the GridFS layer. Dependency was introduced by c5a99b5b5e.
2013-04-02 10:31:08 +02:00
Martin Baumgartner
c5a99b5b5e DATAMONGO-140, DATAMONGO-628 - Namespace elements for MongoTemplate and GridFsTemplate. 2013-04-02 09:50:45 +02:00
Oliver Gierke
9564bcb280 DATAMONGO-603 - Expose abbreviate field names flag in JavaConfig.
We're now exposing a abbreviateFieldNames() flag in AbstractMongoConfiguration to allow easy enablement of field abbreviation.
2013-03-28 17:16:50 +01:00
Oliver Gierke
f1e961a1ee DATAMONGO-607 - Introduced FieldNamingStrategy SPI interface.
MongoMappingContext can now get a FieldNamingStrategy configured to allow the customization of field names used to persist property values to in case *no manual mapping is defined* (e.g. through @Field). The default strategy will simply use the property name as it did before.

We now also expose a abbreviate-field-names attribute on the <mongo:mapping-context /> XML namespace element to transparently register a CamelCaseAbbreviatingFieldNamingStrategy which abbreviates the property's name to the first letters of its camel-case structure. A property fooBar would then be persisted to a field named fb. If you're not using the XML namespace simply configure the strategy on your MongoMappingContext instance.

To avoid field name mapping ambiguities being introduced through a custom FieldNamingStrategy (imagine the camel-case strategy just mentioned and two properties lastname and level which would both map to l) the PersistentEntity implementation verifies the mapping metadata and throws an exception in case an ambiguity is found.
2013-03-28 16:41:42 +01:00
Oliver Gierke
0c69c87787 DATAMONGO-638 - MappingContext does not create PersistentEntities for AbstractMaps. 2013-03-28 16:28:02 +01:00
Oliver Gierke
48b4a88a6a DATAMONGO-629 - Fixed QueryMapper not to massage queries without type information.
So far the QueryMapper applied the id massaging (especially interpreting the default id keys) even if there was no persistence metadata available to do so. This caused e.g. queries handed into MongoTemplate.count(Query, String) to get keys of "id" massaged into "_id" which shouldn't be the case as we cannot assume anything about the documents and the keys contained in them.

So we now only apply the defaults if there is at least persistence metadata present. This means that for methods on MongoOperations that don't take type information of any kind the queries have to be defined in terms of the document, not the object model as we cannot refer to it.
2013-03-27 12:01:53 +01:00
Andrey Bloschetsov
d0c0866f88 DATAMONGO-637 - Fixed typo in Query.query(…).
Correcting mispelled "critera". Polished JavaDoc a little.
2013-03-27 11:23:35 +01:00
Oliver Gierke
ab18bb5b96 DATAMONGO-636 - Added support for countBy in derived queries.
We now change the query execution to a count execution in case a derived query has the PartTree.isCountProjection() set to true or a query defined in @Query has the newly introduced count() attribute set to true.

As the native Mongo count() returns a long we use a default ConversionService to potentially massage the query result into other numerical types.
2013-03-26 17:51:46 +01:00
Oliver Gierke
509be3d681 DATAMONGO-633 - Upgraded to Querydsl APT plugin 1.0.8. 2013-03-26 17:26:33 +01:00
Oliver Gierke
8e01f95b29 DATAMONGO-635 - Fixed some Sonar warnings. 2013-03-25 18:13:08 +01:00
Oliver Gierke
4dcec1f6e2 DATAMONGO-634 - Expand scope of CDI bean to application scope.
We simply inherit the application scope from the parent implementation.
2013-03-25 17:16:32 +01:00
Oliver Gierke
d3bf6c0a19 DATAMONGO-632 - Removed schemaLocation attribute from namespace references. 2013-03-25 17:16:26 +01:00
Oliver Gierke
3410a0589c DATAMONGO-631 - Explictly reject Order instances with ignore-case flag. 2013-03-25 17:13:02 +01:00
Oliver Gierke
7a64766496 DATAMONGO-633 - Upgraded to Querydsl 3.0.0.
Adapted custom APT serializer implementation to the new API.
2013-03-25 17:12:55 +01:00
Oliver Gierke
e56a8597b8 DATAMONGO-622 - Saving unversioned object now uses doInsert(…).
We now rather use doInsert(…) if a versioned object is saved the first time to prevent accidental updates not bumping the version number.
2013-02-26 11:31:48 +01:00
Oliver Gierke
e13208b4b3 DATAMONGO-621 - Initializing version property now uses ConversionService.
The BeanWrapper used in MongoTemplate.initializeVersionProperty(…) now uses the ConversionService held in the MongoConverter.
2013-02-26 11:29:55 +01:00
Oliver Gierke
6139e83d8d DATAMONGO-620 - Updating versioned object uses explicit collection name.
We're now handing the collection name given to doSaveVersioned(…) to the update method.
2013-02-26 11:29:40 +01:00
Oliver Gierke
f33790013f DATAMONGO-617 - Fixed potential NullPointerException in MongoTemplate.insert(…).
If MongoTemplate.insert(…) was called with a Mongo-simple type (such as a raw DBBobject) it caused a NullPointerException during the lookup of a version property. This is now fixed by correcting the guard.
2013-02-20 12:04:24 +01:00
Oliver Gierke
bf81d95d21 DATAMONGO-613 - Readded JConsole image and upgrade to Spring Data Build 1.1. 2013-02-11 12:40:41 +01:00
Oliver Gierke
158e4f033c DATAMONGO-612 - Fixed reconfiguration of dist.id. 2013-02-11 12:38:40 +01:00
Spring Buildmaster
81097061ad DATAMONGO-609 - Prepare next development iteration. 2013-02-08 04:15:50 -08:00
Spring Buildmaster
2bc6ebc250 DATAMONGO-609 - Release version 1.2.0.RELEASE 2013-02-08 04:15:46 -08:00
Oliver Gierke
909110cf4e DATAMONGO-609 - Upgraded to parent project and Spring Data Commons in release versions. 2013-02-08 13:05:49 +01:00
Oliver Gierke
ba094da5a7 DATAMONGO-611 - Upgraded to MongoDB Java driver 2.10.1. 2013-02-08 13:04:33 +01:00
Oliver Gierke
876b31bc52 DATAMONGO-609 - Updated changelog. 2013-02-08 13:00:20 +01:00
Oliver Gierke
dc8e8281eb DATAMONGO-606 - Add JodaTime specific Converter implementations if JodaTime is present on classpath. 2013-02-06 17:53:41 +01:00
Oliver Gierke
48deb1a150 DATAMONGO-603 - Polishing unit tests. 2013-02-06 17:39:33 +01:00
Michal Vich
48625956b7 DATAMONGO-568 - MongoTemplate.find(…) does not throw NullPointerException anymore.
Trigger findAll(…) if the Query object given to a find(…) method is null.
2013-02-06 15:04:39 +01:00
Michal Vich
782cf6e10d DATAMONGO-81 - Added more unit tests for MongoExceptionTranslator. 2013-02-06 14:41:56 +01:00
Oliver Gierke
c28e51cf86 DATAMONGO-603 - Make sure geo queries and repositories get correct Metric applied.
Heavily refactored NearQuery to simplify implementation and testability. Made sure the metric of the maximum distance is applied if no metric had been defined before. Adapted query preparation code to re-enforce the correct metric being set to not rely on the auto-application behavior of NearQuery to be safe against potential further refactorings.
2013-02-01 15:38:29 +01:00
Oliver Gierke
4b1065cac5 DATAMONGO-598 - Set needed properties and activate distribution artifact upload. 2013-01-29 20:36:41 +01:00
Oliver Gierke
c807b2abcf DATAMONGO-601 - Fixed exposing password by MongoDbUtils.
MongoDbUtils is now not exposing the plain password in the exception message piped into CannotGetMongoDbConnectionException but uses the newly introduced toString() method of UserCredentials (see DATACMNS-275).
2013-01-29 13:16:06 +01:00
Oliver Gierke
19ad2d3aac DATACMNS-274 - Adapt to moved type MappingContextIsNewStrategyFactory. 2013-01-29 10:54:36 +01:00
Oliver Gierke
bd7fe5bfd3 DATAMONGO-600 - Fixed parameter binding for derived queries on properties using polymorphism.
We need to retain the type in the serialized DBObject in case a derived query binds parameters for properties that use polymorphism as we serialize the object as nested document and this only matches in an all-or-nothing way. So if the type information is missing, we won't see any results.

So we now hand the TypeInformation of the property into the conversion logic and transparently skip the type information removal in case the types differ.
2013-01-25 11:29:39 +01:00
Oliver Gierke
a237999037 DATAMONGO-598 - Upgraded to new build infrastructure.
Fixed test ordering issue in Cross-Store module.
2013-01-18 14:45:48 +01:00
Oliver Gierke
d8a9752724 DATAMONGO-592 - Fix handling of SpEL evaluation values.
During constructor parameter value resolution the value to be used can result from evaluating a SpEL expression on the root document. This value has to be converted recursively if it is a nested document. Adapted to the altered API in SD Commons and override potentiallyConvertSpelValue(…) to re-invoke converter.
2012-12-16 15:26:25 +01:00
Philipp Schneider
4c8bf0dec2 DATAMONGO-583 - Using while (…) instead of for (…) for DBCursors to avoid memory leak.
Instead of iterating over the DBCursor using a for-loop we now use a while-loop to avoid the potential memory leak outlined in [0].

[0] https://jira.mongodb.org/browse/JAVA-664
2012-12-13 12:07:11 +05:30
Oliver Gierke
cffc27d83a DATAMONGO-590 - Code cleanups in MongoTemplate and error handling APIs. 2012-12-13 12:06:55 +05:30
Oliver Gierke
42ab4cb7b4 DATAMONGO-588 - Version attribute now gets initialized on insert.
The version attribute of an entity has not been correctly initialized to 0 when inserting objects using MongoTemplate.insert(…). We now set it to 0 correctly.
2012-12-10 17:38:16 +09:00
Oliver Gierke
42df25434f DATAMONGO-585 - Fix concurrency issue in database authentication.
So far the authentication of the database was synchronized *after* the check whether the database under consideration had already been authenticated. This can cause threads to concurrently try to authenticate the database which is rejected by the driver. Improved the synchronization block to include both the ….isAuthenticated() check as well as the authentication.
2012-12-03 11:37:06 +01:00
Oliver Gierke
4e16f7ebe2 DATACMNS-378 - Fixed ClassCastException in MapReduceResults.
The parseCount(…) method now safely converts both integer and long values from the source DBObject.
2012-11-29 13:22:38 +01:00
Oliver Gierke
04a87b373d DATAMONGO-577 - Cleaned up obsolete property.
The version property in BasicMongoPersistentEntity became obsolete after the upgrade in Spring Data Commons.
2012-11-28 17:39:20 +01:00
Oliver Gierke
c4eca7eadd DATAMONGO-581 - Expose the PersistentEntity managed in the repository factory bean.
MongoRepositoryFactoryBean now populates the MappingContext of the super class when the MongoOperations instance is set.
2012-11-28 17:35:47 +01:00
Philipp Schneider
3a9dc2b98c DATAMONGO-503 - Added support for content type on GridFsOperations.
Added methods to take the content type of the file to be created. Clarified nullability of arguments in JavaDoc.

Pull request: #19.
2012-11-28 14:30:05 +01:00
Oliver Gierke
c568b7cbc2 DATAMONGO-580 - Improved BeanDefinitionParser for MappingMongoConverter. 2012-11-27 14:44:00 +01:00
Oliver Gierke
9482350062 DATAMONGO-577 - Added support for auditing.
Introduced AuditingEventListener to invoke auditing subsystem available through Spring Data Commons. Added <mongo:auditing /> namespace element to transparently activate it.
2012-11-27 14:00:52 +01:00
Oliver Gierke
772a140def DATAMONGO-576 - Configure java.util.logging to prevent verbose test logging.
Added Slf4j bridge and configured surefire to configure JUL accordingly.
2012-11-23 10:50:29 +01:00
Oliver Gierke
936259a766 DATAMONGO-575 - Improved implementation of entity metadata lookup in MongoQueryMethod.
Got rid of obsolete EntityInformationCreator API and moved to a custom EntityMetadata extension instead.
2012-11-23 10:00:49 +01:00
Oliver Gierke
533d21281e DATAMONGO-570 - Guard against null values in references.
QueryMapper does not try to convert null values into DBRef objects anymore but returns the plain null value as is.
2012-11-13 18:23:02 +01:00
Oliver Gierke
6977fa87e6 DATAMONGO-573 - Moved to Logback for test logging. 2012-11-13 17:54:19 +01:00
Oliver Gierke
41dcebb010 DATAMONGO-559 - Upgraded to Spring Data Commons 1.5.0.BUILD-SNAPSHOT. 2012-11-13 17:54:19 +01:00
Oliver Gierke
ef077182f6 DATAMONGO-563 - Upgraded MongoDB Java driver to 2.9.2. 2012-11-13 17:54:19 +01:00
Dave Syer
21e7c63766 Update README.md 2012-10-26 18:02:05 +02:00
Jan Kronquist
4b018a9d7d DATAMONGO-562 - Saving versioned entity with already set id now works.
Instead of inspecting the id property we now assume the version property unset for initialization and work from there.
2012-10-24 16:07:26 +02:00
Spring Buildmaster
ecf15b93e0 DATAMONGO-559 - Prepare next development iteration. 2012-10-17 15:00:21 -07:00
Spring Buildmaster
6323a86560 DATAMONGO-559 - Release version 1.1.1.RELEASE. 2012-10-17 14:59:59 -07:00
Oliver Gierke
3b0b7315e1 DATAMONGO-559 - Prepare 1.1.1.RELEASE release. 2012-10-17 17:53:39 -04:00
Oliver Gierke
4aeba6f92d DATAMONGO-553 - MappingMongoConverter now uses MongoPersistentProperty.usePropertyAccess() to decide whether to use property access.
Introduced usePropertyAccess() method on MongoPersistentProperty and added an implementation that forces that into being true for Throwable.cause as using the field would cause a cycle as it points to this in the first place but rather massages the value in the getter.
2012-10-17 17:27:15 -04:00
Oliver Gierke
342f9ae837 DATAMONGO-551 - MongoTemplate can now persist plain Strings if they are valid JSON.
Added some code to MongoTemplate that inspects the object to be saved for being a String. If it is we try to parse the given String into a JSON document and continue as if we had been given a DBObject initially. Non-parseable Strings are rejected with a MappingException.
2012-10-15 11:49:29 -04:00
Oliver Gierke
66d98b355e DATAMONGO-550 - Fixed potential NullPointerExceptions in MongoTemplate.
Guarded access to results of mappingContext.getPersistentEntity(…) to prevent NullPointerExceptions in case the template is used with non-entity types like a plain DBObject. Added some custom logic for plain BasicDBObjects to get the _id field populated appropriately.
2012-10-15 11:38:00 -04:00
Oliver Gierke
cabbe747f8 DATAMONGO-549 - Fixed potential NullPointerException in MongoTemplate.
Added assertions and guards against MongoPersistentEntity lookups resulting in null values.
2012-10-11 08:49:49 +02:00
Spring Buildmaster
5ff3064acd DATAMONGO-541 - Prepare next development iteration. 2012-10-10 05:30:21 -07:00
Spring Buildmaster
3001e2941f DATAMONGO-541 - Release 1.1.0.RELEASE. 2012-10-10 05:30:15 -07:00
Oliver Gierke
9cf72fbdd4 DATAMONGO-541 - Polished pom.xml for Maven central propagation. 2012-10-10 14:23:11 +02:00
Oliver Gierke
cb5144de0f DATAMONGO-541 - Updated reference docs and changelog. 2012-10-10 14:04:43 +02:00
Oliver Gierke
4be90e51ae DATAMONGO-541 - Upgrade to Spring Data Commons 1.4.0.RELEASE. 2012-10-10 14:04:27 +02:00
Oliver Gierke
6c368d557b DATAMONGO-548 - Upgrade to Querydsl 2.8.0. 2012-10-09 09:11:27 +02:00
Oliver Gierke
a8432e13a1 DATAMONGO-543 - Polished quick start section.
Updated dependency versions to Spring Data MongoDB and Spring. Removed explicit dependency listing. Removed section on how to migrate between 1.0 milestones. Removed obsolete paragraphs.
2012-09-20 10:38:46 +02:00
Oliver Gierke
05ac139554 DATAMONGO-528 - Documented GridFs support. 2012-09-17 15:04:19 +02:00
Oliver Gierke
3661b2981e DATAMONGO-456 - Fixed <db-factory /> element documentation in XSD. 2012-09-17 12:22:48 +02:00
Oliver Gierke
69bd7acf74 DATAMONGO-457 - Fixed links in reference documentation. 2012-09-17 12:07:52 +02:00
Oliver Gierke
d882af257f DATAMONGO-539 - Fixed MongoTemplate.remove(object, collectionName).
If the entity being removed using MongoTemplate.remove(object, collectionName) contained an id that could be converted into an ObjectID it wasn't removed correctly currently. This was caused by the fact that the intermediate call didn't hand over the entity type and thus the id conversion failed. This in turn caused the query not to match the previous saved object.
2012-09-17 11:57:09 +02:00
Oliver Gierke
c92058a79a DATAMONGO-539 - Added test case to show removing entity from explicit collection works. 2012-09-13 17:12:50 +02:00
Oliver Gierke
ed2b576261 DATAMONGO-538 - Query API can now work with Sort and Pageable from Spring Data Commons.
Introduced with(Sort sort) and with(Pageable pageable) on Query. Deprecated sort() method and the custom Sort class. Deprecated QueryUtils.applyPagination(…) and ….applySorting(…) and changed internal calls to this to use the Query API directly. Some JavaDoc polishing.
2012-09-13 10:54:23 +02:00
Oliver Gierke
6744446a48 DATAMONGO-532 - Synchronize DB authentication.
In multithreaded environments Mongo database authentication can be triggered twice if two or more threads refer to the same db instance. This is now prevented by synchronizing calls to db.authenticate(…).
2012-09-12 12:58:31 +02:00
Oliver Gierke
fdecec48b2 DATAMONGO-484 - Upgraded to MongoDB driver 2.9.1. 2012-09-12 11:54:20 +02:00
Oliver Gierke
f1289c46e6 DATAMONGO-535 - Fixed broken MongoDBUtils resource synchronization.
We now make sure we really re-use the database instance bound to the thread avoiding duplicate lookups of Mongo.getDb(…). This doesn't seem to gain a big performance benefit anymore as the Mongo instance caches the DB instances internally anyway.
2012-09-11 20:44:33 +02:00
Oliver Gierke
aa0b87be57 DATAMONGO-536 - Fixed package cycle introduced by SerializationUtils. 2012-09-11 20:44:32 +02:00
noter
2040f02d07 DATAMONGO-279 - Added support for optimistic locking.
Introduced @Version annotation to demarcate a version property on an entity. MongoTemplate will initialize this property on the first save of an instance if not set already. If it is already set, the template will bump the version number on subsequent saves and actually trigger an update backed by a query including the old version number. A failure to update the entity accordingly will then trigger an OptimisticLockingFailureException.

General JavaDoc polish in mapping package.
2012-09-11 20:44:15 +02:00
Oliver Gierke
13a69ecdfd DATAMONGO-533 - Fixed index creator registration.
In cases an ApplicationContext already contains a MongoPersistentEntityIndexCreator the default one is not registered, even if the one in the ApplicationContext listens to another MappingContext's events.

Polished iterable classes setup in MongoTemplate along the way. Some JavaDoc polishes as well.
2012-09-07 21:21:17 +02:00
Oliver Gierke
f0051deff0 Formatting. 2012-09-06 16:25:40 +02:00
Oliver Gierke
05a8148084 DATAMONGO-530 - Fixed propagation of setApplicationContext(…) in MappingMongoConverter. 2012-09-06 16:06:47 +02:00
Oliver Gierke
aaa44b3369 DATAMONGO-529 - Update Querydsl setup to use 1.0.4.
Raised Maven compiler plugin version to 2.5.1.
2012-09-06 15:48:41 +02:00
Oliver Gierke
7f35c4430d DATAMONGO-527 - Fixed Criteria.equals(…). 2012-09-03 18:47:25 +02:00
Oliver Gierke
114489d19a DATAMONGO-521 - Added test case to show that repository AND query works. 2012-09-03 16:46:53 +02:00
Oliver Gierke
eda8200d51 DATAMONGO-523 - Added test case to verify type alias detection. 2012-09-03 15:24:21 +02:00
Oliver Gierke
b078ea9ceb DATAMONGO-513 - Update to Spring Data Commons 1.4.0.BUILD-SNAPSHOT. 2012-09-03 15:24:20 +02:00
mpollack
d90b1a0ddd DATAMONGO-526 - Polished README.md.
Update README to remove references to old API, Docs links as well as CouchDB. Remove reference to Spring Data Document, copy initial paragraph introducing the project from http://www.springsource.org/spring-data/mongodb
2012-09-03 15:23:33 +02:00
Spring Buildmaster
737a42e07a DATAMONGO-513 - Prepare next development iteration. 2012-08-24 02:24:09 -07:00
Spring Buildmaster
22d5d4c019 DATAMONGO-513 - Release 1.1.0.RC1. 2012-08-24 02:24:06 -07:00
Oliver Gierke
7ac1e7b6e1 DATAMONGO-513 - Prepare changelog for 1.1.0.RC1. 2012-08-24 11:16:54 +02:00
Oliver Gierke
e86ab783f3 DATAMONGO-513 - Update to Spring Data Commons 1.4.0.RC1. 2012-08-23 20:12:41 +02:00
Oliver Gierke
6fe3e67ecb DATAMONGO-517 - Fixed complex keyword handling.
Introduced intermediate getMappedKeyword(Keyword keyword, MongoPersistentProperty property) to correctly return a DBObject for keyword plus converted value. A few refactorings and improvements in the implementation of QueryMapper (Keyword value object etc.).
2012-08-21 12:25:30 +02:00
Oliver Gierke
3b78034c55 DATAMONGO-519 - Make Spring 3.1.2.RELEASE default Spring dependency version.
We move away from Maven version ranges as they complicate the build and dependency resolution process. They make the build in-reproducible. Users stuck with a 3.0.x version of Spring will now have to manually declare Spring dependencies in their needed 3.0.x version. Not that at least Spring 3.0.7 is required currently.
2012-08-20 17:19:37 +02:00
Oliver Gierke
a06a69797f DATACMNS-214 - Adapted API change in Spring Data Commons.
Plus additional cleanups.
2012-08-16 14:10:02 +02:00
Oliver Gierke
8b1557e38c DATAMONGO-506 - Added test case to show BasicQuery is working for nested properties. 2012-08-15 19:40:02 +02:00
Oliver Gierke
fcdc6d0df2 DATAMONGO-511 - QueryMapper now maps associations correctly.
Complete overhaul of the QueryMapper to better handle complex scenarios like property paths and association references.
2012-08-15 18:30:29 +02:00
Oliver Gierke
83b6cd7f05 DATAMONGO-510 - Criteria now only uses BasicDBList internally. 2012-08-15 18:30:15 +02:00
Oliver Gierke
38a9a6d51d DATAMONGO-509 - SimpleMongoRepository.exists(…) now avoids loading unnecessary data.
We're explicitly ruling out the entities attributes via a field spec to avoid unnecessary object marshaling just to find out whether it exists or not.
2012-08-15 18:16:03 +02:00
Oliver Gierke
5e2f16c678 DATAMONGO-508 - Eagerly return DBRef creation if the given value already is a DBRef. 2012-08-15 16:38:56 +02:00
Oliver Gierke
d7ae95a779 DATAMONGO-505 - Fixed handling of parameter binding of associations and collection values.
Instead of converting given association values as-is, ConvertingParameterAccessor now converts each collection value into a DBRef individually.
2012-08-15 15:16:44 +02:00
Oliver Gierke
8fbdf9afbd DATACMNS-212 - Apply refactorings in Spring Data Commons. 2012-08-13 14:27:19 +02:00
Oliver Gierke
f5a4d78e62 DATAMONGO-476 - @EnableMongoRepositories is now inherited into sub-classes. 2012-08-13 11:37:59 +02:00
Oliver Gierke
1f4264e6a7 DATAMONGO-472 - MongoQueryCreator now correctly translates Not keyword.
We're now translating a negating property reference into a ne(…) call instead of a not().is(…).
2012-08-10 19:58:36 +02:00
Oliver Gierke
05baa851d8 DATAMONGO-502 - QueryMapper now translates property names into field names. 2012-08-08 18:56:18 +02:00
Oliver Gierke
35e8ae1224 DATAMONGO-500 - Index creation is only done for the correct MappingContext.
Index creation now double checks the MappingContext a MappingContextEvent originates from before actually creating indexes. This avoids invalid indexes being created in a multi-database scenario.
2012-07-31 15:34:50 +02:00
Oliver Gierke
ceec0bcc4a DATAMONGO-499 - Fixed namespace reference to repository XSD. 2012-07-31 10:39:03 +02:00
Oliver Gierke
0d87e7fa5f DATAMONGO-496 - AbstractMongoConfiguration now defaults mapping base package.
AbstractMappingConfiguration now uses the package of the class extending it to enable entity scanning for that package. To disable entity scanning entirely override the method to return null or an empty String.
2012-07-30 18:00:42 +02:00
Oliver Gierke
a530629d97 DATAMONGO-497 - Fixed reading of empty collections.
Reading an empty collection always returned a HashSet assuming the returned value would be converted into the assigned properties value later on. However the method should rather return the correct type already which we do now by invoking the potential conversion.
2012-07-30 15:39:22 +02:00
Oliver Gierke
ba0232b187 DATAMONGO-494 - QueryMapper now forwards entity metadata into nested $(n)or criterias.
Introduced helper class to ease assertions on DBObjects as well.
2012-07-27 10:39:31 +02:00
Oliver Gierke
04a17cacb7 DATAMONGO-495 - Fixed debug output in MongoTemplate.doFind(…).
Using SerializationUtils to safely output the query to be executed.
2012-07-26 10:35:30 +02:00
Oliver Gierke
761d725fce DATAMONGO-493 - Fixed broken $ne handling in QueryMapper.
$ne expressions are now only being tried to be converted into an ObjectId in case they follow an id property. Previously they tried in every case which might have led to Strings being converted into ObjectIds that accidentally were valid ObjectIds but didn't represent an id at all.
2012-07-24 20:43:52 +02:00
Oliver Gierke
726b0b1bcc DATAMONGO-493 - Added test case to show the described scenario is working. 2012-07-24 20:13:50 +02:00
Spring Buildmaster
888e031452 DATAMONGO-491 - Prepare next development iteration. 2012-07-24 07:57:13 -07:00
Spring Buildmaster
91b818b8c1 DATAMONGO-491 - Release 1.1.0.M2. 2012-07-24 07:57:10 -07:00
Oliver Gierke
7646a64770 DATAMONGO-491 - Prepare 1.1.0.M2 release.
Updated changelog and links to reference documentation.
2012-07-24 16:50:53 +02:00
Oliver Gierke
94c057e89c DATAMONGO-491 - Upgrade to Spring Data Commons 1.4.0.M1. 2012-07-24 15:27:55 +02:00
Oliver Gierke
190d7cefb0 DATAMONGO-474 - Populating id's after save now inspects field only.
So far the algorithm to inspect whether an id property has to be set after a save(…) operation has used the plain BeanWrapper.getProperty(PersistentProperty property) method. This caused problems in case the getter of the id field returned something completely different (to be precise: a complex type not convertible out of the box).

We now inspect the id field only to retrieve the value.
2012-07-24 13:23:40 +02:00
Oliver Gierke
669bc071b1 DATAMONGO-490 - Fixed typos. 2012-07-23 17:01:12 +02:00
Oliver Gierke
31b9b6b5c0 DATAMONGO-489 - Ensure read collections get converted to appropriate target type.
When reading BasicDBLists we now make sure the resulting collection is converted into the actual target type eventually. It might be an array and thus need an additional round of massaging before being returned as value.
2012-07-23 16:33:24 +02:00
Oliver Gierke
914ecd34fe DATAMONGO-486 - Polished namespace implementation.
Be better citizen regarding namespace support in STS. Moved ParsingUtils to Spring Data Commons.
2012-07-19 01:24:02 +02:00
Oliver Gierke
74532ff199 DATAMONGO-485 - Added test case to show complex id's are working. 2012-07-17 12:40:27 +02:00
Oliver Gierke
c7bcb55bda DATAMONGO-476 - JavaConfig support for repositories. 2012-07-17 11:58:49 +02:00
Amol Nayak
afe560ca7d DATAMONGO-480 - Consider WriteResult for insert(…) and save(…) methods. 2012-07-16 18:51:05 +02:00
Oliver Gierke
1380c49fd0 DATAMONGO-483 - Indexes now use the field name even if index name is defined. 2012-07-16 18:30:54 +02:00
Oliver Gierke
90240a8da5 DATAMONGO-482 - Fixed typo in reference documentation. 2012-07-16 17:42:37 +02:00
Oliver Gierke
ad587f63ed DATAMONGO-474 - Fixed criteria mapping for MongoTemplate.group(…).
The criteria object handed to the group object needs to be mapped correctly to map complex values. Improved error handling on the way.
2012-07-16 16:33:18 +02:00
Oliver Gierke
3c7cb592b3 DATAMONGO-475 - Fixed debug output in map-reduce operations.
Using SerializationUtils.serializeToJsonSafely(…) instead of plain toString() as this might cause SerializationExceptions for complex objects.
2012-07-16 14:56:03 +02:00
Raman Gupta
5b15c9500a DATAMONGO-477 - Change upper bound of Guava dependency to 14. 2012-07-15 17:55:51 +02:00
Oliver Gierke
2d25f0d6e4 DATAMONGO-469 - Polishing in MongoQueryCreator. 2012-06-26 17:36:33 +02:00
Oliver Gierke
557fc869eb DATAMONGO-469 - Fixed parsing of And keyword in derived queries. 2012-06-26 13:32:54 +02:00
Oliver Gierke
033f44e802 DATAMONGO-470 - Implemented equals(…) and hashCode() for Query and Criteria. 2012-06-26 13:30:00 +02:00
Oliver Gierke
ee3c1bc007 DATAMONGO-467 - Fix identifier handling for Querydsl.
As we try to massage the value of the id property into an ObjectId if possible we need to do so as well when mapping the Querydsl query. Adapted SpringDataMongoDbSerializer accordingly.
2012-06-25 13:09:18 +02:00
Oliver Gierke
f507fe2e4d DATAMONGO-464 - Fixed resource synchronization in MongoDbUtils.
MongoDbUtils now correctly returns DB instances for others than the first one bound. So far the lookup for an alternate database resulted in the first one bound to be returned. Polished log statements a bit.
2012-06-25 11:12:54 +02:00
Oliver Gierke
4a27ba0a3f DATAMONGO-466 - QueryMapper now only tries id conversion for top level document.
So far the QueryMapper has tried to map id properties of nested documents to ObjectIds which it shouldn't do actually.
2012-06-22 14:46:35 +02:00
Oliver Gierke
e2e5fd8b31 DATACMNS-186, DATACMNS-185 - Upgrade to Spring Data Commons 1.3.2.BUILD-SNAPSHOT.
Use the new initialize() method in test cases for MappingContext. Benefit from rejection of multiple id properties.
2012-06-22 14:43:23 +02:00
Oliver Gierke
ba9abd1dd0 DATAMONGO-455 - Documentation mentions BasicQuery. 2012-06-20 12:24:59 +02:00
Oliver Gierke
ab66614843 DATAMONGO-454 - Improvements to ServerAddressPropertyEditor.
ServerAddressPropertyEditor now only eventually fails if none of the configured addresses can be parsed correctly. Strengthened the parsing implementation to not fail for host-only parsing or accidental double commas.
Cleaned up integration tests for replica set configuration.
2012-06-20 10:57:32 +02:00
Oliver Gierke
caa2f5f030 DATAMONGO-378 - Fixed potential ClassCastException for MapReduceResults and upcoming MongoDB release.
The type of the value returned for the total field of the timing map in map-reduce results has changed from Integer to Long as of MongoDB version 2.1.0 apparently. Changed MapReduceResults to accommodate either Integer or Long types.
2012-06-20 10:05:16 +02:00
Oliver Gierke
6e7c8f5771 DATAMONGO-462 - Added custom converters for URL.
So far URL instances were treated as entities and serialized as nested document. As there was no custom converter registered to re-instantiate the objects and URL does not contain a no-arg constructor, reading the instances back in resulted in an ugly exception in ReflectionEntityInstantiator. We now register a custom Converter to serialize URL instances as their plain toString() representation. This causes the reading working out of the box as the StringToObjectConverter registered by default uses the constructor taking a String on URL accidentally. To make sure this still works we added an explicit StringToURLConverter to implement symmetric conversions.
2012-06-19 18:44:42 +02:00
Oliver Gierke
44c4566c9d DATAMONGO-428 - Fixed parsing of output collection for complex MapReduce result.
The raw result for a map-reduce operation might contain a complex element containing the output collection in case the original request configured an output database as option. Adapted the parsing of the output collection to accommodate both scenarios (plain String value as well as DBObject wrapper).
2012-06-19 09:30:35 +02:00
Oliver Gierke
9a1e6226b1 DATAMONGO-424 - Don't resolve DBRefs eagerly if property type is DBRef.
So far we have resolved DBRef values eagerly without inspecting the actual property type the would have to be assigned eventually. Now we simply skip the recursive resolve process if the property type (or component type, map value type) is DBRef actually.
2012-06-19 09:01:28 +02:00
Oliver Gierke
cdb6d54d6a DATAMONGO-460 - Improvements to Querydsl repository implementation.
Internalized setup of MongodbSerializer which makes the constructors of SpringDataMongodbQuery much simpler.
2012-06-18 20:18:48 +02:00
Oliver Gierke
1fbfd3f0cb DATAMONGO-450 - Log output uses mapped query for debug logging. 2012-06-14 12:37:14 +02:00
Oliver Gierke
848e6f59c2 DATAMONGO-447 - Fixed broken log output in debug level.
The debug output now uses the already mapped query object when concatenating the log string. Improved applying the id after save operations by inspecting whether the object already has the id set before trying to set it. This could have caused problems in case you use a complex id and don't provide a custom converter as it can be serialized out of the box. Fixed minor glitch in MappingMongoConverter which was not really a bug as another path through the code has covered the scenario later on. Introduced SerializationUtils class that provides a method to safely serialize objects to pseudo JSON. Pseudo in the sense that it simply renders a complex object as { $java : object.toString() }. This is useful for debug output before the DBObject was mapped into Mongo-native types.
2012-06-14 12:26:55 +02:00
Oliver Gierke
b3a891c69b DATAMONGO-458 - Read empty collections are modifiable now.
So far we've read empty collections and populated the property value of the Java object being created with Collections.emptySet(). This returns an unmodifiable Set so that further modifications fail with an UnsupportedOperationException. We now simply use new HashSet<Object>().
2012-06-08 12:59:10 +02:00
Oliver Gierke
54e105d19d General dependency and plugin version upgrades.
Upgraded to JUnit 4.10. Move to junit-dep dependency to allow cleaning up hamcrest dependencies. Use hamcrest-library ins tread of hamcrest-all. Upgrade to Mockito 1.9.0, use mockito-core instead of mockito-all. Upgrade to Spring Data Core 1.3.1.BUILD-SNAPSHOT. Upgraded to Querydsl 2.6.0. Upgraded compiler and Surefire plugin.
2012-05-29 14:20:53 +02:00
Oliver Gierke
4cfb62a413 DATAMONGO-451 - Tweaked pom.xml to allow build run without Bundlor.
Expose the failOnWarnings property to be able to override it through a command line argument. Necessary as the Sonar build uses Clover which instruments source code and thus creates type dependencies, Bundlor complains about not being declared in template.mf.
2012-05-15 13:43:59 +02:00
Maciej Walkowiak
7cdf9cedf3 DATAMONGO-446 - Fixed bug in paging query methods returning Lists
Using List as return type for paginating methods didn't work for query methods currently. Fixed by inspecting the Pageable parameter potentially handed into them and restricting the result set accordingly.
2012-05-15 13:17:56 +02:00
Spring Buildmaster
fd198c172b DATAMONGO-396 - Released 1.1.0.M1. 2012-05-07 06:11:26 -07:00
Spring Buildmaster
4be229312c DATAMONGO-396 - Release 1.1.0.M1. 2012-05-07 06:11:25 -07:00
Oliver Gierke
54ec7af462 DATAMONGO-396 - Updated changelog before 1.1.0.M1 release.
Fixed dependency to Spring Data Commons to 1.3.0.RC2.
2012-05-07 14:54:20 +02:00
Oliver Gierke
7162677992 DATAMONGO-443 - Upgraded to Querydsl 2.5.0.
Updated repository implementation and support classes to use Google Guava API instead of Commons Collections introduced in Querydsl 2.5.0.
2012-05-07 14:28:46 +02:00
Oliver Gierke
cf131aaa7e DATAMONGO-441 - Improved MongoDBUtils API.
Use UserCredentials value object instead of low level String and char[] for username and password. Adapted clients accordingly.
2012-05-07 14:28:45 +02:00
Thomas Risberg
6cd3b7a3aa DATAMONGO-299 - Adding tests ensuring exception alert for multiple conditions for same key. 2012-05-07 14:28:22 +02:00
Oliver Gierke
71e639402c DATAMONGO-439 - Created performance tests.
Added a performance tests that tests two things. Write performance in a variety of WriteConcern configurations as well as write-read performance against WriteConcern.SAFE. The tests can be executed by running the Maven build with the performance-test profile. Fixed some performance hotspots discovered in the course of profiling the test case.
2012-05-03 17:57:26 +02:00
Oliver Gierke
fed31c74e1 DATACMNS-166 - Adapted changes of Spring Data Commons. 2012-05-02 19:54:31 +02:00
Maciej Walkowiak
5b13307e39 DATAMONGO-36 - Added support for JSR-303 validation
Added event listener that uses a java.validation.Validator to trigger validation on an entity before persisting it. This validation is enabled by default if the javx.validation API is present on the classpath. Added namespace attribute 'disable-validation' to allow disabling that auto enabling behavior.
2012-04-27 15:36:29 +02:00
Oliver Gierke
d317bcfa71 DATAMONGO-396 - Upgrade to current Spring Data Commons snapshot version. 2012-04-26 19:23:34 +02:00
Oliver Gierke
883bfbe79e Added Eclipse formatter settings.
Formatted cross-store module with the Spring Data formatter.
2012-04-16 15:29:48 +02:00
Oliver Gierke
7502ac137c DATAMONGO-432 - Upgraded to Spring Data Commons 1.3.0.RC1. 2012-04-16 15:19:59 +02:00
Oliver Gierke
3dfc59bfb8 DATAMONGO-429 - Fixed handling of nested arrays in QueryMapper.
QueryMapper now correctly transforms arrays not concreting them into BasicDBObjects anymore.
2012-04-16 15:19:59 +02:00
Oliver Gierke
9e50621c1b DATAMONGO-431 - Adapted changes in CrudRepository.save(…). 2012-04-16 15:19:59 +02:00
Oliver Gierke
472beaf6d2 DATAMONGO-427 - Added support for Before and After keywords for query creation.
The query creator now supports methods as follows: List<User> findByCreatedAtBefore(Date date);
2012-04-16 15:19:58 +02:00
Oliver Gierke
d6d8a13b0c DATAMONGO-423 - Fixed handling of negated regular expressions.
When using the not() method combined with the regex(…) methods on Criteria we created an invalid query so far. Fixed the regex(…) method to always transform the regex expressions and options into a Pattern instance and render that according to the $not state.
2012-04-16 15:19:58 +02:00
Oliver Gierke
3dcf93744b DATAMONGO-425 - Fixed parameter binding for Dates and manually defined queries.
Replaced manual JSON serialization for special parameters inside StringBasedMongoQuery by calling JSON.serialize(…).
2012-04-16 15:19:58 +02:00
Oliver Gierke
6373a70ca4 DATAMONGO-181 - Improved resource handling for Mongo instance.
SimpleMongoDbFactory now only closes the Mongo instance if it created it itself. Removed public getter for WriteConcern and hold a UserCredentials instead of its parts. Uses improved UserCredentials API introduced in DATACMNS-142.
2012-04-16 15:19:44 +02:00
Oliver Gierke
858afa80f1 DATAMONGO-422 - Fixed invalid UUID conversion.
Removed UUIDToBinaryConverter and BinaryToUUIDConverter as the MongoDB Java driver can handle it itself. Added UUID as Mongo-simple type. Added integration test for reading and writing a UUID property.
2012-04-02 13:32:54 +02:00
Oliver Gierke
97f57e3541 DATAMONGO-418 - Added support for StartingWith, EndingWith and Containing keywords. 2012-03-21 20:44:48 +01:00
Oliver Gierke
05c2045e45 DATAMONGO-347 - Derived repository queries now correctly resolve referenced DBRef properties.
Added toDBRef(…) method to MongoWriter to be able to create a DBRef representation of an object. Polished DBRef creation implementation inside MappingMongoConverter. Removed collection and id attributes from @DBRef annotation as they have neither been documented nor used so far. Refactored MongoQueryCreator to be aware of the property and convert the given value into a DBRef if necessary.
2012-03-12 16:21:56 +01:00
Oliver Gierke
d3598198a6 DATAMONGO-360 - Beautify IndexInfo API.
Added a method to IndexInfo to allow asking the object whether it is an index with a given collection of fields. Refactored RepositoryIndexCreationIntegrationTest to use that API to be more safe against potential duplicate index definitions.
2012-03-12 16:20:15 +01:00
Oliver Gierke
2a8fe5bac7 DATAMONGO-413 - Fixed bug in MongoQueryCreator.
MongoQueryCreator used the outdated OrQuery class to concatenate parts with OR. Refactored the class to use the Criteria.orOperator(…) method. Removed OrQuery class as it is deprecated in the 1.0.x branch.
2012-03-12 11:53:50 +01:00
Oliver Gierke
cdc8f52bad DATAMONGO-356 - CDI integration for Mongo repositories. 2012-03-09 17:52:33 +01:00
Oliver Gierke
39492daa50 DATAMONGO-366 - Polished reference documentation.
Fixed link to bug tracker. Polished docbook files a bit.
2012-03-09 16:03:28 +01:00
Oliver Gierke
16904ba1a7 DATAMONGO-402 - Adapt new entity instantiation API.
Adapt to the entity instantiation API refactored to be able to cope with non-static member classes.
2012-03-09 15:22:24 +01:00
Oliver Gierke
51a469b46f DATAMONGO-412 - General overhaul of the JavaConfig base class.
Refactored JavaConfig base class to reflect the XML namespace a bit more closely (esp. regarding configuration of custom converters and thus registering "simple" types). Prevent duplicate invocation of getUserCredentials(). Added JavaDoc to explain which configuration methods use which other ones to ease detailed configuration.
2012-03-08 13:10:50 +01:00
Oliver Gierke
9f2b1f4ed0 DATAMONGO-273, DATAMONGO-294 - Re-enabled accidentally disabled test case. 2012-03-08 12:16:59 +01:00
Oliver Gierke
e0ea8a6088 Fixed some architecture inconsistencies.
Updated Sonargraph architecture description file to include GridFS subsystem. Moved QueryMapper into convert subsystem. Polished MongoRepository interface. Let SimpleMongoRepository implement MongoRepository.
2012-03-08 12:16:42 +01:00
Oliver Gierke
8861934336 DATAMONGO-360 - Fixed index information creation for geo indexes.
Fixed a ClassCastException that occurred because we didn't consider index information of geo indexes (they return "2d" as direction). Introduced new IndexField abstraction that supersedes the fieldSpec Map in IndexInfo.
2012-03-08 12:11:25 +01:00
Oliver Gierke
93c9713adf DATAMONGO-382 - Fixed potential ClassCastException in MappingMongoConverter.
MappingMongoConverter's convertToMongoType(…) now deals with Sets (and more generally all Collections) correctly.
2012-03-06 21:26:10 +01:00
Oliver Gierke
68a0c1ee2c DATAMONGO-394 - Upgrade to Querydsl 2.3.2. 2012-03-06 19:24:45 +01:00
Oliver Gierke
c3d533d59d DATAMONGO-411 - Double check type of PersistentEntity for index creation.
The Spring container does not check nested generic types of the type parameter of  ApplicationEvent<T>. As T is parameterized in our case as well (PersistentEntity<…, …>) we can code an event listener against that fully parameterized type but might run into ClassCastExceptions as we might get other implementations handed into the method at runtime. We now do an instanceof check to safely invoke checkForIndexes(…) only in case we get the correct event type.
2012-03-02 09:08:24 +01:00
Oliver Gierke
e4b9be6b38 DATAMONGO-408 - Added StringToWriteConverter for XML setup convenience.
When using a PropertyPlaceHolderConfigurer to set WriteConcerns on a MongoFactoryBean just like this:

<bean class="….mongodb.core.MongoFactoryBean">
  <property name="writeConcern" value="${mongodb.writeConcern}"/>
</bean>

we might create invalid WriteConcerns as the BeanFactory will use the WriteConcern's constructor taking a String to create the instance by default. To make Spring use the valueOf(…) method in advance one needs to register either our already existing WriteConcernPropertyEditor or the newly introduced StringToWriteConcernConverter in Springs ConversionService.
2012-03-02 09:07:59 +01:00
Oliver Gierke
09a8b9c9c4 DATAMONGO-401 - Fixed NullPointerException in StringBasedMongoQuery. 2012-02-11 14:52:21 +01:00
Oliver Gierke
e531508bdc DATAMONGO-380 - Improved map handling for keys containing dots.
MappingMongoConverter now rejects objects that would result in field keys containing a dot as we cannot reliably escape and unescape them without potentially wrecking correct keys on reading. However I added a property mapKeyReplacement that can be set to e.g. ~ to have all dots in map keys replaced with ~. This will of course cause ~ to be transformed into dots when reading. If further customization is necessary override potentiallyEscapeMapKey(…) and potentiallyUnescapeMapKey(…).
2012-02-10 18:19:23 +01:00
Oliver Gierke
289c4b95ae DATAMONGO-397 - Replaced references to MongoTemplate with MongoOperations in repository package.
The reference in MongoRepositoryFactoryBean to MongoTemplate was unnecessary on the one hand and could cause problems in case the MongoTemplate is proxied as it can't be wired into the factory anymore then.
2012-02-10 17:16:15 +01:00
Oliver Gierke
25e6d6e025 DATAMONGO-396 - Update Spring Data Commons dependency to 1.3.0.M1. 2012-02-08 21:15:03 +01:00
Oliver Gierke
83d18b5ce6 Readded Spring Test dependency to cross-store module. 2012-02-08 20:39:08 +01:00
Oliver Gierke
8f09748d78 DATAMONGO-394 - Upgrade to Querydsl 2.3.1. 2012-02-08 10:57:29 +01:00
Oliver Gierke
4429971548 DATACMNS-390 - Added UUIDToBinaryConverter to be able to handle UUIDs by default. 2012-02-06 15:08:21 +01:00
Oliver Gierke
065a50bc8c DATAMONGO-391 - Move to SLF4J for logging.
Polished pom.xml files. Upgraded to Log4J 1.2.16 as it excludes unnecessary dependencies. Consolidated test dependencies into parent pom. Remove dependency-management sections. Exclude commons-logging in favor of SLF4J.
2012-02-06 15:00:34 +01:00
Oliver Gierke
f8daa69e53 DATACMNS-129 - Implement CrudRepository.findAll(Iterable<ID> ids). 2012-02-02 16:55:09 +01:00
Oliver Gierke
8a18cd4aef DATACMNS-128 - Adapted to API changes of ParametersParameterAccessor. 2012-02-02 16:54:28 +01:00
Oliver Gierke
3320208a1d DATAMONGO-358 - Fixed collection reading when property type is no a collection.
If you have a property of type object and it contains a collection we didn't property read it back in as creating the collection instance failed due to an invalid call to CollectionFactory. We now default the parameter handed to that call to List in case the property type is not a Collection at all.
2012-02-01 16:29:57 +01:00
Oliver Gierke
4ebc6e96d3 DATAMONGO-385 - Added test case to show repositories working with Long id. 2012-02-01 15:30:20 +01:00
Oliver Gierke
91fe5480a8 DATAMONGO-387 - Repository query execution for GeoPage results are now working correctly.
Added special handling of GeoPage return types for repository query methods.
2012-02-01 14:23:29 +01:00
Oliver Gierke
b6322d7bbc DATAMONGO-374 - Added test case showing custom converters being used for updates. 2012-02-01 11:47:01 +01:00
Oliver Gierke
572c4bb2cd DATAMONGO-6 - Initial support for GridFS.
We now provide a GridFsTemplate for storing, retrieving and deleting documents from GridFS. Introduced GridFsResource implementation to integrate with Spring's Resource abstraction. Beyond that GridFsOperations extends ResourcePatternResolver and thus allows to retrieve GridFsResources via Ant-style filename patterns.
2012-02-01 10:28:28 +01:00
Oliver Gierke
9caa8b15cd DATAMONGO-375 - Polished XSD by removing unnecessary version numbers. 2012-02-01 09:59:43 +01:00
Oliver Gierke
275f4fe6b8 Consolidate Maven repository usage to use repo.springsource.org/libs-snapshot. 2012-01-31 18:46:36 +01:00
Oliver Gierke
4c40ef7e3d DATAMONGO-383 - Adapt new EntityInstantiator API of Spring Data Commons. 2012-01-24 10:49:53 +01:00
Oliver Gierke
609097c0dc DATAMONGO-379 - Improved entity instantiation.
Huge refactoring of the way MappingMongoConverter instantiates entities. The constructor arguments now have to mirror a property exactly in terms of name. Thus we can pick up mapping information from the property to lookup the correct value from the source document. The @Value annotation can be used to either inject completely arbitrary values into the instance (e.g. by referring to a Spring bean) or simply define an expression against DBObject's fields:

class Sample {
  String foo;
  String bar;
  
  Sample(String foo, @Value("#root._bar") String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

trying to create an instance of this class from

{ "foo" : "FOO" } -> new Sample("FOO", null)
{ "_bar" : "BAR" } -> new Sample(null, "BAR").
2012-01-16 18:19:15 +00:00
Oliver Gierke
307d86498e DATAMONGO-368 - MappingMongoConverter does not remove null values from collections anymore. 2012-01-12 15:51:38 +01:00
Oliver Gierke
19879b8dab DATAMONGO-376 - Fixed potential NPE in SpringDataMongodbSerializer. 2012-01-12 12:23:51 +01:00
Oliver Gierke
b190f22551 Polished formatting and Javadoc. 2012-01-12 12:23:18 +01:00
Oliver Gierke
0e9c6a5de8 DATAMONGO-369 - Fixed query mapping when a DBObject is included in the query object.
Replaced premature return with continue to break the for loop appropriately.
2012-01-12 10:52:27 +01:00
Oliver Gierke
5ec0cc7327 DATAMONGO-373 - Fixed potential ClassCastException in QueryMapper.
QueryMapper assumed finding a BasicBSONList for $(n)or operators. This is generally true if the DBObject was created through our Query abstraction. If you use the MongoDB driver QueryBuilder this will fail. We're now only insisting on an Iterable which fixes the issue.
2012-01-11 20:25:10 +01:00
Oliver Gierke
817e167056 Fixed failing test case.
Include dependency to Person repository to make sure instance creation is triggered.
2012-01-11 20:03:34 +01:00
Oliver Gierke
4a6fa5cade DATAMONGO-357 - Prepare 1.1.0 development branch. 2011-12-22 21:16:53 +01:00
Oliver Gierke
9cd33763b6 DATAMONGO-357 - Prepare 1.0.0 release.
Updated changelog, changed reference documentation inclusion links to point to SD Commons 1.0.0.RELEASE documentation. Updated dependency information in reference documentation.
2011-12-22 21:04:43 +01:00
Oliver Gierke
de8d2a1c74 DATAMONGO-355 - Upgraded to Spring 3.0.7. 2011-12-22 20:40:26 +01:00
Oliver Gierke
9f940cd2b6 DATAMONGO-257 - Documented TypeMapper abstraction. 2011-12-22 16:23:15 +01:00
Oliver Gierke
0d69baa32c DATAMONGO-350 - Upgraded to Querydsl 2.3.0.
Adapted changes in annotation processor API.
2011-12-22 08:27:11 +01:00
Oliver Gierke
433e5a660e DATAMONGO-260 - Fixed setup of ConversionService.
Remonde removal of generic ObjectToStringConverter as it doesn't break our converter lookup due to the changed algorithm how to involve Spring Converters in the conversion process.
2011-12-22 08:25:48 +01:00
Oliver Gierke
ee33ce1571 DATAMONGO-336 - Fixed potential NullPointerException in MongoTemplate.
The execution of MongoTemplate.geoNear(…) potentially caused NullPointerExceptions in case the actual query does not return any results. The wrapping return object returns null for the result list and general statistics which we didn't shield against.
2011-12-21 18:47:50 +01:00
Mark Pollack
9ac11e967a DATAMONGO-260 - MapReduce fails when using with Long as key-type. 2011-12-21 12:34:12 -05:00
Thomas Risberg
a89a0ac542 DATAMONGO-139 Adding test to verify that MongoTemplate does not eagerly try to connect to MongoDB 2011-12-20 12:44:10 -05:00
Oliver Gierke
0ad0dad124 DATAMONGO-330 - Updated reference documentation regarding custom converters.
Documented classpath scanning feature of custom converters (DATAMONGO-301). Documented converter disambiguation using @ReadingConverter, @WritingConverter (DATACMNS-113, DATAMONGO-342). Fixed some code formatting on the way.
2011-12-20 18:20:59 +01:00
Oliver Gierke
8f2771416e DATACMNS-110 - Narrow repository base-package reference to avoid picking up types from SD Commons. 2011-12-20 16:39:48 +01:00
Oliver Gierke
7af009cc7f DATAMONGO-347 - Added documentation to mention query parameter binding does not work with DBRefs. 2011-12-19 15:12:25 +01:00
Oliver Gierke
35ad949c18 DATAMONGO-326 - Fixed enum handling in $in, $nin and $ne criterias.
Updated Criteria implementation to consistently store collections instead of arrays internally.
2011-12-19 14:22:52 +01:00
Oliver Gierke
8206b5f950 DATAMONGO-342 - Introduced support for @ReadingConverter / @WritingConverter.
CustomConversions now evaluates @ReadingConverter / @WritingConverter when adding Converter implementations. See DATACMNS-113 and the appropriate commit for details. Added unit test to verify StringToBigIntegerConverter does not get added as writing converter.
2011-12-16 14:23:31 +01:00
Oliver Gierke
170081137a DATAMONGO-346 - Fixed id-handling in queries.
In case a query referencing an entity's id needs massaging before being executed (e.g. Strings that can be ObjectID) the massaging failed in case the query was built using _id already as we did not detect that one being an id reference as we compared it to the entity's id property name. We now also compare against it's field name.
2011-12-14 20:31:08 +01:00
Oliver Gierke
6862bd8a45 DATAMONGO-343 - Fixed registration of ServerAddressPropertyEditor.
Changed the setter parameters for ServerAddresses to use arrays instead of List. We now register the ServerAddressPropertyEditor to convert a ServerAddress[] and thus don't register a PropertyEditor for List which caused unwanted side effects before.
2011-12-12 18:03:27 +01:00
Oliver Gierke
490db7c39f DATAMONGO-341 - Eagerly reject null values in MongoTemplate.geoNear(…). 2011-12-07 18:06:25 +01:00
Oliver Gierke
f3979c3676 DATAMONGO-340 - Prepare 1.0.0.BUILD-SNAPSHOT. 2011-12-07 01:10:16 +01:00
Oliver Gierke
8d18729898 DATAMONGO-340 - Prepare 1.0.0.RC1. 2011-12-07 01:09:58 +01:00
Oliver Gierke
b5e0b2bec2 DATAMONGO-340 - Polished reference documentation.
Added section ids to generate stable URLs for HTML documentation.
2011-12-07 01:09:58 +01:00
Oliver Gierke
9eb47827c1 DATAMONGO-337 - Added Criteria.nin(…) and ….all(…) taking a Collection. 2011-12-07 01:09:57 +01:00
Oliver Gierke
f97ab25411 DATAMONGO-338 - Updated reference documentation regarding new keywords. 2011-12-07 01:09:57 +01:00
Oliver Gierke
6616761f50 DATAMONGO-322 - MongoTemplates refuses to save entities with unset id if not auto-generateable.
If an entity is handed into the template to be saved or inserted we now check that the auto-generated ObjectId can actually be applied to the id property after saving the object.
2011-12-07 01:09:57 +01:00
Mark Pollack
89de566893 Add findAndModify to docs, update test to include findAndModify with upsert 2011-12-06 13:04:24 -05:00
Mark Pollack
ea1f090b40 Add docs for index ops and clean up 'bare' references to Mongo, change to MongoDB 2011-12-06 11:09:23 -05:00
Oliver Gierke
b5958fb5cc DATAMONGO-338 - Query parser implementation go Regex, Exists, True and False keywords. 2011-12-06 17:01:56 +01:00
Oliver Gierke
75b7aff80a DATAMONGO-318 - Don't throw exceptions for updates not affecting any documents.
Throwing an exception if an update does not affect any documents doesn't make sense in all cases. Removed throwing an exception by default but made the relevant method (handleAnyWriteResultErrors(…)) protected so that subclasses might override this behavior.
2011-12-06 15:15:13 +01:00
Oliver Gierke
7da0fcdd0c DATAMONGO-199 - Fixed bug in CachingMongoPersistentProperty. 2011-12-06 14:48:25 +01:00
Oliver Gierke
c88b6d89db DATAMONGO-251 - Polishing.
JavaDoc, Formatting. Made dependencies in DefaultIndexOperations final. Reduced dependency to MongoOperations instead of depending on MongoTemplate directly. Added not-null assertion to constructor of DIO.
2011-12-06 14:33:45 +01:00
Oliver Gierke
de1540aadc DATAMONGO-234 - Polishing.
Removed unused imports, corrected whitespace, formatting.
2011-12-06 14:24:51 +01:00
Oliver Gierke
d1b24d6cfb DATAMONGO-332 - Updated reference documentation to list correct dependencies.
Fixed formatting of log output along the way.
2011-12-06 14:06:59 +01:00
Mark Pollack
e85f3d3299 DATAMONGO-251 - Support geting index information on a collection or mapped class. 2011-12-06 02:25:13 -05:00
Mark Pollack
ef6e08d3f4 DATAMONGO-234 - MongoTemplate should support the findAndModify operation to update version fields 2011-12-06 00:26:18 -05:00
Oliver Gierke
21010fbd49 DATACMNS-91 - Reject null parameters in SimpleMongoRepository.
According to the specification in CrudRepository we now reject null values for ids and entities in CRUD methods.
2011-12-02 13:34:44 +01:00
Oliver Gierke
4325d6c9fa Reactivated accidentally disabled unit tests. 2011-12-02 13:02:57 +01:00
Oliver Gierke
bc16ccfded DATACMNS-77 - Using constants from ClassTypeInformation inside MappingMongoConverter. 2011-12-02 11:33:37 +01:00
Oliver Gierke
04f5f9f662 DATACMNS-103 - Adapt changes in BeanWrapper.
Removed obsolete exception handling code.
2011-12-02 10:08:48 +01:00
Oliver Gierke
b1f1b8efaa DATAMONGO-321 - Overhaul of id handling.
Cleaned up the id handling on query mapping and mapping in general. We now only try to convert id values into an ObjectId and store it as is using potentially registered custom converters. Register BigInteger<->String converters by default now.
2011-12-01 17:50:36 +01:00
Oliver Gierke
de300e2643 DATAMONGO-328 - Set required MongoDB version to 0.
The MANIFEST.MF in current MongoDB driver version is broken in terms of not stating package versions. Thus we unfortunately cannot refer to a particular version range but have to use the generic 0 as required version.
2011-12-01 17:05:28 +01:00
Oliver Gierke
20088b83d9 Removed compiler warnings. 2011-12-01 16:47:40 +01:00
Oliver Gierke
58f200f15e DATAMONGO-335 - Set up hybrid Spring 3.1/3.0.6 build.
Also see DATACMNS-103.
2011-12-01 16:06:38 +01:00
Oliver Gierke
8718700249 DATAMONGO-334 - Switched to use http://repo.springsource.org as repository.
Fixed versions of build plugins along the way.
2011-12-01 16:05:08 +01:00
Oliver Gierke
f4063d1679 DATAMONGO-333 - Default to Object for AbstractMongoEventlistener domain type.
In case an extension of AbstractMongoEventListener does not define a parameter type we now default to Object as handled domain type as we'd cause a NullPointerException if not.
2011-12-01 12:16:27 +01:00
Oliver Gierke
ef063613c7 DATAMONGO-325 - MongoTemplate now correctly refuses not found map reduce JavaScript files.
We now check whether a URL was passed in as map and/or reduce function and throw an exception in case the file either does not exist or cannot be read.
2011-11-30 22:53:56 +01:00
Oliver Gierke
2eda0f1701 DATAMONGO-185 - Expose hints on Query.
Query now exposes a withHint(…) method which will be applied to the DBCursor on query execution. Reduced CursorPreparer's visibility to the package and removed methods exposing it from MongoOperations.
2011-11-30 22:29:59 +01:00
Oliver Gierke
ec7b65e21d DATAMONGO-331 - Fixed typo in WriteConcern enumeration for db-factory element. 2011-11-30 18:27:33 +01:00
Oliver Gierke
c7f7571f3f DATAMONGO-326 - QueryMapper now delegates type conversion to MongoConverter.
QueryMapper now delegates to a MongoConverter instead of a plain ConversionService and invokes optional conversion on it. This optional conversion now removes type information from the created DBObject.
2011-11-30 17:56:44 +01:00
Oliver Gierke
9f71af42e8 DATAMONGO-329 - Fixed Collection and Map value handling for more open properties.
The decision whether a property value was handled as Collection or Map was based on inspecting the property's type which failed for classes using very open property declarations such as:

class MyClass {

  Object something;
}

We now rather inspect the value type instead of the property.
2011-11-30 16:20:25 +01:00
Oliver Gierke
92775170e1 DATAMONGO-301 - Allow classpath-scanning for Converters.
<mongo:custom-conversions /> now has a base-package attribute that scans for Converter and GenericConverter beans. Added <tool:exports /> metadata for MappingMongoConverter.
2011-11-30 15:26:18 +01:00
Oliver Gierke
4c7e338770 Adapt refactorings in SD Commons. 2011-11-28 18:07:33 +01:00
Oliver Gierke
e3fff52d17 DATAMONGO-298 - CustomConversions now also considers sub-types of Number as simple.
CustomConversions now delegates to MongoSimpleTypes.HOLDER.isSimpleType(…) instead of maintaining an additional list of Mongo-primitive types. Added DBObject to the list of Mongo-primitive types.
2011-11-24 15:20:49 +01:00
Oliver Gierke
5477ab20b2 DATAMONGO-324 - Added shortcut in MappingMongoConverter to allow reading DBObjects without conversion.
Added check in MappingMongoConverter.read(…) to shortcut object conversion if the requested type is DBObject.
2011-11-24 12:59:47 +01:00
Oliver Gierke
4cf3567f42 DATAMONGO-310 - MappingMongoConverter now creates native Mongo types for Maps and Collections in convertToMongoType(…).
MappingMongoConverter.convertToMongoType(…) not only converts elements of collections and maps but also converts the wrapper into the appropriate MongoDB type (BasicDBList, BasicDBObject).
2011-11-23 13:15:50 +01:00
Oliver Gierke
b26bb62a63 DATAMONGO-305 - Removed synchronization from Query class.
As Query is not intended to be thread-safe at all, we can safely remove the synchronized blocks from sort() and fields().
2011-11-23 12:48:39 +01:00
Oliver Gierke
f156d7b5af DATAMONGO-312 - MappingMongoConverter handles complex enum types correctly.
If an Enum implements abstract methods, the Class object derived from ${ENUM}.getClass() does not return true for ….isEnum(). Thus we have to rather check Enum.class.isAssignableFrom(…) as this catches this scenario as well. Also see DATACMNS-99 for a related fix in simple type handling in the core infrastructure.
2011-11-23 11:58:09 +01:00
Oliver Gierke
7bf3643902 DATAMONGO-304 - Removed document subpackage from Log4jAppender module. 2011-11-23 11:07:47 +01:00
Oliver Gierke
201ae3e92d Polished unit test. 2011-11-23 10:58:35 +01:00
Oliver Gierke
07556ec58c DATAMONGO-323 - Annotated repository queries consider dynamic sort now.
Applying a Sort parameter handed into a repository query method now for string based (aka. @Query annotated) queries.
2011-11-23 10:58:22 +01:00
Oliver Gierke
39807b17e1 DATAMONGO-309 - MappingMongoConverter now correctly maps Arrays as Map values.
We now not only convert collection values of Maps into BasicDBLists but arrays as well.
2011-11-23 10:28:40 +01:00
Oliver Gierke
4913fe26ac DATAMONGO-296 - Hook into Querydsl serialization to get predicate parameters converted.
Overrode MongoDbSerializer.asDBObject(…) and delegate to our MongoConverter to potentially convert predicate parameters. Upgraded to Querydsl 2.2.5.
2011-11-23 10:15:46 +01:00
Oliver Gierke
7642a719ff Polishing.
Removed unused imports, removed compiler warnings, polished JavaDoc.
2011-11-23 09:26:19 +01:00
Oliver Gierke
6c1ce576a4 DATACMNS-98 - Reflect refactoring.
MongoQueryMethod now uses RepositoryMetadata.getReturnedDomainType(…) instead of the static method of ClassUtils.
2011-11-21 19:21:22 +01:00
Mark Pollack
c99882201d DATAMONGO-234 - MongoTemplate should support the findAndModify operation to update version fields 2011-11-17 17:38:54 -05:00
Mark Pollack
ce6a64e4a9 DATAMONGO-308 - Add support for upsert methods 2011-11-16 16:25:58 -05:00
Mark Pollack
2fcc323bcd DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods 2011-11-16 14:55:35 -05:00
Mark Pollack
17c7b1d2b5 DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods 2011-11-16 14:30:56 -05:00
Mark Pollack
cfefe46cd4 DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods
DATAMONGO-320 - Remove use of slaveOk boolean option in MongoTemplate as it is deprecated. Replace with ReadPreference
2011-11-16 13:28:13 -05:00
Mark Pollack
64921ddad1 DATAMONGO-319 - WriteConcern not parsed correctly in namespace handlers
DATAMONGO-311 - Update MongoDB driver to v 2.7.x
2011-11-15 16:54:02 -05:00
Mark Pollack
edda1764fe DATAMONGO-311 - Update MongoDB driver to v 2.7.x
Still investigating write_concern compatiblity as mentioned in the ticket
2011-11-15 12:44:25 -05:00
Mark Pollack
8113b79109 DATAMONGO-315 - MongoTemplate.findOne(query) methods ignore SortOrder on query 2011-11-14 23:26:16 -05:00
Mark Pollack
9fde4dff3e DATAMONGO-195 - Add description of @Field mapping annotation to reference docs 2011-11-14 22:53:08 -05:00
Mark Pollack
d4b3e2b99d DATAMONGO-306 - NullPointerException if mongo factory created via URI with out credentials 2011-11-14 22:48:07 -05:00
Mark Pollack
68a31d75f3 DATAMONGO-313 [Refactoring] - Use MongoOperations interface instead of MongoTemplate class 2011-11-14 22:20:50 -05:00
Mark Pollack
3e15c21419 DATAMONGO-208 - Add suppoprt for group() operation on collection in MongoOperations 2011-11-14 22:08:29 -05:00
Mark Pollack
e9f253d34f DATAMONGO-316 - Replica Set configuration via properties file throws ArrayIndexOutOfBoundsException 2011-11-14 16:38:18 -05:00
Thomas Risberg
80aa057acb Preparing for snapshot builds 2011-11-04 09:24:45 -04:00
558 changed files with 80724 additions and 9772 deletions

1
CONTRIBUTING.MD Normal file
View File

@@ -0,0 +1 @@
You find the contribution guidelines for Spring Data projects [here](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.md).

210
README.md
View File

@@ -1,155 +1,147 @@
Spring Data - Document
======================
# Spring Data MongoDB
The primary goal of the [Spring Data](http://www.springsource.org/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
As the name implies, the **Document** modules provides integration with document databases such as [MongoDB](http://www.mongodb.org/) and [CouchDB](http://couchdb.apache.org/).
The primary goal of the [Spring Data](http://projects.spring.io/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
Getting Help
------------
The Spring Data MongoDB project aims to provide a familiar and consistent Spring-based programming model for new datastores while retaining store-specific features and capabilities. The Spring Data MongoDB project provides integration with the MongoDB document database. Key functional areas of Spring Data MongoDB are a POJO centric model for interacting with a MongoDB DBCollection and easily writing a repository style data access layer.
At this point your best bet is to look at the Look at the [JavaDocs](http://static.springsource.org/spring-data/data-document/docs/1.0.0.BUILD-SNAPSHOT/spring-data-mongodb/apidocs/) for MongoDB integration and corresponding and source code. For more detailed questions, use the [forum](http://forum.springsource.org/forumdisplay.php?f=80). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://www.springsource.org/projects).
## Getting Help
The [User Guide](http://static.springsource.org/spring-data/data-document/docs/1.0.0.BUILD-SNAPSHOT/reference/html/) (A work in progress).
For a comprehensive treatment of all the Spring Data MongoDB features, please refer to:
* the [User Guide](http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/)
* the [JavaDocs](http://docs.spring.io/spring-data/mongodb/docs/current/api/) have extensive comments in them as well.
* the home page of [Spring Data MongoDB](http://projects.spring.io/spring-data-mongodb) contains links to articles and other resources.
* for more detailed questions, use [Spring Data Mongodb on Stackoverflow](http://stackoverflow.com/questions/tagged/spring-data-mongodb).
If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://projects.spring.io/).
## Quick Start
Quick Start
-----------
### Maven configuration
## MongoDB
Add the Maven dependency:
For those in a hurry:
```xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.5.0.RELEASE</version>
</dependency>
```
If you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version.
* Download the jar through Maven:
```xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.6.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<repository>
<id>spring-maven-snapshot</id>
<snapshots><enabled>true</enabled></snapshots>
<name>Springframework Maven SNAPSHOT Repository</name>
<url>http://maven.springframework.org/snapshot</url>
</repository>
<repository>
<id>spring-libs-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
```
### MongoTemplate
MongoTemplate is the central support class for Mongo database operations. It provides
MongoTemplate is the central support class for Mongo database operations. It provides:
* Basic POJO mapping support to and from BSON
* Connection Affinity callback
* Exception translation into Spring's [technology agnostic DAO exception hierarchy](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/dao.html#dao-exceptions).
* Convenience methods to interact with the store (insert object, update objects) and MongoDB specific ones (geo-spatial operations, upserts, map-reduce etc.)
* Connection affinity callback
* Exception translation into Spring's [technology agnostic DAO exception hierarchy](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions).
Future plans are to support optional logging and/or exception throwing based on WriteResult return value, common map-reduce operations, GridFS operations. A simple API for partial document updates is also planned.
### Spring Data repositories
### Easy Data Repository generation
To simplify the creation of data repositories Spring Data MongoDB provides a generic repository programming model. It will automatically create a repository proxy for you that adds implementations of finder methods you specify on an interface.
To simplify the creation of Data Repositories a generic Repository interface and default implementation is provided. Furthermore, Spring will automatically create a Repository implementation for you that adds implementations of finder methods you specify on an interface.
For example, given a `Person` class with first and last name properties, a `PersonRepository` interface that can query for `Person` by last name and when the first name matches a like expression is shown below:
The Repository interface is
```java
public interface PersonRepository extends CrudRepository<Person, Long> {
public interface Repository<T, ID extends Serializable> {
List<Person> findByLastname(String lastname);
T save(T entity);
List<Person> findByFirstnameLike(String firstname);
}
```
List<T> save(Iterable<? extends T> entities);
The queries issued on execution will be derived from the method name. Extending `CrudRepository` causes CRUD methods being pulled into the interface so that you can easily save and find single entities and collections of them.
T findById(ID id);
You can have Spring automatically create a proxy for the interface by using the following JavaConfig:
boolean exists(ID id);
```java
@Configuration
@EnableMongoRepositories
class ApplicationConfig extends AbstractMongoConfiguration {
List<T> findAll();
@Override
public Mongo mongo() throws Exception {
return new MongoClient();
}
Long count();
@Override
protected String getDatabaseName() {
return "springdata";
}
}
```
void delete(T entity);
This sets up a connection to a local MongoDB instance and enables the detection of Spring Data repositories (through `@EnableMongoRepositories`). The same configuration would look like this in XML:
void delete(Iterable<? extends T> entities);
```xml
<bean id="template" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg>
<bean class="com.mongodb.MongoClient">
<constructor-arg value="localhost" />
<constructor-arg value="27017" />
</bean>
</constructor-arg>
<constructor-arg value="database" />
</bean>
void deleteAll();
}
<mongo:repositories base-package="com.acme.repository" />
```
This will find the repository interface and register a proxy object in the container. You can use it as shown below:
The MongoRepository extends Repository and will in future add more Mongo specific methods.
```java
@Service
public class MyService {
public interface MongoRepository<T, ID extends Serializable> extends
Repository<T, ID> {
}
private final PersonRepository repository;
SimpleMongoRepository is the out of the box implementation of the MongoRepository you can use for basid CRUD operations.
@Autowired
public MyService(PersonRepository repository) {
this.repository = repository;
}
To go beyond basic CRUD, extend the MongoRepository interface and supply your own finder methods that follow simple naming conventions such that they can be easily converted into queries.
public void doWork() {
For example, given a Person class with first and last name properties, a PersonRepository interface that can query for Person by last name and when the first name matches a regular expression is shown below
repository.deleteAll();
public interface PersonRepository extends MongoRepository<Person, Long> {
Person person = new Person();
person.setFirstname("Oliver");
person.setLastname("Gierke");
person = repository.save(person);
List<Person> findByLastname(String lastname);
List<Person> lastNameResults = repository.findByLastname("Gierke");
List<Person> firstNameResults = repository.findByFirstnameLike("Oli*");
}
}
```
List<Person> findByFirstnameLike(String firstname);
}
You can have Spring automatically generate the implemention as shown below
<bean id="template" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg>
<bean class="com.mongodb.Mongo">
<constructor-arg value="localhost" />
<constructor-arg value="27017" />
</bean>
</constructor-arg>
<constructor-arg value="database" />
<property name="defaultCollectionName" value="springdata" />
</bean>
<bean class="org.springframework.data.document.mongodb.repository.MongoRepositoryFactoryBean">
<property name="template" ref="template" />
<property name="repositoryInterface" value="org.springframework.data.document.mongodb.repository.PersonRepository" />
</bean>
This will register an object in the container named PersonRepository. You can use it as shown below
@Service
public class MyService {
@Autowired
PersonRepository repository;
public void doWork() {
repository.deleteAll();
Person person = new Person();
person.setFirstname("Oliver");
person.setLastname("Gierke");
person = repository.save(person);
List<Person> lastNameResults = repository.findByLastname("Gierke");
List<Person> firstNameResults = repository.findByFirstnameLike("Oli*");
}
}
## CouchDB
TBD
Contributing to Spring Data
---------------------------
## Contributing to Spring Data
Here are some ways for you to get involved in the community:
* Get involved with the Spring community on the Spring Community Forums. Please help out on the [forum](http://forum.springsource.org/forumdisplay.php?f=80) by responding to questions and joining the debate.
* Get involved with the Spring community on Stackoverflow and help out on the [spring-data-mongodb](http://stackoverflow.com/questions/tagged/spring-data-mongodb) tag by responding to questions and joining the debate.
* Create [JIRA](https://jira.springframework.org/browse/DATADOC) tickets for bugs and new features and comment and vote on the ones that you are interested in.
* Github is for social coding: if you want to write code, we encourage contributions through pull requests from [forks of this repository](http://help.github.com/forking/). If you want to contribute code this way, please reference a JIRA ticket as well covering the specific issue you are addressing.
* Watch for upcoming articles on Spring by [subscribing](http://www.springsource.org/node/feed) to springframework.org
* Watch for upcoming articles on Spring by [subscribing](http://spring.io/blog) to spring.io.
Before we accept a non-trivial patch or pull request we will need you to sign the [contributor's agreement](https://support.springsource.com/spring_committer_signup). Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests.

291
etc/formatting.xml Normal file
View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring Data" version="12">
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
</profile>
</profiles>

435
pom.xml
View File

@@ -1,300 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongo-dist</artifactId>
<name>Spring Data MongoDB Distribution</name>
<version>1.0.0.M5</version>
<packaging>pom</packaging>
<modules>
<module>spring-data-mongodb</module>
<module>spring-data-mongodb-cross-store</module>
<module>spring-data-mongodb-log4j</module>
<module>spring-data-mongodb-parent</module>
</modules>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<developers>
<developer>
<id>trisberg</id>
<name>Thomas Risberg</name>
<email>trisberg at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.SpringSource.com</organizationUrl>
<roles>
<role>Project Admin</role>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>mpollack</id>
<name>Mark Pollack</name>
<email>mpollack at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.SpringSource.com</organizationUrl>
<roles>
<role>Project Admin</role>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>jbrisbin</id>
<name>Jon Brisbin</name>
<email>jbrisbin at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>-6</timezone>
</developer>
</developers>
<modelVersion>4.0.0</modelVersion>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<comments>
Copyright 2010 the original author or authors.
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.M1</version>
<packaging>pom</packaging>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
<name>Spring Data MongoDB</name>
<description>MongoDB support for Spring Data</description>
<url>http://projects.spring.io/spring-data-mongodb</url>
http://www.apache.org/licenses/LICENSE-2.0
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.5.0.M1</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
See the License for the specific language governing permissions and
limitations under the License.
</comments>
</license>
</licenses>
<modules>
<module>spring-data-mongodb</module>
<module>spring-data-mongodb-cross-store</module>
<module>spring-data-mongodb-log4j</module>
<module>spring-data-mongodb-distribution</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- dist.* properties are used by the antrun tasks below -->
<dist.id>spring-data-mongo</dist.id>
<dist.name>Spring Data Mongo</dist.name>
<dist.key>SDMONGO</dist.key>
<dist.version>${project.version}</dist.version>
<dist.releaseType>snapshot</dist.releaseType>
<dist.finalName>${dist.id}-${dist.version}</dist.finalName>
<dist.fileName>${dist.finalName}.zip</dist.fileName>
<dist.filePath>target/${dist.fileName}</dist.filePath>
<dist.bucketName>dist.springframework.org</dist.bucketName>
<!-- these properties should be in ~/.m2/settings.xml
<dist.accessKey>s3 access key</dist.accessKey>
<dist.secretKey>s3 secret key</dist.secretKey>
-->
</properties>
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>1.9.0.M1</springdata.commons>
<mongo>2.12.1</mongo>
<mongo.osgi>2.12.1</mongo.osgi>
</properties>
<build>
<extensions>
<extension>
<groupId>org.springframework.build.aws</groupId>
<artifactId>org.springframework.build.aws.maven</artifactId>
<version>3.1.0.RELEASE</version>
</extension>
</extensions>
<developers>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at gopivotal.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Project Lead</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>trisberg</id>
<name>Thomas Risberg</name>
<email>trisberg at vmware.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>mpollack</id>
<name>Mark Pollack</name>
<email>mpollack at gopivotal.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>jbrisbin</id>
<name>Jon Brisbin</name>
<email>jbrisbin at gopivotal.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>-6</timezone>
</developer>
<developer>
<id>tdarimont</id>
<name>Thomas Darimont</name>
<email>tdarimont at gopivotal.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>cstrobl</id>
<name>Christoph Strobl</name>
<email>cstrobl at gopivotal.com</email>
<organization>Pivotal</organization>
<organizationUrl>http://www.gopivotal.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
</developers>
<plugins>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<!-- yes it really needs to be this (2.0.7) otherwise pdf generation from a clean build doesn't work -->
<version>2.0.7</version>
<executions>
<execution>
<goals>
<goal>generate-html</goal>
<goal>generate-pdf</goal>
</goals>
<phase>pre-site</phase>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.docbook</groupId>
<artifactId>docbook-xml</artifactId>
<version>4.4</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<configuration>
<includes>index.xml</includes>
<xincludeSupported>true</xincludeSupported>
<foCustomization>${project.basedir}/src/docbkx/resources/xsl/fopdf.xsl</foCustomization>
<htmlStylesheet>css/html.css</htmlStylesheet>
<chunkedOutput>false</chunkedOutput>
<htmlCustomization>${project.basedir}/src/docbkx/resources/xsl/html.xsl</htmlCustomization>
<useExtensions>1</useExtensions>
<highlightSource>1</highlightSource>
<highlightDefaultLanguage></highlightDefaultLanguage>
<!-- callouts -->
<entities>
<entity>
<name>version</name>
<value>${pom.version}</value>
</entity>
</entities>
<postProcess>
<copy todir="${project.basedir}/target/site/reference">
<fileset dir="${project.basedir}/target/docbkx">
<include name="**/*.html"/>
<include name="**/*.pdf"/>
</fileset>
</copy>
<copy todir="${project.basedir}/target/site/reference/html">
<fileset dir="${project.basedir}/src/docbkx/resources">
<include name="**/*.css"/>
<include name="**/*.png"/>
<include name="**/*.gif"/>
<include name="**/*.jpg"/>
</fileset>
</copy>
<copy todir="${project.basedir}/target/site/reference/html">
<fileset dir="${project.basedir}/src/docbkx/resources/images">
<include name="*.png"/>
</fileset>
</copy>
<move file="${project.basedir}/target/site/reference/pdf/index.pdf"
tofile="${project.basedir}/target/site/reference/pdf/spring-data-mongo-reference.pdf"
failonerror="false"/>
</postProcess>
</configuration>
</plugin>
<profiles>
<profile>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.5</version>
<configuration>
<javadoc:aggregate>true</javadoc:aggregate>
<breakiterator>true</breakiterator>
<header>Spring Data Document</header>
<source>1.5</source>
<quiet>true</quiet>
<javadocDirectory>${project.basedir}/src/main/javadoc</javadocDirectory>
<overview>${project.basedir}/src/main/javadoc/overview.html</overview>
<stylesheetfile>${project.basedir}/src/main/javadoc/spring-javadoc.css</stylesheetfile>
<!-- copies doc-files subdirectory which contains image resources -->
<docfilessubdirs>true</docfilessubdirs>
<links>
<link>http://static.springframework.org/spring/docs/3.0.x/javadoc-api</link>
<link>http://download.oracle.com/javase/1.5.0/docs/api</link>
<link>http://api.mongodb.org/java/2.3</link>
</links>
</configuration>
</plugin>
<plugin><!--
run `mvn package assembly:assembly` to trigger assembly creation.
see http://www.sonatype.com/books/mvnref-book/reference/assemblies-set-dist-assemblies.html -->
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<inherited>false</inherited>
<executions>
<execution>
<id>distribution</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/assembly/distribution.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>upload-dist</id>
<phase>deploy</phase>
<configuration>
<tasks>
<ant antfile="${basedir}/src/ant/upload-dist.xml">
<target name="upload-dist"/>
</ant>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.build</groupId>
<artifactId>org.springframework.build.aws.ant</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>net.java.dev.jets3t</groupId>
<artifactId>jets3t</artifactId>
<version>0.7.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<!-- the name of this project is 'spring-data-mongo-dist';
make sure the zip file is just 'spring-data-mongo'. -->
<finalName>${dist.finalName}</finalName>
</build>
<id>mongo-next</id>
<properties>
<mongo>2.12.3-SNAPSHOT</mongo>
</properties>
<pluginRepositories>
<pluginRepository>
<!-- necessary for bundlor and utils -->
<id>repository.plugin.springsource.release</id>
<name>SpringSource Maven Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</pluginRepository>
<pluginRepository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>mongo-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<distributionManagement>
<!-- see 'staging' profile for dry-run deployment settings -->
<downloadUrl>http://www.springsource.com/spring-data</downloadUrl>
<site>
<id>static.springframework.org</id>
<url>
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/snapshot-site/
</url>
</site>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>
<id>mongo-3-next</id>
<properties>
<mongo>3.0.0-SNAPSHOT</mongo>
</properties>
<repositories>
<repository>
<id>mongo-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</profile>
</profiles>
<dependencies>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>http://repo.spring.io/libs-milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>http://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -1,218 +1,138 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
<name>Spring Data MongoDB Cross-store Persistence Support</name>
<dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
<name>Spring Data MongoDB - Cross-Store Support</name>
<properties>
<jpa>1.0.0.Final</jpa>
<hibernate>3.6.10.Final</hibernate>
</properties>
<dependencies>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.6.0.M1</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj}</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<optional>true</optional>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>${jpa}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<!-- For Tests -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<!-- For Tests -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.5.5-Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>jboss-repository</id>
<name>JBoss Public Repository</name>
<url>http://repository.jboss.org/nexus/content/groups/public-jboss</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-maven-milestones</id>
<name>Springframework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-maven-release</id>
<name>Springframework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<!-- <aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
</aspectLibrary> -->
</aspectLibraries>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.6</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>${source.level}</complianceLevel>
<source>${source.level}</source>
<target>${source.level}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2014 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.
@@ -17,8 +17,8 @@ package org.springframework.data.mongodb.crossstore;
import javax.persistence.EntityManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
@@ -34,20 +34,20 @@ import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* @author Thomas Risberg
* @author Oliver Gierke
*/
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
private static final String ENTITY_CLASS = "_entity_class";
private static final String ENTITY_ID = "_entity_id";
private static final String ENTITY_FIELD_NAME = "_entity_field_name";
private static final String ENTITY_FIELD_CLASS = "_entity_field_class";
protected final Log log = LogFactory.getLog(getClass());
protected final Logger log = LoggerFactory.getLogger(getClass());
private MongoTemplate mongoTemplate;
private EntityManagerFactory entityManagerFactory;
public void setMongoTemplate(MongoTemplate mongoTemplate) {
@@ -58,10 +58,12 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
this.entityManagerFactory = entityManagerFactory;
}
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass,
Object id, final ChangeSet changeSet)
throws DataAccessException, NotFoundException {
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#getPersistentState(java.lang.Class, java.lang.Object, org.springframework.data.crossstore.ChangeSet)
*/
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass, Object id, final ChangeSet changeSet)
throws DataAccessException, NotFoundException {
if (id == null) {
log.debug("Unable to load MongoDB data for null id");
@@ -77,8 +79,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
log.debug("Loading MongoDB data for " + dbk);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
for (DBObject dbo : collection.find(dbk)) {
String key = (String) dbo.get(ENTITY_FIELD_NAME);
if (log.isDebugEnabled()) {
@@ -87,9 +88,8 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
if (!changeSet.getValues().containsKey(key)) {
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
if (className == null) {
throw new DataIntegrityViolationException(
"Unble to convert property " + key
+ ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
throw new DataIntegrityViolationException("Unble to convert property " + key + ": Invalid metadata, "
+ ENTITY_FIELD_CLASS + " not available");
}
Class<?> clazz = ClassUtils.resolveClassName(className, ClassUtils.getDefaultClassLoader());
Object value = mongoTemplate.getConverter().read(clazz, dbo);
@@ -104,15 +104,25 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#getPersistentId(org.springframework.data.crossstore.ChangeSetBacked, org.springframework.data.crossstore.ChangeSet)
*/
public Object getPersistentId(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException {
log.debug("getPersistentId called on " + entity);
if (entityManagerFactory == null) {
throw new DataAccessResourceFailureException("EntityManagerFactory cannot be null");
}
Object o = entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity);
return o;
return entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity);
}
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#persistState(org.springframework.data.crossstore.ChangeSetBacked, org.springframework.data.crossstore.ChangeSet)
*/
public Object persistState(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException {
if (cs == null) {
log.debug("Flush: changeset was null, nothing to flush.");
@@ -135,10 +145,8 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
dbQuery.put(ENTITY_ID, getPersistentId(entity, cs));
dbQuery.put(ENTITY_CLASS, entity.getClass().getName());
dbQuery.put(ENTITY_FIELD_NAME, key);
DBObject dbId = mongoTemplate.execute(collName,
new CollectionCallback<DBObject>() {
public DBObject doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
DBObject dbId = mongoTemplate.execute(collName, new CollectionCallback<DBObject>() {
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
return collection.findOne(dbQuery);
}
});
@@ -147,14 +155,12 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
log.debug("Flush: removing: " + dbQuery);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.remove(dbQuery);
return null;
}
});
}
else {
} else {
final DBObject dbDoc = new BasicDBObject();
dbDoc.putAll(dbQuery);
if (log.isDebugEnabled()) {
@@ -166,8 +172,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
dbDoc.put("_id", dbId.get("_id"));
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.save(dbDoc);
return null;
}
@@ -178,8 +183,13 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
return 0L;
}
/**
* Returns the collection the given entity type shall be persisted to.
*
* @param entityClass must not be {@literal null}.
* @return
*/
private String getCollectionNameForEntity(Class<? extends ChangeSetBacked> entityClass) {
return ClassUtils.getQualifiedName(entityClass);
return mongoTemplate.getCollectionName(entityClass);
}
}

View File

@@ -21,13 +21,12 @@ import javax.persistence.EntityManager;
import javax.persistence.Transient;
import javax.persistence.Entity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.crossstore.RelatedDocument;
import org.springframework.data.mongodb.crossstore.DocumentBacked;
import org.springframework.data.crossstore.ChangeSetBackedTransactionSynchronization;
@@ -38,235 +37,234 @@ import org.springframework.data.crossstore.HashMapChangeSet;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Aspect to turn an object annotated with @Document into a persistent document
* using Mongo.
* Aspect to turn an object annotated with @Document into a persistent document using Mongo.
*
* @author Thomas Risberg
*/
public aspect MongoDocumentBacking {
private static final Log LOGGER = LogFactory
.getLog(MongoDocumentBacking.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDocumentBacking.class);
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
public void setChangeSetPersister(
ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
public void setChangeSetPersister(ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @RelatedDocument * (@Entity+ *).*:@Transient;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @RelatedDocument * (@Entity+ *).*:@Transient;
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
execution((DocumentBacked+).new(..)) &&
!execution((DocumentBacked+).new(ChangeSet)) &&
this(entity);
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity,
ChangeSet cs) :
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity, ChangeSet cs) :
execution((DocumentBacked+).new(ChangeSet)) &&
this(entity) &&
args(cs);
protected pointcut entityFieldGet(DocumentBacked entity) :
protected pointcut entityFieldGet(DocumentBacked entity) :
get(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
!get(* DocumentBacked.*);
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
set(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
args(newVal) &&
!set(* DocumentBacked.*);
// intercept EntityManager.merge calls
public pointcut entityManagerMerge(EntityManager em, Object entity) :
// intercept EntityManager.merge calls
public pointcut entityManagerMerge(EntityManager em, Object entity) :
call(* EntityManager.merge(Object)) &&
target(em) &&
args(entity);
// intercept EntityManager.remove calls
// public pointcut entityManagerRemove(EntityManager em, Object entity) :
// call(* EntityManager.remove(Object)) &&
// target(em) &&
// args(entity);
// move changeSet from detached entity to the newly merged persistent object
Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) {
Object mergedEntity = proceed(em, entity);
if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) {
((DocumentBacked)mergedEntity).changeSet = ((DocumentBacked)entity).getChangeSet();
}
return mergedEntity;
}
// intercept EntityManager.remove calls
// public pointcut entityManagerRemove(EntityManager em, Object entity) :
// call(* EntityManager.remove(Object)) &&
// target(em) &&
// args(entity);
// clear changeSet from removed entity
// Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) {
// if (entity instanceof DocumentBacked) {
// removeChangeSetValues((DocumentBacked)entity);
// }
// return proceed(em, entity);
// }
private static void removeChangeSetValues(DocumentBacked entity) {
LOGGER.debug("Removing all change-set values for " + entity);
ChangeSet nulledCs = new HashMapChangeSet();
DocumentBacked documentEntity = (DocumentBacked) entity;
@SuppressWarnings("unchecked")
ChangeSetPersister<Object> changeSetPersister = (ChangeSetPersister<Object>)documentEntity.itdChangeSetPersister;
try {
changeSetPersister.getPersistentState(
documentEntity.getClass(),
documentEntity.get_persistent_id(),
documentEntity.getChangeSet());
}
catch (DataAccessException e) {}
catch (NotFoundException e) {}
for (String key :entity.getChangeSet().getValues().keySet()) {
nulledCs.set(key, null);
// move changeSet from detached entity to the newly merged persistent object
Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) {
Object mergedEntity = proceed(em, entity);
if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) {
((DocumentBacked) mergedEntity).changeSet = ((DocumentBacked) entity).getChangeSet();
}
return mergedEntity;
}
entity.setChangeSet(nulledCs);
}
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER
.debug("User-defined constructor called on DocumentBacked object of class "
+ entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
entity.itdTransactionSynchronization =
new ChangeSetBackedTransactionSynchronization(changeSetPersister, entity);
//registerTransactionSynchronization(entity);
}
// clear changeSet from removed entity
// Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) {
// if (entity instanceof DocumentBacked) {
// removeChangeSetValues((DocumentBacked)entity);
// }
// return proceed(em, entity);
// }
private static void registerTransactionSynchronization(DocumentBacked entity) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding transaction synchronization for " + entity);
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity);
}
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization is not active for " + entity);
}
}
}
private static void removeChangeSetValues(DocumentBacked entity) {
LOGGER.debug("Removing all change-set values for " + entity);
ChangeSet nulledCs = new HashMapChangeSet();
DocumentBacked documentEntity = (DocumentBacked) entity;
@SuppressWarnings("unchecked")
ChangeSetPersister<Object> changeSetPersister = (ChangeSetPersister<Object>) documentEntity.itdChangeSetPersister;
try {
changeSetPersister.getPersistentState(documentEntity.getClass(), documentEntity.get_persistent_id(),
documentEntity.getChangeSet());
} catch (DataAccessException e) {
} catch (NotFoundException e) {
}
for (String key : entity.getChangeSet().getValues().keySet()) {
nulledCs.set(key, null);
}
entity.setChangeSet(nulledCs);
}
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
@Transient private ChangeSet DocumentBacked.changeSet;
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER.debug("User-defined constructor called on DocumentBacked object of class " + entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
entity.itdTransactionSynchronization = new ChangeSetBackedTransactionSynchronization(changeSetPersister, entity);
// registerTransactionSynchronization(entity);
}
@Transient private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
private static void registerTransactionSynchronization(DocumentBacked entity) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding transaction synchronization for " + entity);
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity);
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization is not active for " + entity);
}
}
}
@Transient private ChangeSetBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
@Transient
private ChangeSet DocumentBacked.changeSet;
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
@Transient
private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
@Transient
private ChangeSetBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
Object id = itdChangeSetPersister.getPersistentId(this, this.changeSet);
itdChangeSetPersister.persistState(this, this.changeSet);
}
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
// lifecycle methods
@javax.persistence.PostPersist public void DocumentBacked.itdPostPersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate public void DocumentBacked.itdPreUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostUpdate public void DocumentBacked.itdPostUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostRemove public void DocumentBacked.itdPostRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
removeChangeSetValues(this);
}
@javax.persistence.PostLoad public void DocumentBacked.itdPostLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName
+ "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister.getPersistentState(entity.getClass(),
entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
Object id = itdChangeSetPersister.getPersistentId(this, this.changeSet);
itdChangeSetPersister.persistState(this, this.changeSet);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName
+ "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
return proceed(entity, newVal);
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();
}
// lifecycle methods
@javax.persistence.PostPersist
public void DocumentBacked.itdPostPersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate
public void DocumentBacked.itdPreUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostUpdate
public void DocumentBacked.itdPostUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostRemove
public void DocumentBacked.itdPostRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
removeChangeSetValues(this);
}
@javax.persistence.PostLoad
public void DocumentBacked.itdPostLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName + "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister
.getPersistentState(entity.getClass(), entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName + "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
return proceed(entity, newVal);
}
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();
}
}

View File

@@ -24,8 +24,6 @@ import java.lang.annotation.Target;
* @author Thomas Risberg
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.FIELD
})
@Target({ ElementType.FIELD })
public @interface RelatedDocument {
}

View File

@@ -0,0 +1,5 @@
/**
* Infrastructure for Spring Data's MongoDB cross store support.
*/
package org.springframework.data.mongodb.crossstore;

View File

@@ -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.
@@ -18,7 +18,9 @@ package org.springframework.data.mongodb.crossstore;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,7 +28,6 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.crossstore.test.Address;
import org.springframework.data.mongodb.crossstore.test.Person;
import org.springframework.data.mongodb.crossstore.test.Resume;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
@@ -35,92 +36,119 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
/**
* Integration tests for MongoDB cross-store persistence (mainly {@link MongoChangeSetPersister}).
*
* @author Thomas Risberg
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
@ContextConfiguration("classpath:/META-INF/spring/applicationContext.xml")
public class CrossStoreMongoTests {
@Autowired
private MongoTemplate mongoTemplate;
private EntityManager entityManager;
@Autowired
private PlatformTransactionManager transactionManager;
MongoTemplate mongoTemplate;
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
EntityManager entityManager;
@Autowired
PlatformTransactionManager transactionManager;
TransactionTemplate txTemplate;
@Before
public void setUp() {
txTemplate = new TransactionTemplate(transactionManager);
clearData(Person.class);
Address address = new Address(12, "MAin St.", "Boston", "MA", "02101");
Resume resume = new Resume();
resume.addEducation("Skanstulls High School, 1975");
resume.addEducation("Univ. of Stockholm, 1980");
resume.addJob("DiMark, DBA, 1990-2000");
resume.addJob("VMware, Developer, 2007-");
final Person person = new Person("Thomas", 20);
person.setAddress(address);
person.setResume(resume);
person.setId(1L);
txTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus status) {
entityManager.persist(person);
return null;
}
});
}
private void clearData(String collectionName) {
DBCollection col = this.mongoTemplate.getCollection(collectionName);
if (col != null) {
this.mongoTemplate.dropCollection(collectionName);
}
@After
public void tearDown() {
txTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus status) {
entityManager.remove(entityManager.find(Person.class, 1L));
return null;
}
});
}
private void clearData(Class<?> domainType) {
String collectionName = mongoTemplate.getCollectionName(domainType);
mongoTemplate.dropCollection(collectionName);
}
@Test
@Transactional
@Rollback(false)
public void testCreateJpaToMongoEntityRelationship() {
clearData(Person.class.getName());
Person p = new Person("Thomas", 20);
Address a = new Address(12, "MAin St.", "Boston", "MA", "02101");
p.setAddress(a);
Resume r = new Resume();
r.addEducation("Skanstulls High School, 1975");
r.addEducation("Univ. of Stockholm, 1980");
r.addJob("DiMark, DBA, 1990-2000");
r.addJob("VMware, Developer, 2007-");
p.setResume(r);
p.setId(1L);
entityManager.persist(p);
}
@Test
@Transactional
@Rollback(false)
public void testReadJpaToMongoEntityRelationship() {
Person found = entityManager.find(Person.class, 1L);
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; "
+ "VMware, Developer, 2007-", found.getResume().getJobs());
found.getResume().addJob("SpringDeveloper.com, Consultant, 2005-2006");
found.setAge(44);
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-", found.getResume().getJobs());
}
@Test
@Transactional
@Rollback(false)
public void testUpdatedJpaToMongoEntityRelationship() {
Person found = entityManager.find(Person.class, 1L);
found.setAge(44);
found.getResume().addJob("SpringDeveloper.com, Consultant, 2005-2006");
entityManager.merge(found);
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; "
+ "VMware, Developer, 2007-" + "; "
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-" + "; "
+ "SpringDeveloper.com, Consultant, 2005-2006", found.getResume().getJobs());
}
@Test
public void testMergeJpaEntityWithMongoDocument() {
TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
final Person detached = entityManager.find(Person.class, 1L);
entityManager.detach(detached);
detached.getResume().addJob("TargetRx, Developer, 2000-2005");
Person merged = txTemplate.execute(new TransactionCallback<Person>() {
public Person doInTransaction(TransactionStatus status) {
return entityManager.merge(detached);
Person result = entityManager.merge(detached);
entityManager.flush();
return result;
}
});
Assert.assertTrue(detached.getResume().getJobs().contains("TargetRx, Developer, 2000-2005"));
Assert.assertTrue(merged.getResume().getJobs().contains("TargetRx, Developer, 2000-2005"));
final Person updated = entityManager.find(Person.class, 1L);
@@ -129,7 +157,7 @@ public class CrossStoreMongoTests {
@Test
public void testRemoveJpaEntityWithMongoDocument() {
TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
txTemplate.execute(new TransactionCallback<Person>() {
public Person doInTransaction(TransactionStatus status) {
Person p2 = new Person("Thomas", 20);
@@ -156,8 +184,10 @@ public class CrossStoreMongoTests {
return null;
}
});
boolean weFound3 = false;
for (DBObject dbo : this.mongoTemplate.getCollection(Person.class.getName()).find()) {
for (DBObject dbo : this.mongoTemplate.getCollection(mongoTemplate.getCollectionName(Person.class)).find()) {
Assert.assertTrue(!dbo.get("_entity_id").equals(2L));
if (dbo.get("_entity_id").equals(3L)) {
weFound3 = true;

View File

@@ -23,8 +23,7 @@ public class Address {
private String state;
private String zip;
public Address(Integer streetNumber, String streetName, String city,
String state, String zip) {
public Address(Integer streetNumber, String streetName, String city, String state, String zip) {
super();
this.streetNumber = streetNumber;
this.streetName = streetName;
@@ -36,34 +35,41 @@ public class Address {
public Integer getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(Integer streetNumber) {
this.streetNumber = streetNumber;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}

View File

@@ -1,12 +0,0 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n
log4j.category.org.springframework=INFO
log4j.category.org.springframework.data=TRACE
log4j.category.org.hibernate.SQL=DEBUG
# for debugging datasource initialization
# log4j.category.test.jdbc=DEBUG

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<!--
<logger name="org.springframework" level="debug" />
-->
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -0,0 +1,18 @@
Bundle-SymbolicName: org.springframework.data.mongodb.crossstore
Bundle-Name: Spring Data MongoDB Cross Store Support
Bundle-Vendor: Pivotal Software, Inc.
Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Export-Template:
org.springframework.data.mongodb.crossstore.*;version="${project.version}"
Import-Template:
com.mongodb.*;version="${mongo.osgi:[=.=.=,+1.0.0)}",
javax.persistence.*;version="${jpa:[=.=.=,+1.0.0)}",
org.aspectj.*;version="${aspectj:[1.0.0, 2.0.0)}",
org.bson.*;version="0",
org.slf4j.*;version="${slf4j:[=.=.=,+1.0.0)}",
org.springframework.*;version="${spring:[=.=.=.=,+1.0.0)}",
org.springframework.data.*;version="${springdata.commons:[=.=.=.=,+1.0.0)}",
org.springframework.data.mongodb.*;version="${project.version:[=.=.=.=,+1.0.0)}",
org.w3c.dom.*;version="0"

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-mongodb-distribution</artifactId>
<packaging>pom</packaging>
<name>Spring Data MongoDB - Distribution</name>
<description>Distribution build for Spring Data MongoDB</description>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<project.root>${basedir}/..</project.root>
<dist.key>SDMONGO</dist.key>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -5,7 +5,7 @@ and connects directly to the MongoDB server using the driver. It has no dependen
To use it, configure a host, port, (optionally) applicationId, and database property in your Log4J configuration:
log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost
@@ -32,7 +32,7 @@ An example log entry might look like:
{
"_id" : ObjectId("4d89341a8ef397e06940d5cd"),
"applicationId" : "my.application",
"name" : "org.springframework.data.document.mongodb.log4j.AppenderTest",
"name" : "org.springframework.data.mongodb.log4j.MongoLog4jAppenderIntegrationTests",
"level" : "DEBUG",
"timestamp" : ISODate("2011-03-23T16:53:46.778Z"),
"properties" : {

View File

@@ -1,83 +1,30 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-log4j</artifactId>
<name>Spring Data MongoDB Log4J Appender</name>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<mongo.version>2.3</mongo.version>
</properties>
<artifactId>spring-data-mongodb-log4j</artifactId>
<name>Spring Data MongoDB - Log4J Appender</name>
<dependencies>
<properties>
<log4j>1.2.16</log4j>
</properties>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo.version}</version>
</dependency>
<dependencies>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</dependencies>
</project>

View File

@@ -1,208 +0,0 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.log4j;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.WriteConcern;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.MDC;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoLog4jAppender extends AppenderSkeleton {
public static final String LEVEL = "level";
public static final String NAME = "name";
public static final String APP_ID = "applicationId";
public static final String TIMESTAMP = "timestamp";
public static final String PROPERTIES = "properties";
public static final String TRACEBACK = "traceback";
public static final String MESSAGE = "message";
public static final String YEAR = "year";
public static final String MONTH = "month";
public static final String DAY = "day";
public static final String HOUR = "hour";
protected String host = "localhost";
protected int port = 27017;
protected String database = "logs";
protected String collectionPattern = "%c";
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
protected String applicationId = System.getProperty("APPLICATION_ID", null);
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
protected Mongo mongo;
protected DB db;
public MongoLog4jAppender() {
}
public MongoLog4jAppender(boolean isActive) {
super(isActive);
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getCollectionPattern() {
return collectionPattern;
}
public void setCollectionPattern(String collectionPattern) {
this.collectionPattern = collectionPattern;
this.collectionLayout = new PatternLayout(collectionPattern);
}
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public void setWarnOrHigherWriteConcern(String wc) {
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc);
}
public String getWarnOrHigherWriteConcern() {
return warnOrHigherWriteConcern.toString();
}
public String getInfoOrLowerWriteConcern() {
return infoOrLowerWriteConcern.toString();
}
public void setInfoOrLowerWriteConcern(String wc) {
this.infoOrLowerWriteConcern = WriteConcern.valueOf(wc);
}
protected void connectToMongo() throws UnknownHostException {
this.mongo = new Mongo(host, port);
this.db = mongo.getDB(database);
}
@SuppressWarnings({"unchecked"})
@Override
protected void append(final LoggingEvent event) {
if (null == db) {
try {
connectToMongo();
} catch (UnknownHostException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
BasicDBObject dbo = new BasicDBObject();
if (null != applicationId) {
dbo.put(APP_ID, applicationId);
MDC.put(APP_ID, applicationId);
}
dbo.put(NAME, event.getLogger().getName());
dbo.put(LEVEL, event.getLevel().toString());
Calendar tstamp = Calendar.getInstance();
tstamp.setTimeInMillis(event.getTimeStamp());
dbo.put(TIMESTAMP, tstamp.getTime());
// Copy properties into document
Map<Object, Object> props = event.getProperties();
if (null != props && props.size() > 0) {
BasicDBObject propsDbo = new BasicDBObject();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
propsDbo.put(entry.getKey().toString(), entry.getValue().toString());
}
dbo.put(PROPERTIES, propsDbo);
}
// Copy traceback info (if there is any) into the document
String[] traceback = event.getThrowableStrRep();
if (null != traceback && traceback.length > 0) {
BasicDBList tbDbo = new BasicDBList();
tbDbo.addAll(Arrays.asList(traceback));
dbo.put(TRACEBACK, tbDbo);
}
// Put the rendered message into the document
dbo.put(MESSAGE, event.getRenderedMessage());
// Insert the document
Calendar now = Calendar.getInstance();
MDC.put(YEAR, now.get(Calendar.YEAR));
MDC.put(MONTH, String.format("%1$02d", now.get(Calendar.MONTH) + 1));
MDC.put(DAY, String.format("%1$02d", now.get(Calendar.DAY_OF_MONTH)));
MDC.put(HOUR, String.format("%1$02d", now.get(Calendar.HOUR_OF_DAY)));
String coll = collectionLayout.format(event);
MDC.remove(YEAR);
MDC.remove(MONTH);
MDC.remove(DAY);
MDC.remove(HOUR);
if (null != applicationId) {
MDC.remove(APP_ID);
}
WriteConcern wc;
if (event.getLevel().isGreaterOrEqual(Level.WARN)) {
wc = warnOrHigherWriteConcern;
} else {
wc = infoOrLowerWriteConcern;
}
db.getCollection(coll).insert(dbo, wc);
}
public void close() {
mongo.close();
}
public boolean requiresLayout() {
return true;
}
}

View File

@@ -0,0 +1,226 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.log4j;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.MDC;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.WriteConcern;
/**
* Log4j appender writing log entries into a MongoDB instance.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class MongoLog4jAppender extends AppenderSkeleton {
public static final String LEVEL = "level";
public static final String NAME = "name";
public static final String APP_ID = "applicationId";
public static final String TIMESTAMP = "timestamp";
public static final String PROPERTIES = "properties";
public static final String TRACEBACK = "traceback";
public static final String MESSAGE = "message";
public static final String YEAR = "year";
public static final String MONTH = "month";
public static final String DAY = "day";
public static final String HOUR = "hour";
protected String host = "localhost";
protected int port = 27017;
protected String database = "logs";
protected String collectionPattern = "%c";
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
protected String applicationId = System.getProperty("APPLICATION_ID", null);
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
protected Mongo mongo;
protected DB db;
public MongoLog4jAppender() {
}
public MongoLog4jAppender(boolean isActive) {
super(isActive);
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getCollectionPattern() {
return collectionPattern;
}
public void setCollectionPattern(String collectionPattern) {
this.collectionPattern = collectionPattern;
this.collectionLayout = new PatternLayout(collectionPattern);
}
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public void setWarnOrHigherWriteConcern(String wc) {
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc);
}
public String getWarnOrHigherWriteConcern() {
return warnOrHigherWriteConcern.toString();
}
public String getInfoOrLowerWriteConcern() {
return infoOrLowerWriteConcern.toString();
}
public void setInfoOrLowerWriteConcern(String wc) {
this.infoOrLowerWriteConcern = WriteConcern.valueOf(wc);
}
protected void connectToMongo() throws UnknownHostException {
this.mongo = new Mongo(host, port);
this.db = mongo.getDB(database);
}
/*
* (non-Javadoc)
* @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent)
*/
@Override
@SuppressWarnings({ "unchecked" })
protected void append(final LoggingEvent event) {
if (null == db) {
try {
connectToMongo();
} catch (UnknownHostException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
BasicDBObject dbo = new BasicDBObject();
if (null != applicationId) {
dbo.put(APP_ID, applicationId);
MDC.put(APP_ID, applicationId);
}
dbo.put(NAME, event.getLogger().getName());
dbo.put(LEVEL, event.getLevel().toString());
Calendar tstamp = Calendar.getInstance();
tstamp.setTimeInMillis(event.getTimeStamp());
dbo.put(TIMESTAMP, tstamp.getTime());
// Copy properties into document
Map<Object, Object> props = event.getProperties();
if (null != props && props.size() > 0) {
BasicDBObject propsDbo = new BasicDBObject();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
propsDbo.put(entry.getKey().toString(), entry.getValue().toString());
}
dbo.put(PROPERTIES, propsDbo);
}
// Copy traceback info (if there is any) into the document
String[] traceback = event.getThrowableStrRep();
if (null != traceback && traceback.length > 0) {
BasicDBList tbDbo = new BasicDBList();
tbDbo.addAll(Arrays.asList(traceback));
dbo.put(TRACEBACK, tbDbo);
}
// Put the rendered message into the document
dbo.put(MESSAGE, event.getRenderedMessage());
// Insert the document
Calendar now = Calendar.getInstance();
MDC.put(YEAR, now.get(Calendar.YEAR));
MDC.put(MONTH, String.format("%1$02d", now.get(Calendar.MONTH) + 1));
MDC.put(DAY, String.format("%1$02d", now.get(Calendar.DAY_OF_MONTH)));
MDC.put(HOUR, String.format("%1$02d", now.get(Calendar.HOUR_OF_DAY)));
String coll = collectionLayout.format(event);
MDC.remove(YEAR);
MDC.remove(MONTH);
MDC.remove(DAY);
MDC.remove(HOUR);
if (null != applicationId) {
MDC.remove(APP_ID);
}
WriteConcern wc;
if (event.getLevel().isGreaterOrEqual(Level.WARN)) {
wc = warnOrHigherWriteConcern;
} else {
wc = infoOrLowerWriteConcern;
}
db.getCollection(coll).insert(dbo, wc);
}
/*
* (non-Javadoc)
* @see org.apache.log4j.AppenderSkeleton#close()
*/
public void close() {
if (mongo != null) {
mongo.close();
}
}
/*
* (non-Javadoc)
* @see org.apache.log4j.AppenderSkeleton#requiresLayout()
*/
public boolean requiresLayout() {
return true;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Infrastructure for to use MongoDB as a logging sink.
*/
package org.springframework.data.mongodb.log4j;

View File

@@ -1,75 +0,0 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.log4j;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.UnknownHostException;
import java.util.Calendar;
import com.mongodb.DB;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.junit.Before;
import org.junit.Test;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class AppenderTest {
private static final String NAME = AppenderTest.class.getName();
private Logger log = Logger.getLogger(NAME);
private Mongo mongo;
private DB db;
private String collection;
@Before
public void setup() {
try {
mongo = new Mongo("localhost", 27017);
db = mongo.getDB("logs");
Calendar now = Calendar.getInstance();
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
db.getCollection(collection).drop();
} catch (UnknownHostException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Test
public void testLogging() {
log.debug("DEBUG message");
log.info("INFO message");
log.warn("WARN message");
log.error("ERROR message");
DBCursor msgs = db.getCollection(collection).find();
assertThat(msgs.count(), is(4));
}
@Test
public void testProperties() {
MDC.put("property", "one");
log.debug("DEBUG message");
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.log4j;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Calendar;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.DB;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
/**
* Integration tests for {@link MongoLog4jAppender}.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class MongoLog4jAppenderIntegrationTests {
static final String NAME = MongoLog4jAppenderIntegrationTests.class.getName();
Logger log = Logger.getLogger(NAME);
Mongo mongo;
DB db;
String collection;
@Before
public void setUp() throws Exception {
mongo = new Mongo("localhost", 27017);
db = mongo.getDB("logs");
Calendar now = Calendar.getInstance();
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
db.getCollection(collection).drop();
}
@Test
public void testLogging() {
log.debug("DEBUG message");
log.info("INFO message");
log.warn("WARN message");
log.error("ERROR message");
DBCursor msgs = db.getCollection(collection).find();
assertThat(msgs.count(), is(4));
}
@Test
public void testProperties() {
MDC.put("property", "one");
log.debug("DEBUG message");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 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.
@@ -13,21 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
package org.springframework.data.mongodb.log4j;
import org.springframework.data.convert.TypeMapper;
import org.junit.Test;
/**
* Interfaces for components being able to provide a {@link TypeMapper}.
* Unit tests for {@link MongoLog4jAppender}.
*
* @author Oliver Gierke
*/
public interface TypeKeyAware {
public class MongoLog4jAppenderUnitTests {
/**
* Returns the {@link TypeMapper}.
*
* @return the {@link TypeMapper} or {@literal null} if none available.
* @see DATAMONGO-641
*/
boolean isTypeKey(String key);
@Test
public void closesWithoutMongoInstancePresent() {
new MongoLog4jAppender().close();
}
}

View File

@@ -1,6 +1,6 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost
@@ -10,11 +10,4 @@ log4j.appender.stdout.collectionPattern = %X{year}%X{month}
log4j.appender.stdout.applicationId = my.application
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE
log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG
log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.category.org.springframework.transaction=INFO
log4j.category.org.hibernate.SQL=DEBUG
# for debugging datasource initialization
# log4j.category.test.jdbc=DEBUG
log4j.category.org.springframework.data.mongodb=DEBUG

View File

@@ -1,10 +1,9 @@
Bundle-SymbolicName: org.springframework.data.mongodb.log4j
Bundle-Name: Spring Data Mongo DB Log4J Appender
Bundle-Vendor: SpringSource
Bundle-Vendor: Pivotal Software, Inc.
Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
com.mongodb.*;version="${mongo.version:[=.=,+1.0.0)}",
org.apache.log4j.*;version="[1.2.15, 2.0.0)",
org.apache.log4j.spi.*;version="[1.2.15, 2.0.0)"
com.mongodb.*;version="${mongo.osgi:[=.=.=,+1.0.0)}",
org.apache.log4j.*;version="${log4j:[=.=.=,+1.0.0)}"

View File

@@ -1,415 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<name>Spring Data MongoDB Parent</name>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.0.0.M5</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- versions for commonly-used dependencies -->
<junit.version>4.8.1</junit.version>
<log4j.version>1.2.15</log4j.version>
<org.mockito.version>1.8.4</org.mockito.version>
<org.slf4j.version>1.5.10</org.slf4j.version>
<org.codehaus.jackson.version>1.6.1</org.codehaus.jackson.version>
<org.springframework.version>3.0.6.RELEASE</org.springframework.version>
<data.commons.version>1.2.0.M2</data.commons.version>
<aspectj.version>1.6.11.RELEASE</aspectj.version>
</properties>
<profiles>
<profile>
<id>strict</id>
<properties>
<maven.test.failure.ignore>false</maven.test.failure.ignore>
</properties>
</profile>
<profile>
<id>fast</id>
<properties>
<maven.test.skip>true</maven.test.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
</profile>
<profile>
<id>staging</id>
<distributionManagement>
<site>
<id>spring-site-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/docs</url>
</site>
<repository>
<id>spring-milestone-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/snapshot</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>
<id>bootstrap</id>
<!-- TODO: move the repositories in here before release -->
</profile>
</profiles>
<distributionManagement>
<!-- see 'staging' profile for dry-run deployment settings -->
<downloadUrl>http://www.springsource.com/download/community
</downloadUrl>
<site>
<id>static.springframework.org</id>
<url>
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/mongodb/docs/${project.version}
</url>
</site>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement>
<dependencyManagement>
<!--
inheritable <dependency> declarations for child poms. children still
must explicitly declare the groupId/artifactId of these dependencies
in order for them to show up on the classpath, but metadata like
<version> and <scope> are inherited, which cuts down on verbosity.
see
http://www.sonatype.com/books/mvnref-book/reference/pom-relationships-sect-dep-manage.html
-->
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
<version>${data.commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
<version>${data.commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchdb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${org.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--
dependency definitions to be inherited by child poms. any
<dependency> declarations here will automatically show up on child
project classpaths. only items that are truly common across all
projects (modules and samples) should go here. otherwise, consider
<dependencyManagement> above
-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<!--
available only in the springframework maven repository. see
<repositories> section below
-->
<groupId>org.springframework.build.aws</groupId>
<artifactId>org.springframework.build.aws.maven</artifactId>
<version>3.1.0.RELEASE</version>
</extension>
</extensions>
<resources>
<resource>
<directory>${project.basedir}/src/main/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-Xlint:-path</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>false</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<useDefaultManifestFile>true</useDefaultManifestFile>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<!--
configures the springsource bundlor plugin, which generates
OSGI-compatible MANIFEST.MF files during the 'compile' phase of
the maven build. this plugin is declared within the
pluginManagement section because not every module that inherits
from this pom needs bundlor's services, e.g.:
spring-integration-samples and all its children. for this reason,
all modules that wish to use bundlor must declare it explicitly.
it is not necessary to specify the <version> or <configuration>
sections, but groupId and artifactId are required. see
http://static.springsource.org/s2-bundlor/1.0.x/user-guide/html/ch04s03.html
for more info
-->
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
<version>1.0.0.RELEASE</version>
<configuration>
<failOnWarnings>true</failOnWarnings>
</configuration>
<executions>
<execution>
<id>bundlor</id>
<goals>
<goal>bundlor</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<pluginRepositories>
<pluginRepository>
<!-- necessary for bundlor and utils -->
<id>repository.plugin.springsource.release</id>
<name>SpringSource Maven Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</pluginRepository>
<pluginRepository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>repository.springframework.maven.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>repository.springframework.maven.snapshot</id>
<name>Spring Framework Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url>
</repository>
</repositories>
<reporting>
<plugins>
<plugin>
<!--
significantly speeds up the 'Dependencies' report during site
creation see
http://old.nabble.com/Skipping-dependency-report-during-Maven2-site-generation-td20116761.html
-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.1</version>
<configuration>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -1,140 +1,182 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<context version="7.0.3.1152">
<scope name="spring-data-mongodb" type="Project">
<element name="Filter" type="TypeFilterReferenceOverridden">
<element name="org.springframework.data.mongodb.**" type="IncludeTypePattern"/>
<context version="7.1.9.205">
<scope type="Project" name="spring-data-mongodb">
<element type="TypeFilterReferenceOverridden" name="Filter">
<element type="IncludeTypePattern" name="org.springframework.data.mongodb.**"/>
</element>
<architecture>
<element name="Config" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.config.**" type="WeakTypePattern"/>
<element type="Layer" name="Repositories">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.repository.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Monitoring"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Repositories"/>
<element type="Subsystem" name="API">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.repository.*"/>
</element>
</element>
<element type="Subsystem" name="Query">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.query.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|API" type="AllowedDependency"/>
</element>
<element type="Subsystem" name="Implementation">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.support.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|API" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|Query" type="AllowedDependency"/>
</element>
<element type="Subsystem" name="Config">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.config.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|Implementation" type="AllowedDependency"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Config" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core" type="AllowedDependency"/>
</element>
<element name="Repositories" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.repository.**" type="IncludeTypePattern"/>
<element type="Layer" name="Config">
<element type="TypeFilter" name="Assignment">
<element type="WeakTypePattern" name="**.config.**"/>
</element>
<element name="API" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.repository.*" type="IncludeTypePattern"/>
</element>
</element>
<element name="Query" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.query.**" type="IncludeTypePattern"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|API"/>
</element>
<element name="Implementation" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.support.**" type="IncludeTypePattern"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|API"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|Query"/>
</element>
<element name="Config" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.config.**" type="IncludeTypePattern"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|Implementation"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|GridFS" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Monitoring" type="AllowedDependency"/>
</element>
<element name="Monitoring" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.monitor.**" type="IncludeTypePattern"/>
<element type="Layer" name="Monitoring">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.monitor.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core" type="AllowedDependency"/>
</element>
<element name="Core" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.core.**" type="IncludeTypePattern"/>
<element type="Layer" name="GridFS">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.gridfs.**"/>
</element>
<element name="Mapping" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.mapping.**" type="IncludeTypePattern"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core" type="AllowedDependency"/>
</element>
<element type="Layer" name="Core">
<element type="TypeFilter" name="Assignment"/>
<element type="Subsystem" name="Mapping">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.mapping.**"/>
</element>
</element>
<element name="Geospatial" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.geo.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Geospatial">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.geo.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping"/>
</element>
<element name="Query" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.query.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Query">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.query.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
</element>
<element name="Index" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.index.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Conversion">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.convert.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
</element>
<element name="Core" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="**.core.**" type="WeakTypePattern"/>
<element type="Subsystem" name="SpEL">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.spel.**"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Index"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping"/>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query"/>
</element>
<element type="Subsystem" name="Aggregation">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.aggregation.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Conversion" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|SpEL" type="AllowedDependency"/>
</element>
<element type="Subsystem" name="Index">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.core.index.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
</element>
<element type="Subsystem" name="Core">
<element type="TypeFilter" name="Assignment">
<element type="WeakTypePattern" name="**.core.**"/>
</element>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Aggregation" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Conversion" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Index" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
</element>
<element type="Subsystem" name="Util">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="**.util.**"/>
</element>
<stereotype name="Unrestricted"/>
<stereotype name="Public"/>
</element>
</element>
<element type="Subsystem" name="API">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="org.springframework.data.mongodb.*"/>
</element>
<stereotype name="Public"/>
</element>
</architecture>
<workspace>
<element name="src/main/java" type="JavaRootDirectory">
<element type="JavaRootDirectory" name="src/main/java">
<reference name="Project|spring-data-mongodb::BuildUnit|spring-data-mongodb"/>
</element>
<element name="target/classes" type="JavaRootDirectory">
<element type="JavaRootDirectory" name="target/classes">
<reference name="Project|spring-data-mongodb::BuildUnit|spring-data-mongodb"/>
</element>
</workspace>
<physical>
<element name="spring-data-mongodb" type="BuildUnit"/>
<element type="BuildUnit" name="spring-data-mongodb"/>
</physical>
</scope>
<scope name="External" type="External">
<element name="Filter" type="TypeFilter">
<element name="**" type="IncludeTypePattern"/>
<element name="java.**" type="ExcludeTypePattern"/>
<element name="javax.**" type="ExcludeTypePattern"/>
<scope type="External" name="External">
<element type="TypeFilter" name="Filter">
<element type="IncludeTypePattern" name="**"/>
<element type="ExcludeTypePattern" name="java.**"/>
<element type="ExcludeTypePattern" name="javax.**"/>
</element>
<architecture>
<element name="Spring" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="org.springframework.**" type="IncludeTypePattern"/>
<element name="org.springframework.data.**" type="ExcludeTypePattern"/>
<element type="Subsystem" name="Spring">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="org.springframework.**"/>
<element type="ExcludeTypePattern" name="org.springframework.data.**"/>
</element>
</element>
<element name="Spring Data Core" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="org.springframework.data.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Spring Data Core">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="org.springframework.data.**"/>
</element>
</element>
<element name="Mongo Java Driver" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="com.mongodb.**" type="IncludeTypePattern"/>
<element name="org.bson.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Mongo Java Driver">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="com.mongodb.**"/>
<element type="IncludeTypePattern" name="org.bson.**"/>
</element>
</element>
<element name="Querydsl" type="Subsystem">
<element name="Assignment" type="TypeFilter">
<element name="com.mysema.query.**" type="IncludeTypePattern"/>
<element type="Subsystem" name="Querydsl">
<element type="TypeFilter" name="Assignment">
<element type="IncludeTypePattern" name="com.mysema.query.**"/>
</element>
</element>
</architecture>
</scope>
<scope name="Global" type="Global">
<element name="Configuration" type="Configuration"/>
<element name="Filter" type="TypeFilter">
<element name="**" type="IncludeTypePattern"/>
<scope type="Global" name="Global">
<element type="Configuration" name="Configuration"/>
<element type="TypeFilter" name="Filter">
<element type="IncludeTypePattern" name="**"/>
</element>
</scope>
</context>

View File

@@ -1,144 +1,165 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-mongodb</artifactId>
<name>Spring Data MongoDB - Core</name>
<description>MongoDB support for Spring Data</description>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
<version>1.6.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb</artifactId>
<name>Spring Data MongoDB</name>
<properties>
<mongo.version>2.6.5</mongo.version>
<querydsl.version>2.2.4</querydsl.version>
<validation>1.0.0.GA</validation>
<objenesis>1.3</objenesis>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<artifactId>spring-expression</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
<version>${data.commons.version}</version>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo.version}</version>
<exclusions>
<exclusion>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
</exclusion>
</exclusions>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl.version}</version>
<version>${querydsl}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>com.google.code.morphia</groupId>
<artifactId>morphia</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<version>${querydsl}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>${cdi}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans.test</groupId>
<artifactId>cditest-owb</artifactId>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>test</scope>
</dependency>
<!-- JSR 303 Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis}</version>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
<version>${jodatime}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>maven-apt-plugin</artifactId>
<version>1.0.2</version>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt}</version>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-test-sources</phase>
@@ -152,17 +173,25 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/PerformanceTests.java</exclude>
</excludes>
<systemPropertyVariables>
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>querydsl</id>
<name>Mysema QueryDsl</name>
<url>http://source.mysema.com/maven2/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2010-2012 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.
@@ -16,42 +16,51 @@
package org.springframework.data.mongodb;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.authentication.UserCredentials;
/**
* Exception being thrown in case we cannot connect to a MongoDB instance.
*
* @author Oliver Gierke
*/
public class CannotGetMongoDbConnectionException extends DataAccessResourceFailureException {
private String username;
private char[] password;
private String database;
private final UserCredentials credentials;
private final String database;
private static final long serialVersionUID = 1172099106475265589L;
public CannotGetMongoDbConnectionException(String msg, Throwable cause) {
super(msg, cause);
this.database = null;
this.credentials = UserCredentials.NO_CREDENTIALS;
}
public CannotGetMongoDbConnectionException(String msg) {
super(msg);
this(msg, null, UserCredentials.NO_CREDENTIALS);
}
public CannotGetMongoDbConnectionException(String msg, String database, String username, char[] password) {
public CannotGetMongoDbConnectionException(String msg, String database, UserCredentials credentials) {
super(msg);
this.username = username;
this.password = password == null ? null : password.clone();
this.database = database;
this.credentials = credentials;
}
public String getUsername() {
return username;
}
public char[] getPassword() {
return password;
/**
* Returns the {@link UserCredentials} that were used when trying to connect to the MongoDB instance.
*
* @return
*/
public UserCredentials getCredentials() {
return this.credentials;
}
/**
* Returns the name of the database trying to be accessed.
*
* @return
*/
public String getDatabase() {
return database;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* @author Oliver Gierke
*/
public class LazyLoadingException extends UncategorizedDataAccessException {
private static final long serialVersionUID = -7089224903873220037L;
/**
* @param msg
* @param cause
*/
public LazyLoadingException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,6 +1,23 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import com.mongodb.DB;
@@ -8,6 +25,7 @@ import com.mongodb.DB;
* Interface for factories creating {@link DB} instances.
*
* @author Mark Pollack
* @author Thomas Darimont
*/
public interface MongoDbFactory {
@@ -27,4 +45,11 @@ public interface MongoDbFactory {
* @throws DataAccessException
*/
DB getDb(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2011-2014 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,88 +15,232 @@
*/
package org.springframework.data.mongodb.config;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.mongodb.Mongo;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.support.CachingIsNewStrategyFactory;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
/**
* Base class for Spring Data MongoDB configuration using JavaConfig.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Ryan Tenney
*/
@Configuration
public abstract class AbstractMongoConfiguration {
public abstract String getDatabaseName();
/**
* Return the name of the database to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getDatabaseName();
@Bean
/**
* Return the name of the authentication database to use. Defaults to {@literal null} and will turn into the value
* returned by {@link #getDatabaseName()} later on effectively.
*
* @return
*/
protected String getAuthenticationDatabaseName() {
return null;
}
/**
* Return the {@link Mongo} instance to connect to. Annotate with {@link Bean} in case you want to expose a
* {@link Mongo} instance to the {@link org.springframework.context.ApplicationContext}.
*
* @return
* @throws Exception
*/
public abstract Mongo mongo() throws Exception;
/**
* Creates a {@link MongoTemplate}.
*
* @return
* @throws Exception
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
/**
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link Mongo} instance
* configured in {@link #mongo()}.
*
* @see #mongo()
* @see #mongoTemplate()
* @return
* @throws Exception
*/
@Bean
public MongoDbFactory mongoDbFactory() throws Exception {
if (getUserCredentials() == null) {
return new SimpleMongoDbFactory(mongo(), getDatabaseName());
} else {
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
}
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials(), getAuthenticationDatabaseName());
}
public String getMappingBasePackage() {
return "";
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
* {@link AbstractMongoConfiguration} the base package will be considered {@code com.acme} unless the method is
* overriden to implement alternate behaviour.
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
*/
protected String getMappingBasePackage() {
Package mappingBasePackage = getClass().getPackage();
return mappingBasePackage == null ? null : mappingBasePackage.getName();
}
public UserCredentials getUserCredentials() {
/**
* Return {@link UserCredentials} to be used when connecting to the MongoDB instance or {@literal null} if none shall
* be used.
*
* @return
*/
protected UserCredentials getUserCredentials() {
return null;
}
/**
* Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
@Bean
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException, LinkageError {
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
return mappingContext;
}
/**
* Returns a {@link MappingContextIsNewStrategyFactory} wrapped into a {@link CachingIsNewStrategyFactory}.
*
* @return
* @throws ClassNotFoundException
*/
@Bean
public IsNewStrategyFactory isNewStrategyFactory() throws ClassNotFoundException {
return new CachingIsNewStrategyFactory(new MappingContextIsNewStrategyFactory(mongoMappingContext()));
}
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
* {@link #mongoMappingContext()}. Returns an empty {@link CustomConversions} instance by default.
*
* @return must not be {@literal null}.
*/
@Bean
public CustomConversions customConversions() {
return new CustomConversions(Collections.emptyList());
}
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
return converter;
}
/**
* Scans the mapping base package for classes annotated with {@link Document}.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), mappingContext.getClass()
.getClassLoader()));
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
AbstractMongoConfiguration.class.getClassLoader()));
}
mappingContext.setInitialEntitySet(initialEntitySet);
}
return mappingContext;
}
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
afterMappingMongoConverterCreation(converter);
return converter;
return initialEntitySet;
}
/**
* Hook that allows post-processing after the MappingMongoConverter has been successfully created.
* Configures whether to abbreviate field names for domain objects by configuring a
* {@link CamelCaseAbbreviatingFieldNamingStrategy} on the {@link MongoMappingContext} instance created. For advanced
* customization needs, consider overriding {@link #mappingMongoConverter()}.
*
* @param converter
* @return
*/
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
protected boolean abbreviateFieldNames() {
return false;
}
/**
* Configures a {@link FieldNamingStrategy} on the {@link MongoMappingContext} instance created.
*
* @return
* @since 1.5
*/
protected FieldNamingStrategy fieldNamingStrategy() {
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy()
: PropertyNameFieldNamingStrategy.INSTANCE;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,16 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* Constants to declare bean names used by the namespace configuration.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Martin Baumgartner
*/
public abstract class BeanNames {
static final String MAPPING_CONTEXT = "mappingContext";
static final String INDEX_HELPER = "indexCreationHelper";
static final String MONGO = "mongo";
static final String DB_FACTORY = "mongoDbFactory";
public static final String MAPPING_CONTEXT_BEAN_NAME = "mongoMappingContext";
static final String INDEX_HELPER_BEAN_NAME = "indexCreationHelper";
static final String MONGO_BEAN_NAME = "mongo";
static final String DB_FACTORY_BEAN_NAME = "mongoDbFactory";
static final String VALIDATING_EVENT_LISTENER_BEAN_NAME = "validatingMongoEventListener";
static final String IS_NEW_STRATEGY_FACTORY_BEAN_NAME = "isNewStrategyFactory";
static final String DEFAULT_CONVERTER_BEAN_NAME = "mappingConverter";
static final String MONGO_TEMPLATE_BEAN_NAME = "mongoTemplate";
static final String GRID_FS_TEMPLATE_BEAN_NAME = "gridFsTemplate";
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.domain.AuditorAware;
/**
* Annotation to enable auditing in MongoDB via annotation configuration.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MongoAuditingRegistrar.class)
public @interface EnableMongoAuditing {
/**
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
*
* @return
*/
String auditorAwareRef() default "";
/**
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
*
* @return
*/
boolean setDates() default true;
/**
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
*
* @return
*/
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
* used for setting creation and modification dates.
*
* @return
*/
String dateTimeProviderRef() default "";
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} to parse {@code gridFsTemplate} elements into {@link BeanDefinition}s.
*
* @author Martin Baumgartner
*/
class GridFsTemplateParser extends AbstractBeanDefinitionParser {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.GRID_FS_TEMPLATE_BEAN_NAME;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
String converterRef = element.getAttribute("converter-ref");
String dbFactoryRef = element.getAttribute("db-factory-ref");
String bucket = element.getAttribute("bucket");
BeanDefinitionBuilder gridFsTemplateBuilder = BeanDefinitionBuilder.genericBeanDefinition(GridFsTemplate.class);
if (StringUtils.hasText(dbFactoryRef)) {
gridFsTemplateBuilder.addConstructorArgReference(dbFactoryRef);
} else {
gridFsTemplateBuilder.addConstructorArgReference(BeanNames.DB_FACTORY_BEAN_NAME);
}
if (StringUtils.hasText(converterRef)) {
gridFsTemplateBuilder.addConstructorArgReference(converterRef);
} else {
gridFsTemplateBuilder.addConstructorArgReference(BeanNames.DEFAULT_CONVERTER_BEAN_NAME);
}
if (StringUtils.hasText(bucket)) {
gridFsTemplateBuilder.addConstructorArgValue(bucket);
}
return (AbstractBeanDefinition) helper.getComponentIdButFallback(gridFsTemplateBuilder, BeanNames.GRID_FS_TEMPLATE_BEAN_NAME)
.getBeanDefinition();
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,66 +13,97 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import static org.springframework.data.mongodb.config.BeanNames.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.parsing.ReaderContext;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* Bean definition parser for the {@code mapping-converter} element.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Maciej Walkowiak
* @author Thomas Darimont
* @author Christoph Strobl
*/
public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
public class MappingMongoConverterParser implements BeanDefinitionParser {
private static final String BASE_PACKAGE = "base-package";
private static final boolean JSR_303_PRESENT = ClassUtils.isPresent("javax.validation.Validator",
MappingMongoConverterParser.class.getClassLoader());
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : "mappingConverter";
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
if (parserContext.isNested()) {
parserContext.getReaderContext().error("Mongo Converter must not be defined as nested bean.", element);
}
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
id = StringUtils.hasText(id) ? id : DEFAULT_CONVERTER_BEAN_NAME;
parserContext.pushContainingComponent(new CompositeComponentDefinition("Mapping Mongo Converter", element));
BeanDefinition conversionsDefinition = getCustomConversions(element, parserContext);
String ctxRef = potentiallyCreateMappingContext(element, parserContext, conversionsDefinition);
String ctxRef = potentiallyCreateMappingContext(element, parserContext, conversionsDefinition, id);
createIsNewStrategyFactoryBeanDefinition(ctxRef, parserContext, element);
// Need a reference to a Mongo instance
String dbFactoryRef = element.getAttribute("db-factory-ref");
if (!StringUtils.hasText(dbFactoryRef)) {
dbFactoryRef = DB_FACTORY;
dbFactoryRef = DB_FACTORY_BEAN_NAME;
}
// Converter
@@ -80,75 +111,181 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
converterBuilder.addConstructorArgReference(dbFactoryRef);
converterBuilder.addConstructorArgReference(ctxRef);
String typeMapperRef = element.getAttribute("type-mapper-ref");
if (StringUtils.hasText(typeMapperRef)) {
converterBuilder.addPropertyReference("typeMapper", typeMapperRef);
}
if (conversionsDefinition != null) {
converterBuilder.addPropertyValue("customConversions", conversionsDefinition);
}
try {
registry.getBeanDefinition(INDEX_HELPER);
registry.getBeanDefinition(INDEX_HELPER_BEAN_NAME);
} catch (NoSuchBeanDefinitionException ignored) {
if (!StringUtils.hasText(dbFactoryRef)) {
dbFactoryRef = DB_FACTORY;
dbFactoryRef = DB_FACTORY_BEAN_NAME;
}
BeanDefinitionBuilder indexHelperBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoPersistentEntityIndexCreator.class);
indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(ctxRef));
indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(dbFactoryRef));
registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition());
indexHelperBuilder.addConstructorArgReference(ctxRef);
indexHelperBuilder.addConstructorArgReference(dbFactoryRef);
indexHelperBuilder.addDependsOn(ctxRef);
parserContext.registerBeanComponent(new BeanComponentDefinition(indexHelperBuilder.getBeanDefinition(),
INDEX_HELPER_BEAN_NAME));
}
return converterBuilder.getBeanDefinition();
BeanDefinition validatingMongoEventListener = potentiallyCreateValidatingMongoEventListener(element, parserContext);
if (validatingMongoEventListener != null) {
parserContext.registerBeanComponent(new BeanComponentDefinition(validatingMongoEventListener,
VALIDATING_EVENT_LISTENER_BEAN_NAME));
}
parserContext.registerBeanComponent(new BeanComponentDefinition(converterBuilder.getBeanDefinition(), id));
parserContext.popAndRegisterContainingComponent();
return null;
}
private String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
BeanDefinition conversionsDefinition) {
private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {
String ctxRef = element.getAttribute("mapping-context-ref");
if (!StringUtils.hasText(ctxRef)) {
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoMappingContext.class);
String disableValidation = element.getAttribute("disable-validation");
boolean validationDisabled = StringUtils.hasText(disableValidation) && Boolean.valueOf(disableValidation);
Set<String> classesToAdd = getInititalEntityClasses(element, mappingContextBuilder);
if (classesToAdd != null) {
mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd);
if (!validationDisabled) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
RuntimeBeanReference validator = getValidator(builder, parserContext);
if (validator != null) {
builder.getRawBeanDefinition().setBeanClass(ValidatingMongoEventListener.class);
builder.addConstructorArgValue(validator);
return builder.getBeanDefinition();
}
if (conversionsDefinition != null) {
AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition();
simpleTypesDefinition.setFactoryBeanName("customConversions");
simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder");
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
}
parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition());
ctxRef = MAPPING_CONTEXT;
}
return null;
}
private RuntimeBeanReference getValidator(Object source, ParserContext parserContext) {
if (!JSR_303_PRESENT) {
return null;
}
RootBeanDefinition validatorDef = new RootBeanDefinition(
"org.springframework.validation.beanvalidation.LocalValidatorFactoryBean");
validatorDef.setSource(source);
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
parserContext.registerBeanComponent(new BeanComponentDefinition(validatorDef, validatorName));
return new RuntimeBeanReference(validatorName);
}
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
BeanDefinition conversionsDefinition, String converterId) {
String ctxRef = element.getAttribute("mapping-context-ref");
if (StringUtils.hasText(ctxRef)) {
return ctxRef;
}
BeanComponentDefinitionBuilder componentDefinitionBuilder = new BeanComponentDefinitionBuilder(element,
parserContext);
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoMappingContext.class);
Set<String> classesToAdd = getInititalEntityClasses(element);
if (classesToAdd != null) {
mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd);
}
if (conversionsDefinition != null) {
AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition();
simpleTypesDefinition.setFactoryBeanName("customConversions");
simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder");
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
}
parseFieldNamingStrategy(element, parserContext.getReaderContext(), mappingContextBuilder);
ctxRef = converterId == null || DEFAULT_CONVERTER_BEAN_NAME.equals(converterId) ? MAPPING_CONTEXT_BEAN_NAME
: converterId + "." + MAPPING_CONTEXT_BEAN_NAME;
parserContext.registerBeanComponent(componentDefinitionBuilder.getComponent(mappingContextBuilder, ctxRef));
return ctxRef;
}
private static void parseFieldNamingStrategy(Element element, ReaderContext context, BeanDefinitionBuilder builder) {
String abbreviateFieldNames = element.getAttribute("abbreviate-field-names");
String fieldNamingStrategy = element.getAttribute("field-naming-strategy-ref");
boolean fieldNamingStrategyReferenced = StringUtils.hasText(fieldNamingStrategy);
boolean abbreviationActivated = StringUtils.hasText(abbreviateFieldNames)
&& Boolean.parseBoolean(abbreviateFieldNames);
if (fieldNamingStrategyReferenced && abbreviationActivated) {
context.error("Field name abbreviation cannot be activated if a field-naming-strategy-ref is configured!",
element);
return;
}
Object value = null;
if ("true".equals(abbreviateFieldNames)) {
value = new RootBeanDefinition(CamelCaseAbbreviatingFieldNamingStrategy.class);
} else if (fieldNamingStrategyReferenced) {
value = new RuntimeBeanReference(fieldNamingStrategy);
}
if (value != null) {
builder.addPropertyValue("fieldNamingStrategy", value);
}
}
private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) {
List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
if (customConvertersElements.size() == 1) {
Element customerConvertersElement = customConvertersElements.get(0);
ManagedList<BeanMetadataElement> converterBeans = new ManagedList<BeanMetadataElement>();
List<Element> converterElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter");
if (converterElements != null) {
for (Element listenerElement : converterElements) {
converterBeans.add(parseConverter(listenerElement, parserContext));
}
}
// Scan for Converter and GenericConverter beans in the given base-package
String packageToScan = customerConvertersElement.getAttribute(BASE_PACKAGE);
if (StringUtils.hasText(packageToScan)) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.addExcludeFilter(new NegatingFilter(new AssignableTypeFilter(Converter.class),
new AssignableTypeFilter(GenericConverter.class)));
for (BeanDefinition candidate : provider.findCandidateComponents(packageToScan)) {
converterBeans.add(candidate);
}
}
BeanDefinitionBuilder conversionsBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomConversions.class);
conversionsBuilder.addConstructorArgValue(converterBeans);
AbstractBeanDefinition conversionsBean = conversionsBuilder.getBeanDefinition();
conversionsBean.setSource(parserContext.extractSource(element));
parserContext.getRegistry().registerBeanDefinition("customConversions", conversionsBean);
parserContext.registerBeanComponent(new BeanComponentDefinition(conversionsBean, "customConversions"));
return conversionsBean;
}
@@ -156,7 +293,7 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
return null;
}
public Set<String> getInititalEntityClasses(Element element, BeanDefinitionBuilder builder) {
private static Set<String> getInititalEntityClasses(Element element) {
String basePackage = element.getAttribute(BASE_PACKAGE);
@@ -194,4 +331,53 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
"Element <converter> must specify 'ref' or contain a bean definition for the converter", element);
return null;
}
public static String createIsNewStrategyFactoryBeanDefinition(String mappingContextRef, ParserContext context,
Element element) {
BeanDefinitionBuilder mappingContextStrategyFactoryBuilder = BeanDefinitionBuilder
.rootBeanDefinition(MappingContextIsNewStrategyFactory.class);
mappingContextStrategyFactoryBuilder.addConstructorArgReference(mappingContextRef);
BeanComponentDefinitionBuilder builder = new BeanComponentDefinitionBuilder(element, context);
context.registerBeanComponent(builder.getComponent(mappingContextStrategyFactoryBuilder,
IS_NEW_STRATEGY_FACTORY_BEAN_NAME));
return IS_NEW_STRATEGY_FACTORY_BEAN_NAME;
}
/**
* {@link TypeFilter} that returns {@literal false} in case any of the given delegates matches.
*
* @author Oliver Gierke
*/
private static class NegatingFilter implements TypeFilter {
private final Set<TypeFilter> delegates;
/**
* Creates a new {@link NegatingFilter} with the given delegates.
*
* @param filters
*/
public NegatingFilter(TypeFilter... filters) {
Assert.notNull(filters);
this.delegates = new HashSet<TypeFilter>(Arrays.asList(filters));
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.filter.TypeFilter#match(org.springframework.core.type.classreading.MetadataReader, org.springframework.core.type.classreading.MetadataReaderFactory)
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeFilter delegate : delegates) {
if (delegate.match(metadataReader, metadataReaderFactory)) {
return false;
}
}
return true;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2012-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import static org.springframework.data.config.ParsingUtils.*;
import static org.springframework.data.mongodb.config.BeanNames.*;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDefinitionParser;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} to register a {@link AuditingEventListener} to transparently set auditing information on
* an entity.
*
* @author Oliver Gierke
*/
public class MongoAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return AuditingEventListener.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateId()
*/
@Override
protected boolean shouldGenerateId() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String mappingContextRef = element.getAttribute("mapping-context-ref");
if (!StringUtils.hasText(mappingContextRef)) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, new RootBeanDefinition(MongoMappingContext.class));
}
mappingContextRef = MAPPING_CONTEXT_BEAN_NAME;
}
IsNewAwareAuditingHandlerBeanDefinitionParser parser = new IsNewAwareAuditingHandlerBeanDefinitionParser(
mappingContextRef);
parser.parse(element, parserContext);
builder.addConstructorArgValue(getObjectFactoryBeanDefinition(parser.getResolvedBeanName(),
parserContext.extractSource(element)));
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import static org.springframework.beans.factory.config.BeanDefinition.*;
import static org.springframework.data.mongodb.config.BeanNames.*;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.Assert;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMongoAuditing} annotation.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableMongoAuditing.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
*/
@Override
protected String getAuditingHandlerBeanName() {
return "mongoAuditingHandler";
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
defaultDependenciesIfNecessary(registry, annotationMetadata);
super.registerBeanDefinitions(annotationMetadata, registry);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
return configureDefaultAuditHandlerAttributes(configuration, builder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEventListener.class);
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
getAuditingHandlerBeanName(), registry));
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEventListener.class.getName(), registry);
}
/**
* Register default bean definitions for a {@link MongoMappingContext} and an {@link IsNewStrategyFactory} in case we
* don't find beans with the assumed names in the registry.
*
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
* @param source the source which the registered components shall be registered with
*/
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
definition.setRole(ROLE_INFRASTRUCTURE);
definition.setSource(source);
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 by the original author(s).
* Copyright 2011-2014 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,19 @@
*/
package org.springframework.data.mongodb.config;
import static org.springframework.data.mongodb.config.BeanNames.*;
import static org.springframework.data.mongodb.config.ParsingUtils.*;
import static org.springframework.data.config.ParsingUtils.*;
import static org.springframework.data.mongodb.config.MongoParsingUtils.*;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mongodb.core.MongoFactoryBean;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.util.StringUtils;
@@ -41,52 +41,70 @@ import com.mongodb.MongoURI;
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
id = DB_FACTORY;
}
return id;
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.DB_FACTORY_BEAN_NAME;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
String uri = element.getAttribute("uri");
String mongoRef = element.getAttribute("mongo-ref");
String dbname = element.getAttribute("dbname");
BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element, parserContext);
// Common setup
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern");
setPropertyValue(dbFactoryBuilder, element, "write-concern", "writeConcern");
if (StringUtils.hasText(uri)) {
if(StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) {
parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", parserContext.extractSource(element));
if (StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) {
parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", source);
}
dbFactoryBuilder.addConstructorArgValue(getMongoUri(uri));
return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element);
}
// Defaulting
mongoRef = StringUtils.hasText(mongoRef) ? mongoRef : registerMongoBeanDefinition(element, parserContext);
dbname = StringUtils.hasText(dbname) ? dbname : "db";
dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef));
dbFactoryBuilder.addConstructorArgValue(dbname);
if (userCredentials != null) {
dbFactoryBuilder.addConstructorArgValue(userCredentials);
// Defaulting
if (StringUtils.hasText(mongoRef)) {
dbFactoryBuilder.addConstructorArgReference(mongoRef);
} else {
dbFactoryBuilder.addConstructorArgValue(registerMongoBeanDefinition(element, parserContext));
}
return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element);
dbFactoryBuilder.addConstructorArgValue(StringUtils.hasText(dbname) ? dbname : "db");
dbFactoryBuilder.addConstructorArgValue(userCredentials);
dbFactoryBuilder.addConstructorArgValue(element.getAttribute("authentication-dbname"));
BeanDefinitionBuilder writeConcernPropertyEditorBuilder = getWriteConcernPropertyEditorBuilder();
BeanComponentDefinition component = helper.getComponent(writeConcernPropertyEditorBuilder);
parserContext.registerBeanComponent(component);
return (AbstractBeanDefinition) helper.getComponentIdButFallback(dbFactoryBuilder, BeanNames.DB_FACTORY_BEAN_NAME)
.getBeanDefinition();
}
/**
@@ -97,14 +115,13 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
* @param parserContext must not be {@literal null}.
* @return
*/
private String registerMongoBeanDefinition(Element element, ParserContext parserContext) {
private BeanDefinition registerMongoBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder mongoBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class);
ParsingUtils.setPropertyValue(element, mongoBuilder, "host");
ParsingUtils.setPropertyValue(element, mongoBuilder, "port");
setPropertyValue(mongoBuilder, element, "host");
setPropertyValue(mongoBuilder, element, "port");
return BeanDefinitionReaderUtils.registerWithGeneratedName(mongoBuilder.getBeanDefinition(),
parserContext.getRegistry());
return getSourceBeanDefinition(mongoBuilder, parserContext, element);
}
/**
@@ -128,7 +145,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
return getSourceBeanDefinition(userCredentialsBuilder, context, element);
}
/**
* Creates a {@link BeanDefinition} for a {@link MongoURI}.
*
@@ -136,10 +153,10 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
* @return
*/
private BeanDefinition getMongoUri(String uri) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoURI.class);
builder.addConstructorArgValue(uri);
return builder.getBeanDefinition();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2014 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.
@@ -16,26 +16,27 @@
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.data.mongodb.repository.config.MongoRepositoryConfigParser;
/**
* {@link org.springframework.beans.factory.xml.NamespaceHandler} for Mongo DB based repositories.
* {@link org.springframework.beans.factory.xml.NamespaceHandler} for Mongo DB configuration.
*
* @author Oliver Gierke
* @author Martin Baumgartner
*/
public class MongoNamespaceHandler extends NamespaceHandlerSupport {
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
*/
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
*/
public void init() {
registerBeanDefinitionParser("repositories", new MongoRepositoryConfigParser());
registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser());
registerBeanDefinitionParser("mongo", new MongoParser());
registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser());
registerBeanDefinitionParser("jmx", new MongoJmxParser());
registerBeanDefinitionParser("auditing", new MongoAuditingBeanDefinitionParser());
registerBeanDefinitionParser("template", new MongoTemplateParser());
registerBeanDefinitionParser("gridFsTemplate", new GridFsTemplateParser());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2014 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.
@@ -13,50 +13,81 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mongodb.core.MongoFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;mongo;gt; definitions. If no name
* Parser for &lt;mongo;gt; definitions.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public class MongoParser extends AbstractSingleBeanDefinitionParser {
public class MongoParser implements BeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return MongoFactoryBean.class;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
String id = element.getAttribute("id");
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class);
ParsingUtils.setPropertyValue(builder, element, "port", "port");
ParsingUtils.setPropertyValue(builder, element, "host", "host");
ParsingUtils.setPropertyValue(builder, element, "write-concern", "writeConcern");
MongoParsingUtils.parseMongoOptions(element, builder);
MongoParsingUtils.parseReplicaSet(element, builder);
String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO_BEAN_NAME;
parserContext.pushContainingComponent(new CompositeComponentDefinition("Mongo", source));
BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId);
parserContext.registerBeanComponent(mongoComponent);
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(registerServerAddressPropertyEditor());
parserContext.registerBeanComponent(serverAddressPropertyEditor);
BeanComponentDefinition writeConcernPropertyEditor = helper.getComponent(MongoParsingUtils
.getWriteConcernPropertyEditorBuilder());
parserContext.registerBeanComponent(writeConcernPropertyEditor);
parserContext.popAndRegisterContainingComponent();
return mongoComponent.getBeanDefinition();
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
/**
* One should only register one bean definition but want to have the convenience of using
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
* container.
*/
private BeanDefinitionBuilder registerServerAddressPropertyEditor() {
ParsingUtils.setPropertyValue(element, builder, "port", "port");
ParsingUtils.setPropertyValue(element, builder, "host", "host");
ParsingUtils.setPropertyValue(element, builder, "write-concern", "writeConcern");
ParsingUtils.parseMongoOptions(parserContext, element, builder);
ParsingUtils.parseReplicaSet(parserContext, element, builder);
Map<String, String> customEditors = new ManagedMap<String, String>();
customEditors.put("com.mongodb.ServerAddress[]",
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String name = super.resolveId(element, definition, parserContext);
if (!StringUtils.hasText(name)) {
name = "mongo";
}
return name;
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import static org.springframework.data.config.ParsingUtils.*;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.data.mongodb.core.MongoOptionsFactoryBean;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Utility methods for {@link BeanDefinitionParser} implementations for MongoDB.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
*/
abstract class MongoParsingUtils {
private MongoParsingUtils() {
}
/**
* Parses the mongo replica-set element.
*
* @param parserContext the parser context
* @param element the mongo element
* @param mongoBuilder the bean definition builder to populate
* @return
*/
static void parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) {
setPropertyValue(mongoBuilder, element, "replica-set", "replicaSetSeeds");
}
/**
* Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes.
*
* @return true if parsing actually occured, false otherwise
*/
static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBuilder) {
Element optionsElement = DomUtils.getChildElementByTagName(element, "options");
if (optionsElement == null) {
return false;
}
BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoOptionsFactoryBean.class);
setPropertyValue(optionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost");
setPropertyValue(optionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier",
"threadsAllowedToBlockForConnectionMultiplier");
setPropertyValue(optionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime");
setPropertyValue(optionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout");
setPropertyValue(optionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout");
setPropertyValue(optionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive");
setPropertyValue(optionsDefBuilder, optionsElement, "auto-connect-retry", "autoConnectRetry");
setPropertyValue(optionsDefBuilder, optionsElement, "max-auto-connect-retry-time", "maxAutoConnectRetryTime");
setPropertyValue(optionsDefBuilder, optionsElement, "write-number", "writeNumber");
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;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link WriteConcernPropertyEditor}.
*
* @return
*/
static BeanDefinitionBuilder getWriteConcernPropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
customEditors.put("com.mongodb.WriteConcern", WriteConcernPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import static org.springframework.data.config.ParsingUtils.*;
import static org.springframework.data.mongodb.config.MongoParsingUtils.*;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} to parse {@code template} elements into {@link BeanDefinition}s.
*
* @author Martin Baumgartner
* @author Oliver Gierke
*/
class MongoTemplateParser extends AbstractBeanDefinitionParser {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.MONGO_TEMPLATE_BEAN_NAME;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
String converterRef = element.getAttribute("converter-ref");
String dbFactoryRef = element.getAttribute("db-factory-ref");
BeanDefinitionBuilder mongoTemplateBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoTemplate.class);
setPropertyValue(mongoTemplateBuilder, element, "write-concern", "writeConcern");
if (StringUtils.hasText(dbFactoryRef)) {
mongoTemplateBuilder.addConstructorArgReference(dbFactoryRef);
} else {
mongoTemplateBuilder.addConstructorArgReference(BeanNames.DB_FACTORY_BEAN_NAME);
}
if (StringUtils.hasText(converterRef)) {
mongoTemplateBuilder.addConstructorArgReference(converterRef);
}
BeanDefinitionBuilder writeConcernPropertyEditorBuilder = getWriteConcernPropertyEditorBuilder();
BeanComponentDefinition component = helper.getComponent(writeConcernPropertyEditorBuilder);
parserContext.registerBeanComponent(component);
return (AbstractBeanDefinition) helper.getComponentIdButFallback(mongoTemplateBuilder,
BeanNames.MONGO_TEMPLATE_BEAN_NAME).getBeanDefinition();
}
}

View File

@@ -1,130 +0,0 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.mongodb.core.MongoOptionsFactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.mongodb.ServerAddress;
abstract class ParsingUtils {
/**
* Parses the mongo replica-set element.
*
* @param parserContext the parser context
* @param element the mongo element
* @param mongoBuilder the bean definition builder to populate
* @return true if parsing actually occured, false otherwise
*/
static boolean parseReplicaSet(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) {
String replicaSetString = element.getAttribute("replica-set");
if (StringUtils.hasText(replicaSetString)) {
ManagedList<Object> serverAddresses = new ManagedList<Object>();
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
for (String element2 : replicaSetStringArray) {
String[] hostAndPort = StringUtils.delimitedListToStringArray(element2, ":");
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(ServerAddress.class);
defBuilder.addConstructorArgValue(hostAndPort[0]);
defBuilder.addConstructorArgValue(hostAndPort[1]);
serverAddresses.add(defBuilder.getBeanDefinition());
}
if (!serverAddresses.isEmpty()) {
mongoBuilder.addPropertyValue("replicaSetSeeds", serverAddresses);
}
}
return true;
}
/**
* Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes.
*
* @return true if parsing actually occured, false otherwise
*/
static boolean parseMongoOptions(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) {
Element optionsElement = DomUtils.getChildElementByTagName(element, "options");
if (optionsElement == null) {
return false;
}
BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoOptionsFactoryBean.class);
setPropertyValue(optionsElement, optionsDefBuilder, "connections-per-host", "connectionsPerHost");
setPropertyValue(optionsElement, optionsDefBuilder, "threads-allowed-to-block-for-connection-multiplier",
"threadsAllowedToBlockForConnectionMultiplier");
setPropertyValue(optionsElement, optionsDefBuilder, "max-wait-time", "maxWaitTime");
setPropertyValue(optionsElement, optionsDefBuilder, "connect-timeout", "connectTimeout");
setPropertyValue(optionsElement, optionsDefBuilder, "socket-timeout", "socketTimeout");
setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive");
setPropertyValue(optionsElement, optionsDefBuilder, "auto-connect-retry", "autoConnectRetry");
setPropertyValue(optionsElement, optionsDefBuilder, "max-auto-connect-retry-time", "maxAutoConnectRetryTime");
setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber");
setPropertyValue(optionsElement, optionsDefBuilder, "write-timeout", "writeTimeout");
setPropertyValue(optionsElement, optionsDefBuilder, "write-fsync", "writeFsync");
setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk");
mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition());
return true;
}
static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName, String propertyName) {
String attr = element.getAttribute(attrName);
if (StringUtils.hasText(attr)) {
builder.addPropertyValue(propertyName, attr);
}
}
/**
* Sets the property with the given attribute name on the given {@link BeanDefinitionBuilder} to the value of the
* attribute with the given name.
*
* @param element must not be {@literal null}.
* @param builder must not be {@literal null}.
* @param attrName must not be {@literal null} or empty.
*/
static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName) {
String attr = element.getAttribute(attrName);
if (StringUtils.hasText(attr)) {
builder.addPropertyValue(attrName, attr);
}
}
/**
* Returns the {@link BeanDefinition} built by the given {@link BeanDefinitionBuilder} enriched with source
* information derived from the given {@link Element}.
*
* @param builder must not be {@literal null}.
* @param context must not be {@literal null}.
* @param element must not be {@literal null}.
* @return
*/
static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context, Element element) {
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(context.extractSource(element));
return definition;
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.ServerAddress;
/**
* Parse a {@link String} to a {@link ServerAddress} array. The format is host1:port1,host2:port2,host3:port3.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class ServerAddressPropertyEditor extends PropertyEditorSupport {
/**
* A port is a number without a leading 0 at the end of the address that is proceeded by just a single :.
*/
private static final String HOST_PORT_SPLIT_PATTERN = "(?<!:):(?=[123456789]\\d*$)";
private static final String COULD_NOT_PARSE_ADDRESS_MESSAGE = "Could not parse address {} '{}'. Check your replica set configuration!";
private static final Logger LOG = LoggerFactory.getLogger(ServerAddressPropertyEditor.class);
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(String replicaSetString) {
if (!StringUtils.hasText(replicaSetString)) {
setValue(null);
return;
}
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
Set<ServerAddress> serverAddresses = new HashSet<ServerAddress>(replicaSetStringArray.length);
for (String element : replicaSetStringArray) {
ServerAddress address = parseServerAddress(element);
if (address != null) {
serverAddresses.add(address);
}
}
if (serverAddresses.isEmpty()) {
throw new IllegalArgumentException(
"Could not resolve at least one server of the replica set configuration! Validate your config!");
}
setValue(serverAddresses.toArray(new ServerAddress[serverAddresses.size()]));
}
/**
* Parses the given source into a {@link ServerAddress}.
*
* @param source
* @return the
*/
private ServerAddress parseServerAddress(String source) {
if (!StringUtils.hasText(source)) {
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source);
return null;
}
String[] hostAndPort = extractHostAddressAndPort(source.trim());
if (hostAndPort.length > 2) {
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source);
return null;
}
try {
InetAddress hostAddress = InetAddress.getByName(hostAndPort[0]);
Integer port = hostAndPort.length == 1 ? null : Integer.parseInt(hostAndPort[1]);
return port == null ? new ServerAddress(hostAddress) : new ServerAddress(hostAddress, port);
} catch (UnknownHostException e) {
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "host", hostAndPort[0]);
} catch (NumberFormatException e) {
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "port", hostAndPort[1]);
}
return null;
}
/**
* Extract the host and port from the given {@link String}.
*
* @param addressAndPortSource must not be {@literal null}.
* @return
*/
private String[] extractHostAddressAndPort(String addressAndPortSource) {
Assert.notNull(addressAndPortSource, "Address and port source must not be null!");
String[] hostAndPort = addressAndPortSource.split(HOST_PORT_SPLIT_PATTERN);
String hostAddress = hostAndPort[0];
if (isHostAddressInIPv6BracketNotation(hostAddress)) {
hostAndPort[0] = hostAddress.substring(1, hostAddress.length() - 1);
}
return hostAndPort;
}
private boolean isHostAddressInIPv6BracketNotation(String hostAddress) {
return hostAddress.startsWith("[") && hostAddress.endsWith("]");
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.core.convert.converter.Converter;
import com.mongodb.WriteConcern;
/**
* Converter to create {@link WriteConcern} instances from String representations.
*
* @author Oliver Gierke
*/
public class StringToWriteConcernConverter implements Converter<String, WriteConcern> {
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public WriteConcern convert(String source) {
WriteConcern writeConcern = WriteConcern.valueOf(source);
return writeConcern != null ? writeConcern : new WriteConcern(source);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import com.mongodb.WriteConcern;
/**
* Parse a string to a {@link WriteConcern}. If it is a well know {@link String} as identified by the
* {@link WriteConcern#valueOf(String)}, use the well known {@link WriteConcern} value, otherwise pass the string as is
* to the constructor of the write concern. There is no support for other constructor signatures when parsing from a
* string value.
*
* @author Mark Pollack
*/
public class WriteConcernPropertyEditor extends PropertyEditorSupport {
/**
* Parse a string to a List<ServerAddress>
*/
@Override
public void setAsText(String writeConcernString) {
WriteConcern writeConcern = WriteConcern.valueOf(writeConcernString);
if (writeConcern != null) {
// have a well known string
setValue(writeConcern);
} else {
// pass on the string to the constructor
setValue(new WriteConcern(writeConcernString));
}
}
}

View File

@@ -22,7 +22,7 @@ import com.mongodb.DBCursor;
*
* @author Oliver Gierke
*/
public interface CursorPreparer {
interface CursorPreparer {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import static org.springframework.data.domain.Sort.Direction.*;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* Default implementation of {@link IndexOperations}.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Komi Innocent
* @author Christoph Strobl
*/
public class DefaultIndexOperations implements IndexOperations {
private static final Double ONE = Double.valueOf(1);
private static final Double MINUS_ONE = Double.valueOf(-1);
private final MongoOperations mongoOperations;
private final String collectionName;
/**
* Creates a new {@link DefaultIndexOperations}.
*
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
Assert.notNull(collectionName, "Collection name can not be null!");
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
*/
public void ensureIndex(final IndexDefinition indexDefinition) {
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject indexOptions = indexDefinition.getIndexOptions();
if (indexOptions != null) {
collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions);
} else {
collection.ensureIndex(indexDefinition.getIndexKeys());
}
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropIndex(java.lang.String)
*/
public void dropIndex(final String name) {
mongoOperations.execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.dropIndex(name);
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropAllIndexes()
*/
public void dropAllIndexes() {
dropIndex("*");
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#resetIndexCache()
*/
public void resetIndexCache() {
mongoOperations.execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.resetIndexCache();
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#getIndexInfo()
*/
public List<IndexInfo> getIndexInfo() {
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List<DBObject> dbObjectList = collection.getIndexInfo();
return getIndexData(dbObjectList);
}
private List<IndexInfo> getIndexData(List<DBObject> dbObjectList) {
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
for (DBObject ix : dbObjectList) {
DBObject keyDbObject = (DBObject) ix.get("key");
int numberOfElements = keyDbObject.keySet().size();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if ("2d".equals(value)) {
indexFields.add(IndexField.geo(key));
} else if ("text".equals(value)) {
DBObject weights = (DBObject) ix.get("weights");
for (String fieldName : weights.keySet()) {
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
}
} else {
Double keyValue = new Double(value.toString());
if (ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, ASC));
} else if (MINUS_ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, DESC));
}
}
}
String name = ix.get("name").toString();
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
}
return indexInfoList;
}
});
}
}

View File

@@ -21,15 +21,16 @@ import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* An interface used by {@link MongoTemplate} for processing documents returned from a MongoDB query on a per-document basis.
* Implementations of this interface perform the actual work of prcoessing each document but don't need to worry about
* exception handling. {@MongoException}s will be caught and translated by the calling MongoTemplate
* An interface used by {@link MongoTemplate} for processing documents returned from a MongoDB query on a per-document
* basis. Implementations of this interface perform the actual work of prcoessing each document but don't need to worry
* about exception handling. {@MongoException}s will be caught and translated by the calling
* MongoTemplate
*
* An DocumentCallbackHandler is typically stateful: It keeps the result state within the object, to be available later for later
* inspection.
* An DocumentCallbackHandler is typically stateful: It keeps the result state within the object, to be available later
* for later inspection.
*
* @author Mark Pollack
*
*
*/
public interface DocumentCallbackHandler {

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2010-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
public class FindAndModifyOptions {
boolean returnNew;
boolean upsert;
boolean remove;
/**
* Static factory method to create a FindAndModifyOptions instance
*
* @return a new instance
*/
public static FindAndModifyOptions options() {
return new FindAndModifyOptions();
}
public FindAndModifyOptions returnNew(boolean returnNew) {
this.returnNew = returnNew;
return this;
}
public FindAndModifyOptions upsert(boolean upsert) {
this.upsert = upsert;
return this;
}
public FindAndModifyOptions remove(boolean remove) {
this.remove = remove;
return this;
}
public boolean isReturnNew() {
return returnNew;
}
public boolean isUpsert() {
return upsert;
}
public boolean isRemove() {
return remove;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
/**
* Index operations on a collection.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public interface IndexOperations {
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition must not be {@literal null}.
*/
void ensureIndex(IndexDefinition indexDefinition);
/**
* Drops an index from this collection.
*
* @param name name of index to drop
*/
void dropIndex(String name);
/**
* Drops all indices from this collection.
*/
void dropAllIndexes();
/**
* Clears all indices that have not yet been applied to this collection.
*/
void resetIndexCache();
/**
* Returns the index information on the collection.
*
* @return index information on the collection
*/
List<IndexInfo> getIndexInfo();
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2011-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
/**
* Represents an action taken against the collection. Used by {@link WriteConcernResolver} to determine a custom
* {@link WriteConcern} based on this information.
* <ul>
* <li>INSERT, SAVE have null query</li>
* <li>REMOVE has null document</li>
* <li>INSERT_LIST has null entityType, document, and query</li>
* </ul>
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public class MongoAction {
private final String collectionName;
private final WriteConcern defaultWriteConcern;
private final Class<?> entityType;
private final MongoActionOperation mongoActionOperation;
private final DBObject query;
private final DBObject document;
/**
* Create an instance of a {@link MongoAction}.
*
* @param defaultWriteConcern the default write concern.
* @param mongoActionOperation action being taken against the collection
* @param collectionName the collection name, must not be {@literal null} or empty.
* @param entityType the POJO that is being operated against
* @param document the converted DBObject from the POJO or Spring Update object
* @param query the converted DBOjbect from the Spring Query object
*/
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
String collectionName, Class<?> entityType, DBObject document, DBObject query) {
Assert.hasText(collectionName, "Collection name must not be null or empty!");
this.defaultWriteConcern = defaultWriteConcern;
this.mongoActionOperation = mongoActionOperation;
this.collectionName = collectionName;
this.entityType = entityType;
this.query = query;
this.document = document;
}
public String getCollectionName() {
return collectionName;
}
public WriteConcern getDefaultWriteConcern() {
return defaultWriteConcern;
}
/**
* @deprecated use {@link #getEntityType()} instead.
*/
@Deprecated
public Class<?> getEntityClass() {
return entityType;
}
public Class<?> getEntityType() {
return entityType;
}
public MongoActionOperation getMongoActionOperation() {
return mongoActionOperation;
}
public DBObject getQuery() {
return query;
}
public DBObject getDocument() {
return document;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2011-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
/**
* Enumeration for operations on a collection. Used with {@link MongoAction} to help determine the WriteConcern to use
* for a given mutating operation
*
* @author Mark Pollack
* @author Oliver Gierke
* @see MongoAction
*/
public enum MongoActionOperation {
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-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,32 +15,30 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.DB;
import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import com.mongodb.DB;
import com.mongodb.Mongo;
/**
* Mongo server administration exposed via JMX annotations
*
* @author Mark Pollack
* @author Thomas Darimont
*/
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
/**
* Logger available to subclasses
*/
protected final Log logger = LogFactory.getLog(getClass());
private Mongo mongo;
private final Mongo mongo;
private String username;
private String password;
private String authenticationDatabaseName;
public MongoAdmin(Mongo mongo) {
Assert.notNull(mongo);
this.mongo = mongo;
}
@@ -83,11 +81,19 @@ public class MongoAdmin implements MongoAdminOperations {
* @param password The password to use
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Sets the authenticationDatabaseName to use to authenticate with the Mongo database.
*
* @param authenticationDatabaseName The authenticationDatabaseName to use.
*/
public void setAuthenticationDatabaseName(String authenticationDatabaseName) {
this.authenticationDatabaseName = authenticationDatabaseName;
}
DB getDB(String databaseName) {
return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
return MongoDbUtils.getDB(mongo, databaseName, new UserCredentials(username, password), authenticationDatabaseName);
}
}

View File

@@ -1,16 +1,34 @@
/*
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import org.springframework.jmx.export.annotation.ManagedOperation;
/**
* @author Mark Pollack
* @author Oliver Gierke
*/
public interface MongoAdminOperations {
@ManagedOperation
public abstract void dropDatabase(String databaseName);
void dropDatabase(String databaseName);
@ManagedOperation
public abstract void createDatabase(String databaseName);
void createDatabase(String databaseName);
@ManagedOperation
public abstract String getDatabaseStats(String databaseName);
}
String getDatabaseStats(String databaseName);
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.util.Assert;
import com.mongodb.WriteResult;
/**
* Mongo-specific {@link DataIntegrityViolationException}.
*
* @author Oliver Gierke
*/
public class MongoDataIntegrityViolationException extends DataIntegrityViolationException {
private static final long serialVersionUID = -186980521176764046L;
private final WriteResult writeResult;
private final MongoActionOperation actionOperation;
/**
* Creates a new {@link MongoDataIntegrityViolationException} using the given message and {@link WriteResult}.
*
* @param message the exception message
* @param writeResult the {@link WriteResult} that causes the exception, must not be {@literal null}.
* @param actionOperation the {@link MongoActionOperation} that caused the exception, must not be {@literal null}.
*/
public MongoDataIntegrityViolationException(String message, WriteResult writeResult,
MongoActionOperation actionOperation) {
super(message);
Assert.notNull(writeResult, "WriteResult must not be null!");
Assert.notNull(actionOperation, "MongoActionOperation must not be null!");
this.writeResult = writeResult;
this.actionOperation = actionOperation;
}
/**
* Returns the {@link WriteResult} that caused the exception.
*
* @return the writeResult
*/
public WriteResult getWriteResult() {
return writeResult;
}
/**
* Returns the {@link MongoActionOperation} in which the current exception occured.
*
* @return the actionOperation
*/
public MongoActionOperation getActionOperation() {
return actionOperation;
}
}

View File

@@ -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,28 +15,30 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.DB;
import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import com.mongodb.DB;
import com.mongodb.Mongo;
/**
* Helper class featuring helper methods for internal MongoDb classes.
* <p/>
* <p>
* Mainly intended for internal use within the framework.
* Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the
* framework.
*
* @author Thomas Risberg
* @author Graeme Rocher
* @author Oliver Gierke
* @author Randy Watler
* @author Thomas Darimont
* @since 1.0
*/
public abstract class MongoDbUtils {
private static final Log LOGGER = LogFactory.getLog(MongoDbUtils.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDbUtils.class);
/**
* Private constructor to prevent instantiation.
@@ -48,74 +50,102 @@ public abstract class MongoDbUtils {
/**
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
*
* @param mongo The {@link Mongo} instance
* @param databaseName The database name
* @return The {@link DB} connection
* @param mongo the {@link Mongo} instance, must not be {@literal null}.
* @param databaseName the database name, must not be {@literal null} or empty.
* @return the {@link DB} connection
*/
public static DB getDB(Mongo mongo, String databaseName) {
return doGetDB(mongo, databaseName, null, null, true);
return doGetDB(mongo, databaseName, UserCredentials.NO_CREDENTIALS, true, databaseName);
}
/**
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
*
* @param mongo The {@link Mongo} instance
* @param databaseName The database name
* @param username The username to authenticate with
* @param password The password to authenticate with
* @return The {@link DB} connection
* @param mongo the {@link Mongo} instance, must not be {@literal null}.
* @param databaseName the database name, must not be {@literal null} or empty.
* @param credentials the credentials to use, must not be {@literal null}.
* @return the {@link DB} connection
*/
public static DB getDB(Mongo mongo, String databaseName, String username, char[] password) {
return doGetDB(mongo, databaseName, username, password, true);
public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials) {
return getDB(mongo, databaseName, credentials, databaseName);
}
public static DB doGetDB(Mongo mongo, String databaseName, String username, char[] password, boolean allowCreate) {
Assert.notNull(mongo, "No Mongo instance specified");
public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials,
String authenticationDatabaseName) {
Assert.notNull(mongo, "No Mongo instance specified!");
Assert.hasText(databaseName, "Database name must be given!");
Assert.notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!");
Assert.hasText(authenticationDatabaseName, "Authentication database name must not be null or empty!");
return doGetDB(mongo, databaseName, credentials, true, authenticationDatabaseName);
}
private static DB doGetDB(Mongo mongo, String databaseName, UserCredentials credentials, boolean allowCreate,
String authenticationDatabaseName) {
DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
if (dbHolder != null && !dbHolder.isEmpty()) {
// pre-bound Mongo DB
DB db = null;
if (TransactionSynchronizationManager.isSynchronizationActive() && dbHolder.doesNotHoldNonDefaultDB()) {
// Spring transaction management is active ->
db = dbHolder.getDB();
if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
dbHolder.setSynchronizedWithTransaction(true);
}
// Do we have a populated holder and TX sync active?
if (dbHolder != null && !dbHolder.isEmpty() && TransactionSynchronizationManager.isSynchronizationActive()) {
DB db = dbHolder.getDB(databaseName);
// DB found but not yet synchronized
if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
LOGGER.debug("Registering Spring transaction synchronization for existing MongoDB {}.", databaseName);
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
dbHolder.setSynchronizedWithTransaction(true);
}
if (db != null) {
return db;
}
}
LOGGER.trace("Getting Mongo Database name=[" + databaseName + "]");
DB db = mongo.getDB(databaseName);
// Lookup fresh database instance
LOGGER.debug("Getting Mongo Database name=[{}]", databaseName);
boolean credentialsGiven = username != null && password != null;
if (credentialsGiven && !db.isAuthenticated()) {
// Note, can only authenticate once against the same com.mongodb.DB object.
if (!db.authenticate(username, password)) {
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
+ "], username = [" + username + "], password = [" + new String(password) + "]", databaseName, username,
password);
DB db = mongo.getDB(databaseName);
boolean credentialsGiven = credentials.hasUsername() && credentials.hasPassword();
DB authDb = databaseName.equals(authenticationDatabaseName) ? db : mongo.getDB(authenticationDatabaseName);
synchronized (authDb) {
if (credentialsGiven && !authDb.isAuthenticated()) {
String username = credentials.getUsername();
String password = credentials.hasPassword() ? credentials.getPassword() : null;
if (!authDb.authenticate(username, password == null ? null : password.toCharArray())) {
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], "
+ credentials.toString(), databaseName, credentials);
}
}
}
// Use same Session for further Mongo actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
// TX sync active, bind new database to thread
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// We're within a Spring-managed transaction, possibly from JtaTransactionManager.
LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
LOGGER.debug("Registering Spring transaction synchronization for MongoDB instance {}.", databaseName);
DbHolder holderToUse = dbHolder;
if (holderToUse == null) {
holderToUse = new DbHolder(db);
holderToUse = new DbHolder(databaseName, db);
} else {
holderToUse.addDB(db);
holderToUse.addDB(databaseName, db);
}
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
holderToUse.setSynchronizedWithTransaction(true);
// synchronize holder only if not yet synchronized
if (!holderToUse.isSynchronizedWithTransaction()) {
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
holderToUse.setSynchronizedWithTransaction(true);
}
if (holderToUse != dbHolder) {
TransactionSynchronizationManager.bindResource(mongo, holderToUse);
}
@@ -139,11 +169,12 @@ public abstract class MongoDbUtils {
* @return whether the DB is transactional
*/
public static boolean isDBTransactional(DB db, Mongo mongo) {
if (mongo == null) {
return false;
}
DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
return (dbHolder != null && dbHolder.containsDB(db));
return dbHolder != null && dbHolder.containsDB(db);
}
/**
@@ -152,6 +183,7 @@ public abstract class MongoDbUtils {
* @param db the DB to close (may be <code>null</code>)
*/
public static void closeDB(DB db) {
if (db != null) {
LOGGER.debug("Closing Mongo DB object");
try {

View File

@@ -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,12 +15,6 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.MongoException;
import com.mongodb.MongoException.CursorNotFound;
import com.mongodb.MongoException.DuplicateKey;
import com.mongodb.MongoException.Network;
import com.mongodb.MongoInternalException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DuplicateKeyException;
@@ -29,37 +23,63 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import com.mongodb.MongoCursorNotFoundException;
import com.mongodb.MongoException;
import com.mongodb.MongoException.CursorNotFound;
import com.mongodb.MongoException.DuplicateKey;
import com.mongodb.MongoException.Network;
import com.mongodb.MongoInternalException;
import com.mongodb.MongoServerSelectionException;
import com.mongodb.MongoSocketException;
import com.mongodb.MongoTimeoutException;
/**
* Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate
* exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
*
* @author Oliver Gierke
* @author Michal Vich
*/
public class MongoExceptionTranslator implements PersistenceExceptionTranslator {
/*
* (non-Javadoc)
*
* @see org.springframework.dao.support.PersistenceExceptionTranslator#
* translateExceptionIfPossible(java.lang.RuntimeException)
*/
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
// Check for well-known MongoException subclasses.
// All other MongoExceptions
if (ex instanceof DuplicateKey) {
if (ex instanceof DuplicateKey || ex instanceof DuplicateKeyException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof Network) {
if (ex instanceof Network || ex instanceof MongoSocketException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof CursorNotFound) {
if (ex instanceof CursorNotFound || ex instanceof MongoCursorNotFoundException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof MongoServerSelectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof MongoTimeoutException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof MongoInternalException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
// All other MongoExceptions
if (ex instanceof MongoException) {
int code = ((MongoException) ex).getCode();
if (code == 11000 || code == 11001) {
throw new DuplicateKeyException(ex.getMessage(), ex);
} else if (code == 12000 || code == 13440) {
@@ -69,9 +89,6 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
}
return new UncategorizedMongoDbException(ex.getMessage(), ex);
}
if (ex instanceof MongoInternalException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
// If we get here, we have an exception that resulted from user code,
// rather than the persistence provider, so we return null to indicate

View File

@@ -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.
@@ -13,17 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
@@ -35,14 +39,13 @@ import com.mongodb.WriteConcern;
* @author Thomas Risberg
* @author Graeme Rocher
* @author Oliver Gierke
* @author Thomas Darimont
* @since 1.0
*/
public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptionTranslator {
public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, DisposableBean,
PersistenceExceptionTranslator {
/**
* Logger, available to subclasses.
*/
protected final Log logger = LogFactory.getLog(getClass());
private Mongo mongo;
private MongoOptions mongoOptions;
private String host;
@@ -57,12 +60,39 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
this.mongoOptions = mongoOptions;
}
public void setReplicaSetSeeds(List<ServerAddress> replicaSetSeeds) {
this.replicaSetSeeds = replicaSetSeeds;
public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) {
this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds);
}
public void setReplicaPair(List<ServerAddress> replicaPair) {
this.replicaPair = replicaPair;
/**
* @deprecated use {@link #setReplicaSetSeeds(ServerAddress[])} instead
*
* @param replicaPair
*/
@Deprecated
public void setReplicaPair(ServerAddress[] replicaPair) {
this.replicaPair = filterNonNullElementsAsList(replicaPair);
}
/**
* @param elements the elements to filter <T>
* @return a new unmodifiable {@link List#} from the given elements without nulls
*/
private <T> List<T> filterNonNullElementsAsList(T[] elements) {
if (elements == null) {
return Collections.emptyList();
}
List<T> candidateElements = new ArrayList<T>();
for (T element : elements) {
if (element != null) {
candidateElements.add(element);
}
}
return Collections.unmodifiableList(candidateElements);
}
public void setHost(String host) {
@@ -87,34 +117,7 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
}
public Mongo getObject() throws Exception {
Mongo mongo;
ServerAddress defaultOptions = new ServerAddress();
if (mongoOptions == null) {
mongoOptions = new MongoOptions();
}
if (replicaPair != null) {
if (replicaPair.size() < 2) {
throw new CannotGetMongoDbConnectionException("A replica pair must have two server entries");
}
mongo = new Mongo(replicaPair.get(0), replicaPair.get(1), mongoOptions);
} else if (replicaSetSeeds != null) {
mongo = new Mongo(replicaSetSeeds, mongoOptions);
} else {
String mongoHost = host != null ? host : defaultOptions.getHost();
mongo = port != null ? new Mongo(new ServerAddress(mongoHost, port), mongoOptions) : new Mongo(mongoHost,
mongoOptions);
}
if (writeConcern != null) {
mongo.setWriteConcern(writeConcern);
}
return mongo;
}
/*
@@ -140,4 +143,50 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@SuppressWarnings("deprecation")
public void afterPropertiesSet() throws Exception {
Mongo mongo;
ServerAddress defaultOptions = new ServerAddress();
if (mongoOptions == null) {
mongoOptions = new MongoOptions();
}
if (!isNullOrEmpty(replicaPair)) {
if (replicaPair.size() < 2) {
throw new CannotGetMongoDbConnectionException("A replica pair must have two server entries");
}
mongo = new Mongo(replicaPair.get(0), replicaPair.get(1), mongoOptions);
} else if (!isNullOrEmpty(replicaSetSeeds)) {
mongo = new Mongo(replicaSetSeeds, mongoOptions);
} else {
String mongoHost = StringUtils.hasText(host) ? host : defaultOptions.getHost();
mongo = port != null ? new Mongo(new ServerAddress(mongoHost, port), mongoOptions) : new Mongo(mongoHost,
mongoOptions);
}
if (writeConcern != null) {
mongo.setWriteConcern(writeConcern);
}
this.mongo = mongo;
}
private boolean isNullOrEmpty(Collection<?> elements) {
return elements == null || elements.isEmpty();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
this.mongo.close();
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright 2010-2011 the original author or authors.
*
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
@@ -19,21 +19,26 @@ import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.CommandResult;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
/**
* Interface that specifies a basic set of MongoDB operations. Implemented by {@link MongoTemplate}. Not often used but
* a useful option for extensibility and testability (as it can be easily mocked, stubbed, or be the target of a JDK
@@ -42,12 +47,18 @@ import org.springframework.data.mongodb.core.query.Update;
* @author Thomas Risberg
* @author Mark Pollack
* @author Oliver Gierke
* @author Tobias Trelle
* @author Chuong Ngo
* @author Christoph Strobl
* @author Thomas Darimont
*/
@SuppressWarnings("deprecation")
public interface MongoOperations {
/**
* The collection name used for the specified class by this template.
*
* @param entityClass must not be {@literal null}.
* @return
*/
String getCollectionName(Class<?> entityClass);
@@ -68,7 +79,7 @@ public interface MongoOperations {
* @param command a MongoDB command
*/
CommandResult executeCommand(DBObject command);
/**
* Execute a MongoDB command. Any errors that result from executing this command will be converted into Spring's DAO
* exception hierarchy.
@@ -77,7 +88,7 @@ public interface MongoOperations {
* @param options query options to use
*/
CommandResult executeCommand(DBObject command, int options);
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler.
*
@@ -87,18 +98,6 @@ public interface MongoOperations {
* @param dch the handler that will extract results, one document at a time
*/
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch);
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler using the
* provided CursorPreparer.
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification
* @param collectionName name of the collection to retrieve the objects from
* @param dch the handler that will extract results, one document at a time
* @param preparer allows for customization of the DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
*/
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer);
/**
* Executes a {@link DbCallback} translating any exceptions as necessary.
@@ -237,11 +236,25 @@ public interface MongoOperations {
*/
void dropCollection(String collectionName);
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection
*/
IndexOperations indexOps(String collectionName);
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection associated with the given entity class
*/
IndexOperations indexOps(Class<?> entityClass);
/**
* Query for a list of objects of type T from the collection used by the entity class.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* If your collection does not contain a homogeneous collection of types, this operation will not be an efficient way
* to map objects since the test for class type is done in the client and not on the server.
@@ -255,7 +268,7 @@ public interface MongoOperations {
* Query for a list of objects of type T from the specified collection.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* If your collection does not contain a homogeneous collection of types, this operation will not be an efficient way
* to map objects since the test for class type is done in the client and not on the server.
@@ -265,11 +278,90 @@ public interface MongoOperations {
* @return the converted collection
*/
<T> List<T> findAll(Class<T> entityClass, String collectionName);
/**
* Execute a map-reduce operation. The map-reduce operation will be formed with an output type of INLINE
* Execute a group operation over the entire collection. The group operation entity class should match the 'shape' of
* the returned object that takes int account the initial document structure as well as any finalize functions.
*
* @param criteria The criteria that restricts the row that are considered for grouping. If not specified all rows are
* considered.
* @param inputCollectionName the collection where the group operation will read from
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
* reduce function.
* @param entityClass The parameterized type of the returned list
* @return The results of the group operation
*/
<T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/**
* Execute a group operation restricting the rows to those which match the provided Criteria. The group operation
* entity class should match the 'shape' of the returned object that takes int account the initial document structure
* as well as any finalize functions.
*
* @param criteria The criteria that restricts the row that are considered for grouping. If not specified all rows are
* considered.
* @param inputCollectionName the collection where the group operation will read from
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
* reduce function.
* @param entityClass The parameterized type of the returned list
* @return The results of the group operation
*/
<T> GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/**
* Execute an aggregation operation. The raw results will be mapped to the given entity class. The name of the
* inputCollection is derived from the inputType of the aggregation.
*
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
* {@literal null}.
* @param collectionName The name of the input collection to use for the aggreation.
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
* @return The results of the aggregation operation.
* @since 1.3
*/
<O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, String collectionName, Class<O> outputType);
/**
* Execute an aggregation operation. The raw results will be mapped to the given entity class. The name of the
* inputCollection is derived from the inputType of the aggregation.
*
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
* {@literal null}.
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
* @return The results of the aggregation operation.
* @since 1.3
*/
<O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType);
/**
* Execute an aggregation operation. The raw results will be mapped to the given entity class.
*
* @param aggregation The {@link Aggregation} specification holding the aggregation operations, must not be
* {@literal null}.
* @param inputType the inputType where the aggregation operation will read from, must not be {@literal null} or
* empty.
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
* @return The results of the aggregation operation.
* @since 1.3
*/
<O> AggregationResults<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType);
/**
* Execute an aggregation operation. The raw results will be mapped to the given entity class.
*
* @param aggregation The {@link Aggregation} specification holding the aggregation operations, must not be
* {@literal null}.
* @param collectionName the collection where the aggregation operation will read from, must not be {@literal null} or
* empty.
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
* @return The results of the aggregation operation.
* @since 1.3
*/
<O> AggregationResults<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType);
/**
* Execute a map-reduce operation. The map-reduce operation will be formed with an output type of INLINE
*
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
@@ -277,11 +369,12 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
Class<T> entityClass);
/**
* Execute a map-reduce operation that takes additional map-reduce options.
*
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
@@ -289,36 +382,40 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, MapReduceOptions mapReduceOptions, Class<T> entityClass );
/**
* Execute a map-reduce operation that takes a query. The map-reduce operation will be formed with an output type of INLINE
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
* @param mapReduceOptions Options that specify detailed map-reduce behavior
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass );
/**
* Execute a map-reduce operation that takes a query and additional map-reduce options
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
* @param mapReduceOptions Options that specify detailed map-reduce behavior
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction, MapReduceOptions mapReduceOptions, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
MapReduceOptions mapReduceOptions, Class<T> entityClass);
/**
* Returns {@link GeoResult} for all entities matching the given {@link NearQuery}. Will consider entity mapping
* Execute a map-reduce operation that takes a query. The map-reduce operation will be formed with an output type of
* INLINE
*
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
* @param mapReduceOptions Options that specify detailed map-reduce behavior
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
Class<T> entityClass);
/**
* Execute a map-reduce operation that takes a query and additional map-reduce options
*
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
* @param mapReduceOptions Options that specify detailed map-reduce behavior
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
MapReduceOptions mapReduceOptions, Class<T> entityClass);
/**
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}. Will consider entity mapping
* information to determine the collection the query is ran against.
*
* @param near must not be {@literal null}.
@@ -328,7 +425,7 @@ public interface MongoOperations {
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass);
/**
* Returns {@link GeoResult} for all entities matching the given {@link NearQuery}.
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}.
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
@@ -338,29 +435,12 @@ public interface MongoOperations {
*/
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName);
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition
* @param entityClass class that determines the collection to use
*/
void ensureIndex(IndexDefinition indexDefinition, Class<?> entityClass);
/**
* Ensure that an index for the provided {@link IndexDefinition} exists. If not it will be created.
*
* @param index
* @param collectionName
*/
void ensureIndex(IndexDefinition indexDefinition, String collectionName);
/**
* Map the results of an ad-hoc query on the collection for the entity class to a single instance of an object of the
* specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
@@ -377,7 +457,7 @@ public interface MongoOperations {
* type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
@@ -386,16 +466,43 @@ public interface MongoOperations {
* specification
* @param entityClass the parameterized type of the returned list.
* @param collectionName name of the collection to retrieve the objects from
*
* @return the converted object
*/
<T> T findOne(Query query, Class<T> entityClass, String collectionName);
/**
* Determine result of given {@link Query} contains at least one element.
*
* @param query the {@link Query} class that specifies the criteria used to find a record.
* @param collectionName name of the collection to check for objects.
* @return
*/
boolean exists(Query query, String collectionName);
/**
* Determine result of given {@link Query} contains at least one element.
*
* @param query the {@link Query} class that specifies the criteria used to find a record.
* @param entityClass the parameterized type.
* @return
*/
boolean exists(Query query, Class<?> entityClass);
/**
* Determine result of given {@link Query} contains at least one element.
*
* @param query the {@link Query} class that specifies the criteria used to find a record.
* @param entityClass the parameterized type.
* @param collectionName name of the collection to check for objects.
* @return
*/
boolean exists(Query query, Class<?> entityClass, String collectionName);
/**
* Map the results of an ad-hoc query on the collection for the entity class to a List of the specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
@@ -411,7 +518,7 @@ public interface MongoOperations {
* Map the results of an ad-hoc query on the specified collection to a List of the specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
@@ -420,31 +527,10 @@ public interface MongoOperations {
* specification
* @param entityClass the parameterized type of the returned list.
* @param collectionName name of the collection to retrieve the objects from
*
* @return the List of converted objects
*/
<T> List<T> find(Query query, Class<T> entityClass, String collectionName);
/**
* Map the results of an ad-hoc query on the specified collection to a List of the specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification
* @param entityClass the parameterized type of the returned list.
* @param preparer allows for customization of the DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
* @param collectionName name of the collection to retrieve the objects from
*
* @return the List of converted objects.
*/
<T> List<T> find(Query query, Class<T> entityClass, CursorPreparer preparer, String collectionName);
/**
* Returns a document with the given id mapped onto the given class. The collection the query is ran against will be
* derived from the given target class as well.
@@ -462,12 +548,66 @@ public interface MongoOperations {
* @param id the id of the document to return
* @param entityClass the type to convert the document to
* @param collectionName the collection to query for the document
*
* @param <T>
* @return
*/
<T> T findById(Object id, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link Update} to apply on matching documents.
* @param entityClass the parameterized type.
* @return
*/
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
/**
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link Update} to apply on matching documents.
* @param entityClass the parameterized type.
* @param collectionName the collection to query.
* @return
*/
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
* {@link FindAndModifyOptions} into account.
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parameterized type.
* @return
*/
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
/**
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
* {@link FindAndModifyOptions} into account.
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parameterized type.
* @param collectionName the collection to query.
* @return
*/
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
* Map the results of an ad-hoc query on the collection for the entity type to a single instance of an object of the
* specified type. The first document that matches the query is returned and also removed from the collection in the
@@ -490,7 +630,7 @@ public interface MongoOperations {
* type. The first document that matches the query is returned and also removed from the collection in the database.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
@@ -499,7 +639,6 @@ public interface MongoOperations {
* specification
* @param entityClass the parameterized type of the returned list.
* @param collectionName name of the collection to retrieve the objects from
*
* @return the converted object
*/
<T> T findAndRemove(Query query, Class<T> entityClass, String collectionName);
@@ -512,7 +651,7 @@ public interface MongoOperations {
* @return
*/
long count(Query query, Class<?> entityClass);
/**
* Returns the number of documents for the given {@link Query} querying the given collection.
*
@@ -521,7 +660,7 @@ public interface MongoOperations {
* @return
*/
long count(Query query, String collectionName);
/**
* Insert the object into the collection for the entity type of the object to save.
* <p/>
@@ -529,9 +668,9 @@ public interface MongoOperations {
* <p/>
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Conversion API.
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
* Conversion"</a> for more details.
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
* >Spring's Type Conversion"</a> for more details.
* <p/>
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
@@ -544,7 +683,7 @@ public interface MongoOperations {
* Insert the object into the specified collection.
* <p/>
* The object is converted to the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
*
@@ -582,13 +721,13 @@ public interface MongoOperations {
* object is not already present, that is an 'upsert'.
* <p/>
* The object is converted to the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Conversion API.
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
* Conversion"</a> for more details.
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
* >Spring's Type Conversion"</a> for more details.
*
* @param objectToSave the object to store in the collection
*/
@@ -599,19 +738,54 @@ public interface MongoOperations {
* is an 'upsert'.
* <p/>
* The object is converted to the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* configured otherwise, an instance of MappingMongoConverter will be used.
* <p/>
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Cobnversion API.
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
* Conversion"</a> for more details.
* property type will be handled by Spring's BeanWrapper class that leverages Type Cobnversion API. See <a
* http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">Spring's
* Type Conversion"</a> for more details.
*
* @param objectToSave the object to store in the collection
* @param collectionName name of the collection to store the object in
*/
void save(Object objectToSave, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* @param query the query document that specifies the criteria used to select a record to be upserted
* @param update the update document that contains the updated object or $ operators to manipulate the existing object
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* @param query the query document that specifies the criteria used to select a record to be updated
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult upsert(Query query, Update update, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* @param query the query document that specifies the criteria used to select a record to be upserted
* @param update the update document that contains the updated object or $ operators to manipulate the existing object
* @param entityClass class of the pojo to be operated on
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult upsert(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document.
@@ -620,6 +794,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateFirst(Query query, Update update, Class<?> entityClass);
@@ -631,9 +806,23 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateFirst(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document.
*
* @param query the query document that specifies the criteria used to select a record to be updated
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class of the pojo to be operated on
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateFirst(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
* with the provided updated document.
@@ -642,6 +831,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateMulti(Query query, Update update, Class<?> entityClass);
@@ -653,33 +843,56 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateMulti(Query query, Update update, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
* with the provided updated document.
*
* @param query the query document that specifies the criteria used to select a record to be updated
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class of the pojo to be operated on
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateMulti(final Query query, final Update update, Class<?> entityClass, String collectionName);
/**
* Remove the given object from the collection by id.
*
* @param object
*/
void remove(Object object);
WriteResult remove(Object object);
/**
* Removes the given object from the given collection.
*
* @param object
* @param collection must not be {@literal null} or empty.
*/
void remove(Object object, String collection);
WriteResult remove(Object object, String collection);
/**
* Remove all documents that match the provided query document criteria from the the collection used to store the
* entityClass. The Class parameter is also used to help convert the Id of the object if it is present in the query.
*
* @param <T>
* @param query
* @param entityClass
*/
<T> void remove(Query query, Class<T> entityClass);
WriteResult remove(Query query, Class<?> entityClass);
/**
* Remove all documents that match the provided query document criteria from the the collection used to store the
* entityClass. The Class parameter is also used to help convert the Id of the object if it is present in the query.
*
* @param query
* @param entityClass
* @param collectionName
*/
WriteResult remove(Query query, Class<?> entityClass, String collectionName);
/**
* Remove all documents from the specified collection that match the provided query document criteria. There is no
@@ -688,7 +901,40 @@ public interface MongoOperations {
* @param query the query document that specifies the criteria used to remove a record
* @param collectionName name of the collection where the objects will removed
*/
void remove(Query query, String collectionName);
WriteResult remove(Query query, String collectionName);
/**
* Returns and removes all documents form the specified collection that match the provided query.
*
* @param query
* @param collectionName
* @return
* @since 1.5
*/
<T> List<T> findAllAndRemove(Query query, String collectionName);
/**
* Returns and removes all documents matching the given query form the collection used to store the entityClass.
*
* @param query
* @param entityClass
* @return
* @since 1.5
*/
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass);
/**
* Returns and removes all documents that match the provided query document criteria from the the collection used to
* store the entityClass. The Class parameter is also used to help convert the Id of the object if it is present in
* the query.
*
* @param query
* @param entityClass
* @param collectionName
* @return
* @since 1.5
*/
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass, String collectionName);
/**
* Returns the underlying {@link MongoConverter}.
@@ -696,4 +942,4 @@ public interface MongoOperations {
* @return
*/
MongoConverter getConverter();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2010-2014 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,128 +15,92 @@
*/
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
*/
@SuppressWarnings("deprecation")
public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, InitializingBean {
private static final MongoOptions MONGO_OPTIONS = new MongoOptions();
/**
* number of connections allowed per host will block if run out
*/
private int connectionsPerHost = MONGO_OPTIONS.connectionsPerHost;
private static final MongoOptions DEFAULT_MONGO_OPTIONS = new MongoOptions();
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.connectionsPerHost;
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier;
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.maxWaitTime;
private int connectTimeout = DEFAULT_MONGO_OPTIONS.connectTimeout;
private int socketTimeout = DEFAULT_MONGO_OPTIONS.socketTimeout;
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.socketKeepAlive;
private boolean autoConnectRetry = DEFAULT_MONGO_OPTIONS.autoConnectRetry;
private long maxAutoConnectRetryTime = DEFAULT_MONGO_OPTIONS.maxAutoConnectRetryTime;
private int writeNumber = DEFAULT_MONGO_OPTIONS.w;
private int writeTimeout = DEFAULT_MONGO_OPTIONS.wtimeout;
private boolean writeFsync = DEFAULT_MONGO_OPTIONS.fsync;
private boolean slaveOk = DEFAULT_MONGO_OPTIONS.slaveOk;
private boolean ssl;
private SSLSocketFactory sslSocketFactory;
private MongoOptions options;
/**
* 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
*/
private int threadsAllowedToBlockForConnectionMultiplier = MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier;
/**
* 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
*/
private int connectTimeout = MONGO_OPTIONS.connectTimeout;
/**
* socket timeout. 0 is default and infinite
*/
private int socketTimeout = MONGO_OPTIONS.socketTimeout;
/**
* This controls whether or not to have socket keep alive turned on (SO_KEEPALIVE).
* Configures the maximum number of connections allowed per host until we will block.
*
* defaults to false
*/
public boolean socketKeepAlive = MONGO_OPTIONS.socketKeepAlive;
/**
* this controls whether or not on a connect, the system retries automatically
*/
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.
*/
private int writeNumber;
/**
* This controls timeout for write operations in milliseconds.
*
* Defaults to 0 (indefinite). Greater than zero is number of milliseconds to wait.
*/
private int writeTimeout;
/**
* This controls whether or not to fsync.
*
* Defaults to false.
*/
private boolean writeFsync;
/**
* Specifies if the driver is allowed to read from secondaries or slaves.
*
* Defaults to false
*/
private boolean slaveOk = MONGO_OPTIONS.slaveOk;
/**
* number of connections allowed per host will block if run out
* @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. If connectionsPerHost is 10, and
* threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block. If more threads try to block an
* exception will be thrown.
*
* @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
* Configures the connect timeout in milliseconds. Defaults to 0 (infinite time).
*
* @param connectTimeout
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* socket timeout. 0 is default and infinite
* Configures the socket timeout. Defaults to 0 (infinite time).
*
* @param socketTimeout
*/
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
/**
* This controls whether or not to have socket keep alive
* Configures whether or not to have socket keep alive turned on (SO_KEEPALIVE). Defaults to {@literal false}.
*
* @param socketKeepAlive
*/
@@ -151,7 +115,7 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
* <li>-1 = don't even report network errors</li>
* <li>0 = default, don't call getLastError by default</li>
* <li>1 = basic, call getLastError, but don't wait for slaves</li>
* <li>2+= wait for slaves</li>
* <li>2 += wait for slaves</li>
* </ul>
*
* @param writeNumber the number of servers to wait for on the write operation, and exception raising behavior.
@@ -161,33 +125,33 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
}
/**
* This controls timeout for write operations in milliseconds. The 'wtimeout' option to the getlasterror command.
* Configures the timeout for write operations in milliseconds. This defaults to {@literal 0} (indefinite).
*
* @param writeTimeout Defaults to 0 (indefinite). Greater than zero is number of milliseconds to wait.
* @param writeTimeout
*/
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.
* Configures 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 <code>write (true)<code>, otherwise {@literal false}.
*/
public void setWriteFsync(boolean writeFsync) {
this.writeFsync = writeFsync;
}
/**
* this controls whether or not on a connect, the system retries automatically
* Configures whether or not the system retries automatically on a failed connect. This defaults to {@literal false}.
*/
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.
* Configures 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
*/
@@ -196,7 +160,7 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, 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. Defaults to {@literal false}.
*
* @param slaveOk true if the driver should read from secondaries or slaves.
*/
@@ -204,31 +168,81 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
this.slaveOk = slaveOk;
}
/**
* Specifies if the driver should use an SSL connection to Mongo. This defaults to {@literal false}. By default
* {@link SSLSocketFactory#getDefault()} will be used. See {@link #setSslSocketFactory(SSLSocketFactory)} if you want
* to configure a custom factory.
*
* @param ssl true if the driver should use an SSL connection.
* @see #setSslSocketFactory(SSLSocketFactory)
*/
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
/**
* Specifies the {@link SSLSocketFactory} to use for creating SSL connections to Mongo. Defaults to
* {@link SSLSocketFactory#getDefault()}. Implicitly activates {@link #setSsl(boolean)} if a non-{@literal null} value
* is given.
*
* @param sslSocketFactory the sslSocketFactory to use.
* @see #setSsl(boolean)
*/
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
setSsl(sslSocketFactory != null);
this.sslSocketFactory = sslSocketFactory;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
MONGO_OPTIONS.connectionsPerHost = connectionsPerHost;
MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
MONGO_OPTIONS.maxWaitTime = maxWaitTime;
MONGO_OPTIONS.connectTimeout = connectTimeout;
MONGO_OPTIONS.socketTimeout = socketTimeout;
MONGO_OPTIONS.socketKeepAlive = socketKeepAlive;
MONGO_OPTIONS.autoConnectRetry = autoConnectRetry;
MONGO_OPTIONS.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
MONGO_OPTIONS.slaveOk = slaveOk;
MONGO_OPTIONS.w = writeNumber;
MONGO_OPTIONS.wtimeout = writeTimeout;
MONGO_OPTIONS.fsync = writeFsync;
MongoOptions options = new MongoOptions();
options.connectionsPerHost = connectionsPerHost;
options.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
options.maxWaitTime = maxWaitTime;
options.connectTimeout = connectTimeout;
options.socketTimeout = socketTimeout;
options.socketKeepAlive = socketKeepAlive;
options.autoConnectRetry = autoConnectRetry;
options.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
options.slaveOk = slaveOk;
options.w = writeNumber;
options.wtimeout = writeTimeout;
options.fsync = writeFsync;
if (ssl) {
options.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault());
}
this.options = options;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public MongoOptions getObject() {
return MONGO_OPTIONS;
return this.options;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<?> getObjectType() {
return MongoOptions.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
}

View File

@@ -1,8 +1,26 @@
/*
* Copyright 2012-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import org.springframework.transaction.support.ResourceHolder;
import org.springframework.transaction.support.ResourceHolderSynchronization;
/**
* @author Oliver Gierke
*/
class MongoSynchronization extends ResourceHolderSynchronization<ResourceHolder, Object> {
public MongoSynchronization(ResourceHolder resourceHolder, Object resourceKey) {

View File

@@ -1,138 +0,0 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.bson.types.BasicBSONList;
import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* A helper class to encapsulate any modifications of a Query object before it gets submitted to the database.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke
*/
public class QueryMapper {
private final ConversionService conversionService;
/**
* Creates a new {@link QueryMapper} with the given {@link ConversionService}.
*
* @param conversionService must not be {@literal null}.
*/
public QueryMapper(ConversionService conversionService) {
Assert.notNull(conversionService);
this.conversionService = conversionService;
}
/**
* Replaces the property keys used in the given {@link DBObject} with the appropriate keys by using the
* {@link PersistentEntity} metadata.
*
* @param query
* @param entity
* @return
*/
public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity) {
String idKey = null;
if (null != entity && entity.getIdProperty() != null) {
idKey = entity.getIdProperty().getName();
} else if (query.containsField("id")) {
idKey = "id";
} else if (query.containsField("_id")) {
idKey = "_id";
}
DBObject newDbo = new BasicDBObject();
for (String key : query.keySet()) {
String newKey = key;
Object value = query.get(key);
if (key.equals(idKey)) {
if (value instanceof DBObject) {
DBObject valueDbo = (DBObject) value;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List<Object> ids = new ArrayList<Object>();
for (Object id : (Object[]) valueDbo.get(inKey)) {
ids.add(convertId(id));
}
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
} else {
value = getMappedObject((DBObject) value, entity);
}
} else {
value = convertId(value);
}
newKey = "_id";
} else if (key.startsWith("$") && key.endsWith("or")) {
// $or/$nor
BasicBSONList conditions = (BasicBSONList) value;
BasicBSONList newConditions = new BasicBSONList();
Iterator<Object> iter = conditions.iterator();
while (iter.hasNext()) {
newConditions.add(getMappedObject((DBObject) iter.next(), entity));
}
value = newConditions;
} else if (key.equals("$ne")) {
value = convertId(value);
}
newDbo.put(newKey, value);
}
return newDbo;
}
/**
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
*
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public Object convertId(Object id) {
for (Class<?> type : Arrays.asList(ObjectId.class, String.class)) {
if (id.getClass().isAssignableFrom(type)) {
return id;
}
try {
return conversionService.convert(id, type);
} catch (ConversionException e) {
// Ignore
}
}
return id;
}
}

View File

@@ -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.
@@ -17,13 +17,13 @@ package org.springframework.data.mongodb.core;
import java.net.UnknownHostException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.DB;
import com.mongodb.Mongo;
@@ -36,44 +36,53 @@ import com.mongodb.WriteConcern;
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
protected final Log logger = LogFactory.getLog(getClass());
private final Mongo mongo;
private final String databaseName;
private String username;
private String password;
private final boolean mongoInstanceCreated;
private final UserCredentials credentials;
private final PersistenceExceptionTranslator exceptionTranslator;
private final String authenticationDatabaseName;
private WriteConcern writeConcern;
/**
* Create an instance of {@link SimpleMongoDbFactory} given the {@link Mongo} instance and database name.
*
* @param mongo Mongo instance, must not be {@literal null}.
* @param databaseName database name, not be {@literal null}.
* @param databaseName database name, not be {@literal null} or empty.
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName) {
Assert.notNull(mongo, "Mongo must not be null");
Assert.hasText(databaseName, "Database name must not be empty");
Assert.isTrue(databaseName.matches("[\\w-]+"), "Database name must only contain letters, numbers, underscores and dashes!");
this.mongo = mongo;
this.databaseName = databaseName;
this(mongo, databaseName, null);
}
/**
* Create an instance of SimpleMongoDbFactory given the Mongo instance, database name, and username/password
*
* @param mongo Mongo instance, must not be {@literal null}.
* @param databaseName Database name, must not be {@literal null}.
* @param userCredentials username and password must not be {@literal null}.
* @param databaseName Database name, must not be {@literal null} or empty.
* @param credentials username and password.
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials userCredentials) {
this(mongo, databaseName);
this.username = userCredentials.getUsername();
this.password = userCredentials.getPassword();
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials) {
this(mongo, databaseName, credentials, false, null);
}
/**
* Create an instance of SimpleMongoDbFactory given the Mongo instance, database name, and username/password
*
* @param mongo Mongo instance, must not be {@literal null}.
* @param databaseName Database name, must not be {@literal null} or empty.
* @param credentials username and password.
* @param authenticationDatabaseName the database name to use for authentication
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
String authenticationDatabaseName) {
this(mongo, databaseName, credentials, false, authenticationDatabaseName);
}
/**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoURI}.
*
@@ -82,9 +91,30 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
* @throws UnknownHostException
* @see MongoURI
*/
@SuppressWarnings("deprecation")
public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException {
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), String.valueOf(uri.getPassword())));
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())),
true, uri.getDatabase());
}
private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
boolean mongoInstanceCreated, String authenticationDatabaseName) {
Assert.notNull(mongo, "Mongo must not be null");
Assert.hasText(databaseName, "Database name must not be empty");
Assert.isTrue(databaseName.matches("[\\w-]+"),
"Database name must only contain letters, numbers, underscores and dashes!");
this.mongo = mongo;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.credentials = credentials == null ? UserCredentials.NO_CREDENTIALS : credentials;
this.exceptionTranslator = new MongoExceptionTranslator();
this.authenticationDatabaseName = StringUtils.hasText(authenticationDatabaseName) ? authenticationDatabaseName
: databaseName;
Assert.isTrue(this.authenticationDatabaseName.matches("[\\w-]+"),
"Authentication database name must only contain letters, numbers, underscores and dashes!");
}
/**
@@ -112,7 +142,7 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
Assert.hasText(dbName, "Database name must not be empty.");
DB db = MongoDbUtils.getDB(mongo, dbName, username, password == null ? null : password.toCharArray());
DB db = MongoDbUtils.getDB(mongo, dbName, credentials, authenticationDatabaseName);
if (writeConcern != null) {
db.setWriteConcern(writeConcern);
@@ -122,9 +152,26 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
}
/**
* Clean up the Mongo instance.
* Clean up the Mongo instance if it was created by the factory itself.
*
* @see DisposableBean#destroy()
*/
public void destroy() throws Exception {
mongo.close();
if (mongoInstanceCreated) {
mongo.close();
}
}
private static String parseChars(char[] chars) {
return chars == null ? null : String.valueOf(chars);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2011-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import com.mongodb.WriteConcern;
/**
* A strategy interface to determine the {@link WriteConcern} to use for a given {@link MongoAction}. Return the passed
* in default {@link WriteConcern} (a property on {@link MongoAction}) if no determination can be made.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public interface WriteConcernResolver {
/**
* Resolve the {@link WriteConcern} given the {@link MongoAction}.
*
* @param action describes the context of the Mongo action. Contains a default {@link WriteConcern} to use if one
* should not be resolved.
* @return a {@link WriteConcern} based on the passed in {@link MongoAction} value, maybe {@literal null}.
*/
WriteConcern resolve(MongoAction action);
}

View File

@@ -1,5 +1,28 @@
/*
* Copyright 2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
/**
* Enum to represent how strict the check of {@link com.mongodb.WriteResult} shall be. It can either be skipped entirely
* (use {@link #NONE}), or errors can be logged ({@link #LOG}) or cause an exception to be thrown {@link #EXCEPTION}.
*
* @author Thomas Risberg
* @author Oliver Gierke
*/
public enum WriteResultChecking {
NONE, LOG, EXCEPTION
}

View File

@@ -0,0 +1,424 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* An {@code Aggregation} is a representation of a list of aggregation steps to be performed by the MongoDB Aggregation
* Framework.
*
* @author Tobias Trelle
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class Aggregation {
/**
* References the root document, i.e. the top-level document, currently being processed in the aggregation pipeline
* stage.
*/
public static final String ROOT = SystemVariable.ROOT.toString();
/**
* References the start of the field path being processed in the aggregation pipeline stage. Unless documented
* otherwise, all stages start with CURRENT the same as ROOT.
*/
public static final String CURRENT = SystemVariable.CURRENT.toString();
public static final AggregationOperationContext DEFAULT_CONTEXT = new NoOpAggregationOperationContext();
public static final AggregationOptions DEFAULT_OPTIONS = newAggregationOptions().build();
protected final List<AggregationOperation> operations;
private final AggregationOptions options;
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param operations must not be {@literal null} or empty.
*/
public static Aggregation newAggregation(List<? extends AggregationOperation> operations) {
return newAggregation(operations.toArray(new AggregationOperation[operations.size()]));
}
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param operations must not be {@literal null} or empty.
*/
public static Aggregation newAggregation(AggregationOperation... operations) {
return new Aggregation(operations);
}
/**
* Returns a copy of this {@link Aggregation} with the given {@link AggregationOptions} set. Note that options are
* supported in MongoDB version 2.6+.
*
* @param options must not be {@literal null}.
* @return
* @since 1.6
*/
public Aggregation withOptions(AggregationOptions options) {
Assert.notNull(options, "AggregationOptions must not be null.");
return new Aggregation(this.operations, options);
}
/**
* Creates a new {@link TypedAggregation} for the given type and {@link AggregationOperation}s.
*
* @param type must not be {@literal null}.
* @param operations must not be {@literal null} or empty.
*/
public static <T> TypedAggregation<T> newAggregation(Class<T> type, List<? extends AggregationOperation> operations) {
return newAggregation(type, operations.toArray(new AggregationOperation[operations.size()]));
}
/**
* Creates a new {@link TypedAggregation} for the given type and {@link AggregationOperation}s.
*
* @param type must not be {@literal null}.
* @param operations must not be {@literal null} or empty.
*/
public static <T> TypedAggregation<T> newAggregation(Class<T> type, AggregationOperation... operations) {
return new TypedAggregation<T>(type, operations);
}
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param aggregationOperations must not be {@literal null} or empty.
*/
protected Aggregation(AggregationOperation... aggregationOperations) {
this(asAggregationList(aggregationOperations));
}
/**
* @param aggregationOperations must not be {@literal null} or empty.
* @return
*/
protected static List<AggregationOperation> asAggregationList(AggregationOperation... aggregationOperations) {
Assert.notEmpty(aggregationOperations, "AggregationOperations must not be null or empty!");
return Arrays.asList(aggregationOperations);
}
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param aggregationOperations must not be {@literal null} or empty.
*/
protected Aggregation(List<AggregationOperation> aggregationOperations) {
this(aggregationOperations, DEFAULT_OPTIONS);
}
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param aggregationOperations must not be {@literal null} or empty.
* @param options must not be {@literal null} or empty.
*/
protected Aggregation(List<AggregationOperation> aggregationOperations, AggregationOptions options) {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
Assert.isTrue(aggregationOperations.size() > 0, "At least one AggregationOperation has to be provided");
Assert.notNull(options, "AggregationOptions must not be null!");
this.operations = aggregationOperations;
this.options = options;
}
/**
* A pointer to the previous {@link AggregationOperation}.
*
* @return
*/
public static String previousOperation() {
return "_id";
}
/**
* Creates a new {@link ProjectionOperation} including the given fields.
*
* @param fields must not be {@literal null}.
* @return
*/
public static ProjectionOperation project(String... fields) {
return project(fields(fields));
}
/**
* Creates a new {@link ProjectionOperation} includeing the given {@link Fields}.
*
* @param fields must not be {@literal null}.
* @return
*/
public static ProjectionOperation project(Fields fields) {
return new ProjectionOperation(fields);
}
/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
*
* @param fieldName must not be {@literal null} or empty.
* @return
*/
public static UnwindOperation unwind(String field) {
return new UnwindOperation(field(field));
}
/**
* Creates a new {@link GroupOperation} for the given fields.
*
* @param fields must not be {@literal null}.
* @return
*/
public static GroupOperation group(String... fields) {
return group(fields(fields));
}
/**
* Creates a new {@link GroupOperation} for the given {@link Fields}.
*
* @param fields must not be {@literal null}.
* @return
*/
public static GroupOperation group(Fields fields) {
return new GroupOperation(fields);
}
/**
* Factory method to create a new {@link SortOperation} for the given {@link Sort}.
*
* @param sort must not be {@literal null}.
* @return
*/
public static SortOperation sort(Sort sort) {
return new SortOperation(sort);
}
/**
* Factory method to create a new {@link SortOperation} for the given sort {@link Direction} and {@code fields}.
*
* @param direction must not be {@literal null}.
* @param fields must not be {@literal null}.
* @return
*/
public static SortOperation sort(Direction direction, String... fields) {
return new SortOperation(new Sort(direction, fields));
}
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param elementsToSkip must not be less than zero.
* @return
*/
public static SkipOperation skip(int elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
/**
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
*
* @param maxElements must not be less than zero.
* @return
*/
public static LimitOperation limit(long maxElements) {
return new LimitOperation(maxElements);
}
/**
* Creates a new {@link MatchOperation} using the given {@link Criteria}.
*
* @param criteria must not be {@literal null}.
* @return
*/
public static MatchOperation match(Criteria criteria) {
return new MatchOperation(criteria);
}
/**
* Creates a new {@link Fields} instance for the given field names.
*
* @see Fields#fields(String...)
* @param fields must not be {@literal null}.
* @return
*/
public static Fields fields(String... fields) {
return Fields.fields(fields);
}
/**
* Creates a new {@link Fields} instance from the given field name and target reference.
*
* @param name must not be {@literal null} or empty.
* @param target must not be {@literal null} or empty.
* @return
*/
public static Fields bind(String name, String target) {
return Fields.from(field(name, target));
}
/**
* Returns a new {@link AggregationOptions.Builder}.
*
* @return
* @since 1.6
*/
public static AggregationOptions.Builder newAggregationOptions() {
return new AggregationOptions.Builder();
}
/**
* Converts this {@link Aggregation} specification to a {@link DBObject}.
*
* @param inputCollectionName the name of the input collection
* @return the {@code DBObject} representing this aggregation
*/
public DBObject toDbObject(String inputCollectionName, AggregationOperationContext rootContext) {
AggregationOperationContext context = rootContext;
List<DBObject> operationDocuments = new ArrayList<DBObject>(operations.size());
for (AggregationOperation operation : operations) {
operationDocuments.add(operation.toDBObject(context));
if (operation instanceof FieldsExposingAggregationOperation) {
FieldsExposingAggregationOperation exposedFieldsOperation = (FieldsExposingAggregationOperation) operation;
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), rootContext);
}
}
DBObject command = new BasicDBObject("aggregate", inputCollectionName);
command.put("pipeline", operationDocuments);
command = options.applyAndReturnPotentiallyChangedCommand(command);
return command;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return SerializationUtils
.serializeToJsonSafely(toDbObject("__collection__", new NoOpAggregationOperationContext()));
}
/**
* Simple {@link AggregationOperationContext} that just returns {@link FieldReference}s as is.
*
* @author Oliver Gierke
*/
private static class NoOpAggregationOperationContext implements AggregationOperationContext {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
*/
@Override
public DBObject getMappedObject(DBObject dbObject) {
return dbObject;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField)
*/
@Override
public FieldReference getReference(Field field) {
return new FieldReference(new ExposedField(field, true));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
*/
@Override
public FieldReference getReference(String name) {
return new FieldReference(new ExposedField(new AggregationField(name), true));
}
}
/**
* Describes the system variables available in MongoDB aggregation framework pipeline expressions.
*
* @author Thomas Darimont
* @see http://docs.mongodb.org/manual/reference/aggregation-variables
*/
enum SystemVariable {
ROOT, CURRENT;
private static final String PREFIX = "$$";
/**
* Return {@literal true} if the given {@code fieldRef} denotes a well-known system variable, {@literal false}
* otherwise.
*
* @param fieldRef may be {@literal null}.
* @return
*/
public static boolean isReferingToSystemVariable(String fieldRef) {
if (fieldRef == null || !fieldRef.startsWith(PREFIX) || fieldRef.length() <= 2) {
return false;
}
int indexOfFirstDot = fieldRef.indexOf('.');
String candidate = fieldRef.substring(2, indexOfFirstDot == -1 ? fieldRef.length() : indexOfFirstDot);
for (SystemVariable value : values()) {
if (value.name().equals(candidate)) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return PREFIX.concat(name());
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationExpressionTransformer.AggregationExpressionTransformationContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.spel.ExpressionNode;
import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
import org.springframework.data.mongodb.core.spel.ExpressionTransformer;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Interface to type an {@link ExpressionTransformer} to the contained
* {@link AggregationExpressionTransformationContext}.
*
* @author Oliver Gierke
*/
interface AggregationExpressionTransformer extends
ExpressionTransformer<AggregationExpressionTransformationContext<ExpressionNode>> {
/**
* A special {@link ExpressionTransformationContextSupport} to be aware of the {@link AggregationOperationContext}.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public static class AggregationExpressionTransformationContext<T extends ExpressionNode> extends
ExpressionTransformationContextSupport<T> {
private final AggregationOperationContext aggregationContext;
/**
* Creates an {@link AggregationExpressionTransformationContext}.
*
* @param currentNode must not be {@literal null}.
* @param parentNode
* @param previousOperationObject
* @param aggregationContext must not be {@literal null}.
*/
public AggregationExpressionTransformationContext(T currentNode, ExpressionNode parentNode,
DBObject previousOperationObject, AggregationOperationContext context) {
super(currentNode, parentNode, previousOperationObject);
Assert.notNull(context, "AggregationOperationContext must not be null!");
this.aggregationContext = context;
}
/**
* Returns the underlying {@link AggregationOperationContext}.
*
* @return
*/
public AggregationOperationContext getAggregationContext() {
return aggregationContext;
}
/**
* Returns the {@link FieldReference} for the current {@link ExpressionNode}.
*
* @return
*/
public FieldReference getFieldReference() {
return aggregationContext.getReference(getCurrentNode().getName());
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import com.mongodb.DBObject;
/**
* Represents one single operation in an aggregation pipeline.
*
* @author Sebastian Herold
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public interface AggregationOperation {
/**
* Turns the {@link AggregationOperation} into a {@link DBObject} by using the given
* {@link AggregationOperationContext}.
*
* @return the DBObject
*/
DBObject toDBObject(AggregationOperationContext context);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import com.mongodb.DBObject;
/**
* The context for an {@link AggregationOperation}.
*
* @author Oliver Gierke
* @since 1.3
*/
public interface AggregationOperationContext {
/**
* Returns the mapped {@link DBObject}, potentially converting the source considering mapping metadata etc.
*
* @param dbObject will never be {@literal null}.
* @return must not be {@literal null}.
*/
DBObject getMappedObject(DBObject dbObject);
/**
* Returns a {@link FieldReference} for the given field or {@literal null} if the context does not expose the given
* field.
*
* @param field must not be {@literal null}.
* @return
*/
FieldReference getReference(Field field);
/**
* Returns the {@link FieldReference} for the field with the given name or {@literal null} if the context does not
* expose a field with the given name.
*
* @param name must not be {@literal null} or empty.
* @return
*/
FieldReference getReference(String name);
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright 2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
* aggregation options can be found in the MongoDB reference documentation
* http://docs.mongodb.org/manual/reference/command/aggregate/#aggregate
*
* @author Thomas Darimont
* @author Oliver Gierke
* @see Aggregation#withOptions(AggregationOptions)
* @see TypedAggregation#withOptions(AggregationOptions)
* @since 1.6
*/
public class AggregationOptions {
private static final String CURSOR = "cursor";
private static final String EXPLAIN = "explain";
private static final String ALLOW_DISK_USE = "allowDiskUse";
private final boolean allowDiskUse;
private final boolean explain;
private final DBObject cursor;
/**
* Creates a new {@link AggregationOptions}.
*
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options to the aggregation.
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, DBObject cursor) {
this.allowDiskUse = allowDiskUse;
this.explain = explain;
this.cursor = cursor;
}
/**
* Enables writing to temporary files. When set to true, aggregation stages can write data to the _tmp subdirectory in
* the dbPath directory.
*
* @return
*/
public boolean isAllowDiskUse() {
return allowDiskUse;
}
/**
* Specifies to return the information on the processing of the pipeline.
*
* @return
*/
public boolean isExplain() {
return explain;
}
/**
* Specify a document that contains options that control the creation of the cursor object.
*
* @return
*/
public DBObject getCursor() {
return cursor;
}
/**
* Returns a new potentially adjusted copy for the given {@code aggregationCommandObject} with the configuration
* applied.
*
* @param command the aggregation command.
* @return
*/
DBObject applyAndReturnPotentiallyChangedCommand(DBObject command) {
DBObject result = new BasicDBObject(command.toMap());
if (allowDiskUse && !result.containsField(ALLOW_DISK_USE)) {
result.put(ALLOW_DISK_USE, allowDiskUse);
}
if (explain && !result.containsField(EXPLAIN)) {
result.put(EXPLAIN, explain);
}
if (cursor != null && !result.containsField(CURSOR)) {
result.put("cursor", cursor);
}
return result;
}
/**
* Returns a {@link DBObject} representation of this {@link AggregationOptions}.
*
* @return
*/
public DBObject toDbObject() {
DBObject dbo = new BasicDBObject();
dbo.put(ALLOW_DISK_USE, allowDiskUse);
dbo.put(EXPLAIN, explain);
dbo.put(CURSOR, cursor);
return dbo;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toDbObject().toString();
}
/**
* A Builder for {@link AggregationOptions}.
*
* @author Thomas Darimont
*/
public static class Builder {
private boolean allowDiskUse;
private boolean explain;
private DBObject cursor;
/**
* Defines whether to off-load intensive sort-operations to disk.
*
* @param allowDiskUse
* @return
*/
public Builder allowDiskUse(boolean allowDiskUse) {
this.allowDiskUse = allowDiskUse;
return this;
}
/**
* Defines whether to get the execution plan for the aggregation instead of the actual results.
*
* @param explain
* @return
*/
public Builder explain(boolean explain) {
this.explain = explain;
return this;
}
/**
* Additional options to the aggregation.
*
* @param cursor
* @return
*/
public Builder cursor(DBObject cursor) {
this.cursor = cursor;
return this;
}
/**
* Returns a new {@link AggregationOptions} instance with the given configuration.
*
* @return
*/
public AggregationOptions build() {
return new AggregationOptions(allowDiskUse, explain, cursor);
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Collects the results of executing an aggregation operation.
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Thomas Darimont
* @param <T> The class in which the results are mapped onto.
* @since 1.3
*/
public class AggregationResults<T> implements Iterable<T> {
private final List<T> mappedResults;
private final DBObject rawResults;
private final String serverUsed;
/**
* Creates a new {@link AggregationResults} instance from the given mapped and raw results.
*
* @param mappedResults must not be {@literal null}.
* @param rawResults must not be {@literal null}.
*/
public AggregationResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
this.mappedResults = Collections.unmodifiableList(mappedResults);
this.rawResults = rawResults;
this.serverUsed = parseServerUsed();
}
/**
* Returns the aggregation results.
*
* @return
*/
public List<T> getMappedResults() {
return mappedResults;
}
/**
* Returns the unique mapped result. Assumes no result or exactly one.
*
* @return
* @throws IllegalArgumentException in case more than one result is available.
*/
public T getUniqueMappedResult() {
Assert.isTrue(mappedResults.size() < 2, "Expected unique result or null, but got more than one!");
return mappedResults.size() == 1 ? mappedResults.get(0) : null;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<T> iterator() {
return mappedResults.iterator();
}
/**
* Returns the server that has been used to perform the aggregation.
*
* @return
*/
public String getServerUsed() {
return serverUsed;
}
/**
* Returns the raw result that was returned by the server.
*
* @return
* @since 1.6
*/
public DBObject getRawResults() {
return rawResults;
}
private String parseServerUsed() {
Object object = rawResults.get("serverUsed");
return object instanceof String ? (String) object : null;
}
}

View File

@@ -0,0 +1,413 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.util.Assert;
import org.springframework.util.CompositeIterator;
/**
* Value object to capture the fields exposed by an {@link AggregationOperation}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @since 1.3
*/
public final class ExposedFields implements Iterable<ExposedField> {
private static final List<ExposedField> NO_FIELDS = Collections.emptyList();
private static final ExposedFields EMPTY = new ExposedFields(NO_FIELDS, NO_FIELDS);
private final List<ExposedField> originalFields;
private final List<ExposedField> syntheticFields;
/**
* Creates a new {@link ExposedFields} instance from the given {@link ExposedField}s.
*
* @param fields must not be {@literal null}.
* @return
*/
public static ExposedFields from(ExposedField... fields) {
return from(Arrays.asList(fields));
}
/**
* Creates a new {@link ExposedFields} instance from the given {@link ExposedField}s.
*
* @param fields must not be {@literal null}.
* @return
*/
private static ExposedFields from(List<ExposedField> fields) {
ExposedFields result = EMPTY;
for (ExposedField field : fields) {
result = result.and(field);
}
return result;
}
/**
* Creates synthetic {@link ExposedFields} from the given {@link Fields}.
*
* @param fields must not be {@literal null}.
* @return
*/
public static ExposedFields synthetic(Fields fields) {
return createFields(fields, true);
}
/**
* Creates non-synthetic {@link ExposedFields} from the given {@link Fields}.
*
* @param fields must not be {@literal null}.
* @return
*/
public static ExposedFields nonSynthetic(Fields fields) {
return createFields(fields, false);
}
/**
* Creates a new {@link ExposedFields} instance for the given fields in either sythetic or non-synthetic way.
*
* @param fields must not be {@literal null}.
* @param synthetic
* @return
*/
private static ExposedFields createFields(Fields fields, boolean synthetic) {
Assert.notNull(fields, "Fields must not be null!");
List<ExposedField> result = new ArrayList<ExposedField>();
for (Field field : fields) {
result.add(new ExposedField(field, synthetic));
}
return ExposedFields.from(result);
}
/**
* Creates a new {@link ExposedFields} with the given orignals and synthetics.
*
* @param originals must not be {@literal null}.
* @param synthetic must not be {@literal null}.
*/
private ExposedFields(List<ExposedField> originals, List<ExposedField> synthetic) {
this.originalFields = originals;
this.syntheticFields = synthetic;
}
/**
* Creates a new {@link ExposedFields} adding the given {@link ExposedField}.
*
* @param field must not be {@literal null}.
* @return
*/
public ExposedFields and(ExposedField field) {
Assert.notNull(field, "Exposed field must not be null!");
ArrayList<ExposedField> result = new ArrayList<ExposedField>();
result.addAll(field.synthetic ? syntheticFields : originalFields);
result.add(field);
return new ExposedFields(field.synthetic ? originalFields : result, field.synthetic ? result : syntheticFields);
}
/**
* Returns the field with the given name or {@literal null} if no field with the given name is available.
*
* @param name
* @return
*/
public ExposedField getField(String name) {
for (ExposedField field : this) {
if (field.canBeReferredToBy(name)) {
return field;
}
}
return null;
}
/**
* Returns whether the {@link ExposedFields} exposes no non-synthetic fields at all.
*
* @return
*/
boolean exposesNoNonSyntheticFields() {
return originalFields.isEmpty();
}
/**
* Returns whether the {@link ExposedFields} exposes a single non-synthetic field only.
*
* @return
*/
boolean exposesSingleNonSyntheticFieldOnly() {
return originalFields.size() == 1;
}
/**
* Returns whether the {@link ExposedFields} exposes no fields at all.
*
* @return
*/
boolean exposesNoFields() {
return exposedFieldsCount() == 0;
}
/**
* Returns whether the {@link ExposedFields} exposes a single field only.
*
* @return
*/
boolean exposesSingleFieldOnly() {
return exposedFieldsCount() == 1;
}
/**
* @return
*/
private int exposedFieldsCount() {
return originalFields.size() + syntheticFields.size();
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<ExposedField> iterator() {
CompositeIterator<ExposedField> iterator = new CompositeIterator<ExposedField>();
iterator.add(syntheticFields.iterator());
iterator.add(originalFields.iterator());
return iterator;
}
/**
* A single exposed field.
*
* @author Oliver Gierke
*/
static class ExposedField implements Field {
private final boolean synthetic;
private final Field field;
/**
* Creates a new {@link ExposedField} with the given key.
*
* @param key must not be {@literal null} or empty.
* @param synthetic whether the exposed field is synthetic.
*/
public ExposedField(String key, boolean synthetic) {
this(Fields.field(key), synthetic);
}
/**
* Creates a new {@link ExposedField} for the given {@link Field}.
*
* @param delegate must not be {@literal null}.
* @param synthetic whether the exposed field is synthetic.
*/
public ExposedField(Field delegate, boolean synthetic) {
this.field = delegate;
this.synthetic = synthetic;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#getKey()
*/
@Override
public String getName() {
return field.getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#getTarget()
*/
@Override
public String getTarget() {
return field.getTarget();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#isAliased()
*/
@Override
public boolean isAliased() {
return field.isAliased();
}
/**
* @return the synthetic
*/
public boolean isSynthetic() {
return synthetic;
}
/**
* Returns whether the field can be referred to using the given name.
*
* @param name
* @return
*/
public boolean canBeReferredToBy(String name) {
return getName().equals(name) || getTarget().equals(name);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("AggregationField: %s, synthetic: %s", field, synthetic);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ExposedField)) {
return false;
}
ExposedField that = (ExposedField) obj;
return this.field.equals(that.field) && this.synthetic == that.synthetic;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * field.hashCode();
result += 31 * (synthetic ? 0 : 1);
return result;
}
}
/**
* A reference to an {@link ExposedField}.
*
* @author Oliver Gierke
*/
static class FieldReference {
private final ExposedField field;
/**
* Creates a new {@link FieldReference} for the given {@link ExposedField}.
*
* @param field must not be {@literal null}.
*/
public FieldReference(ExposedField field) {
Assert.notNull(field, "ExposedField must not be null!");
this.field = field;
}
/**
* Returns the raw, unqualified reference, i.e. the field reference without a {@literal $} prefix.
*
* @return
*/
public String getRaw() {
String target = field.getTarget();
return field.synthetic ? target : String.format("%s.%s", Fields.UNDERSCORE_ID, target);
}
/**
* Returns the referenve value for the given field reference. Will return 1 for a synthetic, unaliased field or the
* raw rendering of the reference otherwise.
*
* @return
*/
public Object getReferenceValue() {
return field.synthetic && !field.isAliased() ? 1 : toString();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("$%s", getRaw());
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof FieldReference)) {
return false;
}
FieldReference that = (FieldReference) obj;
return this.field.equals(that.field);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return field.hashCode();
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* {@link AggregationOperationContext} that combines the available field references from a given
* {@code AggregationOperationContext} and an {@link FieldsExposingAggregationOperation}.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.4
*/
class ExposedFieldsAggregationOperationContext implements AggregationOperationContext {
private final ExposedFields exposedFields;
private final AggregationOperationContext rootContext;
/**
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
* {@link AggregationOperationContext} to perform a mapping to mongo types if necessary.
*
* @param exposedFields must not be {@literal null}.
* @param rootContext must not be {@literal null}.
*/
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields, AggregationOperationContext rootContext) {
Assert.notNull(exposedFields, "ExposedFields must not be null!");
Assert.notNull(rootContext, "RootContext must not be null!");
this.exposedFields = exposedFields;
this.rootContext = rootContext;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
*/
@Override
public DBObject getMappedObject(DBObject dbObject) {
return rootContext.getMappedObject(dbObject);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField)
*/
@Override
public FieldReference getReference(Field field) {
return getReference(field, field.getTarget());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
*/
@Override
public FieldReference getReference(String name) {
return getReference(null, name);
}
/**
* Returns a {@link FieldReference} to the given {@link Field} with the given {@code name}.
*
* @param field may be {@literal null}
* @param name must not be {@literal null}
* @return
*/
private FieldReference getReference(Field field, String name) {
Assert.notNull(name, "Name must not be null!");
ExposedField exposedField = exposedFields.getField(name);
if (exposedField != null) {
if (field != null) {
// we return a FieldReference to the given field directly to make sure that we reference the proper alias here.
return new FieldReference(new ExposedField(field, exposedField.isSynthetic()));
}
return new FieldReference(exposedField);
}
if (name.contains(".")) {
// for nested field references we only check that the root field exists.
ExposedField rootField = exposedFields.getField(name.split("\\.")[0]);
if (rootField != null) {
// We have to synthetic to true, in order to render the field-name as is.
return new FieldReference(new ExposedField(name, true));
}
}
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name));
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
/**
* Abstraction for a field.
*
* @author Oliver Gierke
* @since 1.3
*/
public interface Field {
/**
* Returns the name of the field.
*
* @return must not be {@literal null}.
*/
String getName();
/**
* Returns the target of the field. In case no explicit target is available {@link #getName()} should be returned.
*
* @return must not be {@literal null}.
*/
String getTarget();
/**
* Returns whether the Field is aliased, which means it has a name set different from the target.
*
* @return
*/
boolean isAliased();
}

View File

@@ -0,0 +1,298 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Value object to capture a list of {@link Field} instances.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @since 1.3
*/
public final class Fields implements Iterable<Field> {
private static final String AMBIGUOUS_EXCEPTION = "Found two fields both using '%s' as name: %s and %s! Please "
+ "customize your field definitions to get to unique field names!";
public static final String UNDERSCORE_ID = "_id";
public static final String UNDERSCORE_ID_REF = "$_id";
private final List<Field> fields;
/**
* Creates a new {@link Fields} instance from the given {@link Fields}.
*
* @param fields must not be {@literal null} or empty.
* @return
*/
public static Fields from(Field... fields) {
Assert.notNull(fields, "Fields must not be null!");
return new Fields(Arrays.asList(fields));
}
/**
* Creates a new {@link Fields} instance for {@link Field}s with the given names.
*
* @param names must not be {@literal null}.
* @return
*/
public static Fields fields(String... names) {
Assert.notNull(names, "Field names must not be null!");
List<Field> fields = new ArrayList<Field>();
for (String name : names) {
fields.add(field(name));
}
return new Fields(fields);
}
/**
* Creates a {@link Field} with the given name.
*
* @param name must not be {@literal null} or empty.
* @return
*/
public static Field field(String name) {
return new AggregationField(name);
}
public static Field field(String name, String target) {
Assert.hasText(target, "Target must not be null or empty!");
return new AggregationField(name, target);
}
/**
* Creates a new {@link Fields} instance using the given {@link Field}s.
*
* @param fields must not be {@literal null}.
*/
private Fields(List<Field> fields) {
Assert.notNull(fields, "Fields must not be null!");
this.fields = verify(fields);
}
private static final List<Field> verify(List<Field> fields) {
Map<String, Field> reference = new HashMap<String, Field>();
for (Field field : fields) {
String name = field.getName();
Field found = reference.get(name);
if (found != null) {
throw new IllegalArgumentException(String.format(AMBIGUOUS_EXCEPTION, name, found, field));
}
reference.put(name, field);
}
return fields;
}
private Fields(Fields existing, Field tail) {
this.fields = new ArrayList<Field>(existing.fields.size() + 1);
this.fields.addAll(existing.fields);
this.fields.add(tail);
}
/**
* Creates a new {@link Fields} instance with a new {@link Field} of the given name added.
*
* @param name must not be {@literal null}.
* @return
*/
public Fields and(String name) {
return and(new AggregationField(name));
}
public Fields and(String name, String target) {
return and(new AggregationField(name, target));
}
public Fields and(Field field) {
return new Fields(this, field);
}
public Fields and(Fields fields) {
Fields result = this;
for (Field field : fields) {
result = result.and(field);
}
return result;
}
public Field getField(String name) {
for (Field field : fields) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Field> iterator() {
return fields.iterator();
}
/**
* Value object to encapsulate a field in an aggregation operation.
*
* @author Oliver Gierke
*/
static class AggregationField implements Field {
private final String name;
private final String target;
/**
* Creates an aggregation field with the given name. As no target is set explicitly, the name will be used as target
* as well.
*
* @param key
*/
public AggregationField(String key) {
this(key, null);
}
public AggregationField(String name, String target) {
String nameToSet = cleanUp(name);
String targetToSet = cleanUp(target);
Assert.hasText(nameToSet, "AggregationField name must not be null or empty!");
if (target == null && name.contains(".")) {
this.name = nameToSet.substring(nameToSet.indexOf('.') + 1);
this.target = nameToSet;
} else {
this.name = nameToSet;
this.target = targetToSet;
}
}
private static final String cleanUp(String source) {
if (source == null) {
return source;
}
if (Aggregation.SystemVariable.isReferingToSystemVariable(source)) {
return source;
}
int dollarIndex = source.lastIndexOf('$');
return dollarIndex == -1 ? source : source.substring(dollarIndex + 1);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#getKey()
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#getAlias()
*/
public String getTarget() {
return StringUtils.hasText(this.target) ? this.target : this.name;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Field#isAliased()
*/
@Override
public boolean isAliased() {
return !getName().equals(getTarget());
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("AggregationField - name: %s, target: %s", name, target);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AggregationField)) {
return false;
}
AggregationField that = (AggregationField) obj;
return this.name.equals(that.name) && ObjectUtils.nullSafeEquals(this.target, that.target);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * name.hashCode();
result += 31 * ObjectUtils.nullSafeHashCode(target);
return result;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
/**
* {@link AggregationOperation} that exposes new {@link ExposedFields} that can be used for later aggregation pipeline
* {@code AggregationOperation}s.
*
* @author Thomas Darimont
*/
public interface FieldsExposingAggregationOperation extends AggregationOperation {
/**
* Returns the fields exposed by the {@link AggregationOperation}.
*
* @return will never be {@literal null}.
*/
ExposedFields getFields();
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* @author Thomas Darimont
* @since 1.3
*/
public class GeoNearOperation implements AggregationOperation {
private final NearQuery nearQuery;
public GeoNearOperation(NearQuery nearQuery) {
Assert.notNull(nearQuery);
this.nearQuery = nearQuery;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$geoNear", context.getMappedObject(nearQuery.toDBObject()));
}
}

View File

@@ -0,0 +1,384 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $group}-operation.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/group/#stage._S_group
* @author Sebastian Herold
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class GroupOperation implements FieldsExposingAggregationOperation {
/**
* Holds the non-synthetic fields which are the fields of the group-id structure.
*/
private final ExposedFields idFields;
private final List<Operation> operations;
/**
* Creates a new {@link GroupOperation} including the given {@link Fields}.
*
* @param fields must not be {@literal null}.
*/
public GroupOperation(Fields fields) {
this.idFields = ExposedFields.nonSynthetic(fields);
this.operations = new ArrayList<Operation>();
}
/**
* Creates a new {@link GroupOperation} from the given {@link GroupOperation}.
*
* @param groupOperation must not be {@literal null}.
*/
protected GroupOperation(GroupOperation groupOperation) {
this(groupOperation, Collections.<Operation> emptyList());
}
/**
* Creates a new {@link GroupOperation} from the given {@link GroupOperation} and the given {@link Operation}s.
*
* @param groupOperation
* @param nextOperations
*/
private GroupOperation(GroupOperation groupOperation, List<Operation> nextOperations) {
Assert.notNull(groupOperation, "GroupOperation must not be null!");
Assert.notNull(nextOperations, "NextOperations must not be null!");
this.idFields = groupOperation.idFields;
this.operations = new ArrayList<Operation>(nextOperations.size() + 1);
this.operations.addAll(groupOperation.operations);
this.operations.addAll(nextOperations);
}
/**
* Creates a new {@link GroupOperation} from the current one adding the given {@link Operation}.
*
* @param operation must not be {@literal null}.
* @return
*/
protected GroupOperation and(Operation operation) {
return new GroupOperation(this, Arrays.asList(operation));
}
/**
* Builder for {@link GroupOperation}s on a field.
*
* @author Thomas Darimont
*/
public static final class GroupOperationBuilder {
private final GroupOperation groupOperation;
private final Operation operation;
/**
* Creates a new {@link GroupOperationBuilder} from the given {@link GroupOperation} and {@link Operation}.
*
* @param groupOperation
* @param operation
*/
private GroupOperationBuilder(GroupOperation groupOperation, Operation operation) {
Assert.notNull(groupOperation, "GroupOperation must not be null!");
Assert.notNull(operation, "Operation must not be null!");
this.groupOperation = groupOperation;
this.operation = operation;
}
/**
* Allows to specify an alias for the new-operation operation.
*
* @param alias
* @return
*/
public GroupOperation as(String alias) {
return this.groupOperation.and(operation.withAlias(alias));
}
}
/**
* Generates an {@link GroupOperationBuilder} for a {@code $sum}-expression.
* <p>
* Count expressions are emulated via {@code $sum: 1}.
* <p>
*
* @return
*/
public GroupOperationBuilder count() {
return newBuilder(GroupOps.SUM, null, 1);
}
/**
* Generates an {@link GroupOperationBuilder} for a {@code $sum}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder sum(String reference) {
return sum(reference, null);
}
private GroupOperationBuilder sum(String reference, Object value) {
return newBuilder(GroupOps.SUM, reference, value);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $add_to_set}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder addToSet(String reference) {
return addToSet(reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $add_to_set}-expression for the given value.
*
* @param value
* @return
*/
public GroupOperationBuilder addToSet(Object value) {
return addToSet(null, value);
}
private GroupOperationBuilder addToSet(String reference, Object value) {
return newBuilder(GroupOps.ADD_TO_SET, reference, value);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $last}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder last(String reference) {
return newBuilder(GroupOps.LAST, reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for a {@code $first}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder first(String reference) {
return newBuilder(GroupOps.FIRST, reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $avg}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder avg(String reference) {
return newBuilder(GroupOps.AVG, reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $push}-expression for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder push(String reference) {
return push(reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $push}-expression for the given value.
*
* @param value
* @return
*/
public GroupOperationBuilder push(Object value) {
return push(null, value);
}
private GroupOperationBuilder push(String reference, Object value) {
return newBuilder(GroupOps.PUSH, reference, value);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $min}-expression that for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder min(String reference) {
return newBuilder(GroupOps.MIN, reference, null);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $max}-expression that for the given field-reference.
*
* @param reference
* @return
*/
public GroupOperationBuilder max(String reference) {
return newBuilder(GroupOps.MAX, reference, null);
}
private GroupOperationBuilder newBuilder(Keyword keyword, String reference, Object value) {
return new GroupOperationBuilder(this, new Operation(keyword, null, reference, value));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields()
*/
@Override
public ExposedFields getFields() {
ExposedFields fields = this.idFields.and(new ExposedField(Fields.UNDERSCORE_ID, true));
for (Operation operation : operations) {
fields = fields.and(operation.asField());
}
return fields;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public com.mongodb.DBObject toDBObject(AggregationOperationContext context) {
BasicDBObject operationObject = new BasicDBObject();
if (idFields.exposesNoNonSyntheticFields()) {
operationObject.put(Fields.UNDERSCORE_ID, null);
} else if (idFields.exposesSingleNonSyntheticFieldOnly()) {
FieldReference reference = context.getReference(idFields.iterator().next());
operationObject.put(Fields.UNDERSCORE_ID, reference.toString());
} else {
BasicDBObject inner = new BasicDBObject();
for (ExposedField field : idFields) {
FieldReference reference = context.getReference(field);
inner.put(field.getName(), reference.toString());
}
operationObject.put(Fields.UNDERSCORE_ID, inner);
}
for (Operation operation : operations) {
operationObject.putAll(operation.toDBObject(context));
}
return new BasicDBObject("$group", operationObject);
}
interface Keyword {
String toString();
}
private static enum GroupOps implements Keyword {
SUM, LAST, FIRST, PUSH, AVG, MIN, MAX, ADD_TO_SET, COUNT;
@Override
public String toString() {
String[] parts = name().split("_");
StringBuilder builder = new StringBuilder();
for (String part : parts) {
String lowerCase = part.toLowerCase(Locale.US);
builder.append(builder.length() == 0 ? lowerCase : StringUtils.capitalize(lowerCase));
}
return "$" + builder.toString();
}
}
static class Operation implements AggregationOperation {
private final Keyword op;
private final String key;
private final String reference;
private final Object value;
public Operation(Keyword op, String key, String reference, Object value) {
this.op = op;
this.key = key;
this.reference = reference;
this.value = value;
}
public Operation withAlias(String key) {
return new Operation(op, key, reference, value);
}
public ExposedField asField() {
return new ExposedField(key, true);
}
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject(key, new BasicDBObject(op.toString(), getValue(context)));
}
public Object getValue(AggregationOperationContext context) {
if (reference == null) {
return value;
}
if (Aggregation.SystemVariable.isReferingToSystemVariable(reference)) {
return reference;
}
return context.getReference(reference).toString();
}
@Override
public String toString() {
return "Operation [op=" + op + ", key=" + key + ", reference=" + reference + ", value=" + value + "]";
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the {@code $limit}-operation
*
* @see http://docs.mongodb.org/manual/reference/aggregation/limit/
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
class LimitOperation implements AggregationOperation {
private final long maxElements;
/**
* @param maxElements Number of documents to consider.
*/
public LimitOperation(long maxElements) {
Assert.isTrue(maxElements >= 0, "Maximum number of elements must be greater or equal to zero!");
this.maxElements = maxElements;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$limit", maxElements);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the {@code $match}-operation
*
* @see http://docs.mongodb.org/manual/reference/aggregation/match/
* @author Sebastian Herold
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class MatchOperation implements AggregationOperation {
private final Criteria criteria;
/**
* Creates a new {@link MatchOperation} for the given {@link Criteria}.
*
* @param criteria must not be {@literal null}.
*/
public MatchOperation(Criteria criteria) {
Assert.notNull(criteria, "Criteria must not be null!");
this.criteria = criteria;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$match", context.getMappedObject(criteria.getCriteriaObject()));
}
}

View File

@@ -0,0 +1,940 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $project}-operation. Projection of field to be used in an
* {@link Aggregation}. A projection is similar to a {@link Field} inclusion/exclusion but more powerful. It can
* generate new fields, change values of given field etc.
* <p>
*
* @see http://docs.mongodb.org/manual/reference/aggregation/project/
* @author Tobias Trelle
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class ProjectionOperation implements FieldsExposingAggregationOperation {
private static final List<Projection> NONE = Collections.emptyList();
private static final String EXCLUSION_ERROR = "Exclusion of field %s not allowed. Projections by the mongodb "
+ "aggregation framework only support the exclusion of the %s field!";
private final List<Projection> projections;
/**
* Creates a new empty {@link ProjectionOperation}.
*/
public ProjectionOperation() {
this(NONE, NONE);
}
/**
* Creates a new {@link ProjectionOperation} including the given {@link Fields}.
*
* @param fields must not be {@literal null}.
*/
public ProjectionOperation(Fields fields) {
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields));
}
/**
* Copy constructor to allow building up {@link ProjectionOperation} instances from already existing
* {@link Projection}s.
*
* @param current must not be {@literal null}.
* @param projections must not be {@literal null}.
*/
private ProjectionOperation(List<? extends Projection> current, List<? extends Projection> projections) {
Assert.notNull(current, "Current projections must not be null!");
Assert.notNull(projections, "Projections must not be null!");
this.projections = new ArrayList<ProjectionOperation.Projection>(current.size() + projections.size());
this.projections.addAll(current);
this.projections.addAll(projections);
}
/**
* Creates a new {@link ProjectionOperation} with the current {@link Projection}s and the given one.
*
* @param projection must not be {@literal null}.
* @return
*/
private ProjectionOperation and(Projection projection) {
return new ProjectionOperation(this.projections, Arrays.asList(projection));
}
/**
* Creates a new {@link ProjectionOperation} with the current {@link Projection}s replacing the last current one with
* the given one.
*
* @param projection must not be {@literal null}.
* @return
*/
private ProjectionOperation andReplaceLastOneWith(Projection projection) {
List<Projection> projections = this.projections.isEmpty() ? Collections.<Projection> emptyList() : this.projections
.subList(0, this.projections.size() - 1);
return new ProjectionOperation(projections, Arrays.asList(projection));
}
/**
* Creates a new {@link ProjectionOperationBuilder} to define a projection for the field with the given name.
*
* @param name must not be {@literal null} or empty.
* @return
*/
public ProjectionOperationBuilder and(String name) {
return new ProjectionOperationBuilder(name, this, null);
}
public ExpressionProjectionOperationBuilder andExpression(String expression, Object... params) {
return new ExpressionProjectionOperationBuilder(expression, this, params);
}
/**
* Excludes the given fields from the projection.
*
* @param fieldNames must not be {@literal null}.
* @return
*/
public ProjectionOperation andExclude(String... fieldNames) {
for (String fieldName : fieldNames) {
Assert.isTrue(Fields.UNDERSCORE_ID.equals(fieldName),
String.format(EXCLUSION_ERROR, fieldName, Fields.UNDERSCORE_ID));
}
List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fieldNames), false);
return new ProjectionOperation(this.projections, excludeProjections);
}
/**
* Includes the given fields into the projection.
*
* @param fieldNames must not be {@literal null}.
* @return
*/
public ProjectionOperation andInclude(String... fieldNames) {
List<FieldProjection> projections = FieldProjection.from(Fields.fields(fieldNames), true);
return new ProjectionOperation(this.projections, projections);
}
/**
* Includes the given fields into the projection.
*
* @param fields must not be {@literal null}.
* @return
*/
public ProjectionOperation andInclude(Fields fields) {
return new ProjectionOperation(this.projections, FieldProjection.from(fields, true));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
*/
@Override
public ExposedFields getFields() {
ExposedFields fields = null;
for (Projection projection : projections) {
ExposedField field = projection.getExposedField();
fields = fields == null ? ExposedFields.from(field) : fields.and(field);
}
return fields;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
BasicDBObject fieldObject = new BasicDBObject();
for (Projection projection : projections) {
fieldObject.putAll(projection.toDBObject(context));
}
return new BasicDBObject("$project", fieldObject);
}
/**
* Base class for {@link ProjectionOperationBuilder}s.
*
* @author Thomas Darimont
*/
private static abstract class AbstractProjectionOperationBuilder implements AggregationOperation {
protected final Object value;
protected final ProjectionOperation operation;
/**
* Creates a new {@link AbstractProjectionOperationBuilder} fot the given value and {@link ProjectionOperation}.
*
* @param value must not be {@literal null}.
* @param operation must not be {@literal null}.
*/
public AbstractProjectionOperationBuilder(Object value, ProjectionOperation operation) {
Assert.notNull(value, "value must not be null or empty!");
Assert.notNull(operation, "ProjectionOperation must not be null!");
this.value = value;
this.operation = operation;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return this.operation.toDBObject(context);
}
/**
* Returns the finally to be applied {@link ProjectionOperation} with the given alias.
*
* @param alias will never be {@literal null} or empty.
* @return
*/
public abstract ProjectionOperation as(String alias);
}
/**
* An {@link ProjectionOperationBuilder} that is used for SpEL expression based projections.
*
* @author Thomas Darimont
*/
public static class ExpressionProjectionOperationBuilder extends ProjectionOperationBuilder {
private final Object[] params;
private final String expression;
/**
* Creates a new {@link ExpressionProjectionOperationBuilder} for the given value, {@link ProjectionOperation} and
* parameters.
*
* @param expression must not be {@literal null}.
* @param operation must not be {@literal null}.
* @param parameters
*/
public ExpressionProjectionOperationBuilder(String expression, ProjectionOperation operation, Object[] parameters) {
super(expression, operation, null);
this.expression = expression;
this.params = parameters.clone();
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder#project(java.lang.String, java.lang.Object[])
*/
@Override
public ProjectionOperationBuilder project(String operation, final Object... values) {
OperationProjection operationProjection = new OperationProjection(Fields.field(value.toString()), operation,
values) {
@Override
protected List<Object> getOperationArguments(AggregationOperationContext context) {
List<Object> result = new ArrayList<Object>(values.length + 1);
result.add(ExpressionProjection.toMongoExpression(context,
ExpressionProjectionOperationBuilder.this.expression, ExpressionProjectionOperationBuilder.this.params));
result.addAll(Arrays.asList(values));
return result;
}
};
return new ProjectionOperationBuilder(value, this.operation.and(operationProjection), operationProjection);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#as(java.lang.String)
*/
@Override
public ProjectionOperation as(String alias) {
Field expressionField = Fields.field(alias, alias);
return this.operation.and(new ExpressionProjection(expressionField, this.value.toString(), params));
}
/**
* A {@link Projection} based on a SpEL expression.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
static class ExpressionProjection extends Projection {
private static final SpelExpressionTransformer TRANSFORMER = new SpelExpressionTransformer();
private final String expression;
private final Object[] params;
/**
* Creates a new {@link ExpressionProjection} for the given field, SpEL expression and parameters.
*
* @param field must not be {@literal null}.
* @param expression must not be {@literal null} or empty.
* @param parameters must not be {@literal null}.
*/
public ExpressionProjection(Field field, String expression, Object[] parameters) {
super(field);
Assert.hasText(expression, "Expression must not be null!");
Assert.notNull(parameters, "Parameters must not be null!");
this.expression = expression;
this.params = parameters.clone();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject(getExposedField().getName(), toMongoExpression(context, expression, params));
}
protected static Object toMongoExpression(AggregationOperationContext context, String expression, Object[] params) {
return TRANSFORMER.transform(expression, context, params);
}
}
}
/**
* Builder for {@link ProjectionOperation}s on a field.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public static class ProjectionOperationBuilder extends AbstractProjectionOperationBuilder {
private static final String NUMBER_NOT_NULL = "Number must not be null!";
private static final String FIELD_REFERENCE_NOT_NULL = "Field reference must not be null!";
private final String name;
private final OperationProjection previousProjection;
/**
* Creates a new {@link ProjectionOperationBuilder} for the field with the given name on top of the given
* {@link ProjectionOperation}.
*
* @param name must not be {@literal null} or empty.
* @param operation must not be {@literal null}.
* @param previousProjection the previous operation projection, may be {@literal null}.
*/
public ProjectionOperationBuilder(String name, ProjectionOperation operation, OperationProjection previousProjection) {
super(name, operation);
this.name = name;
this.previousProjection = previousProjection;
}
/**
* Creates a new {@link ProjectionOperationBuilder} for the field with the given value on top of the given
* {@link ProjectionOperation}.
*
* @param value
* @param operation
* @param previousProjection
*/
protected ProjectionOperationBuilder(Object value, ProjectionOperation operation,
OperationProjection previousProjection) {
super(value, operation);
this.name = null;
this.previousProjection = previousProjection;
}
/**
* Projects the result of the previous operation onto the current field. Will automatically add an exclusion for
* {@code _id} as what would be held in it by default will now go into the field just projected into.
*
* @return
*/
public ProjectionOperation previousOperation() {
return this.operation.andExclude(Fields.UNDERSCORE_ID) //
.and(new PreviousOperationProjection(name));
}
/**
* Defines a nested field binding for the current field.
*
* @param fields must not be {@literal null}.
* @return
*/
public ProjectionOperation nested(Fields fields) {
return this.operation.and(new NestedFieldProjection(name, fields));
}
/**
* Allows to specify an alias for the previous projection operation.
*
* @param string
* @return
*/
@Override
public ProjectionOperation as(String alias) {
if (this.previousProjection != null) {
return this.operation.andReplaceLastOneWith(this.previousProjection.withAlias(alias));
} else {
return this.operation.and(new FieldProjection(Fields.field(alias, name), null));
}
}
/**
* Generates an {@code $add} expression that adds the given number to the previously mentioned field.
*
* @param number
* @return
*/
public ProjectionOperationBuilder plus(Number number) {
Assert.notNull(number, NUMBER_NOT_NULL);
return project("add", number);
}
/**
* Generates an {@code $add} expression that adds the value of the given field to the previously mentioned field.
*
* @param fieldReference
* @return
*/
public ProjectionOperationBuilder plus(String fieldReference) {
Assert.notNull(fieldReference, "Field reference must not be null!");
return project("add", Fields.field(fieldReference));
}
/**
* Generates an {@code $subtract} expression that subtracts the given number to the previously mentioned field.
*
* @param number
* @return
*/
public ProjectionOperationBuilder minus(Number number) {
Assert.notNull(number, "Number must not be null!");
return project("subtract", number);
}
/**
* Generates an {@code $subtract} expression that subtracts the value of the given field to the previously mentioned
* field.
*
* @param fieldReference
* @return
*/
public ProjectionOperationBuilder minus(String fieldReference) {
Assert.notNull(fieldReference, FIELD_REFERENCE_NOT_NULL);
return project("subtract", Fields.field(fieldReference));
}
/**
* Generates an {@code $multiply} expression that multiplies the given number with the previously mentioned field.
*
* @param number
* @return
*/
public ProjectionOperationBuilder multiply(Number number) {
Assert.notNull(number, NUMBER_NOT_NULL);
return project("multiply", number);
}
/**
* Generates an {@code $multiply} expression that multiplies the value of the given field with the previously
* mentioned field.
*
* @param fieldReference
* @return
*/
public ProjectionOperationBuilder multiply(String fieldReference) {
Assert.notNull(fieldReference, FIELD_REFERENCE_NOT_NULL);
return project("multiply", Fields.field(fieldReference));
}
/**
* Generates an {@code $divide} expression that divides the previously mentioned field by the given number.
*
* @param number
* @return
*/
public ProjectionOperationBuilder divide(Number number) {
Assert.notNull(number, FIELD_REFERENCE_NOT_NULL);
Assert.isTrue(Math.abs(number.intValue()) != 0, "Number must not be zero!");
return project("divide", number);
}
/**
* Generates an {@code $divide} expression that divides the value of the given field by the previously mentioned
* field.
*
* @param fieldReference
* @return
*/
public ProjectionOperationBuilder divide(String fieldReference) {
Assert.notNull(fieldReference, FIELD_REFERENCE_NOT_NULL);
return project("divide", Fields.field(fieldReference));
}
/**
* Generates an {@code $mod} expression that divides the previously mentioned field by the given number and returns
* the remainder.
*
* @param number
* @return
*/
public ProjectionOperationBuilder mod(Number number) {
Assert.notNull(number, NUMBER_NOT_NULL);
Assert.isTrue(Math.abs(number.intValue()) != 0, "Number must not be zero!");
return project("mod", number);
}
/**
* Generates an {@code $mod} expression that divides the value of the given field by the previously mentioned field
* and returns the remainder.
*
* @param fieldReference
* @return
*/
public ProjectionOperationBuilder mod(String fieldReference) {
Assert.notNull(fieldReference, FIELD_REFERENCE_NOT_NULL);
return project("mod", Fields.field(fieldReference));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return this.operation.toDBObject(context);
}
/**
* Adds a generic projection for the current field.
*
* @param operation the operation key, e.g. {@code $add}.
* @param values the values to be set for the projection operation.
* @return
*/
public ProjectionOperationBuilder project(String operation, Object... values) {
OperationProjection operationProjection = new OperationProjection(Fields.field(value.toString()), operation,
values);
return new ProjectionOperationBuilder(value, this.operation.and(operationProjection), operationProjection);
}
/**
* A {@link Projection} to pull in the result of the previous operation.
*
* @author Oliver Gierke
*/
static class PreviousOperationProjection extends Projection {
private final String name;
/**
* Creates a new {@link PreviousOperationProjection} for the field with the given name.
*
* @param name must not be {@literal null} or empty.
*/
public PreviousOperationProjection(String name) {
super(Fields.field(name));
this.name = name;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject(name, Fields.UNDERSCORE_ID_REF);
}
}
/**
* A {@link FieldProjection} to map a result of a previous {@link AggregationOperation} to a new field.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
static class FieldProjection extends Projection {
private final Field field;
private final Object value;
/**
* Creates a new {@link FieldProjection} for the field of the given name, assigning the given value.
*
* @param name must not be {@literal null} or empty.
* @param value
*/
public FieldProjection(String name, Object value) {
this(Fields.field(name), value);
}
private FieldProjection(Field field, Object value) {
super(field);
this.field = field;
this.value = value;
}
/**
* Factory method to easily create {@link FieldProjection}s for the given {@link Fields}. Fields are projected as
* references with their given name. A field {@code foo} will be projected as: {@code foo : 1 } .
*
* @param fields the {@link Fields} to in- or exclude, must not be {@literal null}.
* @return
*/
public static List<? extends Projection> from(Fields fields) {
return from(fields, null);
}
/**
* Factory method to easily create {@link FieldProjection}s for the given {@link Fields}.
*
* @param fields the {@link Fields} to in- or exclude, must not be {@literal null}.
* @param value to use for the given field.
* @return
*/
public static List<FieldProjection> from(Fields fields, Object value) {
Assert.notNull(fields, "Fields must not be null!");
List<FieldProjection> projections = new ArrayList<FieldProjection>();
for (Field field : fields) {
projections.add(new FieldProjection(field, value));
}
return projections;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject(field.getName(), renderFieldValue(context));
}
private Object renderFieldValue(AggregationOperationContext context) {
// implicit reference or explicit include?
if (value == null || Boolean.TRUE.equals(value)) {
if (Aggregation.SystemVariable.isReferingToSystemVariable(field.getTarget())) {
return field.getTarget();
}
// check whether referenced field exists in the context
return context.getReference(field).getReferenceValue();
} else if (Boolean.FALSE.equals(value)) {
// render field as excluded
return 0;
}
return value;
}
}
static class OperationProjection extends Projection {
private final Field field;
private final String operation;
private final List<Object> values;
/**
* Creates a new {@link OperationProjection} for the given field.
*
* @param field the name of the field to add the operation projection for, must not be {@literal null} or empty.
* @param operation the actual operation key, must not be {@literal null} or empty.
* @param values the values to pass into the operation, must not be {@literal null}.
*/
public OperationProjection(Field field, String operation, Object[] values) {
super(field);
Assert.hasText(operation, "Operation must not be null or empty!");
Assert.notNull(values, "Values must not be null!");
this.field = field;
this.operation = operation;
this.values = Arrays.asList(values);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
DBObject inner = new BasicDBObject("$" + operation, getOperationArguments(context));
return new BasicDBObject(getField().getName(), inner);
}
protected List<Object> getOperationArguments(AggregationOperationContext context) {
List<Object> result = new ArrayList<Object>(values.size());
result.add(context.getReference(getField().getName()).toString());
for (Object element : values) {
result.add(element instanceof Field ? context.getReference((Field) element).toString() : element);
}
return result;
}
/**
* Returns the field that holds the {@link OperationProjection}.
*
* @return
*/
protected Field getField() {
return field;
}
/**
* Creates a new instance of this {@link OperationProjection} with the given alias.
*
* @param alias the alias to set
* @return
*/
public OperationProjection withAlias(String alias) {
final Field aliasedField = Fields.field(alias, this.field.getName());
return new OperationProjection(aliasedField, operation, values.toArray()) {
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.OperationProjection#getField()
*/
@Override
protected Field getField() {
return aliasedField;
}
@Override
protected List<Object> getOperationArguments(AggregationOperationContext context) {
// We have to make sure that we use the arguments from the "previous" OperationProjection that we replace
// with this new instance.
return OperationProjection.this.getOperationArguments(context);
}
};
}
}
static class NestedFieldProjection extends Projection {
private final String name;
private final Fields fields;
public NestedFieldProjection(String name, Fields fields) {
super(Fields.field(name));
this.name = name;
this.fields = fields;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
DBObject nestedObject = new BasicDBObject();
for (Field field : fields) {
nestedObject.put(field.getName(), context.getReference(field.getTarget()).toString());
}
return new BasicDBObject(name, nestedObject);
}
}
/**
* Extracts the minute from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractMinute() {
return project("minute");
}
/**
* Extracts the hour from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractHour() {
return project("hour");
}
/**
* Extracts the second from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractSecond() {
return project("second");
}
/**
* Extracts the millisecond from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractMillisecond() {
return project("millisecond");
}
/**
* Extracts the year from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractYear() {
return project("year");
}
/**
* Extracts the month from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractMonth() {
return project("month");
}
/**
* Extracts the week from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractWeek() {
return project("week");
}
/**
* Extracts the dayOfYear from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractDayOfYear() {
return project("dayOfYear");
}
/**
* Extracts the dayOfMonth from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractDayOfMonth() {
return project("dayOfMonth");
}
/**
* Extracts the dayOfWeek from a date expression.
*
* @return
*/
public ProjectionOperationBuilder extractDayOfWeek() {
return project("dayOfWeek");
}
}
/**
* Base class for {@link Projection} implementations.
*
* @author Oliver Gierke
*/
private static abstract class Projection {
private final ExposedField field;
/**
* Creates new {@link Projection} for the given {@link Field}.
*
* @param field must not be {@literal null}.
*/
public Projection(Field field) {
Assert.notNull(field, "Field must not be null!");
this.field = new ExposedField(field, true);
}
/**
* Returns the field exposed by the {@link Projection}.
*
* @return will never be {@literal null}.
*/
public ExposedField getExposedField() {
return field;
}
/**
* Renders the current {@link Projection} into a {@link DBObject} based on the given
* {@link AggregationOperationContext}.
*
* @param context will never be {@literal null}.
* @return
*/
public abstract DBObject toDBObject(AggregationOperationContext context);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $skip}-operation.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/skip/
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class SkipOperation implements AggregationOperation {
private final long skipCount;
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param skipCount number of documents to skip.
*/
public SkipOperation(long skipCount) {
Assert.isTrue(skipCount >= 0, "Skip count must not be negative!");
this.skipCount = skipCount;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$skip", skipCount);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $sort}-operation.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/sort/#pipe._S_sort
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class SortOperation implements AggregationOperation {
private final Sort sort;
/**
* Creates a new {@link SortOperation} for the given {@link Sort} instance.
*
* @param sort must not be {@literal null}.
*/
public SortOperation(Sort sort) {
Assert.notNull(sort, "Sort must not be null!");
this.sort = sort;
}
public SortOperation and(Direction direction, String... fields) {
return and(new Sort(direction, fields));
}
public SortOperation and(Sort sort) {
return new SortOperation(this.sort.and(sort));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
BasicDBObject object = new BasicDBObject();
for (Order order : sort) {
// Check reference
FieldReference reference = context.getReference(order.getProperty());
object.put(reference.getRaw(), order.isAscending() ? 1 : -1);
}
return new BasicDBObject("$sort", object);
}
}

View File

@@ -0,0 +1,513 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.springframework.data.mongodb.util.DBObjectUtils.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.mongodb.core.spel.ExpressionNode;
import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
import org.springframework.data.mongodb.core.spel.LiteralNode;
import org.springframework.data.mongodb.core.spel.MethodReferenceNode;
import org.springframework.data.mongodb.core.spel.OperatorNode;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.Indexer;
import org.springframework.expression.spel.ast.InlineList;
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Renders the AST of a SpEL expression as a MongoDB Aggregation Framework projection expression.
*
* @author Thomas Darimont
*/
class SpelExpressionTransformer implements AggregationExpressionTransformer {
// TODO: remove explicit usage of a configuration once SPR-11031 gets fixed
private static final SpelParserConfiguration CONFIG = new SpelParserConfiguration(false, false);
private static final SpelExpressionParser PARSER = new SpelExpressionParser(CONFIG);
private final List<ExpressionNodeConversion<? extends ExpressionNode>> conversions;
/**
* Creates a new {@link SpelExpressionTransformer}.
*/
public SpelExpressionTransformer() {
List<ExpressionNodeConversion<? extends ExpressionNode>> conversions = new ArrayList<ExpressionNodeConversion<? extends ExpressionNode>>();
conversions.add(new OperatorNodeConversion(this));
conversions.add(new LiteralNodeConversion(this));
conversions.add(new IndexerNodeConversion(this));
conversions.add(new InlineListNodeConversion(this));
conversions.add(new PropertyOrFieldReferenceNodeConversion(this));
conversions.add(new CompoundExpressionNodeConversion(this));
conversions.add(new MethodReferenceNodeConversion(this));
this.conversions = Collections.unmodifiableList(conversions);
}
/**
* Transforms the given SpEL expression to a corresponding MongoDB expression against the given
* {@link AggregationOperationContext} {@code context}.
* <p>
* Exposes the given @{code params} as <code>[0] ... [n]</code>.
*
* @param expression must not be {@literal null}
* @param context must not be {@literal null}
* @param params must not be {@literal null}
* @return
*/
public Object transform(String expression, AggregationOperationContext context, Object... params) {
Assert.notNull(expression, "Expression must not be null!");
Assert.notNull(context, "AggregationOperationContext must not be null!");
Assert.notNull(params, "Parameters must not be null!");
SpelExpression spelExpression = (SpelExpression) PARSER.parseExpression(expression);
ExpressionState state = new ExpressionState(new StandardEvaluationContext(params), CONFIG);
ExpressionNode node = ExpressionNode.from(spelExpression.getAST(), state);
return transform(new AggregationExpressionTransformationContext<ExpressionNode>(node, null, null, context));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.spel.ExpressionTransformer#transform(org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport)
*/
public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
return lookupConversionFor(context.getCurrentNode()).convert(context);
}
/**
* Returns an appropriate {@link ExpressionNodeConversion} for the given {@code node}. Throws an
* {@link IllegalArgumentException} if no conversion could be found.
*
* @param node
* @return the appropriate {@link ExpressionNodeConversion} for the given {@link ExpressionNode}.
*/
@SuppressWarnings("unchecked")
private ExpressionNodeConversion<ExpressionNode> lookupConversionFor(ExpressionNode node) {
for (ExpressionNodeConversion<? extends ExpressionNode> candidate : conversions) {
if (candidate.supports(node)) {
return (ExpressionNodeConversion<ExpressionNode>) candidate;
}
}
throw new IllegalArgumentException("Unsupported Element: " + node + " Type: " + node.getClass()
+ " You probably have a syntax error in your SpEL expression!");
}
/**
* Abstract base class for {@link SpelNode} to (Db)-object conversions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static abstract class ExpressionNodeConversion<T extends ExpressionNode> implements
AggregationExpressionTransformer {
private final AggregationExpressionTransformer transformer;
private final Class<? extends ExpressionNode> nodeType;
/**
* Creates a new {@link ExpressionNodeConversion}.
*
* @param transformer must not be {@literal null}.
*/
@SuppressWarnings("unchecked")
public ExpressionNodeConversion(AggregationExpressionTransformer transformer) {
Assert.notNull(transformer, "Transformer must not be null!");
this.nodeType = (Class<? extends ExpressionNode>) GenericTypeResolver.resolveTypeArgument(this.getClass(),
ExpressionNodeConversion.class);
this.transformer = transformer;
}
/**
* Returns whether the current conversion supports the given {@link ExpressionNode}. By default we will match the
* node type against the genric type the subclass types the type parameter to.
*
* @param node will never be {@literal null}.
* @return true if {@literal this} conversion can be applied to the given {@code node}.
*/
protected boolean supports(ExpressionNode node) {
return nodeType.equals(node.getClass());
}
/**
* Triggers the transformation for the given {@link ExpressionNode} and the given current context.
*
* @param node must not be {@literal null}.
* @param context must not be {@literal null}.
* @return
*/
protected Object transform(ExpressionNode node, AggregationExpressionTransformationContext<?> context) {
Assert.notNull(node, "ExpressionNode must not be null!");
Assert.notNull(context, "AggregationExpressionTransformationContext must not be null!");
return transform(node, context.getParentNode(), null, context);
}
/**
* Triggers the transformation with the given new {@link ExpressionNode}, new parent node, the current operation and
* the previous context.
*
* @param node must not be {@literal null}.
* @param parent
* @param operation
* @param context must not be {@literal null}.
* @return
*/
protected Object transform(ExpressionNode node, ExpressionNode parent, DBObject operation,
AggregationExpressionTransformationContext<?> context) {
Assert.notNull(node, "ExpressionNode must not be null!");
Assert.notNull(context, "AggregationExpressionTransformationContext must not be null!");
return transform(new AggregationExpressionTransformationContext<ExpressionNode>(node, parent, operation,
context.getAggregationContext()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#transform(org.springframework.data.mongodb.core.aggregation.AggregationExpressionTransformer.AggregationExpressionTransformationContext)
*/
@Override
public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
return transformer.transform(context);
}
/**
* Performs the actual conversion from {@link SpelNode} to the corresponding representation for MongoDB.
*
* @param context
* @return
*/
protected abstract Object convert(AggregationExpressionTransformationContext<T> context);
}
/**
* A {@link ExpressionNodeConversion} that converts arithmetic operations.
*
* @author Thomas Darimont
*/
private static class OperatorNodeConversion extends ExpressionNodeConversion<OperatorNode> {
public OperatorNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<OperatorNode> context) {
OperatorNode currentNode = context.getCurrentNode();
DBObject operationObject = createOperationObjectAndAddToPreviousArgumentsIfNecessary(context, currentNode);
Object leftResult = transform(currentNode.getLeft(), currentNode, operationObject, context);
if (currentNode.isUnaryMinus()) {
return convertUnaryMinusOp(context, leftResult);
}
// we deliberately ignore the RHS result
transform(currentNode.getRight(), currentNode, operationObject, context);
return operationObject;
}
private DBObject createOperationObjectAndAddToPreviousArgumentsIfNecessary(
AggregationExpressionTransformationContext<OperatorNode> context, OperatorNode currentNode) {
DBObject nextDbObject = new BasicDBObject(currentNode.getMongoOperator(), new BasicDBList());
if (!context.hasPreviousOperation()) {
return nextDbObject;
}
if (context.parentIsSameOperation()) {
// same operator applied in a row e.g. 1 + 2 + 3 carry on with the operation and render as $add: [1, 2 ,3]
nextDbObject = context.getPreviousOperationObject();
} else if (!currentNode.isUnaryOperator()) {
// different operator -> add context object for next level to list if arguments of previous expression
context.addToPreviousOperation(nextDbObject);
}
return nextDbObject;
}
private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<OperatorNode> context, Object leftResult) {
Object result = leftResult instanceof Number ? leftResult
: new BasicDBObject("$multiply", dbList(-1, leftResult));
if (leftResult != null && context.hasPreviousOperation()) {
context.addToPreviousOperation(result);
}
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#supports(java.lang.Class)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isMathematicalOperation();
}
}
/**
* A {@link ExpressionNodeConversion} that converts indexed expressions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static class IndexerNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
public IndexerNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
return context.addToPreviousOrReturn(context.getCurrentNode().getValue());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(Indexer.class);
}
}
/**
* A {@link ExpressionNodeConversion} that converts in-line list expressions.
*
* @author Thomas Darimont
*/
private static class InlineListNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
public InlineListNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
ExpressionNode currentNode = context.getCurrentNode();
if (!currentNode.hasChildren()) {
return null;
}
// just take the first item
return transform(currentNode.getChild(0), currentNode, null, context);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(InlineList.class);
}
}
/**
* A {@link ExpressionNodeConversion} that converts property or field reference expressions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static class PropertyOrFieldReferenceNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
public PropertyOrFieldReferenceNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#convert(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionTransformationContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
String fieldReference = context.getFieldReference().toString();
return context.addToPreviousOrReturn(fieldReference);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(PropertyOrFieldReference.class);
}
}
/**
* A {@link ExpressionNodeConversion} that converts literal expressions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static class LiteralNodeConversion extends ExpressionNodeConversion<LiteralNode> {
public LiteralNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
@SuppressWarnings("unchecked")
protected Object convert(AggregationExpressionTransformationContext<LiteralNode> context) {
LiteralNode node = context.getCurrentNode();
Object value = node.getValue();
if (context.hasPreviousOperation()) {
if (node.isUnaryMinus(context.getParentNode())) {
// unary minus operator
return NumberUtils.convertNumberToTargetClass(((Number) value).doubleValue() * -1,
(Class<Number>) value.getClass()); // retain type, e.g. int to -int
}
return context.addToPreviousOperation(value);
}
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#supports(org.springframework.expression.spel.SpelNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isLiteral();
}
}
/**
* A {@link ExpressionNodeConversion} that converts method reference expressions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static class MethodReferenceNodeConversion extends ExpressionNodeConversion<MethodReferenceNode> {
public MethodReferenceNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) {
MethodReferenceNode node = context.getCurrentNode();
List<Object> args = new ArrayList<Object>();
for (ExpressionNode childNode : node) {
args.add(transform(childNode, context));
}
return context.addToPreviousOrReturn(new BasicDBObject(node.getMethodName(), dbList(args.toArray())));
}
}
/**
* A {@link ExpressionNodeConversion} that converts method compound expressions.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
private static class CompoundExpressionNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
public CompoundExpressionNodeConversion(AggregationExpressionTransformer transformer) {
super(transformer);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
*/
@Override
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
ExpressionNode currentNode = context.getCurrentNode();
if (currentNode.hasfirstChildNotOfType(Indexer.class)) {
// we have a property path expression like: foo.bar -> render as reference
return context.addToPreviousOrReturn(context.getFieldReference().toString());
}
return context.addToPreviousOrReturn(currentNode.getValue());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
*/
@Override
protected boolean supports(ExpressionNode node) {
return node.isOfType(CompoundExpression.class);
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* {@link AggregationOperationContext} aware of a particular type and a {@link MappingContext} to potentially translate
* property references into document field names.
*
* @author Oliver Gierke
* @since 1.3
*/
public class TypeBasedAggregationOperationContext implements AggregationOperationContext {
private final Class<?> type;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final QueryMapper mapper;
/**
* Creates a new {@link TypeBasedAggregationOperationContext} for the given type, {@link MappingContext} and
* {@link QueryMapper}.
*
* @param type must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
* @param mapper must not be {@literal null}.
*/
public TypeBasedAggregationOperationContext(Class<?> type,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, QueryMapper mapper) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(mappingContext, "MappingContext must not be null!");
Assert.notNull(mapper, "QueryMapper must not be null!");
this.type = type;
this.mappingContext = mappingContext;
this.mapper = mapper;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
*/
@Override
public DBObject getMappedObject(DBObject dbObject) {
return mapper.getMappedObject(dbObject, mappingContext.getPersistentEntity(type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.Field)
*/
@Override
public FieldReference getReference(Field field) {
PropertyPath.from(field.getTarget(), type);
return getReferenceFor(field);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
*/
@Override
public FieldReference getReference(String name) {
return getReferenceFor(field(name));
}
private FieldReference getReferenceFor(Field field) {
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(
field.getTarget(), type);
Field mappedField = field(propertyPath.getLeafProperty().getName(),
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
return new FieldReference(new ExposedField(mappedField, true));
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.List;
import org.springframework.util.Assert;
/**
* A {@code TypedAggregation} is a special {@link Aggregation} that holds information of the input aggregation type.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
public class TypedAggregation<I> extends Aggregation {
private final Class<I> inputType;
/**
* Creates a new {@link TypedAggregation} from the given {@link AggregationOperation}s.
*
* @param inputType must not be {@literal null}.
* @param operations must not be {@literal null} or empty.
*/
public TypedAggregation(Class<I> inputType, AggregationOperation... operations) {
this(inputType, asAggregationList(operations));
}
/**
* Creates a new {@link TypedAggregation} from the given {@link AggregationOperation}s.
*
* @param inputType must not be {@literal null}.
* @param operations must not be {@literal null} or empty.
*/
public TypedAggregation(Class<I> inputType, List<AggregationOperation> operations) {
this(inputType, operations, DEFAULT_OPTIONS);
}
/**
* Creates a new {@link TypedAggregation} from the given {@link AggregationOperation}s and the given
* {@link AggregationOptions}.
*
* @param inputType must not be {@literal null}.
* @param operations must not be {@literal null} or empty.
* @param options must not be {@literal null}.
*/
public TypedAggregation(Class<I> inputType, List<AggregationOperation> operations, AggregationOptions options) {
super(operations, options);
Assert.notNull(inputType, "Input type must not be null!");
this.inputType = inputType;
}
/**
* Returns the input type for the {@link Aggregation}.
*
* @return the inputType will never be {@literal null}.
*/
public Class<I> getInputType() {
return inputType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.Aggregation#withOptions(org.springframework.data.mongodb.core.aggregation.AggregationOptions)
*/
public TypedAggregation<I> withOptions(AggregationOptions options) {
Assert.notNull(options, "AggregationOptions must not be null.");
return new TypedAggregation<I>(inputType, operations, options);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $unwind}-operation.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/unwind/#pipe._S_unwind
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.3
*/
public class UnwindOperation implements AggregationOperation {
private final ExposedField field;
/**
* Creates a new {@link UnwindOperation} for the given {@link Field}.
*
* @param field must not be {@literal null}.
*/
public UnwindOperation(Field field) {
Assert.notNull(field);
this.field = new ExposedField(field, true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$unwind", context.getReference(field).toString());
}
}

View File

@@ -0,0 +1,5 @@
/**
* Support for the MongoDB aggregation framework.
* @since 1.3
*/
package org.springframework.data.mongodb.core.aggregation;

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,43 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import java.math.BigInteger;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter;
/**
* Base class for {@link MongoConverter} implementations. Sets up a {@link GenericConversionService} and populates basic
* converters. Allows registering {@link CustomConversions}.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke ogierke@vmware.com
* @author Jon Brisbin
* @author Oliver Gierke
*/
public abstract class AbstractMongoConverter implements MongoConverter, InitializingBean {
protected final GenericConversionService conversionService;
protected CustomConversions conversions = new CustomConversions();
protected EntityInstantiators instantiators = new EntityInstantiators();
/**
* Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}.
*
* @param conversionService
*/
@SuppressWarnings("deprecation")
public AbstractMongoConverter(GenericConversionService conversionService) {
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
: conversionService;
this.conversionService.removeConvertible(Object.class, String.class);
}
/**
@@ -61,6 +61,15 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
this.conversions = conversions;
}
/**
* Registers {@link EntityInstantiators} to customize entity instantiation.
*
* @param instantiators
*/
public void setInstantiators(EntityInstantiators instantiators) {
this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators;
}
/**
* Registers additional converters that will be available when using the {@link ConversionService} directly (e.g. for
* id conversion). These converters are not custom conversions as they'd introduce unwanted conversions (e.g.
@@ -80,16 +89,18 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) {
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(BigInteger.class, String.class)) {
conversionService.addConverter(BigIntegerToStringConverter.INSTANCE);
}
if (!conversionService.canConvert(String.class, BigInteger.class)) {
conversionService.addConverter(StringToBigIntegerConverter.INSTANCE);
}
conversions.registerConvertersIn(conversionService);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoWriter#convertToMongoType(java.lang.Object)
*/
public Object convertToMongoType(Object obj) {
return convertToMongoType(obj, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.core.convert.MongoConverter#getConversionService()

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.util.Assert;
/**
* Conversion registration information.
*
* @author Oliver Gierke
*/
class ConverterRegistration {
private final ConvertiblePair convertiblePair;
private final boolean reading;
private final boolean writing;
/**
* Creates a new {@link ConverterRegistration}.
*
* @param convertiblePair must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWritingwhether to force to consider the converter for reading.
*/
public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) {
Assert.notNull(convertiblePair);
this.convertiblePair = convertiblePair;
this.reading = isReading;
this.writing = isWriting;
}
/**
* Creates a new {@link ConverterRegistration} from the given source and target type and read/write flags.
*
* @param source the source type to be converted from, must not be {@literal null}.
* @param target the target type to be converted to, must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWriting whether to force to consider the converter for writing.
*/
public ConverterRegistration(Class<?> source, Class<?> target, boolean isReading, boolean isWriting) {
this(new ConvertiblePair(source, target), isReading, isWriting);
}
/**
* Returns whether the converter shall be used for writing.
*
* @return
*/
public boolean isWriting() {
return writing == true || (!reading && isSimpleTargetType());
}
/**
* Returns whether the converter shall be used for reading.
*
* @return
*/
public boolean isReading() {
return reading == true || (!writing && isSimpleSourceType());
}
/**
* Returns the actual conversion pair.
*
* @return
*/
public ConvertiblePair getConvertiblePair() {
return convertiblePair;
}
/**
* Returns whether the source type is a Mongo simple one.
*
* @return
*/
public boolean isSimpleSourceType() {
return isMongoBasicType(convertiblePair.getSourceType());
}
/**
* Returns whether the target type is a Mongo simple one.
*
* @return
*/
public boolean isSimpleTargetType() {
return isMongoBasicType(convertiblePair.getTargetType());
}
/**
* Returns whether the given type is a type that Mongo can handle basically.
*
* @param type
* @return
*/
private static boolean isMongoBasicType(Class<?> type) {
return MongoSimpleTypes.HOLDER.isSimpleType(type);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,13 +17,17 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
@@ -31,14 +35,20 @@ import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.JodaTimeConverters;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.DBObjectToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToURLConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.URLToStringConverter;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Value object to capture custom conversion. That is essentially a {@link List} of converters and some additional logic
* around them. The converters are pretty much builds up two sets of types which Mongo basic types {@see #MONGO_TYPES}
@@ -47,17 +57,19 @@ import com.mongodb.DBObject;
* .
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class CustomConversions {
@SuppressWarnings({ "unchecked" })
private static final List<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, ObjectId.class,
String.class, DBObject.class);
private static final Logger LOG = LoggerFactory.getLogger(CustomConversions.class);
private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a Mongo supported type! You might wanna check you annotation setup at the converter implementation.";
private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a Mongo supported type! You might wanna check you annotation setup at the converter implementation.";
private final Set<ConvertiblePair> readingPairs;
private final Set<ConvertiblePair> writingPairs;
private final Set<Class<?>> customSimpleTypes;
private final SimpleTypeHolder simpleTypeHolder;
private final ConcurrentMap<ConvertiblePair, CacheValue> customReadTargetTypes;
private final List<Object> converters;
@@ -77,20 +89,34 @@ public class CustomConversions {
Assert.notNull(converters);
this.readingPairs = new HashSet<ConvertiblePair>();
this.writingPairs = new HashSet<ConvertiblePair>();
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
this.customSimpleTypes = new HashSet<Class<?>>();
this.customReadTargetTypes = new ConcurrentHashMap<GenericConverter.ConvertiblePair, CacheValue>();
this.converters = new ArrayList<Object>();
this.converters.add(CustomToStringConverter.INSTANCE);
this.converters.add(BigDecimalToStringConverter.INSTANCE);
this.converters.add(StringToBigDecimalConverter.INSTANCE);
this.converters.addAll(converters);
List<Object> toRegister = new ArrayList<Object>();
for (Object c : this.converters) {
// Add user provided converters to make sure they can override the defaults
toRegister.addAll(converters);
toRegister.add(CustomToStringConverter.INSTANCE);
toRegister.add(BigDecimalToStringConverter.INSTANCE);
toRegister.add(StringToBigDecimalConverter.INSTANCE);
toRegister.add(BigIntegerToStringConverter.INSTANCE);
toRegister.add(StringToBigIntegerConverter.INSTANCE);
toRegister.add(URLToStringConverter.INSTANCE);
toRegister.add(StringToURLConverter.INSTANCE);
toRegister.add(DBObjectToStringConverter.INSTANCE);
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
toRegister.addAll(GeoConverters.getConvertersToRegister());
for (Object c : toRegister) {
registerConversion(c);
}
Collections.reverse(toRegister);
this.converters = Collections.unmodifiableList(toRegister);
this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, MongoSimpleTypes.HOLDER);
}
@@ -155,14 +181,18 @@ public class CustomConversions {
*/
private void registerConversion(Object converter) {
Class<?> type = converter.getClass();
boolean isWriting = type.isAnnotationPresent(WritingConverter.class);
boolean isReading = type.isAnnotationPresent(ReadingConverter.class);
if (converter instanceof GenericConverter) {
GenericConverter genericConverter = (GenericConverter) converter;
for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) {
register(pair);
register(new ConverterRegistration(pair, isReading, isWriting));
}
} else if (converter instanceof Converter) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
register(new ConvertiblePair(arguments[0], arguments[1]));
register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting));
} else {
throw new IllegalArgumentException("Unsupported Converter type!");
}
@@ -174,15 +204,27 @@ public class CustomConversions {
*
* @param pair
*/
private void register(ConvertiblePair pair) {
private void register(ConverterRegistration converterRegistration) {
ConvertiblePair pair = converterRegistration.getConvertiblePair();
if (converterRegistration.isReading()) {
if (isMongoBasicType(pair.getSourceType())) {
readingPairs.add(pair);
if (LOG.isWarnEnabled() && !converterRegistration.isSimpleSourceType()) {
LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
}
}
if (isMongoBasicType(pair.getTargetType())) {
if (converterRegistration.isWriting()) {
writingPairs.add(pair);
customSimpleTypes.add(pair.getSourceType());
if (LOG.isWarnEnabled() && !converterRegistration.isSimpleTargetType()) {
LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
}
}
}
@@ -190,81 +232,90 @@ public class CustomConversions {
* Returns the target type to convert to in case we have a custom conversion registered to convert the given source
* type into a Mongo native one.
*
* @param source must not be {@literal null}
* @param sourceType must not be {@literal null}
* @return
*/
public Class<?> getCustomWriteTarget(Class<?> source) {
return getCustomWriteTarget(source, null);
public Class<?> getCustomWriteTarget(Class<?> sourceType) {
return getCustomWriteTarget(sourceType, null);
}
/**
* Returns the target type we can write an onject of the given source type to. The returned type might be a subclass
* oth the given expected type though. If {@code expexctedTargetType} is {@literal null} we will simply return the
* oth the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply return the
* first target type matching or {@literal null} if no conversion can be found.
*
* @param source must not be {@literal null}
* @param expectedTargetType
* @param sourceType must not be {@literal null}
* @param requestedTargetType
* @return
*/
public Class<?> getCustomWriteTarget(Class<?> source, Class<?> expectedTargetType) {
Assert.notNull(source);
return getCustomTarget(source, expectedTargetType, writingPairs);
public Class<?> getCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
}
/**
* Returns whether we have a custom conversion registered to write into a Mongo native type. The returned type might
* be a subclass oth the given expected type though.
* be a subclass of the given expected type though.
*
* @param source must not be {@literal null}
* @param sourceType must not be {@literal null}
* @return
*/
public boolean hasCustomWriteTarget(Class<?> source) {
return hasCustomWriteTarget(source, null);
public boolean hasCustomWriteTarget(Class<?> sourceType) {
Assert.notNull(sourceType);
return hasCustomWriteTarget(sourceType, null);
}
/**
* Returns whether we have a custom conversion registered to write an object of the given source type into an object
* of the given Mongo native target type.
*
* @param source must not be {@literal null}.
* @param expectedTargetType
* @param sourceType must not be {@literal null}.
* @param requestedTargetType
* @return
*/
public boolean hasCustomWriteTarget(Class<?> source, Class<?> expectedTargetType) {
return getCustomWriteTarget(source, expectedTargetType) != null;
public boolean hasCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
return getCustomWriteTarget(sourceType, requestedTargetType) != null;
}
/**
* Returns whether we have a custom conversion registered to read the given source into the given target type.
*
* @param source must not be {@literal null}
* @param expectedTargetType must not be {@literal null}
* @param sourceType must not be {@literal null}
* @param requestedTargetType must not be {@literal null}
* @return
*/
public boolean hasCustomReadTarget(Class<?> source, Class<?> expectedTargetType) {
Assert.notNull(source);
Assert.notNull(expectedTargetType);
return getCustomTarget(source, expectedTargetType, readingPairs) != null;
public boolean hasCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
Assert.notNull(requestedTargetType);
return getCustomReadTarget(sourceType, requestedTargetType) != null;
}
/**
* Inspects the given {@link ConvertiblePair} for ones that have a source compatible type as source. Additionally
* checks assignabilty of the target type if one is given.
* checks assignability of the target type if one is given.
*
* @param source must not be {@literal null}
* @param expectedTargetType
* @param pairs must not be {@literal null}
* @param sourceType must not be {@literal null}.
* @param requestedTargetType can be {@literal null}.
* @param pairs must not be {@literal null}.
* @return
*/
private static Class<?> getCustomTarget(Class<?> source, Class<?> expectedTargetType, Iterable<ConvertiblePair> pairs) {
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
Iterable<ConvertiblePair> pairs) {
Assert.notNull(source);
Assert.notNull(sourceType);
Assert.notNull(pairs);
for (ConvertiblePair typePair : pairs) {
if (typePair.getSourceType().isAssignableFrom(source)) {
if (typePair.getSourceType().isAssignableFrom(sourceType)) {
Class<?> targetType = typePair.getTargetType();
if (expectedTargetType == null || targetType.isAssignableFrom(expectedTargetType)) {
if (requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType)) {
return targetType;
}
}
@@ -274,21 +325,44 @@ public class CustomConversions {
}
/**
* Returns whether the given type is a type that Mongo can handle basically.
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
*
* @param type
* @param sourceType must not be {@literal null}.
* @param requestedTargetType can be {@literal null}.
* @return
*/
private static boolean isMongoBasicType(Class<?> type) {
return MONGO_TYPES.contains(type);
private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
if (requestedTargetType == null) {
return null;
}
ConvertiblePair lookupKey = new ConvertiblePair(sourceType, requestedTargetType);
CacheValue readTargetTypeValue = customReadTargetTypes.get(lookupKey);
if (readTargetTypeValue != null) {
return readTargetTypeValue.getType();
}
readTargetTypeValue = CacheValue.of(getCustomTarget(sourceType, requestedTargetType, readingPairs));
CacheValue cacheValue = customReadTargetTypes.putIfAbsent(lookupKey, readTargetTypeValue);
return cacheValue != null ? cacheValue.getType() : readTargetTypeValue.getType();
}
@WritingConverter
private enum CustomToStringConverter implements GenericConverter {
INSTANCE;
public Set<ConvertiblePair> getConvertibleTypes() {
ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class);
ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class);
return new HashSet<ConvertiblePair>(Arrays.asList(localeToString, booleanToString));
}
@@ -296,4 +370,30 @@ public class CustomConversions {
return source.toString();
}
}
/**
* Wrapper to safely store {@literal null} values in the type cache.
*
* @author Patryk Wasik
* @author Oliver Gierke
* @author Thomas Darimont
*/
private static class CacheValue {
private static final CacheValue ABSENT = new CacheValue(null);
private final Class<?> type;
public CacheValue(Class<?> type) {
this.type = type;
}
public Class<?> getType() {
return type;
}
static CacheValue of(Class<?> type) {
return type == null ? ABSENT : new CacheValue(type);
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Wrapper value object for a {@link BasicDBObject} to be able to access raw values by {@link MongoPersistentProperty}
* references. The accessors will transparently resolve nested document values that a {@link MongoPersistentProperty}
* might refer to through a path expression in field names.
*
* @author Oliver Gierke
*/
class DBObjectAccessor {
private final DBObject dbObject;
/**
* Creates a new {@link DBObjectAccessor} for the given {@link DBObject}.
*
* @param dbObject must be a {@link BasicDBObject} effectively, must not be {@literal null}.
*/
public DBObjectAccessor(DBObject dbObject) {
Assert.notNull(dbObject, "DBObject must not be null!");
Assert.isInstanceOf(BasicDBObject.class, dbObject, "Given DBObject must be a BasicDBObject!");
this.dbObject = dbObject;
}
/**
* Puts the given value into the backing {@link DBObject} based on the coordinates defined through the given
* {@link MongoPersistentProperty}. By default this will be the plain field name. But field names might also consist
* of path traversals so we might need to create intermediate {@link BasicDBObject}s.
*
* @param prop must not be {@literal null}.
* @param value
*/
public void put(MongoPersistentProperty prop, Object value) {
Assert.notNull(prop, "MongoPersistentProperty must not be null!");
String fieldName = prop.getFieldName();
Iterator<String> parts = Arrays.asList(fieldName.split("\\.")).iterator();
DBObject dbObject = this.dbObject;
while (parts.hasNext()) {
String part = parts.next();
if (parts.hasNext()) {
BasicDBObject nestedDbObject = new BasicDBObject();
dbObject.put(part, nestedDbObject);
dbObject = nestedDbObject;
} else {
dbObject.put(part, value);
}
}
}
/**
* Returns the value the given {@link MongoPersistentProperty} refers to. By default this will be a direct field but
* the method will also transparently resolve nested values the {@link MongoPersistentProperty} might refer to through
* a path expression in the field name metadata.
*
* @param property must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public Object get(MongoPersistentProperty property) {
String fieldName = property.getFieldName();
Iterator<String> parts = Arrays.asList(fieldName.split("\\.")).iterator();
Map<Object, Object> source = this.dbObject.toMap();
Object result = null;
while (source != null && parts.hasNext()) {
result = source.get(parts.next());
if (parts.hasNext()) {
source = getAsMap(result);
}
}
return result;
}
@SuppressWarnings("unchecked")
private Map<Object, Object> getAsMap(Object source) {
if (source instanceof BasicDBObject) {
return ((DBObject) source).toMap();
}
if (source instanceof Map) {
return (Map<Object, Object>) source;
}
return null;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2012-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Map;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import com.mongodb.DBObject;
/**
* {@link PropertyAccessor} to allow entity based field access to {@link DBObject}s.
*
* @author Oliver Gierke
*/
class DBObjectPropertyAccessor extends MapAccessor {
static final MapAccessor INSTANCE = new DBObjectPropertyAccessor();
/*
* (non-Javadoc)
* @see org.springframework.context.expression.MapAccessor#getSpecificTargetClasses()
*/
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { DBObject.class };
}
/*
* (non-Javadoc)
* @see org.springframework.context.expression.MapAccessor#canRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String)
*/
@Override
public boolean canRead(EvaluationContext context, Object target, String name) {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.context.expression.MapAccessor#read(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String)
*/
@Override
@SuppressWarnings("unchecked")
public TypedValue read(EvaluationContext context, Object target, String name) {
Map<String, Object> source = (Map<String, Object>) target;
Object value = source.get(name);
return value == null ? TypedValue.NULL : new TypedValue(value);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import com.mongodb.DBRef;
/**
* Used to resolve associations annotated with {@link org.springframework.data.mongodb.core.mapping.DBRef}.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.4
*/
public interface DbRefResolver {
/**
* Resolves the given {@link DBRef} into an object of the given {@link MongoPersistentProperty}'s type. The method
* might return a proxy object for the {@link DBRef} or resolve it immediately. In both cases the
* {@link DbRefResolverCallback} will be used to obtain the actual backing object.
*
* @param property will never be {@literal null}.
* @param dbref the {@link DBRef} to resolve.
* @param callback will never be {@literal null}.
* @return
*/
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback);
/**
* Creates a {@link DBRef} instance for the given {@link org.springframework.data.mongodb.core.mapping.DBRef}
* annotation, {@link MongoPersistentEntity} and id.
*
* @param annotation will never be {@literal null}.
* @param entity will never be {@literal null}.
* @param id will never be {@literal null}.
* @return
*/
DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, MongoPersistentEntity<?> entity,
Object id);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
/**
* Callback interface to be used in conjunction with {@link DbRefResolver}.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
public interface DbRefResolverCallback {
/**
* Resolve the final object for the given {@link MongoPersistentProperty}.
*
* @param property will never be {@literal null}.
* @return
*/
Object resolve(MongoPersistentProperty property);
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2013-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import static org.springframework.util.ReflectionUtils.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.LazyLoadingException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.DB;
import com.mongodb.DBRef;
/**
* A {@link DbRefResolver} that resolves {@link org.springframework.data.mongodb.core.mapping.DBRef}s by delegating to a
* {@link DbRefResolverCallback} than is able to generate lazy loading proxies.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @since 1.4
*/
public class DefaultDbRefResolver implements DbRefResolver {
private final MongoDbFactory mongoDbFactory;
private final PersistenceExceptionTranslator exceptionTranslator;
private final ObjenesisStd objenesis;
/**
* Creates a new {@link DefaultDbRefResolver} with the given {@link MongoDbFactory}.
*
* @param mongoDbFactory must not be {@literal null}.
*/
public DefaultDbRefResolver(MongoDbFactory mongoDbFactory) {
Assert.notNull(mongoDbFactory, "MongoDbFactory translator must not be null!");
this.mongoDbFactory = mongoDbFactory;
this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
this.objenesis = new ObjenesisStd(true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#resolveDbRef(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.convert.DbRefResolverCallback)
*/
@Override
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
Assert.notNull(property, "Property must not be null!");
Assert.notNull(callback, "Callback must not be null!");
if (isLazyDbRef(property)) {
return createLazyLoadingProxy(property, dbref, callback);
}
return callback.resolve(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#created(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.Object)
*/
@Override
public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation,
MongoPersistentEntity<?> entity, Object id) {
DB db = mongoDbFactory.getDb();
db = annotation != null && StringUtils.hasText(annotation.db()) ? mongoDbFactory.getDb(annotation.db()) : db;
return new DBRef(db, entity.getCollection(), id);
}
/**
* Creates a proxy for the given {@link MongoPersistentProperty} using the given {@link DbRefResolverCallback} to
* eventually resolve the value of the property.
*
* @param property must not be {@literal null}.
* @param dbref can be {@literal null}.
* @param callback must not be {@literal null}.
* @return
*/
private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
Class<?> propertyType = property.getType();
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, dbref, exceptionTranslator, callback);
if (!propertyType.isInterface()) {
Factory factory = (Factory) objenesis.newInstance(getEnhancedTypeFor(propertyType));
factory.setCallbacks(new Callback[] { interceptor });
return factory;
}
ProxyFactory proxyFactory = new ProxyFactory();
for (Class<?> type : propertyType.getInterfaces()) {
proxyFactory.addInterface(type);
}
proxyFactory.addInterface(LazyLoadingProxy.class);
proxyFactory.addInterface(propertyType);
proxyFactory.addAdvice(interceptor);
return proxyFactory.getProxy();
}
/**
* Returns the CGLib enhanced type for the given source type.
*
* @param type
* @return
*/
private Class<?> getEnhancedTypeFor(Class<?> type) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(type);
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
enhancer.setInterfaces(new Class[] { LazyLoadingProxy.class });
return enhancer.createClass();
}
/**
* Returns whether the property shall be resolved lazily.
*
* @param property must not be {@literal null}.
* @return
*/
private boolean isLazyDbRef(MongoPersistentProperty property) {
return property.getDBRef() != null && property.getDBRef().lazy();
}
/**
* A {@link MethodInterceptor} that is used within a lazy loading proxy. The property resolving is delegated to a
* {@link DbRefResolverCallback}. The resolving process is triggered by a method invocation on the proxy and is
* guaranteed to be performed only once.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
Serializable {
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD;
private final DbRefResolverCallback callback;
private final MongoPersistentProperty property;
private final PersistenceExceptionTranslator exceptionTranslator;
private volatile boolean resolved;
private Object result;
private DBRef dbref;
static {
try {
INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("initialize");
TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Creates a new {@link LazyLoadingInterceptor} for the given {@link MongoPersistentProperty},
* {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}.
*
* @param property must not be {@literal null}.
* @param dbref can be {@literal null}.
* @param callback must not be {@literal null}.
*/
public LazyLoadingInterceptor(MongoPersistentProperty property, DBRef dbref,
PersistenceExceptionTranslator exceptionTranslator, DbRefResolverCallback callback) {
Assert.notNull(property, "Property must not be null!");
Assert.notNull(exceptionTranslator, "Exception translator must not be null!");
Assert.notNull(callback, "Callback must not be null!");
this.dbref = dbref;
this.callback = callback;
this.exceptionTranslator = exceptionTranslator;
this.property = property;
}
/*
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
}
/*
* (non-Javadoc)
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (INITIALIZE_METHOD.equals(method)) {
return ensureResolved();
}
if (TO_DBREF_METHOD.equals(method)) {
return this.dbref;
}
if (isObjectMethod(method) && Object.class.equals(method.getDeclaringClass())) {
if (ReflectionUtils.isToStringMethod(method)) {
return proxyToString(proxy);
}
if (ReflectionUtils.isEqualsMethod(method)) {
return proxyEquals(proxy, args[0]);
}
if (ReflectionUtils.isHashCodeMethod(method)) {
return proxyHashCode(proxy);
}
}
Object target = ensureResolved();
if (target == null) {
return null;
}
return method.invoke(target, args);
}
/**
* Returns a to string representation for the given {@code proxy}.
*
* @param proxy
* @return
*/
private String proxyToString(Object proxy) {
StringBuilder description = new StringBuilder();
if (dbref != null) {
description.append(dbref.getRef());
description.append(":");
description.append(dbref.getId());
} else {
description.append(System.identityHashCode(proxy));
}
description.append("$").append(LazyLoadingProxy.class.getSimpleName());
return description.toString();
}
/**
* Returns the hashcode for the given {@code proxy}.
*
* @param proxy
* @return
*/
private int proxyHashCode(Object proxy) {
return proxyToString(proxy).hashCode();
}
/**
* Performs an equality check for the given {@code proxy}.
*
* @param proxy
* @param that
* @return
*/
private boolean proxyEquals(Object proxy, Object that) {
if (!(that instanceof LazyLoadingProxy)) {
return false;
}
if (that == proxy) {
return true;
}
return proxyToString(proxy).equals(that.toString());
}
/**
* Will trigger the resolution if the proxy is not resolved already or return a previously resolved result.
*
* @return
*/
private Object ensureResolved() {
if (!resolved) {
this.result = resolve();
this.resolved = true;
}
return this.result;
}
/**
* Callback method for serialization.
*
* @param out
* @throws IOException
*/
private void writeObject(ObjectOutputStream out) throws IOException {
ensureResolved();
out.writeObject(this.result);
}
/**
* Callback method for deserialization.
*
* @param in
* @throws IOException
*/
private void readObject(ObjectInputStream in) throws IOException {
try {
this.resolved = true;
this.result = in.readObject();
} catch (ClassNotFoundException e) {
throw new LazyLoadingException("Could not deserialize result", e);
}
}
/**
* Resolves the proxy into its backing object.
*
* @return
*/
private synchronized Object resolve() {
if (!resolved) {
try {
return callback.resolve(property);
} catch (RuntimeException ex) {
DataAccessException translatedException = this.exceptionTranslator.translateExceptionIfPossible(ex);
throw new LazyLoadingException("Unable to lazily resolve DBRef!", translatedException);
}
}
return result;
}
}
}

View File

@@ -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.
@@ -18,16 +18,19 @@ package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.convert.TypeAliasAccessor;
import org.springframework.data.convert.TypeInformationMapper;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
@@ -37,36 +40,45 @@ import com.mongodb.DBObject;
* respectively.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implements MongoTypeMapper {
public static final String DEFAULT_TYPE_KEY = "_class";
@SuppressWarnings("rawtypes")
@SuppressWarnings("rawtypes")//
private static final TypeInformation<List> LIST_TYPE_INFO = ClassTypeInformation.from(List.class);
@SuppressWarnings("rawtypes")
@SuppressWarnings("rawtypes")//
private static final TypeInformation<Map> MAP_TYPE_INFO = ClassTypeInformation.from(Map.class);
private String typeKey = DEFAULT_TYPE_KEY;
private final TypeAliasAccessor<DBObject> accessor;
private final String typeKey;
public DefaultMongoTypeMapper() {
this(DEFAULT_TYPE_KEY, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
this(DEFAULT_TYPE_KEY);
}
public DefaultMongoTypeMapper(String typeKey) {
super(new DBObjectTypeAliasAccessor(typeKey));
this.typeKey = typeKey;
this(typeKey, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
}
public DefaultMongoTypeMapper(String typeKey, MappingContext<? extends PersistentEntity<?,?>, ?> mappingContext) {
super(new DBObjectTypeAliasAccessor(typeKey), mappingContext, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
this.typeKey = typeKey;
public DefaultMongoTypeMapper(String typeKey, MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext) {
this(typeKey, new DBObjectTypeAliasAccessor(typeKey), mappingContext, Arrays
.asList(SimpleTypeInformationMapper.INSTANCE));
}
public DefaultMongoTypeMapper(String typeKey, List<? extends TypeInformationMapper> mappers) {
super(new DBObjectTypeAliasAccessor(typeKey), mappers);
this.typeKey = typeKey;
this(typeKey, new DBObjectTypeAliasAccessor(typeKey), null, mappers);
}
private DefaultMongoTypeMapper(String typeKey, TypeAliasAccessor<DBObject> accessor,
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext, List<? extends TypeInformationMapper> mappers) {
super(accessor, mappingContext, mappers);
this.typeKey = typeKey;
this.accessor = accessor;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#isTypeKey(java.lang.String)
@@ -75,6 +87,30 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implemen
return typeKey == null ? false : typeKey.equals(key);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#writeTypeRestrictions(java.util.Set)
*/
@Override
public void writeTypeRestrictions(DBObject result, Set<Class<?>> restrictedTypes) {
if (restrictedTypes == null || restrictedTypes.isEmpty()) {
return;
}
BasicDBList restrictedMappedTypes = new BasicDBList();
for (Class<?> restrictedType : restrictedTypes) {
Object typeAlias = getAliasFor(ClassTypeInformation.from(restrictedType));
if (typeAlias != null) {
restrictedMappedTypes.add(typeAlias);
}
}
accessor.writeTypeTo(result, new BasicDBObject("$in", restrictedMappedTypes));
}
/* (non-Javadoc)
* @see org.springframework.data.convert.DefaultTypeMapper#getFallbackTypeFor(java.lang.Object)
@@ -83,29 +119,30 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implemen
protected TypeInformation<?> getFallbackTypeFor(DBObject source) {
return source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO;
}
/**
* {@link TypeAliasAccessor} to store aliases in a {@link DBObject}.
*
* @author Oliver Gierke
*/
public static final class DBObjectTypeAliasAccessor implements TypeAliasAccessor<DBObject> {
private final String typeKey;
public DBObjectTypeAliasAccessor(String typeKey) {
this.typeKey = typeKey;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeAliasAccessor#readAliasFrom(java.lang.Object)
*/
public Object readAliasFrom(DBObject source) {
if (source instanceof BasicDBList) {
return null;
}
return source.get(typeKey);
}

Some files were not shown because too many files have changed in this diff Show More