You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
13 KiB
328 lines
13 KiB
/*
|
|
* ============================================================================
|
|
* 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 final 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;
|
|
}
|
|
}
|