blob: e5c2e7005d878b4b627934653b2e66d5e2f5fb29 [file] [log] [blame]
/**
*
* Copyright 2005 the original author or authors.
*
* 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.gbean.jmx;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.JMException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gbean.kernel.Kernel;
import org.gbean.kernel.LifecycleListener;
import org.gbean.kernel.NoSuchAttributeException;
import org.gbean.kernel.NoSuchOperationException;
import org.gbean.kernel.OperationSignature;
import org.gbean.kernel.ServiceNotFoundException;
import org.gbean.kernel.runtime.ServiceState;
import org.gbean.reflect.OperationInvoker;
import org.gbean.reflect.PropertyInvoker;
import org.gbean.reflect.ServiceInvoker;
import org.gbean.reflect.ServiceInvokerManager;
import org.gbean.service.ConfigurableServiceFactory;
import org.gbean.service.ServiceFactory;
/**
* @version $Rev: 109772 $ $Date: 2004-12-03 21:06:02 -0800 (Fri, 03 Dec 2004) $
*/
public final class ServiceMBean implements DynamicMBean, NotificationEmitter {
private static final Log log = LogFactory.getLog(ServiceMBean.class);
private static final String ATTRIBUTE_CLASS_LOADER = "classLoader";
private static final String ATTRIBUTE_STATE = "state";
private static final String ATTRIBUTE_START_TIME = "startTime";
private static final String ATTRIBUTE_STATE_MANAGEABLE = "stateManageable";
private static final String ATTRIBUTE_STATISTICS_PROVIDER = "statisticsProvider";
private static final String ATTRIBUTE_EVENT_PROVIDER = "eventProvider";
private static final String ATTRIBUTE_EVENT_TYPES = "eventTypes";
private static final String ATTRIBUTE_GBEAN_ENABLED = "gbeanEnabled";
private static final String OPERATION_START = "start";
private static final String OPERATION_START_RECURSIVE = "startRecursive";
private static final String OPERATION_STOP = "stop";
/**
* The kernel
*/
private final Kernel kernel;
/**
* The unique name of this service.
*/
private final ObjectName objectName;
/**
* The broadcaster for notifications
*/
private final NotificationBroadcasterSupport notificationBroadcaster = new NotificationBroadcasterSupport();
/**
* Listenes for kernel lifecycle events for this service and broadcasts them via JMX.
*/
private final LifecycleBridge lifecycleBridge;
/**
* The service invocation manager from which we get the service invoker
*/
private final ServiceInvokerManager serviceInvokerManager;
/**
* The factory for this service
*/
private final ServiceFactory serviceFactory;
/**
* The service invoker
*/
private ServiceInvoker serviceInvoker;
public ServiceMBean(Kernel kernel, ServiceInvokerManager serviceInvokerManager, ObjectName objectName) throws ServiceNotFoundException {
this.kernel = kernel;
serviceFactory = kernel.getServiceFactory(objectName);
this.serviceInvokerManager = serviceInvokerManager;
this.objectName = objectName;
lifecycleBridge = new LifecycleBridge(notificationBroadcaster);
}
public ObjectName getObjectName() {
return objectName;
}
public ObjectName preRegister(MBeanServer mBeanServer, ObjectName objectName) throws Exception {
return objectName;
}
public synchronized void postRegister(Boolean registrationDone) {
if (Boolean.TRUE.equals(registrationDone)) {
// fire the loaded event from the mbean.. it was already fired from the GBeanInstance when it was created
kernel.addLifecycleListener(lifecycleBridge, objectName);
lifecycleBridge.loaded(objectName);
updateState();
}
}
public synchronized void preDeregister() {
kernel.removeLifecycleListener(lifecycleBridge);
lifecycleBridge.unloaded(objectName);
serviceInvoker = null;
}
public void postDeregister() {
}
private synchronized void updateState() {
try {
int serviceState = kernel.getServiceState(objectName);
boolean running = serviceState == ServiceState.RUNNING_INDEX || serviceState == ServiceState.STOPPING_INDEX;
if (running) {
serviceInvoker = serviceInvokerManager.getServiceInvoker(objectName);
return;
}
} catch (Exception e) {
// ignore cleaned up below
}
serviceInvoker = null;
}
public synchronized MBeanInfo getMBeanInfo() {
try {
String className;
String description = "No description available";
MBeanAttributeInfo[] attributes;
MBeanOperationInfo[] operations;
if (serviceInvoker != null) {
className = serviceInvoker.getServiceType().getName();
// attributes
List propertyIndex = serviceInvoker.getPropertyIndex();
attributes = new MBeanAttributeInfo[propertyIndex.size()];
for (ListIterator iterator = propertyIndex.listIterator(); iterator.hasNext();) {
PropertyInvoker propertyInvoker = (PropertyInvoker) iterator.next();
boolean isIs = false;
if (propertyInvoker.isReadable()) {
isIs = propertyInvoker.getGetterSignature().getName().startsWith("is");
}
attributes[iterator.previousIndex()] = new MBeanAttributeInfo(propertyInvoker.getPropertyName(),
propertyInvoker.getType().getName(),
"no description available",
propertyInvoker.isReadable(),
propertyInvoker.isWritable(),
isIs);
}
// operations
List operationIndex = serviceInvoker.getOperationIndex();
operations = new MBeanOperationInfo[operationIndex.size()];
for (ListIterator iterator = operationIndex.listIterator(); iterator.hasNext();) {
OperationInvoker operationInvoker = (OperationInvoker) iterator.next();
OperationSignature signature = operationInvoker.getSignature();
List argumentTypes = signature.getParameterTypes();
MBeanParameterInfo[] parameters = new MBeanParameterInfo[argumentTypes.size()];
for (ListIterator argIterator = argumentTypes.listIterator(); argIterator.hasNext();) {
String type = (String) argIterator.next();
parameters[argIterator.previousIndex()] = new MBeanParameterInfo("parameter" + argIterator.previousIndex(),
type,
"no description available");
}
operations[iterator.previousIndex()] = new MBeanOperationInfo(signature.getName(), "no description available", parameters, "java.lang.Object", MBeanOperationInfo.UNKNOWN);
}
} else {
className = Object.class.getName();
operations = new MBeanOperationInfo[0];
if (serviceFactory instanceof ConfigurableServiceFactory) {
ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
List propertyNames = new ArrayList(configurableServiceFactory.getPropertyNames());
attributes = new MBeanAttributeInfo[propertyNames.size()];
for (ListIterator iterator = propertyNames.listIterator(); iterator.hasNext();) {
String propertyName = (String) iterator.next();
attributes[iterator.previousIndex()] = new MBeanAttributeInfo(propertyName,
Object.class.getName(),
"no description available",
true,
true,
false);
}
} else {
attributes = new MBeanAttributeInfo[0];
}
}
MBeanNotificationInfo[] notifications = new MBeanNotificationInfo[1];
notifications[0] = new MBeanNotificationInfo(NotificationType.TYPES, "javax.management.Notification", "J2EE Notifications");
MBeanInfo mbeanInfo = new MBeanInfo(className, description, attributes, new MBeanConstructorInfo[0], operations, notifications);
return mbeanInfo;
} catch (RuntimeException e) {
log.info("Unable to create MBeanInfo", e);
throw e;
}
}
public Object getAttribute(String attributeName) throws ReflectionException, AttributeNotFoundException {
try {
if (ATTRIBUTE_CLASS_LOADER.equals(attributeName)) {
return kernel.getClassLoaderFor(objectName);
} else if (ATTRIBUTE_STATE.equals(attributeName)) {
return new Integer(kernel.getServiceState(objectName));
} else if (ATTRIBUTE_START_TIME.equals(attributeName)) {
return new Long(kernel.getServiceStartTime(objectName));
} else if (ATTRIBUTE_STATE_MANAGEABLE.equals(attributeName)) {
return Boolean.TRUE;
} else if (ATTRIBUTE_STATISTICS_PROVIDER.equals(attributeName)) {
return Boolean.FALSE;
} else if (ATTRIBUTE_EVENT_PROVIDER.equals(attributeName)) {
return Boolean.TRUE;
} else if (ATTRIBUTE_EVENT_TYPES.equals(attributeName)) {
return NotificationType.TYPES;
} else if (ATTRIBUTE_GBEAN_ENABLED.equals(attributeName)) {
return Boolean.valueOf(kernel.isServiceEnabled(objectName));
}
ServiceInvoker serviceInvoker;
synchronized (this) {
serviceInvoker = this.serviceInvoker;
}
if (serviceInvoker != null) {
Object value = serviceInvoker.getAttribute(attributeName);
return value;
} else {
if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
throw new AttributeNotFoundException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
}
ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
return configurableServiceFactory.getProperty(attributeName);
}
} catch (AttributeNotFoundException e) {
throw e;
} catch (NoSuchAttributeException e) {
throw new AttributeNotFoundException(attributeName);
} catch (Exception e) {
throw new ReflectionException(e);
}
}
public void setAttribute(Attribute attribute) throws ReflectionException, AttributeNotFoundException {
String attributeName = attribute.getName();
Object attributeValue = attribute.getValue();
try {
if (ATTRIBUTE_GBEAN_ENABLED.equals(attributeName)) {
kernel.setServiceEnabled(objectName, ((Boolean) attributeValue).booleanValue());
} else {
ServiceInvoker serviceInvoker;
synchronized (this) {
serviceInvoker = this.serviceInvoker;
}
if (serviceInvoker != null) {
serviceInvoker.setAttribute(attributeName, attributeValue);
} else {
if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
throw new AttributeNotFoundException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
}
ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
configurableServiceFactory.setProperty(attributeName, attributeValue);
}
}
} catch (AttributeNotFoundException e) {
throw e;
} catch (NoSuchAttributeException e) {
throw new AttributeNotFoundException(attributeName);
} catch (Exception e) {
throw new ReflectionException(e);
}
}
public AttributeList getAttributes(String[] attributes) {
AttributeList results = new AttributeList(attributes.length);
for (int i = 0; i < attributes.length; i++) {
String name = attributes[i];
try {
Object value = getAttribute(name);
results.add(new Attribute(name, value));
} catch (JMException e) {
log.warn("Exception while getting attribute " + name, e);
}
}
return results;
}
public AttributeList setAttributes(AttributeList attributes) {
AttributeList results = new AttributeList(attributes.size());
for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
Attribute attribute = (Attribute) iterator.next();
try {
setAttribute(attribute);
results.add(attribute);
} catch (JMException e) {
log.warn("Exception while setting attribute " + attribute.getName(), e);
}
}
return results;
}
public Object invoke(String operationName, Object[] arguments, String[] types) throws ReflectionException {
try {
if (arguments.length == 0 && OPERATION_START.equals(operationName)) {
kernel.startService(objectName);
return null;
} else if (arguments.length == 0 && OPERATION_START_RECURSIVE.equals(operationName)) {
kernel.startRecursiveService(objectName);
return null;
} else if (arguments.length == 0 && OPERATION_STOP.equals(operationName)) {
kernel.startService(objectName);
return null;
} else {
ServiceInvoker serviceInvoker;
synchronized (this) {
serviceInvoker = this.serviceInvoker;
}
if (serviceInvoker == null) {
throw new IllegalStateException("Service is not running: name=" + objectName);
}
return serviceInvoker.invoke(operationName, arguments, types);
}
} catch (NoSuchOperationException e) {
throw new ReflectionException(new NoSuchMethodException(new OperationSignature(operationName, types).toString()));
} catch (Exception e) {
throw new ReflectionException(e);
}
}
public MBeanNotificationInfo[] getNotificationInfo() {
return new MBeanNotificationInfo[]{
new MBeanNotificationInfo(NotificationType.TYPES, "javax.management.Notification", "J2EE Notifications")
};
}
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
notificationBroadcaster.addNotificationListener(listener, filter, handback);
}
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
notificationBroadcaster.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
notificationBroadcaster.removeNotificationListener(listener, filter, handback);
}
public String toString() {
return objectName.toString();
}
private class LifecycleBridge implements LifecycleListener {
/**
* Sequence number used for notifications
*/
private long sequence;
/**
* The notification broadcaster to use
*/
private final NotificationBroadcasterSupport notificationBroadcaster;
public LifecycleBridge(NotificationBroadcasterSupport notificationBroadcaster) {
this.notificationBroadcaster = notificationBroadcaster;
}
public void loaded(ObjectName objectName) {
if (objectName.equals(objectName)) {
notificationBroadcaster.sendNotification(new Notification(NotificationType.OBJECT_CREATED, objectName, nextSequence()));
}
}
public void starting(ObjectName objectName) {
if (objectName.equals(objectName)) {
notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STARTING, objectName, nextSequence()));
}
}
public void running(ObjectName objectName) {
if (objectName.equals(objectName)) {
updateState();
notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_RUNNING, objectName, nextSequence()));
}
}
public void stopping(ObjectName objectName) {
if (objectName.equals(objectName)) {
notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STOPPING, objectName, nextSequence()));
}
}
public void stopped(ObjectName objectName) {
if (objectName.equals(objectName)) {
updateState();
notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STOPPED, objectName, nextSequence()));
}
}
public void unloaded(ObjectName objectName) {
if (objectName.equals(objectName)) {
updateState();
notificationBroadcaster.sendNotification(new Notification(NotificationType.OBJECT_DELETED, objectName, nextSequence()));
}
}
public synchronized long nextSequence() {
return sequence++;
}
}
}