Java tutorial
/* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 2002 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by SuperBonBon Industries (http://www.sbbi.net/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "UPNPLib" and "SuperBonBon Industries" must not be * used to endorse or promote products derived from this software without * prior written permission. For written permission, please contact * info@sbbi.net. * * 5. Products derived from this software may not be called * "SuperBonBon Industries", nor may "SBBI" appear in their name, * without prior written permission of SuperBonBon Industries. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT,INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of SuperBonBon Industries. For more information on * SuperBonBon Industries, please see <http://www.sbbi.net/>. */ package net.sbbi.upnp.jmx; import java.io.IOException; import java.security.MessageDigest; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import net.sbbi.upnp.services.ServiceStateVariable; import net.sbbi.upnp.services.ServiceStateVariableTypes; /** * Class to expose an JMX Mbean as an UPNP device service, this class shouldn't be used * directly * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a> * @version 1.0 */ public class UPNPMBeanService { private final static Log log = LogFactory.getLog(UPNPMBeanService.class); private String serviceType; private String serviceId; private String serviceUUID; private String deviceUUID; private String deviceSCDP; private Map operationsStateVariables; private MBeanServer targetServer; private MBeanInfo mbeanInfo; private ObjectName mbeanName; private Class targetBeanClass; protected UPNPMBeanService(String deviceUUID, String vendorDomain, String serviceId, String serviceType, int serviceVersion, MBeanInfo mbeanInfo, ObjectName mbeanName, MBeanServer targetServer) throws IOException { this.deviceUUID = deviceUUID; if (serviceId == null) { serviceId = generateServiceId(mbeanName); } this.serviceUUID = deviceUUID + "/" + serviceId; this.serviceType = "urn:" + vendorDomain + ":service:" + serviceType + ":" + serviceVersion; this.serviceId = "urn:" + vendorDomain + ":serviceId:" + serviceId; deviceSCDP = getDeviceSSDP(mbeanInfo); try { targetBeanClass = Class.forName(mbeanInfo.getClassName()); } catch (ClassNotFoundException ex) { IOException ex2 = new IOException("Unable to find target MBean class " + mbeanInfo.getClassName()); ex2.initCause(ex); throw ex2; } this.mbeanName = mbeanName; this.mbeanInfo = mbeanInfo; this.targetServer = targetServer; } protected String getServiceUUID() { return serviceUUID; } protected String getDeviceUUID() { return deviceUUID; } private String generateServiceId(ObjectName mbeanName) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); // the uuid is based on the device type, the internal id // and the host name md5.update(mbeanName.toString().getBytes()); StringBuffer hexString = new StringBuffer(); byte[] digest = md5.digest(); for (int i = 0; i < digest.length; i++) { hexString.append(Integer.toHexString(0xFF & digest[i])); } return hexString.toString().toUpperCase(); } catch (Exception ex) { RuntimeException runTimeEx = new RuntimeException( "Unexpected error during MD5 hash creation, check your JRE"); runTimeEx.initCause(ex); throw runTimeEx; } } protected String getServiceInfo() { StringBuffer rtrVal = new StringBuffer(); rtrVal.append("<service>\r\n"); rtrVal.append("<serviceType>").append(serviceType).append("</serviceType>\r\n"); rtrVal.append("<serviceId>").append(serviceId).append("</serviceId>\r\n"); rtrVal.append("<controlURL>").append("/").append(serviceUUID).append("/control") .append("</controlURL>\r\n"); rtrVal.append("<eventSubURL>").append("/").append(serviceUUID).append("/events") .append("</eventSubURL>\r\n"); rtrVal.append("<SCPDURL>").append("/").append(serviceUUID).append("/scpd.xml").append("</SCPDURL>\r\n"); rtrVal.append("</service>\r\n"); return rtrVal.toString(); } protected Map getOperationsStateVariables() { return operationsStateVariables; } protected String getDeviceSCDP() { return deviceSCDP; } protected String getServiceType() { return serviceType; } protected Class getTargetBeanClass() { return targetBeanClass; } protected ObjectName getObjectName() { return mbeanName; } protected Object getAttribute(String attributeName) throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException { return targetServer.getAttribute(mbeanName, attributeName); } protected Object invoke(String actionName, Object[] params, String[] signature) throws ReflectionException, InstanceNotFoundException, MBeanException { return targetServer.invoke(mbeanName, actionName, params, signature); } private String getDeviceSSDP(MBeanInfo info) throws IllegalArgumentException { MBeanOperationInfo[] ops = info.getOperations(); MBeanAttributeInfo[] atts = info.getAttributes(); if ((ops == null || ops.length == 0) && (atts == null || atts.length == 0)) { throw new IllegalArgumentException( "MBean has no operation and no attribute and cannot be exposed as an UPNP device, provide at least one attribute"); } Set deployedActionNames = new HashSet(); operationsStateVariables = new HashMap(); StringBuffer rtrVal = new StringBuffer(); rtrVal.append("<?xml version=\"1.0\" ?>\r\n"); rtrVal.append("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\r\n"); rtrVal.append("<specVersion><major>1</major><minor>0</minor></specVersion>\r\n"); if (ops != null && ops.length > 0) { rtrVal.append("<actionList>\r\n"); for (int i = 0; i < ops.length; i++) { MBeanOperationInfo op = ops[i]; StringBuffer action = new StringBuffer(); if (deployedActionNames.contains(op.getName())) { log.debug("The " + op.getName() + " is allready deplyoed and cannot be reused, skipping operation deployment"); continue; } action.append("<action>\r\n"); action.append("<name>"); action.append(op.getName()); action.append("</name>\r\n"); action.append("<argumentList>\r\n"); // output argument action.append("<argument>\r\n"); action.append("<name>"); // TODO handle specific output vars String outVarName = op.getName() + "_out"; String actionOutDataType = ServiceStateVariable.getUPNPDataTypeMapping(op.getReturnType()); if (actionOutDataType == null) actionOutDataType = ServiceStateVariableTypes.STRING; action.append(outVarName); action.append("</name>\r\n"); action.append("<direction>out</direction>\r\n"); action.append("<relatedStateVariable>"); action.append(outVarName); action.append("</relatedStateVariable>\r\n"); action.append("</argument>\r\n"); // handle now for all input argument boolean nonPrimitiveInputType = false; boolean duplicatedInputVarname = false; Map operationsInputStateVariables = new HashMap(); if (op.getSignature() != null) { for (int z = 0; z < op.getSignature().length; z++) { MBeanParameterInfo param = op.getSignature()[z]; // do some sanity checks String actionInDataType = ServiceStateVariable.getUPNPDataTypeMapping(param.getType()); if (actionInDataType == null) { nonPrimitiveInputType = true; log.debug("The " + param.getType() + " type is not an UPNP compatible data type, use only primitives"); break; } String inVarName = param.getName(); // check that if the name does allready exists it // has the same type String existing = (String) operationsStateVariables.get(inVarName); if (existing != null && !existing.equals(actionInDataType)) { String msg = "The operation " + op.getName() + " " + inVarName + " parameter already exists for another method with another data type (" + existing + ") either match the data type or change the parameter name" + " in you MBeanParameterInfo object for this operation"; duplicatedInputVarname = true; log.debug(msg); break; } action.append("<argument>\r\n"); action.append("<name>"); operationsInputStateVariables.put(inVarName, actionInDataType); action.append(inVarName); action.append("</name>\r\n"); action.append("<direction>in</direction>\r\n"); action.append("<relatedStateVariable>"); action.append(inVarName); action.append("</relatedStateVariable>\r\n"); action.append("</argument>\r\n"); } } action.append("</argumentList>\r\n"); action.append("</action>\r\n"); // finally the action is only added to the UPNP SSDP if no problems have been detected // with the input parameters type and names. if (!nonPrimitiveInputType && !duplicatedInputVarname) { operationsStateVariables.putAll(operationsInputStateVariables); operationsStateVariables.put(outVarName, actionOutDataType); rtrVal.append(action.toString()); deployedActionNames.add(op.getName()); } } rtrVal.append("</actionList>\r\n"); } else { rtrVal.append("<actionList/>\r\n"); } // now append the operation created state vars rtrVal.append("<serviceStateTable>\r\n"); for (Iterator i = operationsStateVariables.keySet().iterator(); i.hasNext();) { String name = (String) i.next(); String type = (String) operationsStateVariables.get(name); // TODO handle sendevents with mbean notifications ??? // TODO handle defaultValue and allowedValueList values rtrVal.append("<stateVariable sendEvents=\"no\">\r\n"); rtrVal.append("<name>"); rtrVal.append(name); rtrVal.append("</name>\r\n"); rtrVal.append("<dataType>"); rtrVal.append(type); rtrVal.append("</dataType>\r\n"); rtrVal.append("</stateVariable>\r\n"); } if (atts != null && atts.length > 0) { for (int i = 0; i < atts.length; i++) { MBeanAttributeInfo att = atts[i]; if (att.isReadable()) { rtrVal.append("<stateVariable sendEvents=\"no\">\r\n"); rtrVal.append("<name>"); rtrVal.append(att.getName()); rtrVal.append("</name>\r\n"); rtrVal.append("<dataType>"); // TODO check if this works String stateVarType = ServiceStateVariable.getUPNPDataTypeMapping(att.getType()); if (stateVarType == null) stateVarType = ServiceStateVariableTypes.STRING; rtrVal.append(stateVarType); rtrVal.append("</dataType>\r\n"); rtrVal.append("</stateVariable>\r\n"); } } } rtrVal.append("</serviceStateTable>\r\n"); rtrVal.append("</scpd>"); return rtrVal.toString(); } public MBeanInfo getMBeanInfo() { return mbeanInfo; } }