>() {});
+ return new MBeanModule(ObjectNameGeneratorStrategy.NO_NAMESPACE);
+ }
- configureMBeans();
+ public static MBeanModule forCustomObjectNameGenerator()
+ {
+ return new MBeanModule(ObjectNameGeneratorStrategy.EXPLICIT_NAMING);
}
+ private final ObjectNameGeneratorStrategy objectNameGeneratorStrategy;
+
/**
- * To be overridden by subclasses. E.g.,
- *
- * protected void configureMBeans() {
- * export(ManagedObject.class).as("test:name=X");
- * export(ManagedObject.class).annotatedWith(SomeAnnotation.class).as("test:name=Y");
- * }
- *
- * When ExportBuilder is used, a raw MBeanModule can be imported to trigger the
- * registration of exported mbeans:
- *
- *
- * {@code
- * Injector injector = Guice.createInjector(new MBeanModule(),
- * new AbstractModule() {
- * @Override
- * protected void configure() {
- * ExportBuilder builder = MBeanModule.newExporter();
- * builder.export(AnotherManagedObject.class).as("test:name="Z");
- * }
- * });
- * }
- *
- *
- * @deprecated subclassing no longer supported. Use ExportBinder instead
+ * @deprecated It should be explicit choice to export MBeans without namespacing, as this can easily lead to name collisions.
+ * Use {@link #withUnnamespacedObjectNames()} if you want this behavior and use {@link #forCustomObjectNameGenerator()} if you
+ * want to provide your own naming strategy perhaps for namespacing.
*/
@Deprecated
- protected void configureMBeans() {
+ public MBeanModule()
+ {
+ this(ObjectNameGeneratorStrategy.LEGACY);
}
- @Deprecated
- protected NamedBindingBuilder export(Key> key)
+ private MBeanModule(ObjectNameGeneratorStrategy objectNameGeneratorStrategy)
{
- return builder.export(key);
+ this.objectNameGeneratorStrategy = requireNonNull(objectNameGeneratorStrategy, "objectNameGeneratorStrategy is null");
}
- @Deprecated
- protected AnnotatedExportBuilder export(Class> clazz)
+ @Override
+ protected void configure()
+ {
+ switch (objectNameGeneratorStrategy) {
+ case LEGACY -> {
+ bind(GuiceMBeanExporter.class)
+ .toProvider(LegacyBindingGuiceMBeanExporterProvider.class)
+ .asEagerSingleton();
+ bind(MBeanExporter.class)
+ .toProvider(LegacyBindingMBeanExporterProvider.class)
+ .in(Scopes.SINGLETON);
+ newOptionalBinder(binder(), ObjectNameGenerator.class);
+ }
+
+ case NO_NAMESPACE -> {
+ bind(GuiceMBeanExporter.class).asEagerSingleton();
+ bind(MBeanExporter.class).in(Scopes.SINGLETON);
+ bind(ObjectNameGenerator.class).toInstance(ObjectNameGenerator.defaultObjectNameGenerator());
+ }
+
+ case EXPLICIT_NAMING -> {
+ bind(GuiceMBeanExporter.class).asEagerSingleton();
+ bind(MBeanExporter.class).in(Scopes.SINGLETON);
+ // require explicit binding
+ bind(ObjectNameGenerator.class);
+ }
+ }
+
+ newSetBinder(binder(), new TypeLiteral() {});
+ newSetBinder(binder(), new TypeLiteral>() {});
+ newSetBinder(binder(), new TypeLiteral>() {});
+ }
+
+ enum ObjectNameGeneratorStrategy
{
- return builder.export(clazz);
+ /**
+ * As it used to be - user can export mbeans with the {@link ObjectNameGenerator#defaultObjectNameGenerator() default strategy},
+ * or bind their own generator to {@code ObjectNameGenerator.class} key (or via optional binder)
+ *
+ * @deprecated exists only to support deprecated legacy mode
+ */
+ @Deprecated
+ LEGACY,
+
+ /**
+ * This strategy forces user can export mbeans with the {@link ObjectNameGenerator#defaultObjectNameGenerator() default strategy}.
+ * If custom strategy is needed, {@link #EXPLICIT_NAMING} should be used.
+ * This strategy is suitable when MBeanModule is used in top level Guice context of an application.
+ */
+ NO_NAMESPACE,
+
+ /**
+ * This strategy requires that {@link ObjectNameGenerator} is separately bound.
+ * This strategy is suitable when MBeanModule is used in a library/module Guice context,
+ * where lack of namespacing may lead to name collisions.
+ */
+ EXPLICIT_NAMING,
}
- @Deprecated
- public static ExportBuilder newExporter(Binder binder)
+ private static class LegacyBindingGuiceMBeanExporterProvider
+ implements Provider
{
- return new ExportBuilder(newSetBinder(binder, Mapping.class));
+ private final Set mappings;
+ private final Set> setMappings;
+ private final Set> mapMappings;
+ private final MBeanExporter exporter;
+ private final ObjectNameGenerator objectNameGenerator;
+ private final Injector injector;
+
+ @Inject
+ public LegacyBindingGuiceMBeanExporterProvider(
+ Set mappings,
+ Set> setMappings,
+ Set> mapMappings,
+ MBeanExporter exporter,
+ Optional objectNameGenerator,
+ Injector injector)
+ {
+ this.mappings = requireNonNull(mappings, "mappings is null");
+ this.setMappings = requireNonNull(setMappings, "setMappings is null");
+ this.mapMappings = requireNonNull(mapMappings, "mapMappings is null");
+ this.exporter = requireNonNull(exporter, "exporter is null");
+ this.objectNameGenerator = objectNameGenerator.orElseGet(ObjectNameGenerator::defaultObjectNameGenerator);
+ this.injector = requireNonNull(injector, "injector is null");
+ }
+
+ @Override
+ public GuiceMBeanExporter get()
+ {
+ return new GuiceMBeanExporter(mappings, setMappings, mapMappings, exporter, objectNameGenerator, injector);
+ }
}
+ private static class LegacyBindingMBeanExporterProvider
+ implements Provider
+ {
+
+ private final MBeanServer server;
+ private final ObjectNameGenerator objectNameGenerator;
+
+ @Inject
+ public LegacyBindingMBeanExporterProvider(MBeanServer server, Optional objectNameGenerator)
+ {
+ this.server = requireNonNull(server, "server is null");
+ this.objectNameGenerator = objectNameGenerator.orElseGet(ObjectNameGenerator::defaultObjectNameGenerator);
+ }
+
+ @Override
+ public MBeanExporter get()
+ {
+ return new MBeanExporter(server, objectNameGenerator);
+ }
+ }
}
diff --git a/src/main/java/org/weakref/jmx/guice/NamedBindingBuilder.java b/src/main/java/org/weakref/jmx/guice/NamedBindingBuilder.java
deleted file mode 100644
index 1eed531..0000000
--- a/src/main/java/org/weakref/jmx/guice/NamedBindingBuilder.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright 2009 Martin Traverso
- *
- * 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 org.weakref.jmx.guice;
-
-import com.google.inject.Key;
-import com.google.inject.multibindings.Multibinder;
-import com.google.inject.name.Named;
-import org.weakref.jmx.ObjectNameGenerator;
-
-import java.util.function.Function;
-
-@Deprecated
-public class NamedBindingBuilder
-{
- protected final Multibinder binder;
- protected final Key> key;
-
- NamedBindingBuilder(Multibinder binder, Key> key)
- {
- this.binder = binder;
- this.key = key;
- }
-
- /**
- * Names the MBean according to {@link org.weakref.jmx.ObjectNames} name generator methods.
- */
- public void withGeneratedName()
- {
- if (key.getAnnotation() != null) {
- if (key.getAnnotation() instanceof Named annotation) {
- as(factory -> factory.generatedNameOf(key.getTypeLiteral().getRawType(), annotation.value()));
- }
- else {
- as(factory -> factory.generatedNameOf(key.getTypeLiteral().getRawType(), key.getAnnotation().annotationType().getSimpleName()));
- }
- }
- else if (key.getAnnotationType() != null) {
- as(factory -> factory.generatedNameOf(key.getTypeLiteral().getRawType(), key.getAnnotationType().getSimpleName()));
- }
- else {
- as(factory -> factory.generatedNameOf(key.getTypeLiteral().getRawType()));
- }
- }
-
- public void as(String name)
- {
- as(factory -> name);
- }
-
- private void as(Function mappingNameFactory)
- {
- binder.addBinding().toInstance(new Mapping(mappingNameFactory, key));
- }
-}
diff --git a/src/test/java/org/weakref/jmx/guice/TestMBeanModule.java b/src/test/java/org/weakref/jmx/guice/TestMBeanModule.java
index 2d6b9cf..6bdbd17 100644
--- a/src/test/java/org/weakref/jmx/guice/TestMBeanModule.java
+++ b/src/test/java/org/weakref/jmx/guice/TestMBeanModule.java
@@ -23,6 +23,7 @@
import com.google.inject.multibindings.Multibinder;
import org.testng.Assert;
import org.testng.annotations.Test;
+import org.weakref.jmx.MBeanExporter;
import org.weakref.jmx.ObjectNameBuilder;
import org.weakref.jmx.ObjectNameGenerator;
import org.weakref.jmx.SimpleObject;
@@ -40,10 +41,38 @@
import static com.google.inject.Stage.PRODUCTION;
import static com.google.inject.name.Names.named;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.weakref.jmx.ObjectNames.generatedNameOf;
public class TestMBeanModule
{
+ @Test
+ public void testLegacy()
+ {
+ Injector injector = Guice.createInjector(
+ new MBeanModule(),
+ binder -> binder.bind(MBeanServer.class).toInstance(ManagementFactory.getPlatformMBeanServer()));
+ injector.getInstance(MBeanExporter.class);
+ }
+
+ @Test
+ public void testWithUnnamespacedObjectNames()
+ {
+ Injector injector = Guice.createInjector(
+ MBeanModule.withUnnamespacedObjectNames(),
+ binder -> binder.bind(MBeanServer.class).toInstance(ManagementFactory.getPlatformMBeanServer()));
+ injector.getInstance(MBeanExporter.class);
+ }
+
+ @Test
+ public void testForCustomObjectNameGenerator()
+ {
+ assertThatThrownBy(() -> Guice.createInjector(
+ MBeanModule.forCustomObjectNameGenerator(),
+ binder -> binder.bind(MBeanServer.class).toInstance(ManagementFactory.getPlatformMBeanServer())))
+ .hasMessageContaining("No implementation for ObjectNameGenerator was bound");
+ }
+
@Test
public void testExportedInDevelopmentStageToo()
throws Exception
@@ -268,6 +297,19 @@ protected void configure()
server.unregisterMBean(objectName2);
}
+ @Test
+ public void testNothingExported()
+ {
+ Injector injector = Guice.createInjector(
+ new MBeanModule(),
+ binder -> {
+ binder.requireExplicitBindings();
+ binder.disableCircularProxies();
+ binder.bind(MBeanServer.class).toInstance(ManagementFactory.getPlatformMBeanServer());
+ });
+ injector.getInstance(MBeanExporter.class);
+ }
+
@Test
public void testSet()
throws Exception
diff --git a/src/test/java/org/weakref/jmx/guice/TestModuleSubclassing.java b/src/test/java/org/weakref/jmx/guice/TestModuleSubclassing.java
deleted file mode 100644
index ec2e168..0000000
--- a/src/test/java/org/weakref/jmx/guice/TestModuleSubclassing.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright 2009 Martin Traverso
- *
- * 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 org.weakref.jmx.guice;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Key;
-import com.google.inject.Module;
-import com.google.inject.Stage;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-import org.weakref.jmx.Managed;
-import org.weakref.jmx.testing.TestingMBeanServer;
-
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-public class TestModuleSubclassing
-{
- @Test
- public void testSubclassing()
- throws Exception
- {
- final Named name1 = Names.named("bean1");
- final Named name2 = Names.named("bean2");
-
- final TestingMBeanServer tms = new TestingMBeanServer();
-
- final Module m1 = new MBeanModule() {
- @Override
- protected void configureMBeans() {
- export(Key.get(Bean.class, name1)).as("bean1:name=bean1");
- }
- };
-
- final Module m2 = new MBeanModule() {
- @Override
- protected void configureMBeans() {
- export(Key.get(Bean.class, name2)).as("bean2:name=bean2");
- }
- };
-
- Guice.createInjector(Stage.PRODUCTION,
- m1,
- m2,
- new AbstractModule() {
- @Override
- public void configure() {
- bind(MBeanServer.class).toInstance(tms);
- bind(Bean.class).annotatedWith(name1).toInstance(new Bean("name1"));
- bind(Bean.class).annotatedWith(name2).toInstance(new Bean("name2"));
- }
- });
-
- Assert.assertEquals(2, tms.getMBeanCount().intValue());
- Assert.assertNotNull(tms.getMBeanInfo(new ObjectName("bean1:name=bean1")));
- Assert.assertNotNull(tms.getMBeanInfo(new ObjectName("bean2:name=bean2")));
- }
-
- public static final class Bean
- {
- private final String name;
-
- Bean(final String name)
- {
- this.name = name;
- }
-
- @Managed
- public String getName()
- {
- return name;
- }
- }
-}
-