package org.jolokia.handler; import java.io.IOException; import java.util.Stack; import javax.management.*; import org.jolokia.backend.executor.MBeanServerExecutor; import org.jolokia.backend.executor.NotChangedException; import org.jolokia.config.ConfigKey; import org.jolokia.handler.list.MBeanInfoData; import org.jolokia.request.JmxListRequest; import org.jolokia.restrictor.Restrictor; import org.jolokia.util.EscapeUtil; 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 obtaining a list of all available MBeans and its attributes * and operations. * * @author roland * @since Jun 12, 2009 */ public class ListHandler extends JsonRequestHandler<JmxListRequest> { /** {@inheritDoc} */ public RequestType getType() { return RequestType.LIST; } /** * Constructor * * @param pRestrictor restrictor to apply */ public ListHandler(Restrictor pRestrictor) { super(pRestrictor); } /** * Return true since a list handler needs to merge all information * * @return always true */ @Override public boolean handleAllServersAtOnce(JmxListRequest pRequest) { return true; } /** {@inheritDoc} */ @Override protected void checkForRestriction(JmxListRequest pRequest) { checkType(); } /** {@inheritDoc} */ @Override public Object doHandleRequest(MBeanServerExecutor pServerManager, JmxListRequest pRequest) throws IOException, NotChangedException { // Throw an exception if list has not changed checkForModifiedSince(pServerManager, pRequest); Stack<String> originalPathStack = EscapeUtil.reversePath(pRequest.getPathParts()); int maxDepth = pRequest.getParameterAsInt(ConfigKey.MAX_DEPTH); boolean useCanonicalName = pRequest.getParameterAsBool(ConfigKey.CANONICAL_NAMING); ObjectName oName = null; try { Stack<String> pathStack = (Stack<String>) originalPathStack.clone(); oName = objectNameFromPath(pathStack); ListMBeanEachAction action = new ListMBeanEachAction(maxDepth,pathStack,useCanonicalName); if (oName == null || oName.isPattern()) { pServerManager.each(oName, action); } else { pServerManager.call(oName,action); } return action.getResult(); } catch (MalformedObjectNameException e) { throw new IllegalArgumentException("Invalid path within the MBean part given. (Path: " + pRequest.getPath() + ")",e); } catch (InstanceNotFoundException e) { throw new IllegalArgumentException("No MBean '" + oName + "' found",e); } catch (JMException e) { throw new IllegalStateException("Internal error while retrieving list: " + e, e); } } // will not be called /** {@inheritDoc} */ @Override public Object doHandleRequest(MBeanServerConnection server, JmxListRequest request) throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException { throw new UnsupportedOperationException("Internal: Method must not be called when all MBeanServers are handled at once"); } @Override /** * Path handling is done directly within this handler to avoid * excessive memory consumption by building up the whole list * into memory only for extracting a part from it. So we return false here. * * @return always false */ public boolean useReturnValueWithPath() { return false; } // ========================================================================================================== /** * Prepare an objectname patttern from a path (or "null" if no path is given) * @param pPathStack path * @return created object name (either plain or a pattern) */ private ObjectName objectNameFromPath(Stack<String> pPathStack) throws MalformedObjectNameException { if (pPathStack.empty()) { return null; } Stack<String> path = (Stack<String>) pPathStack.clone(); String domain = path.pop(); if (path.empty()) { return new ObjectName(domain + ":*"); } String props = path.pop(); ObjectName mbean = new ObjectName(domain + ":" + props); if (mbean.isPattern()) { throw new IllegalArgumentException("Cannot use an MBean pattern as path (given MBean: " + mbean + ")"); } return mbean; } // Class for handling list queries private static class ListMBeanEachAction implements MBeanServerExecutor.MBeanEachCallback, MBeanServerExecutor.MBeanAction<Void> { // Meta data which will get collected private final MBeanInfoData infoMap; /** * Handler used during iterations whe collecting MBean Meta data * * @param pMaxDepth max depth for the list tree to return * @param pPathStack optional stack for picking out a certain path from the list tree * @param pUseCanonicalName whether to use a canonical naming for the MBean property lists or the original * name */ public ListMBeanEachAction(int pMaxDepth, Stack<String> pPathStack, boolean pUseCanonicalName) { infoMap = new MBeanInfoData(pMaxDepth,pPathStack,pUseCanonicalName); } /** * Add the given MBean to the collected Meta-Data for an iteration * * @param pConn connection from where to obtain the meta data * @param pName object name of the bean * @throws ReflectionException * @throws InstanceNotFoundException * @throws IOException */ public void callback(MBeanServerConnection pConn, ObjectName pName) throws ReflectionException, InstanceNotFoundException, IOException, MBeanException { lookupMBeanInfo(pConn, pName); } /** * Add the MBeanInfo for a single MBean * * @param pConn MBeanServer on which the action should be performed * @param pName an objectname interpreted specifically by the action * @param extraArgs any extra args given as context from the outside * @return * @throws ReflectionException * @throws InstanceNotFoundException * @throws IOException * @throws MBeanException * @throws AttributeNotFoundException */ public Void execute(MBeanServerConnection pConn, ObjectName pName, Object... extraArgs) throws ReflectionException, InstanceNotFoundException, IOException, MBeanException, AttributeNotFoundException { lookupMBeanInfo(pConn, pName); return null; } private void lookupMBeanInfo(MBeanServerConnection pConn, ObjectName pName) throws InstanceNotFoundException, ReflectionException, IOException { if (!infoMap.handleFirstOrSecondLevel(pName)) { try { MBeanInfo mBeanInfo = pConn.getMBeanInfo(pName); infoMap.addMBeanInfo(mBeanInfo, pName); } catch (IOException exp) { infoMap.handleException(pName, exp); } catch (InstanceNotFoundException exp) { infoMap.handleException(pName, exp); } catch (IllegalStateException exp) { infoMap.handleException(pName, exp); } catch (IntrospectionException exp) { throw new IllegalArgumentException("Cannot extra MBeanInfo for " + pName + ": " + exp,exp); } } } /** * Get the overall result * * @return the meta data suitable for JSON serialization */ public Object getResult() { return infoMap.truncate(); } } }