/ *
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* 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
* /
@SuppressWarnings ( "unchecked" )
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 ;
}
}