/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.jmx.model;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTRIBUTES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REPLY_PROPERTIES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REQUEST_PROPERTIES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION;
import static org.jboss.as.jmx.JmxMessages.MESSAGES;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import org.jboss.as.controller.ModelController;
import org.jboss.as.controller.ModelController.OperationTransactionControl;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.descriptions.DescriptionProvider;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
import org.jboss.as.jmx.model.ChildAddOperationFinder.ChildAddOperationEntry;
import org.jboss.as.jmx.model.RootResourceIterator.ResourceAction;
import org.jboss.as.server.operations.RootResourceHack;
import org.jboss.as.server.operations.RootResourceHack.ResourceAndRegistration;
import org.jboss.dmr.ModelNode;
/**
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
*/
class ModelControllerMBeanHelper {
static final String CLASS_NAME = ModelController.class.getName();
private final boolean standalone;
private final ModelController controller;
private final PathAddress CORE_SERVICE_PLATFORM_MBEAN = PathAddress.pathAddress(PathElement.pathElement("core-service", "platform-mbean"));
ModelControllerMBeanHelper(ModelController controller) {
this.controller = controller;
ModelNode op = new ModelNode();
op.get(OP).set(READ_ATTRIBUTE_OPERATION);
op.get(OP_ADDR).setEmptyList();
op.get(NAME).set("launch-type");
final ModelNode result = execute(op);
String error = getFailureDescription(result);
if (error != null) {
throw new IllegalStateException(error);
}
standalone = result.require(RESULT).asString().equals("STANDALONE");
}
int getMBeanCount() {
return new RootResourceIterator<Integer>(getRootResourceAndRegistration().getResource(), new ResourceAction<Integer>() {
int count;
public boolean onResource(PathAddress address) {
if (isExcludeAddress(address)) {
return false;
}
count++;
return true;
}
public Integer getResult() {
return count;
}
}).iterate();
}
Set<ObjectInstance> queryMBeans(final ObjectName name, final QueryExp query) {
return new RootResourceIterator<Set<ObjectInstance>>(getRootResourceAndRegistration().getResource(), new ResourceAction<Set<ObjectInstance>>() {
Set<ObjectInstance> set = new HashSet<ObjectInstance>();
@Override
public boolean onResource(PathAddress address) {
if (isExcludeAddress(address)) {
return false;
}
ObjectName resourceName = ObjectNameAddressUtil.createObjectName(address);
if (name == null || name.apply(resourceName)) {
//TODO check query
set.add(new ObjectInstance(resourceName, CLASS_NAME));
}
return true;
}
@Override
public Set<ObjectInstance> getResult() {
return set;
}
}).iterate();
}
Set<ObjectName> queryNames(final ObjectName name, final QueryExp query) {
return new RootResourceIterator<Set<ObjectName>>(getRootResourceAndRegistration().getResource(), new ResourceAction<Set<ObjectName>>() {
Set<ObjectName> set = new HashSet<ObjectName>();
@Override
public boolean onResource(PathAddress address) {
if (isExcludeAddress(address)) {
return false;
}
ObjectName resourceName = ObjectNameAddressUtil.createObjectName(address);
if (name == null || name.apply(resourceName)) {
//TODO check query
set.add(resourceName);
}
return true;
}
@Override
public Set<ObjectName> getResult() {
return set;
}
}).iterate();
}
PathAddress resolvePathAddress(final ObjectName name) {
return ObjectNameAddressUtil.resolvePathAddress(getRootResourceAndRegistration().getResource(), name);
}
MBeanInfo getMBeanInfo(final ObjectName name) throws InstanceNotFoundException {
ResourceAndRegistration reg = getRootResourceAndRegistration();
PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
return MBeanInfoFactory.createMBeanInfo(standalone, address, getMBeanRegistration(address, reg));
}
Object getAttribute(final ObjectName name, final String attribute) throws AttributeNotFoundException, InstanceNotFoundException, ReflectionException {
final ResourceAndRegistration reg = getRootResourceAndRegistration();
final PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
return getAttribute(reg, address, name, attribute);
}
AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException {
final ResourceAndRegistration reg = getRootResourceAndRegistration();
final PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
AttributeList list = new AttributeList();
for (String attribute : attributes) {
try {
list.add(new Attribute(attribute, getAttribute(reg, address, name, attribute)));
} catch (AttributeNotFoundException e) {
throw new ReflectionException(e);
}
}
return list;
}
private Object getAttribute(final ResourceAndRegistration reg, final PathAddress address, final ObjectName name, final String attribute) throws ReflectionException, AttributeNotFoundException, InstanceNotFoundException {
final ImmutableManagementResourceRegistration registration = getMBeanRegistration(address, reg);
final DescriptionProvider provider = registration.getModelDescription(PathAddress.EMPTY_ADDRESS);
if (provider == null) {
throw MESSAGES.descriptionProviderNotFound(address);
}
final ModelNode description = provider.getModelDescription(null);
final String attributeName = findAttributeName(description.get(ATTRIBUTES), attribute);
ModelNode op = new ModelNode();
op.get(OP).set(READ_ATTRIBUTE_OPERATION);
op.get(OP_ADDR).set(address.toModelNode());
op.get(NAME).set(attributeName);
ModelNode result = execute(op);
String error = getFailureDescription(result);
if (error != null) {
throw new AttributeNotFoundException(error);
}
return TypeConverter.fromModelNode(description.require(ATTRIBUTES).require(attributeName), result.get(RESULT));
}
void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException {
final ResourceAndRegistration reg = getRootResourceAndRegistration();
final PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
setAttribute(reg, address, name, attribute);
}
AttributeList setAttributes(ObjectName name, AttributeList attributes) throws InstanceNotFoundException, ReflectionException {
final ResourceAndRegistration reg = getRootResourceAndRegistration();
final PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
for (Attribute attribute : attributes.asList()) {
try {
setAttribute(reg, address, name, attribute);
} catch (Exception e) {
throw MESSAGES.cannotSetAttribute(e, attribute.getName());
}
}
return attributes;
}
private void setAttribute(final ResourceAndRegistration reg, final PathAddress address, final ObjectName name, final Attribute attribute) throws InvalidAttributeValueException, AttributeNotFoundException, InstanceNotFoundException {
final ImmutableManagementResourceRegistration registration = getMBeanRegistration(address, reg);
final DescriptionProvider provider = registration.getModelDescription(PathAddress.EMPTY_ADDRESS);
if (provider == null) {
throw MESSAGES.descriptionProviderNotFound(address);
}
final ModelNode description = provider.getModelDescription(null);
final String attributeName = findAttributeName(description.get(ATTRIBUTES), attribute.getName());
if (!standalone) {
throw MESSAGES.attributeNotWritable(attribute);
}
ModelNode op = new ModelNode();
op.get(OP).set(WRITE_ATTRIBUTE_OPERATION);
op.get(OP_ADDR).set(address.toModelNode());
op.get(NAME).set(attributeName);
try {
op.get(VALUE).set(TypeConverter.toModelNode(description.require(ATTRIBUTES).require(attributeName), attribute.getValue()));
} catch (ClassCastException e) {
throw MESSAGES.invalidAttributeType(e, attribute.getName());
}
ModelNode result = execute(op);
String error = getFailureDescription(result);
if (error != null) {
throw new InvalidAttributeValueException(error);
}
}
ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException {
if (resolvePathAddress(name) == null) {
throw createInstanceNotFoundException(name);
}
return new ObjectInstance(name, CLASS_NAME);
}
Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws InstanceNotFoundException, MBeanException, ReflectionException {
if (operationName == null) {
throw MESSAGES.nullVar("operationName");
}
if (params == null) {
params = new Object[0];
}
if (signature == null) {
signature = new String[0];
}
if (params.length != signature.length) {
throw MESSAGES.differentLengths("params", "signature");
}
final ResourceAndRegistration reg = getRootResourceAndRegistration();
PathAddress address = ObjectNameAddressUtil.resolvePathAddress(reg.getResource(), name);
if (address == null) {
throw createInstanceNotFoundException(name);
}
final ImmutableManagementResourceRegistration registration = getMBeanRegistration(address, reg);
String realOperationName = null;
OperationEntry opEntry = registration.getOperationEntry(PathAddress.EMPTY_ADDRESS, operationName);
if (opEntry != null) {
realOperationName = operationName;
} else {
Map<String, OperationEntry> ops = registration.getOperationDescriptions(PathAddress.EMPTY_ADDRESS, false);
for (Map.Entry<String, OperationEntry> entry : ops.entrySet()) {
if (operationName.equals(NameConverter.convertToCamelCase(entry.getKey()))) {
opEntry = entry.getValue();
realOperationName = entry.getKey();
break;
}
}
}
if (opEntry == null) {
ChildAddOperationEntry entry = ChildAddOperationFinder.findAddChildOperation(reg.getRegistration().getSubModel(address), operationName);
if (entry == null) {
throw MESSAGES.noOperationCalled(null, operationName, address);
}
PathElement element = entry.getElement();
if (element.isWildcard()) {
if (params.length == 0) {
throw MESSAGES.wildcardNameParameterRequired();
}
element = PathElement.pathElement(element.getKey(), (String)params[0]);
Object[] newParams = new Object[params.length - 1];
System.arraycopy(params, 1, newParams, 0, newParams.length);
params = newParams;
}
return invoke(entry.getOperationEntry(), ADD, address.append(element), params);
}
return invoke(opEntry, realOperationName, address, params);
}
private Object invoke(final OperationEntry entry, final String operationName, PathAddress address, Object[] params) throws InstanceNotFoundException, MBeanException, ReflectionException {
if (!standalone && !entry.getFlags().contains(OperationEntry.Flag.READ_ONLY)) {
throw MESSAGES.noOperationCalled(operationName);
}
final ModelNode description = entry.getDescriptionProvider().getModelDescription(null);
ModelNode op = new ModelNode();
op.get(OP).set(operationName);
op.get(OP_ADDR).set(address.toModelNode());
if (params.length > 0) {
ModelNode requestProperties = description.require(REQUEST_PROPERTIES);
Set<String> keys = requestProperties.keys();
if (keys.size() != params.length) {
throw MESSAGES.differentLengths("params", "description");
}
Iterator<String> it = requestProperties.keys().iterator();
for (int i = 0 ; i < params.length ; i++) {
String attributeName = it.next();
ModelNode paramDescription = requestProperties.get(attributeName);
op.get(attributeName).set(TypeConverter.toModelNode(paramDescription, params[i]));
}
}
ModelNode result = execute(op);
String error = getFailureDescription(result);
if (error != null) {
throw new ReflectionException(null, error);
}
if (!description.hasDefined(REPLY_PROPERTIES)) {
return null;
}
//TODO we could have more than one reply property
return TypeConverter.fromModelNode(description.get(REPLY_PROPERTIES), result.get(RESULT));
}
static InstanceNotFoundException createInstanceNotFoundException(ObjectName name) {
return MESSAGES.mbeanNotFound(name);
}
private ResourceAndRegistration getRootResourceAndRegistration() {
return RootResourceHack.INSTANCE.getRootResource(controller);
}
private ModelNode execute(ModelNode op) {
return controller.execute(op, null, OperationTransactionControl.COMMIT, null);
}
private ImmutableManagementResourceRegistration getMBeanRegistration(PathAddress address, ResourceAndRegistration reg) throws InstanceNotFoundException {
//TODO Populate MBeanInfo
ImmutableManagementResourceRegistration resourceRegistration = reg.getRegistration().getSubModel(address);
if (resourceRegistration == null) {
throw MESSAGES.registrationNotFound(address);
}
return resourceRegistration;
}
private String getFailureDescription(ModelNode result) {
if (result.hasDefined(FAILURE_DESCRIPTION)) {
return result.get(FAILURE_DESCRIPTION).asString();
}
return null;
}
private String findAttributeName(ModelNode attributes, String attributeName) throws AttributeNotFoundException{
if (attributes.hasDefined(attributeName)) {
return attributeName;
}
for (String key : attributes.keys()) {
if (NameConverter.convertToCamelCase(key).equals(attributeName)) {
return key;
}
}
throw MESSAGES.attributeNotFound(attributeName);
}
private boolean isExcludeAddress(PathAddress pathAddress) {
return pathAddress.equals(CORE_SERVICE_PLATFORM_MBEAN);
}
}