package org.jolokia.handler;
import java.io.IOException;
import java.util.*;
import javax.management.*;
import org.jolokia.backend.executor.MBeanServerExecutor;
import org.jolokia.converter.json.ValueFaultHandler;
import org.jolokia.request.JmxReadRequest;
import org.jolokia.restrictor.Restrictor;
import org.jolokia.util.RequestType;
/*
* Copyright 2009-2013 Roland Huss
*
* 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.
*/
/**
* Handler for managing READ requests for reading attributes.
*
* @author roland
* @since Jun 12, 2009
*/
public class ReadHandler extends JsonRequestHandler<JmxReadRequest> {
// MBean Handler used for extracting MBean Meta data
private static final MBeanServerExecutor.MBeanAction<MBeanInfo> MBEAN_INFO_HANDLER =
new MBeanServerExecutor.MBeanAction<MBeanInfo>() {
/** {@inheritDoc} */
public MBeanInfo execute(MBeanServerConnection pConn, ObjectName pName, Object... extraArgs)
throws ReflectionException, InstanceNotFoundException, IOException {
try {
return pConn.getMBeanInfo(pName);
} catch (IntrospectionException e) {
throw new IllegalArgumentException("Cannot inspect " + pName + ": " + e, e);
}
}
};
// MBean Handler for getting an attribute
private static final MBeanServerExecutor.MBeanAction<Object> MBEAN_ATTRIBUTE_READ_HANDLER =
new MBeanServerExecutor.MBeanAction<Object>() {
/** {@inheritDoc} */
public Object execute(MBeanServerConnection pConn, ObjectName pName, Object... extraArgs)
throws ReflectionException, InstanceNotFoundException, IOException, MBeanException, AttributeNotFoundException {
String attribute = (String) extraArgs[0];
return pConn.getAttribute(pName, attribute);
}
};
/**
* Read handler constructor
*
* @param pRestrictor access restriction to apply
*/
public ReadHandler(Restrictor pRestrictor) {
super(pRestrictor);
}
@Override
/** {@inheritDoc} */
public RequestType getType() {
return RequestType.READ;
}
/**
* For a simple requests (one MBean, one attribute) we let the dispatching of the servers
* done by the upper level. If the request is for an MBean pattern or multiple attributes
* are required, we try multiple requests for multiple server.
*
* @param pRequest request to decide on whether to handle all request at once
* @return true if this is a multi attribute request, has an MBean pattern to look for or is a request for
* all attributes.
*/
@Override
public boolean handleAllServersAtOnce(JmxReadRequest pRequest) {
return pRequest.getObjectName().isPattern() || pRequest.isMultiAttributeMode() || !pRequest.hasAttribute();
}
/**
* Used for a request to a single attribute from a single MBean. Merging of MBeanServers is done
* one layer above.
*
*
* @param pServer server on which to request the attribute
* @param pRequest the request itself.
* @return the attribute's value
*/
@Override
public Object doHandleRequest(MBeanServerConnection pServer, JmxReadRequest pRequest)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException {
checkRestriction(pRequest.getObjectName(), pRequest.getAttributeName());
return pServer.getAttribute(pRequest.getObjectName(), pRequest.getAttributeName());
}
@Override
/** {@inheritDoc} */
public Object doHandleRequest(MBeanServerExecutor pServerManager, JmxReadRequest pRequest)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException {
ObjectName oName = pRequest.getObjectName();
ValueFaultHandler faultHandler = pRequest.getValueFaultHandler();
if (oName.isPattern()) {
return fetchAttributesForMBeanPattern(pServerManager, pRequest);
} else {
return fetchAttributes(pServerManager,oName,pRequest.getAttributeNames(),faultHandler);
}
}
private Object fetchAttributesForMBeanPattern(MBeanServerExecutor pServerManager, JmxReadRequest pRequest)
throws IOException, InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
ObjectName objectName = pRequest.getObjectName();
ValueFaultHandler faultHandler = pRequest.getValueFaultHandler();
Set<ObjectName> names = searchMBeans(pServerManager, objectName);
Map<String,Object> ret = new HashMap<String, Object>();
List<String> attributeNames = pRequest.getAttributeNames();
for (ObjectName name : names) {
try {
if (!pRequest.hasAttribute()) {
Map values = (Map) fetchAttributes(pServerManager,name, null, faultHandler);
if (values != null && values.size() > 0) {
ret.put(pRequest.getOrderedObjectName(name),values);
}
} else {
List<String> filteredAttributeNames = filterAttributeNames(pServerManager,name,attributeNames);
if (filteredAttributeNames.size() == 0) {
continue;
}
ret.put(pRequest.getOrderedObjectName(name),
fetchAttributes(pServerManager,name,filteredAttributeNames, faultHandler));
}
} catch (InstanceNotFoundException exp) {
// Since MBean can be registered/deregistered dynamically, it can happen here, that
// an MBean has been already unregistered in the meantime. We simply ignore an InstanceNotFoundException
// here and go on ....
}
}
if (ret.size() == 0) {
throw new IllegalArgumentException("No matching attributes " +
pRequest.getAttributeNames() + " found on MBeans " + names);
}
return ret;
}
private Set<ObjectName> searchMBeans(MBeanServerExecutor pServerManager, ObjectName pObjectName) throws IOException, InstanceNotFoundException {
Set<ObjectName> names = pServerManager.queryNames(pObjectName);
if (names.size() == 0) {
throw new InstanceNotFoundException("No MBean with pattern " + pObjectName +
" found for reading attributes");
}
return names;
}
// Return only those attributes of an mbean which has one of the given names
private List<String> filterAttributeNames(MBeanServerExecutor pSeverManager,ObjectName pName, List<String> pNames)
throws IOException, ReflectionException, MBeanException, AttributeNotFoundException, InstanceNotFoundException {
Set<String> attrs = new HashSet<String>(getAllAttributesNames(pSeverManager,pName));
List<String> ret = new ArrayList<String>();
for (String name : pNames) {
if (attrs.contains(name)) {
ret.add(name);
}
}
return ret;
}
private Object fetchAttributes(MBeanServerExecutor pServerManager, ObjectName pMBeanName, List<String> pAttributeNames,
ValueFaultHandler pFaultHandler)
throws InstanceNotFoundException, IOException, ReflectionException, AttributeNotFoundException, MBeanException {
List<String> attributes = resolveAttributes(pServerManager, pMBeanName, pAttributeNames);
Map<String,Object> ret = new HashMap<String, Object>();
for (String attribute : attributes) {
try {
checkRestriction(pMBeanName, attribute);
ret.put(attribute,getAttribute(pServerManager, pMBeanName, attribute));
} catch (MBeanException e) {
// The fault handler might to decide to rethrow the
// exception in which case nothing is put extra into ret.
// Otherwise, the replacement value as returned by the
// fault handler is inserted.
ret.put(attribute, pFaultHandler.handleException(e));
} catch (IllegalArgumentException e) {
ret.put(attribute, pFaultHandler.handleException(e));
} catch (ReflectionException e) {
ret.put(attribute, pFaultHandler.handleException(e));
} catch (IOException e) {
ret.put(attribute, pFaultHandler.handleException(e));
} catch (RuntimeException e) {
ret.put(attribute, pFaultHandler.handleException(e));
} catch (AttributeNotFoundException e) {
ret.put(attribute, pFaultHandler.handleException(e));
}
}
return ret;
}
// Resolve attributes and look up attribute names if all attributes need to be fetched.
private List<String> resolveAttributes(MBeanServerExecutor pServers, ObjectName pMBeanName, List<String> pAttributeNames)
throws IOException, ReflectionException, MBeanException, AttributeNotFoundException, InstanceNotFoundException {
List<String> attributes = pAttributeNames;
if (shouldAllAttributesBeFetched(pAttributeNames)) {
// All attributes are requested, we look them up now
attributes = getAllAttributesNames(pServers,pMBeanName);
}
return attributes;
}
private boolean shouldAllAttributesBeFetched(List<String> pAttributeNames) {
return pAttributeNames == null || pAttributeNames.size() == 0 || pAttributeNames.size() == 1 && pAttributeNames.get(0) == null;
}
// Get the MBeanInfo from one of the provided MBeanServers
private MBeanInfo getMBeanInfo(MBeanServerExecutor pServerManager, ObjectName pObjectName)
throws IOException, ReflectionException, MBeanException, AttributeNotFoundException, InstanceNotFoundException {
return pServerManager.call(pObjectName, MBEAN_INFO_HANDLER);
}
// Try multiple servers for fetching an attribute
private Object getAttribute(MBeanServerExecutor pServerManager, ObjectName pMBeanName, String attribute)
throws MBeanException, ReflectionException, IOException, AttributeNotFoundException, InstanceNotFoundException {
return pServerManager.call(pMBeanName, MBEAN_ATTRIBUTE_READ_HANDLER, attribute);
}
// Return a set of attributes as a map with the attribute name as key and their values as values
private List<String> getAllAttributesNames(MBeanServerExecutor pServerManager, ObjectName pObjectName)
throws IOException, ReflectionException, MBeanException, AttributeNotFoundException, InstanceNotFoundException {
MBeanInfo mBeanInfo = getMBeanInfo(pServerManager, pObjectName);
List<String> ret = new ArrayList<String>();
for (MBeanAttributeInfo attrInfo : mBeanInfo.getAttributes()) {
if (attrInfo.isReadable()) {
ret.add(attrInfo.getName());
}
}
return ret;
}
private void checkRestriction(ObjectName mBeanName, String attribute) {
if (!getRestrictor().isAttributeReadAllowed(mBeanName,attribute)) {
throw new SecurityException("Reading attribute " + attribute +
" is forbidden for MBean " + mBeanName.getCanonicalName());
}
}
/**
* We override it here with a noop since we do a more fine grained
* check during processing of the request.
*/
@Override
protected void checkForRestriction(JmxReadRequest pRequest) {
}
}