0.7 .... I miss the CacheBuilder API !

138 views
Skip to first unread message

bendc...@gmail.com

unread,
Apr 17, 2013, 1:31:19 PM4/17/13
to jsr...@googlegroups.com

I realize the 0.6 mechanism for using javax.cache.CacheBuilder has been re-placed with the 0.7 javax.cache.MutableConfiguration API, and I agree that the MutableConfiguration API is sound and complete, but - IMHO - it is also unattractive wrt that last Constructor's signature.

MutableConfiguration(Iterable<CacheEntryListenerRegistration<? super K,? superV>> cacheEntryListenerRegistrations,                            Factory<CacheLoader<K,V>> cacheLoaderFactory, Factory<CacheWriter<? super K,? super V>> cacheWriterFactory, Factory<ExpiryPolicy<? super K,? super V>> expiryPolicyFactory, boolean isReadThrough, boolean isWriteThrough, boolean isStatisticsEnabled, boolean isStoreByValue, boolean isTransactionsEnabled, IsolationLevel txnIsolationLevel, Mode txnMode)

Was there any discussion here re: CacheBuilder being removed?  Was there any delinquency with the old CacheBuilder ?

Not pressing with too much zeal, but, I really liked how I used to be able to code like this

javax.cache.CacheManager cm = javax.cache.Caching.getCacheManager(id);

javax.cache.Cache c = cm.createCacheBuilder(“otc_swap_cache”)

.setTransactionEnabled(

  javax.cache.transaction.IsolationLevel.READ_COMMITTED,

  javax.cache.transaction.Mode.LOCAL

).build();







Manik Surtani

unread,
Apr 17, 2013, 2:06:56 PM4/17/13
to jsr...@googlegroups.com
Agreed.  I too missed any discussion regarding removing the CacheBuilder.  And FWIW I think it is a bad idea.

We used to have mutable configurations way back in the day, and we moved to an immutable builder for very good reasons (the discussions around that decision are on this mail list).  It seems as though this group has lost its institutional memory and has reverted to committing changes without discussion, which can only be a bad thing.


--
You received this message because you are subscribed to the Google Groups "jsr107" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jsr107+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Brian Oliver

unread,
Jun 6, 2013, 9:48:18 AM6/6/13
to jsr...@googlegroups.com
Sorry for the delay in explaining this, but ultimately the builder approach originally used provided little benefit to implementors and made things harder for developers.

1. While the API seemed to allow implementors the ability to customize the builder implementation, this in reality does not work as the Builder interface was fixed in the types it returned, especially from fluent-methods.

eg:   Consider the following method signatures on a Builder class/interface:

public Builder setTransactionMode(Mode mode);
public Builder setStatisticsEnabled(boolean enabled);

It's clear that you should be able to do the following:

Builder builder = new Builder()
.setTransactionMode(NONE)
.setStatisticsEnabled(false);

This is all well and good until an implementor and developer wants to use something custom, say extend the Builder (which is what all implementors will  and do want to do).  eg: add a Vendor specific properties. 

public CustomBuilder setPersistent(boolean enabled);

CustomBuilder builder = new CustomBuilder()
.setTransactionMode(NONE)
.setStatisticsEnabled(false)
.setPersistent(true);    //<--- this won't compile because setStatisticsEnabled(boolean) returns a Builder, not a CustomBuilder.

This forces developers to either:
i). Cast their builders to the vendor specific type or
ii). Carefully order their setter methods.

So while on the surface it looks nice to use this pattern, it breaks down very quickly.  This is mostly due to Java's type-system - not that we don't like builders.

2. After discussing this at length with the Java EE architect community, they strongly recommended not using the builder pattern in this way.  Five reasons:

1. It is error prone (in that it forces developers to perform potentially unsafe casts and/or carefully order methods)

2. Other parts of Java SE / Java EE, where JSR-107 should eventually end-up, don't do this (for the above reason)

3. The approach used creates unnecessary garbage (builders) that are rarely reused.

4. Essentially we're creating a Configuration object here.   Providing a simple out-of-the-box configuration (like a fluent MutableConfiguration as we have)
is dramatically simpler for end-users.   eg: They can just "new" a configuration and start working with one.   Vendors may create their own implementations and in which case the issue of i) and ii) above never occur.

5. The builder pattern in general is for multiple objects, not building a single object.  It's a bit of a stretch to introduce an entire new class (a Builder) simply to create a Configuration object, that itself has fluent methods on it.
 
While I personally don't mind the Builder pattern (we use them a lot internally with Coherence), it is doesn't add much value for creating a single configuration object.  We rarely expose them to public APIs.

Once we refactored and remove the builder pattern approach from the spec, it dramatically simplified things, especially the RI and test implementations.   Furthermore it also made our examples way simpler, with no less expressive power or flexibility - this has to be a good thing!

Cheers

-- Brian



Ben Cotton

unread,
Jun 6, 2013, 10:14:36 AM6/6/13
to jsr...@googlegroups.com, jsr...@googlegroups.com
Interesting.  Thanks Brian.  Our latest API very definitely exposes a fluent mechanism that gives JCACHE users a "Builder familiar" capability (but without the explicit Bloch-style Builder).

We're good with this.  No doubt about it

Sent from my iPhone 

Brian Oliver

unread,
Jun 10, 2013, 11:31:00 AM6/10/13
to jsr...@googlegroups.com
As a heads up, we removed the huge constructor.

Now there's simply a no-args (default) constructor and a copy constructor.

-- Brian

Yannis Cosmadopoulos

unread,
Jun 10, 2013, 4:40:32 PM6/10/13
to jsr...@googlegroups.com
There is a solution to the builder issue. Something like:
  public interface Builder<T extends Builder<T>> extends Builder<T>




--
You received this message because you are subscribed to the Google Groups "jsr107" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jsr107+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
I will be participating (again) in the ALC bicycle ride, from San Francisco to Los Angeles, raising money to fight AIDS. Please support me: http://www.tofighthiv.org/goto/yannis

Brian Oliver

unread,
Jun 10, 2013, 5:42:09 PM6/10/13
to jsr...@googlegroups.com
Unfortunately this only works for top-level abstract classes and it doesn't really allow extension, which is the whole point - we want a class that can be extended.

eg:  Consider Configuration is "enhanced" to look like this:

interface Configuration<K, V, T> {
...
T setReadThrough(boolean enabled);
}

such that T is the returned type.

Now consider changing the MutableConfiguration class to become:

public MutableConfiguration<K, V> implements Configuration<K, V, MutableConfiguration> { ... }

Of course the "setReadThrough" method now returns  MutableConfiguration.  All good but consider someone / an implementation wanting to extend MutableConfiguration.   eg:

public MyCustomConfiguration<K, V> extends MutableConfiguration<K, V> {...}

The suggested approach provides no benefit over what we have today as the "setReadThrough" method will still return a MutableConfiguration, not a MyCustomConfiguration as people would like when using a fluent approach.

Of course we could change this by creating an AbstractMutableConfiguration that allows parameterizing the MutableConfiguration by how far should we really go?     

eg:

public abstract class AbstractMutableConfiguration<K, V, T> implements Configuration<K, V, T> { .. }

and then make:

public class MutableConfiguration<K, V> extends AbstractMutableConfiguration<K, V, MutableConfiguration> { ... }

Here we've taken something that is really quiet simple, a MutableConfiguration class that you can call "new" on, and made it way more complicated than it needs to be, just to satisfy who?   It's doesn't appear to benefit the end-user or the implementors, which is why we removed the builder pattern in the first place.  (eg: applying the builder pattern to this to create a single configuration object seems a very big stretch of the builder pattern).

The builder pattern:

1. Didn't make it easier for the end-user / developer.

2. It definitely made it harder to use the API.

3. It provided nothing for the implementor.

In every case, someone would need to perform an unsafe-cast to gain access to a customized configuration, which is not what we  want to encourage, especially part of a Java API.

You received this message because you are subscribed to a topic in the Google Groups "jsr107" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jsr107/ebv-c9yvQ3g/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to jsr107+un...@googlegroups.com.

Yannis Cosmadopoulos

unread,
Jun 10, 2013, 6:30:25 PM6/10/13
to jsr...@googlegroups.com
Actually It does allow extension (I took the example from working code ). Here is a full working example:
package com.magnet.opus.facebook.controller.impl.tmp;

public class Tester {
  public void testIt() {
    ExtendedBuilder<ExtendedToBuild, ExtendedBuilder> builder = getExtendedBuilder();
    ExtendedToBuild instance = builder.
        name("fred"). //Note that this returns ExtendedBuilder NOT Builder (as for naive builder)
        count(6).
        build();
  }

  ExtendedBuilder<ExtendedToBuild, ExtendedBuilder> getExtendedBuilder() {
    throw new UnsupportedOperationException();
  }
}

public interface Builder<U extends ToBuild, T extends Builder<U, T>> {
  T name(String name);
  U build();
}

public interface ExtendedBuilder<U extends ExtendedToBuild, T extends ExtendedBuilder> extends Builder<U, T> {
  T count(int count);
  U build();
}

public interface ToBuild {
  String getName();
}

public interface ExtendedToBuild extends ToBuild {
  int getCount();
}



Brian Oliver

unread,
Jun 13, 2013, 10:44:02 AM6/13/13
to jsr...@googlegroups.com
Completely agree.  As long as you declare interfaces for every class that you actually implement, you're golden.   Eventually however someone (an implementor) must implement a class, in which case the question becomes, "how many interfaces should we require them to implement?", or alternatively "how many interfaces should a developer traverse to create a configuration?".


The feedback from the Java EE 7 Architecture group was "make it simple for developers and implementors".   I can't really fault them on that feedback.   This and Java SE is where the community want this work to eventually appear, so consistency and simplicity is very important.   Unfortunately the Builder Pattern, as demonstrated by the code example and from our experience, is not any easier to implement nor easier to use.  Simply having to explain those interfaces to developers was viewed as an impediment to adoption - and we agreed.  Worse, it forces developers to use a level of indirection, just to create a single object.  

The following however is trivially simple and just as expressive:

//create a configuration
MutableConfiguration<Key, Value> config = new MutableConfiguration<Key, Value>()
    .setReadThrough(true)
    .setTypes(Key.class, Value.class);

then...

//use a configuration
Cache<Key, Value> cache = manager.configureCache("my-cache", config);

The MutableConfiguration, a single class, ships with the spec.  It implements the Configuration interface, that of which is used by implementations.  Implementors don't need to create their own implementation of the Configuration interface as the one in the spec provides everything everyone needs, especially for caches based on the spec (which is all of them), including the defaults as specified in the spec.   

eg: For the Coherence implementation we don't have or require a custom configuration implementation.   We simply use the one provided by the spec.   No extension required.   No additional builders, no additional interfaces, nothing.   What developers see in the examples / tutorials is exactly what they can use, with the RI or Coherence, or from what I understand other implementations as well etc. Developers should easily be able to swap implementations without a problem, which is the goal.

Of course, this does not prevent an implementor or developer from constructing alternative approaches to configure their specific implementations.  They are free to use builders / factories / adaptors or what ever pattern fits with their approach or ideology.   The spec doesn't force an approach upon on them, especially one that may not be applicable / compatible with their approach to configuration.   Developers simply need to provide a configuration instance.   How they get that instance, depending on the implementation they use, is up to them and the implementor.  If the implementor chooses not to provide something specific, or isn't required to do so, they can always support what the spec provides.

Reply all
Reply to author
Forward
0 new messages