bitronix.tm.utils.ManagementRegistrar Maven / Gradle / Ivy
/*
* Copyright (C) 2006-2013 Bitronix Software (http://www.bitronix.be)
*
* 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 bitronix.tm.utils;
import bitronix.tm.Configuration;
import bitronix.tm.TransactionManagerServices;
import bitronix.tm.internal.LogDebugCheck;
import javax.management.*;
import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
/**
* JMX facade used to (un)register any JMX enabled instances.
*
* In case there is no JMX implementation available, calling methods of this class have no effect.
* JMX registrations may be synchronous or asynchronous using a work-queue and worker thread.
* The latter enables higher throughput by avoiding the registration of very short lived instances and
* by that fact the JMX registrations can work on uncontended thread synchronization.
*
* @author Ludovic Orban
* @author Juergen Kellerer
*/
public final class ManagementRegistrar
{
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ManagementRegistrar.class.toString());
private static final MBeanServer mbeanServer;
private static final Queue commandQueue;
static
{
boolean enableJmx = !TransactionManagerServices.getConfiguration()
.isDisableJmx();
if (enableJmx)
{
mbeanServer = ManagementFactory.getPlatformMBeanServer();
}
else
{
mbeanServer = null;
}
}
static
{
Configuration configuration = TransactionManagerServices.getConfiguration();
commandQueue = mbeanServer == null || configuration.isSynchronousJmxRegistration() ? null :
new ArrayBlockingQueue<>(1024);
if (commandQueue != null)
{
new ManagementRegistrarThread().start();
}
}
static
{
if (mbeanServer != null)
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("Enabled JMX with MBeanServer " + mbeanServer + "; MBean registration is '" + (commandQueue == null ? "synchronous" : "asynchronous") + "'.");
}
}
else
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("JMX support is disabled.");
}
}
}
/**
* Constructor ManagementRegistrar creates a new ManagementRegistrar instance.
*/
private ManagementRegistrar()
{
}
/**
* Replace characters considered illegal in a management object's name.
*
* @param name
* the name to work on.
*
* @return a fully valid name where all invalid characters have been replaced with '_'.
*/
public static String makeValidName(String name)
{
return name.replaceAll("[\\:\\,\\=,\\.]", "_");
}
/**
* Register the specified management object.
*
* @param name
* the name of the object.
* @param obj
* the management object.
*/
public static void register(String name, Object obj)
{
if (mbeanServer == null)
{
return;
}
runOrEnqueueCommand(new ManagementRegisterCommand(name, obj));
}
/**
* Method runOrEnqueueCommand ...
*
* @param command
* of type ManagementCommand
*/
private static void runOrEnqueueCommand(ManagementCommand command)
{
if (commandQueue == null)
{
command.run();
}
else
{
// Try to enqueue the command unless the queue is full.
// Recover from a full queue by running already queued commands first to protect the async. implementation
// from being vulnerable to DOS attacks.
while (!commandQueue.offer(command))
{
normalizeAndRunQueuedCommands();
}
}
}
/**
* Method normalizeAndRunQueuedCommands ...
*/
static void normalizeAndRunQueuedCommands()
{
if (commandQueue == null)
{
return;
}
// Synchronizing on commandQueue to ensure that even if 2 threads try to poll, only one can process the commands
// that were scheduled at a time (happens if queue is full or during unit tests).
// The latter is important to ensure that the command calling order is kept intact as parallel polling would destroy it.
synchronized (commandQueue)
{
Map mappedCommands = new LinkedHashMap<>(commandQueue.size());
ManagementCommand command;
while ((command = commandQueue.poll()) != null)
{
String name = command.getName();
ManagementCommand previousCommand = mappedCommands.put(name, command);
if (previousCommand instanceof ManagementRegisterCommand)
{
// Avoid that we have unbound un-register commands in the work queue.
if (command instanceof ManagementUnregisterCommand && !((ManagementRegisterCommand) previousCommand).isReplace())
{
mappedCommands.remove(name);
}
}
else if (previousCommand instanceof ManagementUnregisterCommand && command instanceof ManagementRegisterCommand)
{
// We already have this MBean, flagging it for replacement.
((ManagementRegisterCommand) command).setReplace(true);
}
}
for (ManagementCommand c : mappedCommands.values())
{
c.run();
}
}
}
/**
* Unregister the management object with the specified name.
*
* @param name
* the name of the object.
*/
public static void unregister(String name)
{
if (mbeanServer == null)
{
return;
}
runOrEnqueueCommand(new ManagementUnregisterCommand(name));
}
/**
* Registers the given instance within the JMX environment.
*/
private static class ManagementRegisterCommand
extends ManagementCommand
{
private final WeakReference