So what you want is a chain like behavior where the order of the modules in the createInjector method determines the order in the chain. I don't think this is easily possible with the current guice extensions. Multibinder offers a Set and a Map binder. Neither of them preserves the ordering.

You could make a hacky thing where you use Integer as key for the MapBinder. During injector creation you could have a static counter which will map each binding to a unique increasing value (0, 1, 2, ...). In the application you can then iterate over the number of elements in the Map and retrieve them by their key.

Something like this (sorry pseudo code, not my dev machine...):

----

private static int userServiceCounter;

public static addBindingToUserServiceChain(Binder binder, Class<? extends UserServiceFilter> implementation) {
    // make the binding here
}

----

class ChainedUserService implements UserService {

private final CoreUserService coreService
private final Map<Integer, UserServiceFilter> filterServices;

@Override
public List<User> getUsers() {
    final int n = services.size();
    List<User> result = coreService.getUsers();

    for (int i = 0; i < n; i++) {
        final UserServiceFilter filter = filterServices.get(i);
        result = filter.getUser(result);
    }
}

----

Core would then bind ChainedUserService to UserService.




On 02/04/2015 11:34 PM, Aurélien wrote:
Thank you for your responses.

The OptionalBinder lead me to what I want : a plugin-type architecture.
I could not reference a parent implementation with OptionalBinder.
However, the Multibinder enable to add multiple implementations and to reference all these implementations.

Although the Multibinder pattern is good, it means the Framework implementation needs to guess most of the sub plugins uses.

Here is an example of what I would like to do :
1/ The Framework defines a method to fetch the application users : List<User> users();
The base implementation connect to a database and fetch all the users.

2/ The plugin web-service fetch users on an API on the Internet and add them to the default implementation : list(parent.users()).addAll(ws.users());

3/ The plugin group filter users who are in the same group as the current user : parent.users().filter(user -> user.group() == currentUser.group());

Then in the application, I will be able to :
- use the framework only : users() returns all the users in the database,
- use the group plugin : users() returns all the users in the database who are in the same group as the current user - use the web-service plugin : users() returns all the users in the database plus all the users provided by some web-service - use both the group plugin and the web-service plugin : users() returns database users and web-service users but filtered by the group

This would be awesome because in the Framework I wouldn't have to think how plugins will change the default features.

To achieve this, using the MultiBindings Guice extension style, I would have framework and plugins declare modules this way :
|
publicclassModuleGuiceextendsAbstractModule{
@Overrideprotectedvoidconfigure(){
Parentbinder
.newBinder(binder(),UserService.class)
.setBinding()
.to(UserFrameworkService.class);
}
}
|

Then, in the plugins service overriding, I would have something like :
|
publicclassUserWsServiceimplementsUserService{

privatefinalUserServiceparent;
privatefinalUserApiuserApi;

@InjectpublicUserWsService(@ParentUserServiceparent,UserApiuserApi){
this.parent =parent;
this.userApi =userApi;
}

@OverridepublicList<User>users(){
returnImmutableList.builder().addAll(parent.users()).addAll(ws.users()).build();
}

}
|

And eventually, in the Application, I would just have to do :
|
Injectorinjector =Guice.createInjector(newFrameworkModule(),newWebServiceModule(),newGroupModule()); injector.getInstance(UserService.class).users();// database + ws users filtered by group
|

Do I have to look in ExtendingGuice documentation <https://github.com/google/guice/wiki/ExtendingGuice> to implement this or another class in the MultiBindings Guice extension might do the job (or elsewhere!) ?

Thank you for your help!
Aurélien

On Wednesday, February 4, 2015 at 10:42:46 PM UTC+1, scl wrote:

    One thing that will not work is injecting A into either ASub or
    AOtherSub. Because this will create an endless loop sinc A is
    bound to ASub resp. AOtherSub.
    You need to be more specific and either request ACore or introduce
    a name for the parent so you could inject (@Parent A)

    Am 4. Februar 2015 16:59:09 MEZ, schrieb Joshua Moore-Oliva
    <[email protected] <javascript:>>:

        If I am understanding your use case correctly, it sounds like
        you may be interested in using the OptionalBinder that is new
        in guice4. It would allow you to set up a default binding
        (setDefault) in core, and then override that in a sub
        (setBinding).

        The javadoc is fairly thorough and has some examples here:

        
http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/multibindings/OptionalBinder.html
        
<http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/multibindings/OptionalBinder.html>



        On Tuesday, February 3, 2015 at 3:36:28 PM UTC-7, Aurélien wrote:

            Hi,

            I have a peculiar use case:
            - I have 3 libraries, Core, Sub and OtherSub
            - Sub and OtherSub do not know anything about each other,
            but they know the existence of Core
            - Sub and OtherSub override the same functionality in Core
            - In a project, I want to combine Sub and OtherSub.

            It does not work, but here is how I wanted to implement
            this pattern with Guice:
            - in the Core library:
                o bind(A.class).to(ACore.class)
                o class ACore implements A {
                    @Override public String a() { "a"; }
                }
            - in the Sub library:
                o bind(A.class).to(ASub.class)
                o class ASub implements A {
                    private final A parent; // instance of ACore or
            AOtherSub
                    @Inject ASub(A parent) { this.a = parent; }
                    @Override public String a() { parent.a() + "
            overriden"; }
                }
            - in the OtherSub library:
                o bind(A.class).to(AOtherSub.class)
                o class AOtherSub implements A {
                    private final A parent; // instance of ACore or ASub
                    @Inject ASub(A parent) { this.a = parent; }
                    @Override public String a() { parent.a() + " !"; }
                }

            then in the projet using this set of libraries, I would
            have been able to do:
            - Guice.createInjector(Modules.override(new
            ModuleCore()).with(new
            AOtherSub())).getInstance(A.class).a() // returns "a !"
            -
            Guice.createInjector(Modules.override(Modules.override(new
            ModuleCore()).with(new ASub())).with(new
            AOtherSub())).getInstance(A.class).a() // returns "a
            overriden !"
            -
            Guice.createInjector(Modules.override(Modules.override(new
            ModuleCore()).with(new AOtherSub())).with(new
            ASub())).getInstance(A.class).a() // returns "a ! overriden"

            In this implementation, this throws a StackOverflowError
            because in ASub or in AOtherSub the instance creation
            falls in an infinite loop.

            Is there a way of doing something like that with Guice ?
            Else do you have any insight on how I should implement
            this use case ?

            Cheers,
            Aurélien

--
You received this message because you are subscribed to the Google Groups "google-guice" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected] <mailto:[email protected]>. To post to this group, send email to [email protected] <mailto:[email protected]>.
Visit this group at http://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/f348749b-8b33-4d81-a8c4-c5d1256805e7%40googlegroups.com <https://groups.google.com/d/msgid/google-guice/f348749b-8b33-4d81-a8c4-c5d1256805e7%40googlegroups.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-guice/54D32322.8020602%40gmx.ch.
For more options, visit https://groups.google.com/d/optout.

Reply via email to