/*
* ============================================================================
* 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 javax.management.*;
import java.util.*;
import net.sbbi.upnp.services.*;
import net.sbbi.upnp.devices.UPNPDevice;
import net.sbbi.upnp.devices.UPNPRootDevice;
import net.sbbi.upnp.messages.*;
/**
* 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 UPNPService service;
private UPNPDevice device;
private 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 itr = service.getAvailableStateVariableName();
MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[service.getAvailableStateVariableSize()];
int i = 0;
while ( itr.hasNext() ) {
String stateVariable = (String)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 = (String)itr.next();
ServiceAction action = service.getUPNPServiceAction( serviceAction );
List args = action.getInputActionArguments();
MBeanParameterInfo[] params = null;
if ( args != null ) {
List tmp = new ArrayList();
params = new MBeanParameterInfo[args.size()];
int z = 0;
for ( Iterator itr2 = args.iterator(); itr2.hasNext(); ) {
ServiceActionArgument actArg = (ServiceActionArgument)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 itr3 = tmp.iterator(); itr3.hasNext(); ) {
params[z++] = (MBeanParameterInfo)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 msgParams = msg.getInputParameterNames();
if ( paramsValue != null && msgParams != null ) {
if ( paramsValue.length != msgParams.size() ) {
return null;
}
int i = 0;
for ( Iterator itr = msgParams.iterator(); itr.hasNext(); i++ ) {
String argName = (String)itr.next();
try {
msg.setInputParameter( argName, paramsValue[i] );
} catch ( IllegalArgumentException ex ) {
throw new MBeanException( ex );
}
}
}
ActionResponse resp = msg.service();
List outParams = msg.getOutputParameterNames();
if ( outParams != null ) {
Map rtrVal = new HashMap();
for ( Iterator i = outParams.iterator(); i.hasNext(); ) {
String argName = (String)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 services = device.getServices();
Set mBeans = new HashSet();
if ( services != null ) {
registerServices( device, services, mBeans );
}
registerChildDevice( device.getChildDevices(), mBeans );
UPNPServiceMBean[] rtrVal = new UPNPServiceMBean[mBeans.size()];
int z = 0;
for ( Iterator i = mBeans.iterator(); i.hasNext(); ) {
rtrVal[z++] = (UPNPServiceMBean)i.next();
}
return rtrVal;
}
private static void registerServices( UPNPDevice device, List services, Set container ) {
for ( Iterator i = services.iterator(); i.hasNext(); ) {
UPNPService srv = (UPNPService)i.next();
UPNPServiceMBean mBean = new UPNPServiceMBean( device, srv, null, null );
container.add( mBean );
}
}
private static void registerChildDevice( List childDevices, Set container ) {
if ( childDevices != null ) {
for ( Iterator itr = childDevices.iterator(); itr.hasNext(); ) {
UPNPDevice device = (UPNPDevice)itr.next();
List services = device.getServices();
if ( services != null ) {
registerServices( device, services, container );
}
}
}
}
}