/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.jmx; import com.google.inject.*; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.spi.BindingScopingVisitor; import com.google.inject.spi.ProvisionListener; import io.datakernel.worker.WorkerPoolModule; import java.lang.annotation.Annotation; import java.lang.management.ManagementFactory; import java.lang.reflect.Type; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static io.datakernel.util.Preconditions.checkArgument; public final class JmxModule extends AbstractModule { public static final double REFRESH_PERIOD_DEFAULT = 1.0; public static final int MAX_JMX_REFRESHES_PER_ONE_CYCLE_DEFAULT = 50; private final Set<Key<?>> singletonKeys = new HashSet<>(); private final Set<Key<?>> workerKeys = new HashSet<>(); private final Map<Key<?>, MBeanSettings> keyToSettings = new HashMap<>(); private final Map<Type, MBeanSettings> typeToSettings = new HashMap<>(); private double refreshPeriod = REFRESH_PERIOD_DEFAULT; private int maxJmxRefreshesPerOneCycle = MAX_JMX_REFRESHES_PER_ONE_CYCLE_DEFAULT; private final Map<Type, String> globalMBeans = new HashMap<>(); private JmxModule() {} public static JmxModule create() {return new JmxModule();} public JmxModule withRefreshPeriod(double refreshPeriod) { checkArgument(refreshPeriod > 0.0); this.refreshPeriod = refreshPeriod; return this; } public JmxModule withMaxJmxRefreshesPerOneCycle(int max) { checkArgument(max > 0); this.maxJmxRefreshesPerOneCycle = max; return this; } public <T> JmxModule withModifier(Key<?> key, String attrName, AttributeModifier<T> modifier) { ensureSettings(key).addModifier(attrName, modifier); return this; } public <T> JmxModule withModifier(Type type, String attrName, AttributeModifier<T> modifier) { ensureSettings(type).addModifier(attrName, modifier); return this; } public JmxModule withOptional(Key<?> key, String attrName) { ensureSettings(key).addIncludedOptional(attrName); return this; } public JmxModule withOptional(Type type, String attrName) { ensureSettings(type).addIncludedOptional(attrName); return this; } public JmxModule withHistogram(Key<?> key, String attrName, final int[] histogramLevels) { return this .withOptional(key, attrName + "_histogram") .withModifier(key, attrName, new AttributeModifier<ValueStats>() { @Override public void apply(ValueStats attribute) { attribute.setHistogramLevels(histogramLevels); } }); } public JmxModule withHistogram(Class<?> clazz, String attrName, final int[] histogramLevels) { return withHistogram(Key.get(clazz), attrName, histogramLevels); } public JmxModule withGlobalMBean(Type type, String name) { checkArgument(!globalMBeans.containsKey(type), "GlobalMBean for \"%s\" was already specified", type); globalMBeans.put(type, name); return this; } private MBeanSettings ensureSettings(Key<?> key) { MBeanSettings settings = keyToSettings.get(key); if (settings == null) { settings = MBeanSettings.defaultSettings(); keyToSettings.put(key, settings); } return settings; } private MBeanSettings ensureSettings(Type key) { MBeanSettings settings = typeToSettings.get(key); if (settings == null) { settings = MBeanSettings.defaultSettings(); typeToSettings.put(key, settings); } return settings; } @Override protected void configure() { bindListener(new AbstractMatcher<Binding<?>>() { @Override public boolean matches(Binding<?> binding) { return WorkerPoolModule.isWorkerScope(binding); } }, new ProvisionListener() { @Override public <T> void onProvision(ProvisionInvocation<T> provision) { synchronized (JmxModule.this) { if (provision.provision() != null) { workerKeys.add(provision.getBinding().getKey()); } } } }); bindListener(new AbstractMatcher<Binding<?>>() { @Override public boolean matches(Binding<?> binding) { return isSingleton(binding); } }, new ProvisionListener() { @Override public <T> void onProvision(ProvisionInvocation<T> provision) { synchronized (JmxModule.this) { if (provision.provision() != null) { singletonKeys.add(provision.getBinding().getKey()); } } } }); } private static boolean isSingleton(Binding<?> binding) { return binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() { @Override public Boolean visitNoScoping() { return false; } @Override public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) { return visitedAnnotation.equals(Singleton.class); } @Override public Boolean visitScope(Scope visitedScope) { return visitedScope.equals(Scopes.SINGLETON); } @Override public Boolean visitEagerSingleton() { return true; } }); } @Provides DynamicMBeanFactory mbeanFactory() { return JmxMBeans.factory(refreshPeriod, maxJmxRefreshesPerOneCycle); } @Provides JmxRegistrator jmxRegistrator(Injector injector, JmxRegistry jmxRegistry, DynamicMBeanFactory mbeanFactory) { return JmxRegistrator.create(injector, singletonKeys, workerKeys, jmxRegistry, mbeanFactory, keyToSettings, typeToSettings, globalMBeans); } @Provides @Singleton JmxRegistry jmxRegistry(DynamicMBeanFactory mbeanFactory) { return JmxRegistry.create( ManagementFactory.getPlatformMBeanServer(), mbeanFactory ).withRefreshPeriod(refreshPeriod); } }