/* * ============================================================================ * 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.services; import org.apache.commons.jxpath.*; import org.apache.commons.jxpath.xml.*; import java.util.*; import java.io.IOException; import java.net.*; import net.sbbi.upnp.JXPathParser; import net.sbbi.upnp.devices.*; /** * Representation of an UPNP service * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a> * @version 1.0 */ public class UPNPService { protected String serviceType; protected String serviceId; private int specVersionMajor; private int specVersionMinor; protected URL SCPDURL; protected String SCPDURLData; protected URL controlURL; protected URL eventSubURL; protected UPNPDevice serviceOwnerDevice; protected Map UPNPServiceActions; protected Map UPNPServiceStateVariables; private String USN; private boolean parsedSCPD = false; private DocumentContainer UPNPService; public UPNPService( JXPathContext serviceCtx, URL baseDeviceURL, UPNPDevice serviceOwnerDevice ) throws MalformedURLException { this.serviceOwnerDevice = serviceOwnerDevice; serviceType = (String)serviceCtx.getValue( "serviceType" ); serviceId = (String)serviceCtx.getValue( "serviceId" ); SCPDURL = UPNPRootDevice.getURL( (String)serviceCtx.getValue( "SCPDURL" ), baseDeviceURL ); controlURL = UPNPRootDevice.getURL( (String)serviceCtx.getValue( "controlURL" ), baseDeviceURL ); eventSubURL = UPNPRootDevice.getURL( (String)serviceCtx.getValue( "eventSubURL" ), baseDeviceURL ); USN = serviceOwnerDevice.getUDN().concat( "::" ).concat( serviceType ); } public String getServiceType() { return serviceType; } public String getServiceId() { return serviceId; } public String getUSN(){ return USN; } public URL getSCPDURL() { return SCPDURL; } public URL getControlURL() { return controlURL; } public URL getEventSubURL() { return eventSubURL; } public int getSpecVersionMajor() { lazyInitiate(); return specVersionMajor; } public int getSpecVersionMinor() { lazyInitiate(); return specVersionMinor; } public UPNPDevice getServiceOwnerDevice() { return serviceOwnerDevice; } /** * Retreives a service action for its given name * @param actionName the service action name * @return a ServiceAction object or null if no matching action for this service has been found */ public ServiceAction getUPNPServiceAction( String actionName ) { lazyInitiate(); return (ServiceAction)UPNPServiceActions.get( actionName ); } /** * Retreives a service state variable for its given name * @param stateVariableName the state variable name * @return a ServiceStateVariable object or null if no matching state variable has been found */ public ServiceStateVariable getUPNPServiceStateVariable( String stateVariableName ) { lazyInitiate(); return (ServiceStateVariable)UPNPServiceStateVariables.get( stateVariableName ); } public Iterator getAvailableActionsName() { lazyInitiate(); return UPNPServiceActions.keySet().iterator(); } public int getAvailableActionsSize() { lazyInitiate(); return UPNPServiceActions.keySet().size(); } public Iterator getAvailableStateVariableName() { lazyInitiate(); return UPNPServiceStateVariables.keySet().iterator(); } public int getAvailableStateVariableSize() { lazyInitiate(); return UPNPServiceStateVariables.keySet().size(); } private void parseSCPD() { try { DocumentContainer.registerXMLParser( DocumentContainer.MODEL_DOM, new JXPathParser() ); UPNPService = new DocumentContainer( SCPDURL, DocumentContainer.MODEL_DOM ); JXPathContext context = JXPathContext.newContext( this ); Pointer rootPtr = context.getPointer( "UPNPService/scpd" ); JXPathContext rootCtx = context.getRelativeContext( rootPtr ); specVersionMajor = Integer.parseInt( (String)rootCtx.getValue( "specVersion/major" ) ); specVersionMinor = Integer.parseInt( (String)rootCtx.getValue( "specVersion/minor" ) ); parseServiceStateVariables( rootCtx ); Pointer actionsListPtr = rootCtx.getPointer( "actionList" ); JXPathContext actionsListCtx = context.getRelativeContext( actionsListPtr ); Double arraySize = (Double)actionsListCtx.getValue( "count( action )" ); UPNPServiceActions = new HashMap(); for ( int i = 1; i <= arraySize.intValue(); i++ ) { ServiceAction action = new ServiceAction(); action.name = (String)actionsListCtx.getValue( "action["+i+"]/name" ); action.parent = this; Pointer argumentListPtr = null; try { argumentListPtr = actionsListCtx.getPointer( "action["+i+"]/argumentList" ); } catch ( JXPathException ex ) { // there is no arguments list. } if ( argumentListPtr != null ) { JXPathContext argumentListCtx = actionsListCtx.getRelativeContext( argumentListPtr ); Double arraySizeArgs = (Double)argumentListCtx.getValue( "count( argument )" ); List orderedActionArguments = new ArrayList(); for ( int z = 1; z <= arraySizeArgs.intValue(); z++ ) { ServiceActionArgument arg = new ServiceActionArgument(); arg.name = (String)argumentListCtx.getValue( "argument["+z+"]/name" ); String direction = (String)argumentListCtx.getValue( "argument["+z+"]/direction" ); arg.direction = direction.equals( ServiceActionArgument.DIRECTION_IN ) ? ServiceActionArgument.DIRECTION_IN : ServiceActionArgument.DIRECTION_OUT; String stateVarName = (String)argumentListCtx.getValue( "argument["+z+"]/relatedStateVariable" ); ServiceStateVariable stateVar = (ServiceStateVariable)UPNPServiceStateVariables.get( stateVarName ); if ( stateVar == null ) { throw new IllegalArgumentException( "Unable to find any state variable named " + stateVarName + " for service " + getServiceId() + " action " + action.name + " argument " + arg.name ); } arg.relatedStateVariable = stateVar; orderedActionArguments.add( arg ); } if ( arraySizeArgs.intValue() > 0 ) { action.setActionArguments( orderedActionArguments ); } } UPNPServiceActions.put( action.getName(), action ); } parsedSCPD = true; } catch ( Throwable t ) { throw new RuntimeException( "Error during lazy SCDP file parsing at " + SCPDURL, t ); } } private void parseServiceStateVariables( JXPathContext rootContext ) { Pointer serviceStateTablePtr = rootContext.getPointer( "serviceStateTable" ); JXPathContext serviceStateTableCtx = rootContext.getRelativeContext( serviceStateTablePtr ); Double arraySize = (Double)serviceStateTableCtx.getValue( "count( stateVariable )" ); UPNPServiceStateVariables = new HashMap(); for ( int i = 1; i <= arraySize.intValue(); i++ ) { ServiceStateVariable srvStateVar = new ServiceStateVariable(); String sendEventsLcl = null; try { sendEventsLcl = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/@sendEvents" ); } catch ( JXPathException defEx ) { // sendEvents not provided defaulting according to specs to "yes" sendEventsLcl = "yes"; } srvStateVar.parent = this; srvStateVar.sendEvents = sendEventsLcl.equalsIgnoreCase( "no" ) ? false : true; srvStateVar.name = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/name" ); srvStateVar.dataType = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/dataType" ); try { srvStateVar.defaultValue = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/defaultValue" ); } catch ( JXPathException defEx ) { // can happend since default value is not } Pointer allowedValuesPtr = null; try { allowedValuesPtr = serviceStateTableCtx.getPointer( "stateVariable["+i+"]/allowedValueList" ); } catch ( JXPathException ex ) { // there is no allowed values list. } if ( allowedValuesPtr != null ) { JXPathContext allowedValuesCtx = serviceStateTableCtx.getRelativeContext( allowedValuesPtr ); Double arraySizeAllowed = (Double)allowedValuesCtx.getValue( "count( allowedValue )" ); srvStateVar.allowedvalues = new HashSet(); for ( int z = 1; z <= arraySizeAllowed.intValue(); z++ ) { String allowedValue = (String)allowedValuesCtx.getValue( "allowedValue["+z+"]" ); srvStateVar.allowedvalues.add( allowedValue ); } } Pointer allowedValueRangePtr = null; try { allowedValueRangePtr = serviceStateTableCtx.getPointer( "stateVariable["+i+"]/allowedValueRange" ); } catch ( JXPathException ex ) { // there is no allowed values list, can happen } if ( allowedValueRangePtr != null ) { srvStateVar.minimumRangeValue = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/allowedValueRange/minimum" ); srvStateVar.maximumRangeValue = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/allowedValueRange/maximum" ); try { srvStateVar.stepRangeValue = (String)serviceStateTableCtx.getValue( "stateVariable["+i+"]/allowedValueRange/step" ); } catch ( JXPathException stepEx ) { // can happend since step is not mandatory } } UPNPServiceStateVariables.put( srvStateVar.getName(), srvStateVar ); } } private void lazyInitiate() { if ( !parsedSCPD ) synchronized( this ) { if ( !parsedSCPD ) parseSCPD(); } } /** * Used for JXPath parsing, do not use this method * @return a Container object for Xpath parsing capabilities */ public Container getUPNPService() { return UPNPService; } public String getSCDPData() { if ( SCPDURLData == null ) { try { java.io.InputStream in = SCPDURL.openConnection().getInputStream(); int readen = 0; byte[] buff = new byte[512]; StringBuffer strBuff = new StringBuffer(); while( ( readen = in.read( buff ) ) != -1 ) { strBuff.append( new String( buff, 0, readen ) ); } in.close(); SCPDURLData = strBuff.toString(); } catch ( IOException ioEx ) { return null; } } return SCPDURLData; } }