/* * This software copyright by various authors including the RPTools.net * development team, and licensed under the LGPL Version 3 or, at your * option, any later version. * * Portions of this software were originally covered under the Apache * Software License, Version 1.1 or Version 2.0. * * See the file LICENSE elsewhere in this distribution for license details. */ package net.sbbi.upnp.jmx; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import net.sbbi.upnp.devices.UPNPDevice; import net.sbbi.upnp.devices.UPNPRootDevice; import net.sbbi.upnp.messages.ActionMessage; import net.sbbi.upnp.messages.ActionResponse; import net.sbbi.upnp.messages.StateVariableMessage; import net.sbbi.upnp.messages.StateVariableResponse; import net.sbbi.upnp.messages.UPNPMessageFactory; import net.sbbi.upnp.messages.UPNPResponseException; import net.sbbi.upnp.services.ServiceAction; import net.sbbi.upnp.services.ServiceActionArgument; import net.sbbi.upnp.services.ServiceStateVariable; import net.sbbi.upnp.services.UPNPService; /** * This is a dynamic MBean for UPNP services The dynamic MBean will be populated with basic informations about UPNP * service attributes and operations.</br> You can provide a locale object that will try to lookup a resource bundle to * generate attributes and operations informations.</br> The bundle must contains the following data to match the UPNP * devices actions and state variables:</br> service.name=My Service description</br> attribute.ServiceAttribute=My * Attribute Desc</br> operation.ServiceOperation=My Action de description</br> * operation.ServiceOperation.ActionArgument1=My Action first argument description</br> * operation.ServiceOperation.ActionArgument2=My Action second argument description</br> and must be named with the UPNP * service Id + locale, the service id ':' char must be replaced with '_' chars. </br> * urn:upnp-org:serviceId:LANHostCfg1 will be translated into : </br> urn_upnp-org_serviceId_LANHostCfg1_fr.properties * * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a> * @version 1.0 */ public class UPNPServiceMBean implements DynamicMBean { private final UPNPService service; private final UPNPDevice device; private final UPNPMessageFactory fact; private ResourceBundle bundle; /** * Creates a dynamic MBean from an UPNP service with no resource bundle to describe the service * * @param device * the device that is hosting the service * @param service * the service that will be exposed as an MBean */ public UPNPServiceMBean(UPNPDevice device, UPNPService service) { this(device, service, null, null); } /** * Creates a dynamic MBean from an UPNP service * * @param device * the device that is hosting the service * @param service * the service that will be exposed as an MBean * @param locale * the resource bundle locale, null if no bundle needs to be used.. * @param bundlePackage * the resource bundle package location into the classpath I.E net/sbbi/upnplib/myDevicesBundlesPackage, * null if the bundles are located at the classpath root. */ public UPNPServiceMBean(UPNPDevice device, UPNPService service, Locale locale, String bundlePackage) { this.service = service; this.device = device; if (locale != null) { if (bundlePackage != null && bundlePackage.length() == 0) bundlePackage = null; String bundlename = service.getServiceId().replace(':', '_'); if (bundlePackage != null) { if (!bundlePackage.endsWith("/")) { bundlePackage += "/"; } bundlename = bundlePackage + bundlename; } try { bundle = ResourceBundle.getBundle(bundlename, locale); } catch (Exception ex) { // silently ignoring.. } } fact = UPNPMessageFactory.getNewInstance(service); } public Object getAttribute(String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException { StateVariableMessage msg = fact.getStateVariableMessage(attributeName); if (msg != null) { try { StateVariableResponse resp = msg.service(); return resp.getStateVariableValue(); } catch (Exception ex) { if (ex instanceof UPNPResponseException) { UPNPResponseException respEx = (UPNPResponseException) ex; if (respEx.getDetailErrorCode() == 404) { // some devices use that code to say that quering state variables is not supported throw new AttributeNotFoundException(respEx.getDetailErrorCode() + ":" + respEx.getDetailErrorDescription()); } throw new MBeanException(ex, respEx.getDetailErrorCode() + ":" + respEx.getDetailErrorDescription()); } throw new MBeanException(ex); } } throw new AttributeNotFoundException("Unable to find attribute " + attributeName); } public AttributeList getAttributes(String[] attributeNames) { AttributeList list = new AttributeList(); for (int i = 0; i < attributeNames.length; i++) { try { Attribute attr = new Attribute(attributeNames[i], getAttribute(attributeNames[i])); list.add(attr); } catch (Exception ex) { // skip it, that kind of suck since we cannot throw any exception } } return list; } public MBeanInfo getMBeanInfo() { Iterator<String> itr = service.getAvailableStateVariableName(); MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[service.getAvailableStateVariableSize()]; int i = 0; while (itr.hasNext()) { String stateVariable = itr.next(); ServiceStateVariable var = service.getUPNPServiceStateVariable(stateVariable); Class<?> type = var.getDataTypeAsClass(); String variableName = null; if (bundle != null) { try { variableName = bundle.getString("attribute." + stateVariable); } catch (Exception ex) { //silently ignoring.. } } if (variableName == null) { variableName = stateVariable + " description"; } MBeanAttributeInfo info = new MBeanAttributeInfo(stateVariable, type != null ? type.getName() : String.class.getName(), variableName, true, false, false); attrs[i++] = info; } itr = service.getAvailableActionsName(); MBeanOperationInfo[] operations = new MBeanOperationInfo[service.getAvailableActionsSize()]; i = 0; while (itr.hasNext()) { String serviceAction = itr.next(); ServiceAction action = service.getUPNPServiceAction(serviceAction); List<ServiceActionArgument> args = action.getInputActionArguments(); MBeanParameterInfo[] params = null; if (args != null) { List<MBeanParameterInfo> tmp = new ArrayList<MBeanParameterInfo>(); params = new MBeanParameterInfo[args.size()]; int z = 0; for (Iterator<ServiceActionArgument> itr2 = args.iterator(); itr2.hasNext();) { ServiceActionArgument actArg = itr2.next(); Class<?> type = actArg.getRelatedStateVariable().getDataTypeAsClass(); String className = type != null ? type.getName() : String.class.getName(); String actionArgName = null; if (bundle != null) { try { actionArgName = bundle.getString("operation." + action.getName() + "." + actArg.getName()); } catch (Exception ex) { //silently ignoring.. } } if (actionArgName == null) { actionArgName = actArg.getName() + " description"; } MBeanParameterInfo param = new MBeanParameterInfo(actArg.getName(), className, actionArgName); tmp.add(param); params[z++] = param; } z = 0; params = new MBeanParameterInfo[tmp.size()]; for (Iterator<MBeanParameterInfo> itr3 = tmp.iterator(); itr3.hasNext();) { params[z++] = itr3.next(); } } else { params = new MBeanParameterInfo[0]; } String returnType = Map.class.getName(); if (action.getOutputActionArguments() == null) { returnType = void.class.getName(); } String actionName = null; if (bundle != null) { try { actionName = bundle.getString("operation." + action.getName()); } catch (Exception ex) { //silently ignoring.. } } if (actionName == null) { actionName = action.getName() + " description"; } MBeanOperationInfo info = new MBeanOperationInfo( action.getName(), actionName, params, returnType, MBeanOperationInfo.ACTION); operations[i++] = info; } String serviceDescr = null; if (bundle != null) { try { serviceDescr = bundle.getString("service.name"); } catch (Exception ex) { //silently ignoring.. } } if (serviceDescr == null) { serviceDescr = "Service Description"; } return new MBeanInfo(this.getClass().getName(), serviceDescr, attrs, null, operations, null); } public Object invoke(String operationName, Object[] paramsValue, String[] signature) throws MBeanException, ReflectionException { ActionMessage msg = fact.getMessage(operationName); if (msg != null) { try { List<String> msgParams = msg.getInputParameterNames(); if (paramsValue != null && msgParams != null) { if (paramsValue.length != msgParams.size()) { return null; } int i = 0; for (Iterator<String> itr = msgParams.iterator(); itr.hasNext(); i++) { String argName = itr.next(); try { msg.setInputParameter(argName, paramsValue[i]); } catch (IllegalArgumentException ex) { throw new MBeanException(ex); } } } ActionResponse resp = msg.service(); List<String> outParams = msg.getOutputParameterNames(); if (outParams != null) { Map<String, String> rtrVal = new HashMap<String, String>(); for (Iterator<String> i = outParams.iterator(); i.hasNext();) { String argName = i.next(); String val = resp.getOutActionArgumentValue(argName); rtrVal.put(argName, val); } return rtrVal; } return null; } catch (Exception ex) { if (ex instanceof UPNPResponseException) { UPNPResponseException upnpEx = (UPNPResponseException) ex; throw new MBeanException(upnpEx, upnpEx.getMessage()); } else { throw new MBeanException(ex); } } } return null; } public void setAttribute(Attribute attributeName) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { throw new AttributeNotFoundException("Unable to set attributes on an UPNP device"); } public AttributeList setAttributes(AttributeList attributeNames) { return null; } /** * Creates an object name for this device, this name should be used ot avoid any names collision especially with * multiple sames UPNP devices type on the network * * @return a new Object Name */ public ObjectName getObjectName() throws MalformedObjectNameException { return new ObjectName(getDeviceDomainName(device.getDeviceType()) + ":name=" + getDeviceServiceName(service.getServiceId()) + "_" + this.hashCode()); } private String getDeviceDomainName(String deviceType) { // takes an string like this urn:schemas-upnp-org:device:WANDevice:1 // and must return only WANDevice String[] tokens = deviceType.split(":"); return tokens[tokens.length - 2].replace(':', '_'); } private String getDeviceServiceName(String serviceId) { // takes an stirng like this urn:upnp-org:serviceId:LANHostCfg1 // and must return only LANHostCfg1 String[] tokens = serviceId.split(":"); return tokens[tokens.length - 1]; } /** * Creates UPNPServiceMBean device service mBeans for a given UPNPRootDevice * * @param device * the root device * @return an array of UPNPServiceMBean objects representing the UPNP device services MBeans */ public static UPNPServiceMBean[] getUPNPRootDeviceAsMBeans(UPNPRootDevice device) { List<UPNPService> services = device.getServices(); Set<UPNPServiceMBean> mBeans = new HashSet<UPNPServiceMBean>(); if (services != null) { registerServices(device, services, mBeans); } registerChildDevice(device.getChildDevices(), mBeans); UPNPServiceMBean[] rtrVal = new UPNPServiceMBean[mBeans.size()]; int z = 0; for (Iterator<UPNPServiceMBean> i = mBeans.iterator(); i.hasNext();) { rtrVal[z++] = i.next(); } return rtrVal; } private static void registerServices(UPNPDevice device, List<UPNPService> services, Set<UPNPServiceMBean> container) { for (Iterator<UPNPService> i = services.iterator(); i.hasNext();) { UPNPService srv = i.next(); UPNPServiceMBean mBean = new UPNPServiceMBean(device, srv, null, null); container.add(mBean); } } private static void registerChildDevice(List<UPNPDevice> childDevices, Set<UPNPServiceMBean> container) { if (childDevices != null) { for (Iterator<UPNPDevice> itr = childDevices.iterator(); itr.hasNext();) { UPNPDevice device = itr.next(); List<UPNPService> services = device.getServices(); if (services != null) { registerServices(device, services, container); } } } } }