Skip to content

DATACMNS-1035 - Moved CustomConversions to Spring Data Commons. #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from

Conversation

odrotbohm
Copy link
Member

Extracted CustomConversions — whose code has largely been duplicated between the MongoDB, Couchbase and Cassandra modules — into Spring Data Commons. Store-specific extensions can now be contributed via a StoreConversions value type that carries both, store-specific default converters as well as a store-specific SimpleTypeHolder to augment the default list of simple types.

Removed SimpleTypeHolders public default constructor in favour of a protected one and a static DEFAULT instance for plain references.

We now expose the Pageable that was used to create a Slice via its API. Slice itself constructs a default PageRequest so that current implementations of it still work as before.
…on for self link.

We now hand the Pageable contained in the Page to the code that constructs the self link.

Added toEmptyResource(Page, Class) to force clients to resolve an optional base link before calling the method. Removed deprecated method to append pagination template parameters.
Extracted CustomConversions — whose code has largely been duplicated between the MongoDB, Couchbase and Cassandra modules — into Spring Data Commons. Store-specific extensions can now be contributed via a StoreConversions value type that carries both, store-specific default converters as well as a store-specific SimpleTypeHolder to augment the default list of simple types.

Removed SimpleTypeHolders public default constructor in favour of a protected one and a static DEFAULT instance for plain references.

// Add user provided converters to make sure they can override the defaults
toRegister.addAll(converters);
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about holding the default converters in a static held List? They are added in the same order each time the constructor is called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't bother to again extract the converters here into a static List as there should be only one production instance of CustomConversions in place anyway. We'd basically keep yet another List around and introduce a layer of indirection. That said, we can definitely extract that list, no problem.

DEFAULTS.add(Class.class);
DEFAULTS.add(Enum.class);
}
private static final Set<Class<?>> DEFAULTS = new HashSet<Class<?>>() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new HashSet with an inner static initializer creates a subclass. What's the advantage compared to HashSet creation and initialization in a static block of SimpleTypeHolder?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the only way you can have a static DEFAULT instance see below and get the DEFAULTS HashSet populated already, which is used in the constructor. If DEFAULTS is set up in a static initializer block, it's still empty when the constructor is called to create the DEFAULT instance.

this.customWriteTargetTypes = new ConcurrentHashMap<>();
this.rawWriteTargetTypes = new ConcurrentHashMap<>();

List<Object> toRegister = new ArrayList<Object>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The List could be replaced with Stream.concat(…)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least can say new ArrayList<>()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really a benefit of calling ….stream() on each of the Lists? The constructor invocation doesn't need an explicit generic parameter, right.

}

if (!added) {
throw new IllegalArgumentException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make sense to mention the offending object in the exception message

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd favor logging each registration. Then exception can say "couldn't find match". Also avoid tracking state of who was registered.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposing the offending instance is a good idea. Is there added value in logging the converters registered? If so, shouldn't this be functionality GenericConversionService?

*/
public static StoreConversions of(SimpleTypeHolder storeTypeHolder, Object... converters) {

Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two overloads make it quite easy to use by accident the wrong method. Should check here that converters isn't a single Collection element?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? You'd explicitly need to call StoreConversions.of(new Object[] { collection }) to run into this problem, right?

toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
toRegister.addAll(Jsr310Converters.getConvertersToRegister());
toRegister.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
toRegister.addAll(storeConversions.getStoreConverters());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store converters should be probably registered after converters and before JodaTimeConverters to retain registration order. MongoDB and Cassandra modules register their converters after converters and before JodaTimeConverters.

@odrotbohm
Copy link
Member Author

From the mixture of feedback I conclude the following steps.

  1. Switch to StoreConversions.of(…, Collection<?>) to avoid the usage of the potentially wrong overload.
  2. Create a static list for the default converters we register in CustomConversions.
  3. Stick to addAll(…) for final registration as we then align the abstraction level of the registration steps: user converters, store converters, default ones.
  4. As 3 indicates, fix the order of registration to register the default converters last.
  5. Improve exception message on converter registration.

Default converters are now held in a static list so that they're only obtained once. A failed registration of a converter now exposes the offending converter instance. StoreConversions.of(…) now uses a Collection<?> to avoid accidental use of wrong overload.

Original pull request: #210.
odrotbohm added a commit that referenced this pull request Apr 21, 2017
Extracted CustomConversions — whose code has largely been duplicated between the MongoDB, Couchbase and Cassandra modules — into Spring Data Commons. Store-specific extensions can now be contributed via a StoreConversions value type that carries both, store-specific default converters as well as a store-specific SimpleTypeHolder to augment the default list of simple types.

Removed SimpleTypeHolders public default constructor in favour of a protected one and a static DEFAULT instance for plain references.

Original pull request: #210.
@odrotbohm odrotbohm closed this Apr 21, 2017
@odrotbohm odrotbohm deleted the issue/DATACMNS-1035 branch April 21, 2017 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants