Revision: 1075 Author: limpbizkit Date: Sun Sep 6 09:58:11 2009 Log: Edited wiki page through web user interface. http://code.google.com/p/google-guice/source/detail?r=1075
Modified: /wiki/Multibindings.wiki ======================================= --- /wiki/Multibindings.wiki Sun Nov 16 16:25:06 2008 +++ /wiki/Multibindings.wiki Sun Sep 6 09:58:11 2009 @@ -2,5 +2,103 @@ =Multibindings= [http://google-guice.googlecode.com/svn/trunk/latest-javadoc/com/google/inject/multibindings/Multibinder.html Multibinder] and [http://google-guice.googlecode.com/svn/trunk/latest-javadoc/com/google/inject/multibindings/MapBinder.html MapBinder] are intended for plugin-type architectures, where you've got several modules contributing Servlets, Actions, Filters, Components or even just names. +==Using Multibindings to host Plugins== +Multibindings make it easy to support plugins in your application. Made popular by [http://www.eclipseplugincentral.com/ IDEs] and [https://addons.mozilla.org/en-US/firefox/ browsers], this pattern exposes APIs for extending the behaviour of an application. + +Neither the plugin consumer nor the plugin author need write much setup code for extensible applications with Guice. Simply define an interface, bind implementations, and inject implementations! To illustrate, we'll use plugins to summarize ugly URIs like `http://bit.ly/1mzgW1` into something readable on Twitter. + +First, we define an interface that plugin authors can implement. This is usually an interface that lends itself to several implementations. For this example, we would write a different implementation for each website that we could summarize. +{{{ +interface UriSummarizer { + /** + * Returns a short summary of the URI, or null if this summarizer doesn't + * know how to summarize the URI. + */ + String summarize(URI uri); +} +}}} + +Next, we'll get our plugin authors to implement the interface. Here's an implementation that shortens Flickr photo URLs: +{{{ +class FlickrPhotoSummarizer implements UriSummarizer { + private static final Pattern PHOTO_PATTERN + = Pattern.compile("http://www\.flickr\.com/photos/[^/]+/(\d+)/"); + + public String summarize(URI uri) { + Matcher matcher = PHOTO_PATTERN.matcher(uri.toString()); + if (!matcher.matches()) { + return null; + } else { + String id = matcher.group(1); + Photo photo = lookupPhoto(id); + return photo.getTitle(); + } + } +} +}}} + +The plugin author registers their implementation using a multibinder. Some plugins may bind multiple implementations, or implementations of several extension-point interfaces. +{{{ +public class FlickrPluginModule extends AbstractModule { + public void configure() { + Multibinder<UriSummarizer> uriBinder = Multibinder.newSetBinder(binder(), UriSummarizer.class); + uriBinder.addBinding().to(FlickrSummarizer.class); + + ... // bind plugin dependencies, such as our Flickr API key + } +} +}}} + +Now we can consume the services exposed by our plugins. In this case, we're summarizing tweets: +{{{ +public class TweetPrettifier { + + private final Set<UriSummarizer> summarizers; + private final EmoticonImagifier emoticonImagifier; + + @Inject TweetPrettifier(Set<UriSummarizer> summarizers, + EmoticonImagifier emoticonImagifier) { + this.summarizers = summarizers; + this.emoticonImagifier = emoticonImagifier; + } + + public Html prettifyTweet(String tweetMessage) { + ... // split out the URIs and call prettifyUri() for each + } + + public String prettifyUri(URI uri) { + // loop through the implementations, looking for one that supports this URI + for (UrlSummarizer summarizer : summarizers) { + String summary = summarizer.summarize(uri); + if (summary != null) { + return summary; + } + } + + // no summarizer found, just return the URI itself + return uri.toString(); + } +} +}}} + +Finally we must register the plugins themselves. The simplest mechanism to do so is to list them programatically: +{{{ +public class PrettyTweets { + public static void main(String[] args) { + Injector injector = Guice.createInjector( + new GoogleMapsPluginModule(), + new BitlyPluginModule(), + new FlickrPluginModule() + ... + ); + + injector.getInstance(Frontend.class).start(); + } +} +}}} +If it is infeasible to recompile each time the plugin set changes, the list of plugin modules can be loaded from a configuration file. + +Note that this mechanism cannot load or unload plugins while the system is running. If you need to hot-swap application components, investigate [http://code.google.com/p/google-guice/wiki/OSGi Guice's OSGi bindings]. + ==Limitations== When you use !PrivateModules with multibindings, all of the elements must be bound in the same environment. You cannot create collections whose elements span private modules. Otherwise injector creation will fail. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "google-guice-dev" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/google-guice-dev?hl=en -~----------~----~----~----~------~----~------~--~---
