Added a JMX interface to aid debugging.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@153 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/ContainerBuilder.java b/src/com/google/inject/ContainerBuilder.java
index 5b926e7..72fa970 100644
--- a/src/com/google/inject/ContainerBuilder.java
+++ b/src/com/google/inject/ContainerBuilder.java
@@ -82,6 +82,10 @@
     public Container get(InternalContext context) {
       return context.getContainer();
     }
+
+    public String toString() {
+      return "ContainerFactory";
+    }
   };
 
   private static final InternalFactory<Logger> LOGGER_FACTORY
@@ -92,6 +96,10 @@
           ? Logger.getAnonymousLogger()
           : Logger.getLogger(member.getDeclaringClass().getName());
     }
+
+    public String toString() {
+      return "LoggerFactory";
+    }
   };
 
   static final String UNKNOWN_SOURCE = "[unknown source]";
diff --git a/src/com/google/inject/ContainerImpl.java b/src/com/google/inject/ContainerImpl.java
index c0980e3..b45632a 100644
--- a/src/com/google/inject/ContainerImpl.java
+++ b/src/com/google/inject/ContainerImpl.java
@@ -693,6 +693,10 @@
           }
         });
       }
+
+      public String toString() {
+        return factory.toString();
+      }
     };
   }
 
diff --git a/src/com/google/inject/Key.java b/src/com/google/inject/Key.java
index 51ed89a..b1a8cb2 100644
--- a/src/com/google/inject/Key.java
+++ b/src/com/google/inject/Key.java
@@ -143,6 +143,13 @@
     return annotationStrategy.getAnnotationType();
   }
 
+  /**
+   * Gets the annotation.
+   */
+  public Annotation getAnnotation() {
+    return annotationStrategy.getAnnotation();
+  }
+
   boolean hasAnnotationType() {
     return annotationStrategy.getAnnotationType() != null;
   }
@@ -443,7 +450,7 @@
     }
 
     public String toString() {
-      return annotationType.toString();
+      return "@" + annotationType.getName();
     }
   }
 }
diff --git a/src/com/google/inject/name/Named.java b/src/com/google/inject/name/Named.java
index 844d51d..7a3abbf 100644
--- a/src/com/google/inject/name/Named.java
+++ b/src/com/google/inject/name/Named.java
@@ -17,6 +17,8 @@
 package com.google.inject.name;
 
 import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import com.google.inject.Binder;
 
@@ -26,6 +28,7 @@
  * @author crazybob@google.com (Bob Lee)
  */
 @Retention(RUNTIME)
+@Target({ ElementType.FIELD, ElementType.PARAMETER })
 @Binder
 public @interface Named {
   String value();
diff --git a/src/com/google/inject/name/NamedImpl.java b/src/com/google/inject/name/NamedImpl.java
index 911b238..4017715 100644
--- a/src/com/google/inject/name/NamedImpl.java
+++ b/src/com/google/inject/name/NamedImpl.java
@@ -47,7 +47,7 @@
   }
 
   public String toString() {
-    return "@Named(\"" + value + "\")";
+    return "@" + Named.class.getName() + "(value=" + value + ")";
   }
 
   public Class<? extends Annotation> annotationType() {
diff --git a/src/com/google/inject/servlet/ServletModule.java b/src/com/google/inject/servlet/ServletModule.java
index e2210f6..ffe8df1 100644
--- a/src/com/google/inject/servlet/ServletModule.java
+++ b/src/com/google/inject/servlet/ServletModule.java
@@ -46,10 +46,14 @@
     // Bind request.
     Factory<HttpServletRequest> requestFactory =
         new Factory<HttpServletRequest>() {
-      public HttpServletRequest get() {
-        return GuiceFilter.getRequest();
-      }
-    };
+          public HttpServletRequest get() {
+            return GuiceFilter.getRequest();
+          }
+
+          public String toString() {
+            return "RequestFactory";
+          }
+        };
     bind(HttpServletRequest.class).to(requestFactory);
     bind(ServletRequest.class).to(requestFactory);
 
@@ -59,6 +63,10 @@
           public HttpServletResponse get() {
             return GuiceFilter.getResponse();
           }
+
+          public String toString() {
+            return "ResponseFactory";
+          }
         };
     bind(HttpServletResponse.class).to(responseFactory);
     bind(ServletResponse.class).to(responseFactory);
@@ -68,6 +76,10 @@
       public HttpSession get() {
         return GuiceFilter.getRequest().getSession();
       }
+
+      public String toString() {
+        return "SessionFactory";
+      }
     });
 
     // Bind request parameters.
@@ -78,6 +90,10 @@
           public Map<String, String[]> get() {
             return GuiceFilter.getRequest().getParameterMap();
           }
+
+          public String toString() {
+            return "RequestParametersFactory";
+          }
         });
   }
 }
diff --git a/src/com/google/inject/tools/jmx/ManagedBinding.java b/src/com/google/inject/tools/jmx/ManagedBinding.java
new file mode 100644
index 0000000..f25da83
--- /dev/null
+++ b/src/com/google/inject/tools/jmx/ManagedBinding.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.tools.jmx;
+
+import com.google.inject.Binding;
+
+class ManagedBinding implements ManagedBindingMBean {
+
+  final Binding binding;
+
+  ManagedBinding(Binding binding) {
+    this.binding = binding;
+  }
+
+  public String getSource() {
+    return binding.getSource().toString();
+  }
+
+  public String getKey() {
+    return binding.getKey().toString();
+  }
+
+  public String getFactory() {
+    return binding.getFactory().toString();
+  }
+}
diff --git a/src/com/google/inject/tools/jmx/ManagedBindingMBean.java b/src/com/google/inject/tools/jmx/ManagedBindingMBean.java
new file mode 100644
index 0000000..229cd2b
--- /dev/null
+++ b/src/com/google/inject/tools/jmx/ManagedBindingMBean.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.tools.jmx;
+
+/**
+ * JMX interface to bindings.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public interface ManagedBindingMBean {
+
+  /**
+   * Gets the source of this binding.
+   */
+  String getSource();
+
+  /**
+   * Gets the factory to which this binding is bound.
+   */
+  String getFactory();
+
+  /**
+   * Gets the binding key.
+   */
+  String getKey();
+}
diff --git a/src/com/google/inject/tools/jmx/Manager.java b/src/com/google/inject/tools/jmx/Manager.java
new file mode 100644
index 0000000..8a4688f
--- /dev/null
+++ b/src/com/google/inject/tools/jmx/Manager.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.tools.jmx;
+
+import com.google.inject.Binding;
+import com.google.inject.Container;
+import com.google.inject.ContainerBuilder;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import java.lang.annotation.Annotation;
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+/**
+ * Provides a JMX interface to Guice.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class Manager {
+
+  /**
+   * Registers the bindings for the container with the platform MBean server.
+   * Consider using the name of your root {@link Module} class as the domain.
+   */
+  public static void manage(
+      String domain,
+      Container container) {
+    manage(ManagementFactory.getPlatformMBeanServer(), domain, container);
+  }
+
+  /**
+   * Registers the bindings for the container with the given MBean server.
+   * Consider using the name of your root {@link Module} class as the domain.
+   */
+  public static void manage(MBeanServer server, String domain,
+      Container container) {
+    // Register each binding independently.
+    for (Binding<?> binding : container.getBindings().values()) {
+      StringBuilder name = new StringBuilder();
+
+      name.append(domain).append(":");
+
+      Key<?> key = binding.getKey();
+
+      name.append("type=").append(quote(key.getType().toString()));
+
+      Annotation annotation = key.getAnnotation();
+      if (annotation != null) {
+        name.append(",annotation=").append(quote(annotation.toString()));
+      }
+      else {
+        Class<? extends Annotation> annotationType = key.getAnnotationType();
+        if (annotationType != null) {
+          name.append(",annotation=")
+              .append(quote("@" + annotationType.getName()));
+        }
+      }
+
+      try {
+        server.registerMBean(new ManagedBinding(binding),
+            new ObjectName(name.toString()));
+      }
+      catch (MalformedObjectNameException e) {
+        throw new RuntimeException("Bad object name: "
+            + name.toString(), e);
+      }
+      catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  static String quote(String value) {
+    // JMX seems to have a comma bug.
+    return ObjectName.quote(value).replace(',', ';');
+  }
+
+  /**
+   * Run with no arguments for usage instructions.
+   */
+  public static void main(String[] args) throws Exception {
+    if (args.length != 1) {
+      System.err.println("Usage: java -Dcom.sun.management.jmxremote "
+          + Manager.class.getName() + " [module class name]");
+      System.err.println("Then run 'jconsole' to connect.");
+      System.exit(1);
+    }
+
+    Module module = (Module) Class.forName(args[0]).newInstance();
+
+    ContainerBuilder builder = new ContainerBuilder();
+    builder.install(module);
+    Container container = builder.create();
+
+    manage(args[0], container);
+
+    System.out.println("Press Ctrl+C to exit...");
+
+    // Sleep forever.
+    Thread.sleep(Long.MAX_VALUE);
+  }
+}
diff --git a/src/com/google/inject/util/ToStringBuilder.java b/src/com/google/inject/util/ToStringBuilder.java
index 394e7eb..a23a376 100644
--- a/src/com/google/inject/util/ToStringBuilder.java
+++ b/src/com/google/inject/util/ToStringBuilder.java
@@ -47,6 +47,6 @@
   }
 
   public String toString() {
-    return name + map.toString();
+    return name + map.toString().replace('{', '[').replace('}', ']');
   }
 }
diff --git a/test/com/google/inject/ErrorHandlingTest.java b/test/com/google/inject/ErrorHandlingTest.java
index 76cfa49..fda482f 100644
--- a/test/com/google/inject/ErrorHandlingTest.java
+++ b/test/com/google/inject/ErrorHandlingTest.java
@@ -45,7 +45,7 @@
     // Invalid constructor.
     Bar(String s) {}
 
-    @Inject @Named("numbers") void setNumbers(List<Integer> numbers) {}
+    @Inject void setNumbers(@Named("numbers") List<Integer> numbers) {}
 
     @Inject void bar(@Named("foo") String s) {}
   }
@@ -53,7 +53,7 @@
   static class Tee {
     @Inject String s;
 
-    @Inject @Named("foo") void tee(String s, int i) {}
+    @Inject void tee(String s, int i) {}
 
     @Inject Invalid invalid;
   }
diff --git a/test/com/google/inject/tools/jmx/JmxTest.java b/test/com/google/inject/tools/jmx/JmxTest.java
new file mode 100644
index 0000000..49093a0
--- /dev/null
+++ b/test/com/google/inject/tools/jmx/JmxTest.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.tools.jmx;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.ContainerScoped;
+import com.google.inject.Key;
+import com.google.inject.servlet.ServletModule;
+import com.google.inject.name.Names;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class JmxTest {
+
+  interface Foo {}
+
+  static class FooImpl implements Foo {}
+
+  @ContainerScoped
+  static class TransactionalFoo implements Foo {}
+
+  static class Bar {}
+
+  @Binder
+  @Retention(RUNTIME)
+  @interface Transactional {}
+
+  public static void main(String[] args) throws Exception {
+    Manager.main(new String[] { TestModule.class.getName() });
+  }
+  
+  public static class TestModule extends AbstractModule {
+
+    protected void configure() {
+      bind(Foo.class).to(FooImpl.class);
+      bind(Bar.class);
+      bind(Foo.class)
+          .annotatedWith(Transactional.class)
+          .to(FooImpl.class);
+      bindConstant(Names.named("port")).to(8080);
+      link(Key.get(Object.class)).to(Key.get(Bar.class));
+      install(new ServletModule());
+    }
+  }
+}