/*
* Copyright 2012 ClamShell-Cli.
*
* 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.clamshellcli.jmx;
import org.clamshellcli.api.Command;
import org.clamshellcli.api.Context;
import org.clamshellcli.api.IOConsole;
import org.clamshellcli.core.ShellException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ReflectionException;
/**
* The Exec command lets user invoke methods on specified mbean.
* The format for the exec command is:
* <pre>
* exec bean:<MBeanObjectName,MBeanLabel>
* get:<attribName>|[[<attribNameList>]]
* set:<attribName>|[[<attribNameList>]]
* op:<operationName>|[[<opNameList>]]
* params:[<paramValueList>]
* </pre>
* @author vladimir.vivien
*/
public class ExecCommand implements Command{
public static final String CMD_NAME = "exec";
public static final String NAMESPACE = "jmx";
public static final String KEY_ARGS_BEAN = "bean";
public static final String KEY_ARGS_GET = "get";
public static final String KEY_ARGS_SET = "set";
public static final String KEY_ARGS_OP = "op";
public static final String KEY_ARGS_PARAMS = "params";
private Command.Descriptor descriptor = null;
public Descriptor getDescriptor() {
return (descriptor != null ) ? descriptor : (
descriptor = new Command.Descriptor() {
public String getNamespace() {
return NAMESPACE;
}
public String getName() {
return CMD_NAME;
}
public String getDescription() {
return "Execute MBean operations and getter/setter attributes.";
}
public String getUsage() {
return "exec bean:<MBeanNamePattern> [get:<AttributeName> "
+ "set:<AttributeName> op:<OperationName> "
+ "params:<ParamList>]";
}
Map<String,String> args;
public Map<String, String> getArguments() {
if(args != null) return args;
args = new LinkedHashMap<String,String>();
args.put(KEY_ARGS_BEAN + ":<NamePattern>", "The object name pattern for the bean(s)");
args.put(KEY_ARGS_BEAN + ":<MBeanLabel>", "A bean label that refers to an MBean.");
args.put(KEY_ARGS_GET + ":<AttributeName>", "An attribute to retrieve");
args.put(KEY_ARGS_SET + ":<AttributeName>", "Name of attribute to set (use 'params:' for value)");
args.put(KEY_ARGS_OP + ":<OperationName>", "Name of an operation to invoke");
args.put(KEY_ARGS_PARAMS + ":<ParamValue>", "A parameter value used for operation or setter");
args.put(KEY_ARGS_PARAMS + ":[ParamList]", "A list of two or more parameter values used for operation or setter");
return args;
}
}
);
}
public Object execute(Context ctx) {
IOConsole c = ctx.getIoConsole();
List<Object> result = null;
Map<String, Object> argsMap = (Map<String, Object>) ctx.getValue(Context.KEY_COMMAND_LINE_ARGS);
Map<String, ObjectInstance> mbeanMap = (Map<String, ObjectInstance>) ctx.getValue(Management.KEY_MBEANS_MAP);
// validate connection
Management.verifyServerConnection(ctx);
MBeanServerConnection server = (MBeanServerConnection) ctx.getValue(Management.KEY_JMX_MBEANSERVER);
Object mbeanParam = (argsMap != null) ? argsMap.get(KEY_ARGS_BEAN) : null;
Object getParam = (argsMap != null) ? argsMap.get(KEY_ARGS_GET) : null;
Object setParam = (argsMap != null) ? argsMap.get(KEY_ARGS_SET) : null;
Object opParam = (argsMap != null) ? argsMap.get(KEY_ARGS_OP) : null;
Object params = (argsMap != null) ? argsMap.get(KEY_ARGS_PARAMS) : null;
// valdate name params
if (opParam != null && setParam != null) {
throw new ShellException("You cannot specify both 'op:' "
+ "and 'set:' at the same time (see help).");
}
result = new ArrayList<Object>();
ObjectInstance[] objs = Management.findObjectInstances(ctx, (String)mbeanParam);
if(objs == null || objs.length == 0){
throw new ShellException(String.format("No MBeans found %s.",mbeanParam));
}
for (ObjectInstance obj : objs) {
// get attribute
if(getParam != null){
result.add(getObjectAttribute(server, obj, (String) getParam));
c.writeOutput(String.format("%n%s ==> %s", getParam, result));
}
// set attribute
if(setParam != null){
if(params instanceof List){
params = ((List)params).toArray();
}
this.setObjectAttribute(server, obj, (String)setParam, params);
c.writeOutput(String.format("%n%s ==> %s", setParam, params));
}
if(opParam != null){
Object[] paramVals = null;
if(params instanceof List){
paramVals = ((List)params).toArray();
}else{
paramVals = (params != null) ? new Object[]{params} : null;
}
// find all matching operation from object
List<MBeanOperationInfo> ops = null;
try {
ops = findOpsBySignature(server, obj, (String)opParam, paramVals);
} catch (Exception ex) {
throw new ShellException(ex);
}
// invoke all matching op
if(ops != null && ops.size() > 0){
for(MBeanOperationInfo op : ops){
try{
Object val = invokeObjectOperation(server, obj, op, paramVals);
result.add(val);
c.writeOutput(String.format(
"%n%s(%s) : %s",
opParam,
(paramVals != null) ? Arrays.asList(paramVals) : "",
(val != null) ? val : "void"));
}catch(ShellException ex){
c.writeOutput(String.format(
"Operation %s.%s(%s) failed: %s",
obj.getClassName(),
opParam,
Arrays.asList(getOpSignature(op)),
ex.getMessage()));
}
}
}else{
throw new ShellException(String.format(
"Method %s.%s() not found.",
obj.getClassName(),
opParam));
}
}
}
c.writeOutput(String.format("%n%n"));
return result;
}
public void plug(Context plug) {
}
private Object getObjectAttribute(MBeanServerConnection server, ObjectInstance obj, String attrib) throws ShellException {
Object result = null;
try {
result = server.getAttribute(obj.getObjectName(), attrib);
} catch (MBeanException ex) {
throw new ShellException(ex);
} catch (AttributeNotFoundException ex) {
throw new ShellException(ex);
} catch (InstanceNotFoundException ex) {
throw new ShellException(ex);
} catch (ReflectionException ex) {
throw new ShellException(ex);
} catch (IOException ex) {
throw new ShellException(ex);
}catch(Exception ex){
throw new ShellException(ex);
}
return result;
}
private void setObjectAttribute(MBeanServerConnection server, ObjectInstance obj, String attrib, Object value){
try {
server.setAttribute(obj.getObjectName(), new Attribute(attrib,value));
} catch (InstanceNotFoundException ex) {
throw new ShellException(ex);
} catch (AttributeNotFoundException ex) {
throw new ShellException(ex);
} catch (InvalidAttributeValueException ex) {
throw new ShellException(ex);
} catch (MBeanException ex) {
throw new ShellException(ex);
} catch (ReflectionException ex) {
throw new ShellException(ex);
} catch (IOException ex) {
throw new ShellException(ex);
}catch(Exception ex){
throw new ShellException(ex);
}
}
/**
* Invokes one or more operation that matches the specified op name and
* parameter signature.
* @param server
* @param obj
* @param opName
* @param params
* @return
* @throws ShellException
*/
private Object invokeObjectOperation(MBeanServerConnection server, ObjectInstance obj, MBeanOperationInfo op, Object[] params) throws ShellException{
String[] signature = getOpSignature(op);
Object result = null;
try {
result = server.invoke(obj.getObjectName(), op.getName(), params, signature);
} catch (InstanceNotFoundException ex) {
throw new ShellException(ex);
} catch (MBeanException ex) {
throw new ShellException(ex);
} catch (ReflectionException ex) {
throw new ShellException(ex);
} catch (IOException ex) {
throw new ShellException(ex);
}catch(Exception ex){
throw new ShellException(ex);
}
return result;
}
/**
* Return all operations which have the same name and signature size.
* @param server
* @param obj
* @param opName
* @param params
* @return
* @throws Exception
*/
private List<MBeanOperationInfo> findOpsBySignature(MBeanServerConnection server, ObjectInstance obj, String opName, Object[] params) throws Exception{
MBeanOperationInfo[] ops = server.getMBeanInfo(obj.getObjectName()).getOperations();
if(ops == null)
return null;
List<MBeanOperationInfo> result = new ArrayList<MBeanOperationInfo>(ops.length);
for(MBeanOperationInfo op : ops){
int paramLen = (params != null) ? params.length : 0;
if (op.getName().equals(opName) && op.getSignature().length == paramLen) {
result.add(op);
}
}
return result;
}
private String[] getOpSignature(MBeanOperationInfo op){
MBeanParameterInfo[] sig = op.getSignature();
String[] result = new String[sig.length];
for(int i = 0; i < sig.length; i++){
result[i] = sig[i].getType();
}
return result;
}
}