| /* |
| * 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 <code>BaseControlMBean</code> interface. This |
| * abstract class provides the core functionality that all Flex control MBeans |
| * require. |
| * <p> |
| * Defining concrete implementations of <code>getId()</code> and |
| * <code>getType()</code> are left to subclasses, but this base class does |
| * provide access to the parent MBean for each instance. This class also |
| * implements the <code>MBeanRegistration</code> 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 <code>MBeanRegistration</code> interface, but any |
| * overrides should be sure to invoke the overridden method with a call to their |
| * superclass. |
| * </p><p> |
| * The <code>register()</code> method provides a simple and consistent way to |
| * register instances with the MBean server, and the |
| * <code>getObjectName()</code> method gaurantees consistent, well-formed |
| * <code>ObjectName</code>s for all MBean instances.</p> |
| */ |
| 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 <code>BaseControl</code> instance that references its |
| * parent; the parent may be null for root control MBeans. |
| * |
| * @param parent The parent <code>BaseControl</code> 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 <code>BaseControl</code> of this instance. |
| * |
| * @return The parent <code>BaseControl</code>. |
| */ |
| public final BaseControl getParentControl() |
| { |
| return parent; |
| } |
| |
| /** |
| * The <code>MBeanServer</code> that this instance is registered with. If |
| * this instance has not been registered this method returns |
| * <code>null</code>. |
| * |
| * @return The <code>MBeanServer</code> 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 <code>MBeanRegistrationException</code> |
| * or <code>InstanceAlreadyExistsException</code> is thrown while |
| * registering this MBean, the typed exception is wrapped in a |
| * runtime <code>ManagementException</code> 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 <code>ObjectName</code> for this instance, according to the |
| * following format: |
| * <code>{domain}[&#46;{appId}]:type={type}[&#44;{parent type}={parent id}]*[&#44;server={server}]&#63;&#44;id={id}</code>. |
| * <ul> |
| * <li><code>domain</code>: The domain specified by the DOMAIN_PREFIX |
| * constant followed by the application identifier if one is available.</li> |
| * <li><code>type</code>: The short type name of the resource managed by |
| * the MBean.<br /> - The <code>MessageBrokerControlMBean</code> manages |
| * the <code>flex.messaging.MessageBroker</code> so: |
| * <code>type=MessageBroker</code> </li> |
| * <li><code>id</code>: 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:<br /> |
| * <em>id = {type} + N</em> (where N is a numeric increment for instances |
| * of this type) </li> |
| * <li>* optional containment keys</li> |
| * </ul> |
| * The runtime MBean model is hierarchical, with all MBeans ultimately |
| * contained by the root <code>MessageBrokerControlMBean</code>. The |
| * <code>ObjectName</code>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 |
| * <code>ObjectName</code> for an <code>RTMPEndpointControlMBean</code> |
| * would be: <code>type=MessageBroker.RTMPEndpoint</code><br /> |
| * In addition to the hierarchical 'type' key, the full |
| * <code>ObjectName</code> for this <code>RTMPEndpointControlMBean</code> |
| * also contains a containment key: |
| * <code>MessageBroker=MessageBroker1</code><br /> |
| * Optional containment keys have the format: |
| * <em>{parent type}={parent name}</em>. A containment key is added for |
| * each ancestor up to the root of the hierarchy and these keys allow the |
| * <code>ObjectName</code> for any MBean instance to fully describe its |
| * specific location in the hierarchy. To complete the example, the full |
| * <code>ObjectName</code> for the example |
| * <code>RTMPEndpointControlMBean</code> would be: |
| * <code>flex:type=MessageBroker.RTMPEndpoint,MessageBroker=MessageBroker1,id=RTMPEndpoint1</code> |
| * <p> |
| * If the MBean is registered with the MBean server, this method returns the |
| * <code>ObjectName</code> that the MBean was registered under and this |
| * value may contain additional key-value pairs injected by the container or |
| * MBean server. |
| * </p> |
| * |
| * @return The <code>ObjectName</code> 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 <code>javax.management.MBeanRegistration.preRegister</code>. |
| * 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 |
| * <code>getMBeanServer()</code>. If subclasses override, they must call |
| * <code>super.preRegister()</code>. |
| * |
| * @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 <code>javax.management.MBeanRegistration.postRegister</code>. |
| * 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 <code>javax.management.MBeanRegistration.preDeregister</code>. |
| * 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 <code>javax.management.MBeanRegistration.postDeregister</code>. |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| } |
| } |