flex.management.BaseControl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 flex.management;
import flex.management.runtime.AdminConsoleDisplayRegistrar;
import flex.messaging.FlexContext;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanRegistration;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.servlet.ServletConfig;
/**
* The implementation of the BaseControlMBean
interface. This
* abstract class provides the core functionality that all Flex control MBeans
* require.
*
* Defining concrete implementations of getId()
and
* getType()
are left to subclasses, but this base class does
* provide access to the parent MBean for each instance. This class also
* implements the MBeanRegistration
interface, and it
* automatically stores a reference to the MBean server in each instance upon
* registration. Subclasses may choose to override none, any, or all of the
* methods defined by the MBeanRegistration
interface, but any
* overrides should be sure to invoke the overridden method with a call to their
* superclass.
*
* The register()
method provides a simple and consistent way to
* register instances with the MBean server, and the
* getObjectName()
method gaurantees consistent, well-formed
* ObjectName
s for all MBean instances.
*
* @author shodgson
*/
public abstract class BaseControl implements BaseControlMBean,
MBeanRegistration
{
/**
* The prefix used for the domain part of control MBean names.
*/
public static final String DOMAIN_PREFIX = "flex.runtime";
private static final int MALFORMED_OBJECTNAME = 10400;
private static final int UNREG_EXCEPTION = 10401;
private static final int UNREG_NOTFOUND = 10402;
private static final int REG_EXCEPTION = 10403;
private static final int REG_ALREADYEXISTS = 10404;
private static final int REG_NOTCOMPLIANT = 10405;
private static final int DISABLE_MANAGEMENT = 10426;
protected Date startTimestamp;
private BaseControl parent;
private ObjectName objectName;
private ObjectName registeredObjectName;
private MBeanServer server;
private boolean registered = false;
private AdminConsoleDisplayRegistrar registrar;
// Implements flex.management.BaseControlMBean.getId; inherits javadoc
// specification.
public abstract String getId();
// Implements flex.management.BaseControlMBean.getType; inherits javadoc
// specification.
public abstract String getType();
// Implements flex.management.BaseControlMBean.getParent; inherits javadoc
// specification.
public final ObjectName getParent()
{
return (parent != null) ? parent.getObjectName() : null;
}
/**
* Returns an identifier for the application that hosts the component that
* this control manages.
*
* @return An identifier for the application that hosts the component this
* control manages.
*/
public String getApplicationId()
{
String id = null;
// Our base implementation attempts to use the current servlet context
// name as our application identifier.
ServletConfig config = FlexContext.getServletConfig();
if (config != null)
{
id = config.getServletContext().getServletContextName();
}
return (id != null) ? id.replace(":", "") : "";
}
/**
* Set the register object.
* @param registar the registrar to set
*/
protected void setRegistrar(AdminConsoleDisplayRegistrar registrar)
{
this.registrar = registrar;
}
/**
* Return the registar object.
* @return the registrar
*/
public AdminConsoleDisplayRegistrar getRegistrar()
{
if ((parent == null) && (this.registrar == null))
{
return new AdminConsoleDisplayRegistrar(null);
}
return (this.registrar != null) ? this.registrar : parent.getRegistrar();
}
/**
* Constructs a BaseControl
instance that references its
* parent; the parent may be null for root control MBeans.
*
* @param parent The parent BaseControl
for this instance or
* null if this instance is the root of a control hierarchy.
*/
public BaseControl(BaseControl parent)
{
this.parent = parent;
}
/**
* Returns the parent BaseControl
of this instance.
*
* @return The parent BaseControl
.
*/
public final BaseControl getParentControl()
{
return parent;
}
/**
* The MBeanServer
that this instance is registered with. If
* this instance has not been registered this method returns
* null
.
*
* @return The MBeanServer
that this instance is registered
* with.
*/
public final MBeanServer getMBeanServer()
{
return server;
}
/**
* Registers this instance with the MBean server.
*
* It may throw ManagementException If an MBeanRegistrationException
* or InstanceAlreadyExistsException
is thrown while
* registering this MBean, the typed exception is wrapped in a
* runtime ManagementException
and rethrown.
*/
public final void register()
{
if (!registered)
{
MBeanServer server = MBeanServerLocatorFactory.getMBeanServerLocator().getMBeanServer();
ObjectName name = getObjectName();
try
{
if (server.isRegistered(name))
{
server.unregisterMBean(name);
}
registeredObjectName = server.registerMBean(this, name).getObjectName();
registered = true;
onRegistrationComplete();
}
catch (ManagementException me)
{
throw me;
}
catch (MBeanRegistrationException mre)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(REG_EXCEPTION, new Object[] {name.toString()});
me.setRootCause(mre);
throw me;
}
catch (InstanceAlreadyExistsException iaee)
{
// If registration is not working at all, inform the user that
// they may
// work around the issue by disabling management (no MBeans will
// be registered).
if (!server.isRegistered(name))
{
ManagementException me = new ManagementException();
me.setMessage(DISABLE_MANAGEMENT, new Object[] {name.toString()});
throw me;
}
else
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(REG_ALREADYEXISTS, new Object[] {name.toString()});
throw me;
}
}
catch (NotCompliantMBeanException ncme)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(REG_NOTCOMPLIANT, new Object[] {name.toString()});
throw me;
}
catch (InstanceNotFoundException infe)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(UNREG_NOTFOUND, new Object[] {name.toString()});
throw me;
}
}
}
/**
* This method is called after the MBean has been registered and after the
* MBean server has returned the registeredObjectName. Classes that need
* access to the actual Object name should override this method rather than
* the postRegister method.
*/
protected void onRegistrationComplete()
{
}
/**
* Unregisters this instance from the MBean server if it has been registered
* previously.
*/
public final void unregister()
{
if (registered)
{
// This method may be called when the JVM is being unloaded, so if
// our
// external error strings are loaded as missing, fall back to
// hard-coded
// strings.
try
{
if (server.isRegistered(registeredObjectName))
{
server.unregisterMBean(registeredObjectName);
}
registeredObjectName = null;
registered = false;
}
catch (ManagementException me)
{
throw me;
}
catch (MBeanRegistrationException mre)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(UNREG_EXCEPTION, new Object[] {registeredObjectName.toString()});
if (me.getMessage().indexOf(Integer.toString(UNREG_EXCEPTION)) != -1)
{
me.setMessage("The MBean named, '" + registeredObjectName.toString() + "', could not be unregistered because its preDeregister() method threw an exception.");
}
me.setRootCause(mre);
throw me;
}
catch (InstanceNotFoundException infe)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(UNREG_NOTFOUND, new Object[] {registeredObjectName.toString()});
if (me.getMessage().indexOf(Integer.toString(UNREG_NOTFOUND)) != -1)
{
me.setMessage("The MBean named, '" + registeredObjectName.toString() + "', could not be unregistered because it is not currently registered.");
}
throw me;
}
}
}
/**
* Returns the ObjectName
for this instance, according to the
* following format:
* {domain}[.{appId}]:type={type}[,{parent type}={parent id}]*[,server={server}]?,id={id}
.
*
* domain
: The domain specified by the DOMAIN_PREFIX
* constant followed by the application identifier if one is available.
* type
: The short type name of the resource managed by
* the MBean.
- The MessageBrokerControlMBean
manages
* the flex.messaging.MessageBroker
so:
* type=MessageBroker
* id
: The id value for the resource managed by this
* MBean. If no name or id is available on the resource, an id will be
* fabricated according to this strategy:
* id = {type} + N (where N is a numeric increment for instances
* of this type)
* - * optional containment keys
*
* The runtime MBean model is hierarchical, with all MBeans ultimately
* contained by the root MessageBrokerControlMBean
. The
* ObjectName
s used for these MBeans describe this
* containment in the following fashion. First, the 'type' key for a
* contained MBean indicates the containment hierarchy for the bean. So, the
* ObjectName
for an RTMPEndpointControlMBean
* would be: type=MessageBroker.RTMPEndpoint
* In addition to the hierarchical 'type' key, the full
* ObjectName
for this RTMPEndpointControlMBean
* also contains a containment key:
* MessageBroker=MessageBroker1
* Optional containment keys have the format:
* {parent type}={parent name}. A containment key is added for
* each ancestor up to the root of the hierarchy and these keys allow the
* ObjectName
for any MBean instance to fully describe its
* specific location in the hierarchy. To complete the example, the full
* ObjectName
for the example
* RTMPEndpointControlMBean
would be:
* flex:type=MessageBroker.RTMPEndpoint,MessageBroker=MessageBroker1,id=RTMPEndpoint1
*
* If the MBean is registered with the MBean server, this method returns the
* ObjectName
that the MBean was registered under and this
* value may contain additional key-value pairs injected by the container or
* MBean server.
*
*
* @return The ObjectName
for this instance.
*/
public final ObjectName getObjectName()
{
if (registered)
return registeredObjectName;
if (objectName == null)
{
StringBuffer buffer = new StringBuffer();
buffer.append(DOMAIN_PREFIX);
String appId = getApplicationId();
if (appId != null && appId.length() > 0)
{
buffer.append('.');
buffer.append(appId);
}
buffer.append(":type=");
// Build hierarchical type value.
List types = new ArrayList();
List ids = new ArrayList();
types.add(getType());
ids.add(getId());
BaseControl ancestor = parent;
while (ancestor != null)
{
types.add(ancestor.getType());
ids.add(ancestor.getId());
ancestor = ancestor.getParentControl();
}
for (int i = types.size() - 1; i >= 0; --i)
{
buffer.append((String)types.get(i));
if (i > 0)
{
buffer.append('.');
}
}
buffer.append(',');
// Add containment keys.
for (int i = ids.size() - 1; i >= 1; --i)
{
buffer.append((String)types.get(i));
buffer.append('=');
buffer.append((String)ids.get(i));
buffer.append(',');
}
buffer.append("id=");
buffer.append(getId());
String name = buffer.toString();
// TODO: Seth: add server identifier key if we're running in a
// cluster?
try
{
objectName = new ObjectName(name);
}
catch (MalformedObjectNameException mone)
{
// Rethrow with useful message if this ever happens.
ManagementException me = new ManagementException();
me.setMessage(MALFORMED_OBJECTNAME, new Object[] {name});
throw me;
}
}
return objectName;
}
/**
* Implements javax.management.MBeanRegistration.preRegister
.
* Allows the MBean to perform any operations it needs before being
* registered in the MBean server. This base implementation stores a
* reference to the MBean server that may be accessed via
* getMBeanServer()
. If subclasses override, they must call
* super.preRegister()
.
*
* @param server The Mbean server in which the MBean will be registered.
* @param name The object name of the MBean.
* @return The name the MBean will be registered under.
* @throws Exception when the process failed
*/
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception
{
this.server = server;
return (name == null) ? getObjectName() : name;
}
/**
* Implements javax.management.MBeanRegistration.postRegister
.
* Allows the MBean to perform any operations needed after having been
* registered in the MBean server or after the registration has failed. This
* base implementation is a no-op that may be overridden.
*
* @param registrationDone Indicates whether or not the MBean was
* successfully registered in the MBean server.
*/
public void postRegister(Boolean registrationDone)
{
// No-op.
}
/**
* Implements javax.management.MBeanRegistration.preDeregister
.
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server. This base implementation is a no-op
* that may be overridden.
* @throws Exception when the process failed
*/
public void preDeregister() throws Exception
{
// No-op.
}
/**
* Implements javax.management.MBeanRegistration.postDeregister
.
* Allows the MBean to perform any operations it needs before being
* unregistered by the MBean server. This base implementation is a no-op
* that may be overridden.
*/
public void postDeregister()
{
// No-op.
}
/**
* Sets the start timestamp for the managed component.
*
* @param value The start timestamp for the managed component.
*/
public void setStartTimestamp(Date value)
{
startTimestamp = value;
}
/**
* @exclude Returns the difference between a start and end timestamps in
* minutes. Differences of less than one minute are rounded up to
* one minute to avoid frequency calculations reporting infinite
* message frequencies.
* @param startTime The start timestamp in milliseconds.
* @param endTime The end timestamp in milliseconds.
* @return The difference between a start and end timestamps in minutes.
*/
protected double differenceInMinutes(long startTime, long endTime)
{
double minutes = (endTime - startTime) / 60000d;
if (minutes > 1d)
{
return minutes;
}
else
{
return 1d;
}
}
}