Index: src/zabbix_java/src/com/zabbix/gateway/JMXItemChecker.java
===================================================================
--- src/zabbix_java/src/com/zabbix/gateway/JMXItemChecker.java	(revision 43648)
+++ src/zabbix_java/src/com/zabbix/gateway/JMXItemChecker.java	(working copy)
@@ -19,9 +19,14 @@
 
 package com.zabbix.gateway;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
+import javax.management.Attribute;
+import javax.management.AttributeList;
 import javax.management.MBeanAttributeInfo;
 import javax.management.MBeanInfo;
 import javax.management.MBeanServerConnection;
@@ -48,6 +53,12 @@
 	private String username;
 	private String password;
 
+	private Map<ObjectName, Map<String, List<ItemInfo>>> valueItems = new HashMap<>();
+	private Map<String, String> discoveryItems = new HashMap<>();
+	private Map<String, List<DiscoveryInfo>> discoveryResults = new HashMap<>();
+	private Map<String, ValueInfo> valueResults = new HashMap<>();
+	private Map<String, Exception> errors = new HashMap<>();
+
 	public JMXItemChecker(JSONObject request) throws ZabbixException
 	{
 		super(request);
@@ -93,6 +104,15 @@
 			mbsc = jmxc.getMBeanServerConnection();
 
 			for (String key : keys)
+				collectItemInfo(key);
+
+			for (Map.Entry<String, String> item : discoveryItems.entrySet())
+				discoverItems(item.getKey(), item.getValue());
+
+			for (Map.Entry<ObjectName, Map<String, List<ItemInfo>>> item : valueItems.entrySet())
+				retrieveAttributeValues(item.getKey(), item.getValue());
+
+			for (String key : keys)
 				values.put(getJSONValue(key));
 		}
 		catch (Exception e)
@@ -110,8 +130,10 @@
 		return values;
 	}
 
-	@Override
-	protected String getStringValue(String key) throws Exception
+	/**
+	 * Add an entry to the valueItems or discoveryItems info for the given key.
+	 */
+	private void collectItemInfo(String key)
 	{
 		ZabbixItem item = new ZabbixItem(key);
 
@@ -118,9 +140,21 @@
 		if (item.getKeyId().equals("jmx"))
 		{
 			if (2 != item.getArgumentCount())
-				throw new ZabbixException("required key format: jmx[<object name>,<attribute name>]");
+			{
+				errors.put(key, new ZabbixException("required key format: jmx[<object name>,<attribute name>]"));
+				return;
+			}
 
-			ObjectName objectName = new ObjectName(item.getArgument(1));
+			ObjectName objectName;
+			try
+			{
+				objectName = new ObjectName(item.getArgument(1));
+			}
+			catch (Exception e)
+			{
+				errors.put(key, e);
+				return;
+			}
 			String attributeName = item.getArgument(2);
 			String realAttributeName;
 			String fieldNames = "";
@@ -151,15 +185,31 @@
 			logger.trace("attributeName:'{}'", realAttributeName);
 			logger.trace("fieldNames:'{}'", fieldNames);
 
-			return getPrimitiveAttributeValue(mbsc.getAttribute(objectName, realAttributeName), fieldNames);
+			recordValueItem(key, objectName, realAttributeName, fieldNames);
 		}
 		else if (item.getKeyId().equals("jmx.discovery"))
 		{
 			if (0 != item.getArgumentCount())
-				throw new ZabbixException("required key format: jmx.discovery");
+			{
+				errors.put(key, new ZabbixException("required key format: jmx.discovery"));
+				return;
+			}
 
-			JSONArray counters = new JSONArray();
+			discoveryItems.put(key, "");
+		}
+		else
+			errors.put(key, new ZabbixException("key ID '%s' is not supported", item.getKeyId()));
+	}
 
+	/**
+	 * Discovers all items on the server. The retrieval of the values for the items is
+	 * not done here; instead, the items are added to the valueItems to be retrieved
+	 * along with the other requested items.
+	 */
+	private void discoverItems(String key, String args)
+	{
+		try
+		{
 			for (ObjectName name : mbsc.queryNames(null, null))
 			{
 				logger.trace("discovered object '{}'", name);
@@ -174,26 +224,120 @@
 						continue;
 					}
 
-					try
-					{
-						logger.trace("looking for attributes of primitive types");
-						String descr = (attrInfo.getName().equals(attrInfo.getDescription()) ? null : attrInfo.getDescription());
-						findPrimitiveAttributes(counters, name, descr, attrInfo.getName(), mbsc.getAttribute(name, attrInfo.getName()));
-					}
-					catch (Exception e)
-					{
-						Object[] logInfo = {name, attrInfo.getName(), e};
-						logger.trace("processing '{},{}' failed", logInfo);
-					}
+					List<DiscoveryInfo> discoveryInfos = discoveryResults.get(key);
+					if (discoveryInfos == null)
+						discoveryResults.put(key, discoveryInfos = new ArrayList<>());
+
+					DiscoveryInfo discoveryInfo = new DiscoveryInfo(name, attrInfo);
+					discoveryInfos.add(discoveryInfo);
+					recordValueItem(discoveryInfo.getKey(), name, attrInfo.getName(), "");
 				}
 			}
+		}
+		catch (Exception e)
+		{
+			errors.put(key, e);
+		}
+	}
 
+	private void recordValueItem(String key, ObjectName objectName, String realAttributeName, String fieldNames)
+	{
+		Map<String, List<ItemInfo>> attrsMap = valueItems.get(objectName);
+		if (attrsMap == null)
+			valueItems.put(objectName, attrsMap = new HashMap<>());
+
+		List<ItemInfo> items = attrsMap.get(realAttributeName);
+		if (items == null)
+			attrsMap.put(realAttributeName, items = new ArrayList<>());
+
+		items.add(new ItemInfo(key, fieldNames));
+	}
+
+	/**
+	 * Retrieve the desired attributes for the given object from the JMX server.
+	 */
+	private void retrieveAttributeValues(ObjectName objectName, Map<String, List<ItemInfo>> attrsInfo)
+	{
+		try
+		{
+			logger.trace("Retrieving attributes {} for object {}", attrsInfo.keySet(), objectName);
+
+			String[] attributes = attrsInfo.keySet().toArray(new String[attrsInfo.size()]);
+			AttributeList attributeList = mbsc.getAttributes(objectName, attributes);
+
+			for (Attribute attribute : attributeList.asList())
+			{
+				List<ItemInfo> itemInfos = attrsInfo.remove(attribute.getName());
+				if (itemInfos == null)
+				{
+					logger.warn("server returned unknown item '{}'", attribute.getName());
+					continue;
+				}
+
+				logger.trace("received attribute {}", attribute);
+
+				for (ItemInfo itemInfo : itemInfos)
+					valueResults.put(itemInfo.key, new ValueInfo(itemInfo.fieldNames, attribute.getValue()));
+			}
+
+			for (List<ItemInfo> itemInfos : attrsInfo.values())
+				for (ItemInfo itemInfo : itemInfos)
+					valueResults.put(itemInfo.key, new ValueInfo(itemInfo.fieldNames, null));
+		}
+		catch (Exception e)
+		{
+			logger.debug("caught exception retrieving values for " + objectName, e);
+
+			for (List<ItemInfo> itemInfos : attrsInfo.values())
+				for (ItemInfo itemInfo : itemInfos)
+					errors.put(itemInfo.key, e);
+		}
+	}
+
+	@Override
+	protected String getStringValue(String key) throws Exception
+	{
+		Exception exc = errors.get(key);
+		if (exc != null)
+			throw exc;
+
+		ValueInfo valueInfo = valueResults.get(key);
+		if (valueInfo != null)
+			return getPrimitiveAttributeValue(valueInfo.value, valueInfo.fieldNames);
+
+		List<DiscoveryInfo> discoveryInfos = discoveryResults.get(key);
+		if (discoveryInfos != null)
+		{
+			JSONArray counters = new JSONArray();
+
+			for (DiscoveryInfo discoveryInfo : discoveryInfos)
+			{
+				logger.trace("looking for attributes of primitive types");
+				valueInfo = valueResults.get(discoveryInfo.getKey());
+				MBeanAttributeInfo attrInfo = discoveryInfo.attrInfo;
+				String descr = (attrInfo.getName().equals(attrInfo.getDescription()) ? null : attrInfo.getDescription());
+
+				try
+				{
+					exc = errors.get(discoveryInfo.getKey());
+					if (exc != null)
+						throw exc;
+
+					findPrimitiveAttributes(counters, discoveryInfo.name, descr, attrInfo.getName(), valueInfo.value);
+				}
+				catch (Exception e)
+				{
+					Object[] logInfo = {discoveryInfo.name, attrInfo.getName(), e};
+					logger.trace("processing '{},{}' failed", logInfo);
+				}
+			}
+
 			JSONObject mapping = new JSONObject();
 			mapping.put(ItemChecker.JSON_TAG_DATA, counters);
 			return mapping.toString();
 		}
-		else
-			throw new ZabbixException("key ID '%s' is not supported", item.getKeyId());
+
+		throw new ZabbixException("Internal error: key '%s' not found in results", key);
 	}
 
 	private String getPrimitiveAttributeValue(Object dataObject, String fieldNames) throws ZabbixException
@@ -282,4 +426,45 @@
 
 		return HelperFunctionChest.arrayContains(clazzez, clazz);
 	}
+
+	private static class ItemInfo
+	{
+		public final String key;
+		public final String fieldNames;
+
+		public ItemInfo(String key, String fieldNames)
+		{
+			this.key = key;
+			this.fieldNames = fieldNames;
+		}
+	}
+
+	private static class ValueInfo
+	{
+		public final String fieldNames;
+		public final Object value;
+
+		public ValueInfo(String fieldNames, Object value)
+		{
+			this.fieldNames = fieldNames;
+			this.value = value;
+		}
+	}
+
+	private static class DiscoveryInfo
+	{
+		public final ObjectName name;
+		public final MBeanAttributeInfo attrInfo;
+
+		public DiscoveryInfo(ObjectName name, MBeanAttributeInfo attrInfo)
+		{
+			this.name = name;
+			this.attrInfo = attrInfo;
+		}
+
+		public String getKey()
+		{
+			return name.toString() + ":" + attrInfo.getName();
+		}
+	}
 }
