From 001507695ea5fd93f79d1198011872327c48b731 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Fri, 26 Nov 2010 16:37:49 -0800 Subject: [PATCH] experimenting --- core/pom.xml | 11 ++ .../src/main/java/hudson/ExtensionFinder.java | 129 +++++++++++++++++- core/src/main/java/hudson/PluginManager.java | 3 + core/src/main/java/hudson/model/Hudson.java | 5 + core/src/test/java/foo/Animal.java | 7 + core/src/test/java/foo/Banana.java | 7 + core/src/test/java/foo/Cat.java | 9 ++ core/src/test/java/foo/Cute.java | 7 + core/src/test/java/foo/Dog.java | 7 + core/src/test/java/foo/Driver.java | 56 ++++++++ 10 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/foo/Animal.java create mode 100644 core/src/test/java/foo/Banana.java create mode 100644 core/src/test/java/foo/Cat.java create mode 100644 core/src/test/java/foo/Cute.java create mode 100644 core/src/test/java/foo/Dog.java create mode 100644 core/src/test/java/foo/Driver.java diff --git a/core/pom.xml b/core/pom.xml index 750f23ed4a..85ad1a4847 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -364,6 +364,17 @@ THE SOFTWARE. + + com.google.inject + guice + 2.0 + + + com.google.inject.extensions + guice-multibindings + 2.0 + + org.jruby.ext.posix jna-posix diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java index 948db8dfc2..516a4d5bd5 100644 --- a/core/src/main/java/hudson/ExtensionFinder.java +++ b/core/src/main/java/hudson/ExtensionFinder.java @@ -23,6 +23,13 @@ */ package hudson; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.name.Names; import net.java.sezpoz.Index; import net.java.sezpoz.IndexItem; import hudson.model.Hudson; @@ -202,6 +209,126 @@ public abstract class ExtensionFinder implements ExtensionPoint { } } } - + + @Extension + public static final class GuiceFinder extends ExtensionFinder { + private Injector container; + + public GuiceFinder() { + Module m = new AbstractModule() { + @Override + protected void configure() { + ClassLoader cl = Hudson.getInstance().getPluginManager().uberClassLoader; + int id=0; + for (final IndexItem item : Index.load(Extension.class, Object.class, cl)) { + id++; + try { + AnnotatedElement e = item.element(); + Class extType; + if (e instanceof Class) { + extType = (Class) e; + bind((Class)e); + continue; + } else + if (e instanceof Field) { + extType = ((Field)e).getType(); + bind(extType).annotatedWith(Names.named(String.valueOf(id))) + .toInstance(item.instance()); + } else + if (e instanceof Method) { + extType = ((Method)e).getReturnType(); + } else + throw new AssertionError(); + + if(type.isAssignableFrom(extType)) { + Object instance = item.instance(); + if(instance!=null) + result.add(new ExtensionComponent(type.cast(instance),item.annotation())); + } + } catch (LinkageError e) { + // sometimes the instantiation fails in an indirect classloading failure, + // which results in a LinkageError + LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING, + "Failed to load "+item.className(), e); + } catch (InstantiationException e) { + LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING, + "Failed to load "+item.className(), e); + } + } + } + }; + container = Guice.createInjector(m); + } + + public Collection> find(Class type, Hudson hudson) { + List> result = new ArrayList>(); + + ClassLoader cl = hudson.getPluginManager().uberClassLoader; + for (IndexItem item : Index.load(Extension.class, Object.class, cl)) { + try { + AnnotatedElement e = item.element(); + Class extType; + if (e instanceof Class) { + extType = (Class) e; + } else + if (e instanceof Field) { + extType = ((Field)e).getType(); + } else + if (e instanceof Method) { + extType = ((Method)e).getReturnType(); + } else + throw new AssertionError(); + + if(type.isAssignableFrom(extType)) { + Object instance = item.instance(); + if(instance!=null) + result.add(new ExtensionComponent(type.cast(instance),item.annotation())); + } + } catch (LinkageError e) { + // sometimes the instantiation fails in an indirect classloading failure, + // which results in a LinkageError + LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING, + "Failed to load "+item.className(), e); + } catch (InstantiationException e) { + LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING, + "Failed to load "+item.className(), e); + } + } + + return result; + } + + @Override + public void scout(Class extensionType, Hudson hudson) { + ClassLoader cl = hudson.getPluginManager().uberClassLoader; + for (IndexItem item : Index.load(Extension.class, Object.class, cl)) { + try { + AnnotatedElement e = item.element(); + Class extType; + if (e instanceof Class) { + extType = (Class) e; + } else + if (e instanceof Field) { + extType = ((Field)e).getType(); + } else + if (e instanceof Method) { + extType = ((Method)e).getReturnType(); + } else + throw new AssertionError(); + // accroding to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208 + // this appears to be the only way to force a class initialization + Class.forName(extType.getName(),true,extType.getClassLoader()); + } catch (InstantiationException e) { + LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING, + "Failed to scout "+item.className(), e); + } catch (ClassNotFoundException e) { + LOGGER.log(Level.WARNING,"Failed to scout "+item.className(), e); + } catch (LinkageError e) { + LOGGER.log(Level.WARNING,"Failed to scout "+item.className(), e); + } + } + } + } + private static final Logger LOGGER = Logger.getLogger(ExtensionFinder.class.getName()); } diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index cb7a8c6475..e1a459c41e 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -27,6 +27,7 @@ import static hudson.init.InitMilestone.PLUGINS_PREPARED; import static hudson.init.InitMilestone.PLUGINS_STARTED; import static hudson.init.InitMilestone.PLUGINS_LISTED; +import com.google.inject.Guice; import hudson.PluginWrapper.Dependency; import hudson.init.InitStrategy; import hudson.init.InitializerFinder; @@ -266,6 +267,8 @@ public abstract class PluginManager extends AbstractModelObject { Hudson.getInstance().lookup.set(PluginInstanceStore.class,new PluginInstanceStore()); TaskGraphBuilder g = new TaskGraphBuilder(); + Hudson.getInstance().container = Guice.createInjector(); + // schedule execution of loading plugins for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { g.followedBy().notFatal().attains(PLUGINS_PREPARED).add("Loading plugin " + p.getShortName(), new Executable() { diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index ea844812c7..1ae4cfc79c 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -26,6 +26,8 @@ package hudson.model; import antlr.ANTLRException; +import com.google.inject.Guice; +import com.google.inject.Injector; import com.thoughtworks.xstream.XStream; import hudson.BulkChange; import hudson.DNSMultiCast; @@ -243,6 +245,9 @@ public final class Hudson extends Node implements ItemGroup, Stapl */ public transient final Lookup lookup = new Lookup(); + // TODO: fix scope + public transient Injector container; + /** * {@link Computer}s in this Hudson system. Read-only. */ diff --git a/core/src/test/java/foo/Animal.java b/core/src/test/java/foo/Animal.java new file mode 100644 index 0000000000..1c7826f5be --- /dev/null +++ b/core/src/test/java/foo/Animal.java @@ -0,0 +1,7 @@ +package foo; + +/** + * @author Kohsuke Kawaguchi + */ +public interface Animal { +} diff --git a/core/src/test/java/foo/Banana.java b/core/src/test/java/foo/Banana.java new file mode 100644 index 0000000000..61f61eeef1 --- /dev/null +++ b/core/src/test/java/foo/Banana.java @@ -0,0 +1,7 @@ +package foo; + +/** + * @author Kohsuke Kawaguchi + */ +public class Banana implements Cute { +} diff --git a/core/src/test/java/foo/Cat.java b/core/src/test/java/foo/Cat.java new file mode 100644 index 0000000000..3ca72f3f7d --- /dev/null +++ b/core/src/test/java/foo/Cat.java @@ -0,0 +1,9 @@ +package foo; + +import com.google.inject.Singleton; + +/** + * @author Kohsuke Kawaguchi + */ +public class Cat implements Animal, Cute { +} diff --git a/core/src/test/java/foo/Cute.java b/core/src/test/java/foo/Cute.java new file mode 100644 index 0000000000..bec72dadcc --- /dev/null +++ b/core/src/test/java/foo/Cute.java @@ -0,0 +1,7 @@ +package foo; + +/** + * @author Kohsuke Kawaguchi + */ +public interface Cute { +} diff --git a/core/src/test/java/foo/Dog.java b/core/src/test/java/foo/Dog.java new file mode 100644 index 0000000000..3710886f87 --- /dev/null +++ b/core/src/test/java/foo/Dog.java @@ -0,0 +1,7 @@ +package foo; + +/** + * @author Kohsuke Kawaguchi + */ +public class Dog implements Animal { +} diff --git a/core/src/test/java/foo/Driver.java b/core/src/test/java/foo/Driver.java new file mode 100644 index 0000000000..af245ef019 --- /dev/null +++ b/core/src/test/java/foo/Driver.java @@ -0,0 +1,56 @@ +package foo; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Scope; +import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.Multibinder; + +import java.util.Set; + +/** + * @author Kohsuke Kawaguchi + */ +public class Driver { + @Inject + public Set animals; + + @Inject + public Set cute; + + /* + If Cat has @Singleton, multiple bindings result in the same instance. + How can I achieve that without changing annotation? + */ + + public static void main(String[] args) { + Injector i = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(Cat.class).in(Singleton.class); + + Multibinder ab = Multibinder.newSetBinder(binder(), Animal.class); + ab.addBinding().to(Dog.class); + ab.addBinding().to(Cat.class); + + Multibinder cb = Multibinder.newSetBinder(binder(), Cute.class); + cb.addBinding().to(Cat.class); + cb.addBinding().to(Banana.class); + } + }); + i.getInstance(Driver.class).run(); + + // Dog isn't singleton scoped, so you get a different instance, but Cat is singleton, + // so you get the same value + System.out.println(i.getInstance(Key.get(new TypeLiteral>(){}))); + } + + public void run() { + System.out.println(animals); + System.out.println(cute); + } +} -- GitLab