* updated jxpath to latest v1.3

* added upnplib as source
	without packages:
	jmx
	remote
	samples

git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@6218 6c8d7289-2bf4-0310-a012-ef5d649a1542
pull/1/head
lotus 16 years ago
parent 049fb23a8d
commit 477807e0e6

@ -17,7 +17,6 @@
<classpathentry exported="true" kind="lib" path="lib/commons-io-1.4.jar"/>
<classpathentry exported="true" kind="lib" path="lib/commons-fileupload-1.2.1.jar"/>
<classpathentry exported="true" kind="lib" path="lib/servlet-api.jar"/>
<classpathentry exported="true" kind="lib" path="lib/commons-jxpath-1.1.jar"/>
<classpathentry kind="lib" path="lib/xerces.jar"/>
<classpathentry kind="lib" path="lib/bzip2.jar"/>
<classpathentry kind="lib" path="lib/mysql-connector-java-5.1.7-bin.jar"/>
@ -36,6 +35,6 @@
<classpathentry kind="lib" path="lib/odf_utils_05_11_29.jar"/>
<classpathentry kind="lib" path="lib/jrpm-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="lib/activation.jar"/>
<classpathentry kind="lib" path="lib/sbbi-upnplib-1.0.4.jar"/>
<classpathentry kind="lib" path="lib/commons-jxpath-1.3.jar"/>
<classpathentry kind="output" path="gen"/>
</classpath>

@ -9,3 +9,6 @@ Copyright 2001-2008 The Apache Software Foundation
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
This product includes software developed by
SuperBonBon Industries (http://www.sbbi.net/)

@ -172,7 +172,7 @@
<pathelement location="${lib}/commons-fileupload-1.2.1.jar" />
<pathelement location="${lib}/commons-httpclient-3.1.jar" />
<pathelement location="${lib}/commons-io-1.4.jar" />
<pathelement location="${lib}/commons-jxpath-1.1.jar" />
<pathelement location="${lib}/commons-jxpath-1.3.jar" />
<pathelement location="${lib}/commons-logging-1.1.1.jar" />
<pathelement location="${lib}/jakarta-oro-2.0.7.jar" />
<pathelement location="${lib}/jrpm-SNAPSHOT.jar" />
@ -182,7 +182,6 @@
<pathelement location="${lib}/odf_utils_05_11_29.jar" />
<pathelement location="${lib}/poi-3.2-FINAL-20081019.jar" />
<pathelement location="${lib}/poi-scratchpad-3.2-FINAL-20081019.jar" />
<pathelement location="${lib}/sbbi-upnplib-1.0.4.jar" />
<pathelement location="${lib}/servlet-api.jar" />
<pathelement location="${lib}/tm-extractors-1.0.jar" />
<pathelement location="${lib}/webcat-0.1-swf.jar" />

@ -1,55 +0,0 @@
/* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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 (INCLUDING, 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 the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/

Binary file not shown.

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Binary file not shown.

@ -1,46 +0,0 @@
============================================================================
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/>.

Binary file not shown.

@ -0,0 +1,264 @@
/*
* ============================================================================
* 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;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.sbbi.upnp.devices.UPNPRootDevice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class to discover an UPNP device on the network.</br>
* A multicast socket will be created to discover devices, the binding port for this socket is set to 1901,
* if this is causing a problem you can use the net.sbbi.upnp.Discovery.bindPort system property
* to specify another port.
* The discovery methods only accept matching device description and broadcast message response IP
* to avoid a security flaw with the protocol. If you are not happy with such behaviour
* you can set the net.sbbi.upnp.ddos.matchip system property to false to avoid this check.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class Discovery {
private final static Log log = LogFactory.getLog( Discovery.class );
public final static String ROOT_DEVICES = "upnp:rootdevice";
public final static String ALL_DEVICES = "ssdp:all";
public static final int DEFAULT_MX = 3;
public static final int DEFAULT_TTL = 4;
public static final int DEFAULT_TIMEOUT = 1500;
public static final String DEFAULT_SEARCH = ALL_DEVICES;
public static final int DEFAULT_SSDP_SEARCH_PORT = 1901;
public final static String SSDP_IP = "239.255.255.250";
public final static int SSDP_PORT = 1900;
/**
* Devices discovering on all network interfaces with default values, all root devices will be searched
* @return an array of UPNP Root device or null if nothing found with the default timeout.
* Null does NOT means that no UPNP device is available on the network. It only means
* that for this default timeout no devices responded or that effectively no devices
* are available at all.
* @throws IOException if some IOException occurs during discovering
*/
public static UPNPRootDevice[] discover() throws IOException {
return discover( DEFAULT_TIMEOUT, DEFAULT_TTL, DEFAULT_MX, DEFAULT_SEARCH );
}
/**
* Devices discovering on all network interfaces with a given root device to search
* @param searchTarget the device URI to search
* @return an array of UPNP Root device that matches the search or null if nothing found with the default timeout.
* Null does NOT means that no UPNP device is available on the network. It only means
* that for this given timeout no devices responded or that effectively no devices
* are available at all.
* @throws IOException if some IOException occurs during discovering
*/
public static UPNPRootDevice[] discover( String searchTarget ) throws IOException {
return discover( DEFAULT_TIMEOUT, DEFAULT_TTL, DEFAULT_MX, searchTarget );
}
/**
* Devices discovering on all network interfaces with a given timeout and a given root device to search
* @param timeOut the time allowed for a device to give a response
* @param searchTarget the device URI to search
* @return an array of UPNP Root device that matches the search or null if nothing found with the given timeout.
* Null does NOT means that no UPNP device is available on the network. It only means
* that for this given timeout no devices responded or that effectively no devices
* are available at all.
* @throws IOException if some IOException occurs during discovering
*/
public static UPNPRootDevice[] discover( int timeOut, String searchTarget ) throws IOException {
return discover( timeOut, DEFAULT_TTL, DEFAULT_MX, searchTarget );
}
/**
* Devices discovering on all network interfaces with a given timeout and a given root device to search, as well as a ttl and mx param
* @param timeOut the timeout for the a device to give a reponse
* @param ttl the UDP socket packets time to live
* @param mx discovery message mx http header field value
* @param searchTarget the device URI to search
* @return an array of UPNP Root device that matches the search or null if nothing found within the given timeout.
* Null return does NOT means that no UPNP device is available on the network. It only means
* that for this given timeout no devices responded or that effectively no devices
* are available at all.
* @throws IOException if some IOException occurs during discovering
*/
public static UPNPRootDevice[] discover( int timeOut, int ttl, int mx, String searchTarget ) throws IOException {
return discoverDevices( timeOut, ttl, mx, searchTarget, null );
}
/**
* Devices discovering with a given timeout and a given root device to search on an given network interface, as well as a ttl and mx param
* @param timeOut the timeout for the a device to give a reponse
* @param ttl the UDP socket packets time to live
* @param mx discovery message mx http header field value
* @param searchTarget the device URI to search
* @param ni the networkInterface where to search devices, null to lookup all interfaces
* @return an array of UPNP Root device that matches the search or null if nothing found within the given timeout.
* Null return does NOT means that no UPNP device is available on the network. It only means
* that for this given timeout no devices responded or that effectively no devices
* are available at all.
* @throws IOException if some IOException occurs during discovering
*/
public static UPNPRootDevice[] discover( int timeOut, int ttl, int mx, String searchTarget, NetworkInterface ni ) throws IOException {
return discoverDevices( timeOut, ttl, mx, searchTarget, ni );
}
private static UPNPRootDevice[] discoverDevices( int timeOut, int ttl, int mx, String searchTarget, NetworkInterface ni ) throws IOException {
if ( searchTarget == null || searchTarget.trim().length() == 0 ) {
throw new IllegalArgumentException( "Illegal searchTarget" );
}
final Map devices = new HashMap();
DiscoveryResultsHandler handler = new DiscoveryResultsHandler() {
public void discoveredDevice( String usn, String udn, String nt, String maxAge, URL location, String firmware ) {
synchronized( devices ) {
if ( ! devices.containsKey( usn ) ) {
try {
UPNPRootDevice device = new UPNPRootDevice( location, maxAge, firmware, usn, udn );
devices.put( usn, device );
} catch ( Exception ex ) {
log.error( "Error occured during upnp root device object creation from location " + location, ex );
}
}
}
}
};
DiscoveryListener.getInstance().registerResultsHandler( handler, searchTarget );
if ( ni == null ) {
for ( Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) {
NetworkInterface intf = (NetworkInterface)e.nextElement();
for ( Enumeration adrs = intf.getInetAddresses(); adrs.hasMoreElements(); ) {
InetAddress adr = (InetAddress)adrs.nextElement();
if ( adr instanceof Inet4Address && !adr.isLoopbackAddress() ) {
sendSearchMessage( adr, ttl, mx, searchTarget );
}
}
}
} else {
for ( Enumeration adrs = ni.getInetAddresses(); adrs.hasMoreElements(); ) {
InetAddress adr = (InetAddress)adrs.nextElement();
if ( adr instanceof Inet4Address && !adr.isLoopbackAddress() ) {
sendSearchMessage( adr, ttl, mx, searchTarget );
}
}
}
try {
Thread.sleep( timeOut );
} catch ( InterruptedException ex ) {
// don't care
}
DiscoveryListener.getInstance().unRegisterResultsHandler( handler, searchTarget );
if ( devices.size() == 0 ) {
return null;
}
int j = 0;
UPNPRootDevice[] rootDevices = new UPNPRootDevice[devices.size()];
for ( Iterator i = devices.values().iterator(); i.hasNext(); ) {
rootDevices[j++] = (UPNPRootDevice)i.next();
}
return rootDevices;
}
/**
* Sends an SSDP search message on the network
* @param src the sender ip
* @param ttl the time to live
* @param mx the mx field
* @param searchTarget the search target
* @throws IOException if some IO errors occurs during search
*/
public static void sendSearchMessage( InetAddress src, int ttl, int mx, String searchTarget ) throws IOException {
int bindPort = DEFAULT_SSDP_SEARCH_PORT;
String port = System.getProperty( "net.sbbi.upnp.Discovery.bindPort" );
if ( port != null ) {
bindPort = Integer.parseInt( port );
}
InetSocketAddress adr = new InetSocketAddress( InetAddress.getByName( Discovery.SSDP_IP ), Discovery.SSDP_PORT );
java.net.MulticastSocket skt = new java.net.MulticastSocket( null );
skt.bind( new InetSocketAddress( src, bindPort ) );
skt.setTimeToLive( ttl );
StringBuffer packet = new StringBuffer();
packet.append( "M-SEARCH * HTTP/1.1\r\n" );
packet.append( "HOST: 239.255.255.250:1900\r\n" );
packet.append( "MAN: \"ssdp:discover\"\r\n" );
packet.append( "MX: ").append( mx ).append( "\r\n" );
packet.append( "ST: " ).append( searchTarget ).append( "\r\n" ).append( "\r\n" );
if ( log.isDebugEnabled() ) log.debug( "Sending discovery message on 239.255.255.250:1900 multicast address form ip " + src.getHostAddress() + ":\n" + packet.toString() );
String toSend = packet.toString();
byte[] pk = toSend.getBytes();
skt.send( new DatagramPacket( pk, pk.length, adr ) );
skt.disconnect();
skt.close();
}
}

@ -0,0 +1,379 @@
/*
* ============================================================================
* 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;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* SSDP messages listener Thread, notify registered objects implementing the interface DiscoveryEventHandler</br>
* when a device joins the networks or leaves it.<br/>
* The listener thread is set to only accept matching device description and broadcast message sender IP
* to avoid a security flaw with the protocol. If you are not happy with such behaviour
* you can set the net.sbbi.upnp.ddos.matchip system property to false to avoid this check.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class DiscoveryAdvertisement implements Runnable {
private final static Log log = LogFactory.getLog( DiscoveryAdvertisement.class );
private static boolean MATCH_IP = true;
static {
String prop = System.getProperty( "net.sbbi.upnp.ddos.matchip" );
if ( prop != null && prop.equals( "false" ) ) MATCH_IP = false;
}
private static final int DEFAULT_TIMEOUT = 250;
public final static int EVENT_SSDP_ALIVE = 0;
public final static int EVENT_SSDP_BYE_BYE = 1;
private final static String NTS_SSDP_ALIVE = "ssdp:alive";
private final static String NTS_SSDP_BYE_BYE = "ssdp:byebye";
private final static String NT_ALL_EVENTS = "DiscoveryAdvertisement:nt:allevents";
private Map byeByeRegistered = new HashMap();
private Map aliveRegistered = new HashMap();
private Map USNPerIP = new HashMap();
private final Object REGISTRATION_PROCESS = new Object();
private final static DiscoveryAdvertisement singleton = new DiscoveryAdvertisement();
private boolean inService = false;
private boolean daemon = true;
private java.net.MulticastSocket skt;
private DatagramPacket input;
private DiscoveryAdvertisement() {
}
public final static DiscoveryAdvertisement getInstance() {
return singleton;
}
public void setDaemon( boolean daemon ) {
this.daemon = daemon;
}
/**
* Registers an event category sent by UPNP devices
* @param notificationEvent the event type, either DiscoveryAdvertisement.EVENT_SSDP_ALIVE
* or DiscoveryAdvertisement.EVENT_SSDP_BYE_BYE
* @param nt the type of device advertisement, upnp:rootdevice will return you all advertisement in relation with nt upnp:rootdevice
* a null value specify that all nt type are wanted
* @param eventHandler the events handler, this objet will receive notifications..
* @throws IOException if an error ocurs when the SSDP events listeners threads starts
*/
public void registerEvent( int notificationEvent, String nt, DiscoveryEventHandler eventHandler ) throws IOException {
synchronized( REGISTRATION_PROCESS ) {
if ( !inService ) startDevicesListenerThread();
if ( nt == null ) nt = NT_ALL_EVENTS;
if ( notificationEvent == EVENT_SSDP_ALIVE ) {
Set handlers = (Set)aliveRegistered.get( nt );
if ( handlers == null ) {
handlers = new HashSet();
aliveRegistered.put( nt, handlers );
}
handlers.add( eventHandler );
} else if ( notificationEvent == EVENT_SSDP_BYE_BYE ) {
Set handlers = (Set)byeByeRegistered.get( nt );
if ( handlers == null ) {
handlers = new HashSet();
byeByeRegistered.put( nt, handlers );
}
handlers.add( eventHandler );
} else {
throw new IllegalArgumentException( "Unknown notificationEvent type" );
}
}
}
/**
* Unregisters an event category sent by UPNP devices
* @param notificationEvent the event type, either DiscoveryAdvertisement.EVENT_SSDP_ALIVE
* or DiscoveryAdvertisement.EVENT_SSDP_BYE_BYE
* @param nt the type of device advertisement, upnp:rootdevice will unregister all advertisement in relation with nt upnp:rootdevice
* a null value specify that all nt type are unregistered
* @param eventHandler the events handler that needs to be unregistred.
*/
public void unRegisterEvent( int notificationEvent, String nt, DiscoveryEventHandler eventHandler ) {
synchronized( REGISTRATION_PROCESS ) {
if ( nt == null ) nt = NT_ALL_EVENTS;
if ( notificationEvent == EVENT_SSDP_ALIVE ) {
Set handlers = (Set)aliveRegistered.get( nt );
if ( handlers != null ) {
handlers.remove( eventHandler );
if ( handlers.size() == 0 ) {
aliveRegistered.remove( nt );
}
}
} else if ( notificationEvent == EVENT_SSDP_BYE_BYE ) {
Set handlers = (Set)byeByeRegistered.get( nt );
if ( handlers != null ) {
handlers.remove( eventHandler );
if ( handlers.size() == 0 ) {
byeByeRegistered.remove( nt );
}
}
} else {
throw new IllegalArgumentException( "Unknown notificationEvent type" );
}
if ( aliveRegistered.size() == 0 && byeByeRegistered.size() == 0 ) {
stopDevicesListenerThread();
}
}
}
private void startDevicesListenerThread() throws IOException {
synchronized( singleton ) {
if ( !inService ) {
this.startMultiCastSocket();
Thread deamon = new Thread( this, "DiscoveryAdvertisement daemon" );
deamon.setDaemon( daemon );
deamon.start();
// wait for the thread to be started
while( !inService ) {
// let's wait a few ms
try {
Thread.sleep( 2 );
} catch( InterruptedException ex ) {
// don t care
}
}
}
}
}
private void stopDevicesListenerThread() {
synchronized( singleton ) {
inService = false;
}
}
private void startMultiCastSocket() throws IOException {
skt = new java.net.MulticastSocket( null );
skt.bind( new InetSocketAddress( InetAddress.getByName( "0.0.0.0" ), Discovery.SSDP_PORT ) );
skt.setTimeToLive( Discovery.DEFAULT_TTL );
skt.setSoTimeout( DEFAULT_TIMEOUT );
skt.joinGroup( InetAddress.getByName( Discovery.SSDP_IP ) );
byte[] buf = new byte[2048];
input = new DatagramPacket( buf, buf.length );
}
public void run() {
if ( !Thread.currentThread().getName().equals( "DiscoveryAdvertisement daemon" ) ) {
throw new RuntimeException( "No right to call this method" );
}
inService = true;
while ( inService ) {
try {
listenBroadCast();
} catch ( SocketTimeoutException ex ) {
// ignoring
} catch ( IOException ioEx ) {
log.error( "IO Exception during UPNP DiscoveryAdvertisement messages listening thread", ioEx );
} catch( Exception ex ) {
log.error( "Fatal Error during UPNP DiscoveryAdvertisement messages listening thread, thread will exit", ex );
inService = false;
aliveRegistered.clear();
byeByeRegistered.clear();
USNPerIP.clear();
}
}
try {
skt.leaveGroup( InetAddress.getByName( Discovery.SSDP_IP ) );
skt.close();
} catch ( Exception ex ) {
// ignoring
}
}
private void listenBroadCast() throws IOException {
skt.receive( input );
InetAddress from = input.getAddress();
String received = new String( input.getData(), input.getOffset(), input.getLength() );
HttpResponse msg = null;
try {
msg = new HttpResponse( received );
} catch (IllegalArgumentException ex ) {
// crappy http sent
if ( log.isDebugEnabled() ) log.debug( "Skipping uncompliant HTTP message " + received );
return;
}
String header = msg.getHeader();
if ( header != null && header.startsWith( "NOTIFY" ) ) {
if ( log.isDebugEnabled() ) log.debug( received );
String ntsField = msg.getHTTPHeaderField( "nts" );
if( ntsField == null || ntsField.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'ntsField' field" );
return;
}
if ( ntsField.equals( NTS_SSDP_ALIVE ) ) {
String deviceDescrLoc = msg.getHTTPHeaderField( "location" );
if( deviceDescrLoc == null || deviceDescrLoc.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'location' field" );
return;
}
URL loc = new URL( deviceDescrLoc );
if ( MATCH_IP ) {
InetAddress locHost = InetAddress.getByName( loc.getHost() );
if ( !from.equals( locHost ) ) {
log.warn( "Discovery message sender IP " + from +
" does not match device description IP " + locHost +
" skipping message, set the net.sbbi.upnp.ddos.matchip system property" +
" to false to avoid this check" );
return;
}
}
String nt = msg.getHTTPHeaderField( "nt" );
if( nt == null || nt.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'nt' field" );
return;
}
String maxAge = msg.getHTTPFieldElement( "Cache-Control", "max-age" );
if( maxAge == null || maxAge.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'max-age' field" );
return;
}
String usn = msg.getHTTPHeaderField( "usn" );
if( usn == null || usn.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'usn' field" );
return;
}
USNPerIP.put( usn, from );
String udn = usn;
int index = udn.indexOf( "::" );
if ( index != -1 ) udn = udn.substring( 0, index );
synchronized( REGISTRATION_PROCESS ) {
Set handlers = (Set)aliveRegistered.get( NT_ALL_EVENTS );
if ( handlers != null ) {
for ( Iterator i = handlers.iterator(); i.hasNext(); ) {
DiscoveryEventHandler eventHandler = (DiscoveryEventHandler)i.next();
eventHandler.eventSSDPAlive( usn, udn, nt, maxAge, loc );
}
}
handlers = (Set)aliveRegistered.get( nt );
if ( handlers != null ) {
for ( Iterator i = handlers.iterator(); i.hasNext(); ) {
DiscoveryEventHandler eventHandler = (DiscoveryEventHandler)i.next();
eventHandler.eventSSDPAlive( usn, udn, nt, maxAge, loc );
}
}
}
} else if ( ntsField.equals( NTS_SSDP_BYE_BYE ) ) {
String usn = msg.getHTTPHeaderField( "usn" );
if( usn == null || usn.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'usn' field" );
return;
}
String nt = msg.getHTTPHeaderField( "nt" );
if( nt == null || nt.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'nt' field" );
return;
}
InetAddress originalAliveSenderIp = (InetAddress)USNPerIP.get( usn );
if ( originalAliveSenderIp != null ) {
// we check that the sender ip of message for the usn
// match the sender ip of the alive message for wich the usn
// has been received
if ( !originalAliveSenderIp.equals( from ) ) {
// someone else is trying to say that the usn is leaving
// since IP do not match we skip the message
return;
}
}
String udn = usn;
int index = udn.indexOf( "::" );
if ( index != -1 ) udn = udn.substring( 0, index );
synchronized( REGISTRATION_PROCESS ) {
Set handlers = (Set)byeByeRegistered.get( NT_ALL_EVENTS );
if ( handlers != null ) {
for ( Iterator i = handlers.iterator(); i.hasNext(); ) {
DiscoveryEventHandler eventHandler = (DiscoveryEventHandler)i.next();
eventHandler.eventSSDPByeBye( usn, udn, nt );
}
}
handlers = (Set)byeByeRegistered.get( nt );
if ( handlers != null ) {
for ( Iterator i = handlers.iterator(); i.hasNext(); ) {
DiscoveryEventHandler eventHandler = (DiscoveryEventHandler)i.next();
eventHandler.eventSSDPByeBye( usn, udn, nt );
}
}
}
} else {
log.warn( "Unvalid NTS field value (" + ntsField + ") received in NOTIFY message :" + received );
}
}
}
}

@ -0,0 +1,76 @@
/*
* ============================================================================
* 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;
/**
* Interface for object that want to receive events from the
* DiscoveryAdvertisement thread
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public interface DiscoveryEventHandler {
/**
* Called when a device joins the network or advertise it is still alive
* @param usn the device USN (udn::nt)
* @param udn the device UDN
* @param nt the device NT
* @param maxAge the device maxAge
* @param location the device location
*/
public void eventSSDPAlive( String usn, String udn, String nt, String maxAge, java.net.URL location );
/**
* Called when a device is leaving the network
* @param usn the device USN (udn::nt)
* @param udn the device UDN
* @param nt the device NT
*/
public void eventSSDPByeBye( String usn, String udn, String nt );
}

@ -0,0 +1,290 @@
/*
* ============================================================================
* 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;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class can be used to listen for UPNP devices responses when a search message is sent by a control point
* ( using the net.sbbi.upnp.Discovery.sendSearchMessage() method )
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class DiscoveryListener implements Runnable {
private final static Log log = LogFactory.getLog( DiscoveryListener.class );
private static boolean MATCH_IP = true;
static {
String prop = System.getProperty( "net.sbbi.upnp.ddos.matchip" );
if ( prop != null && prop.equals( "false" ) ) MATCH_IP = false;
}
private static final int DEFAULT_TIMEOUT = 250;
private Map registeredHandlers = new HashMap();
private final Object REGISTRATION_PROCESS = new Object();
private final static DiscoveryListener singleton = new DiscoveryListener();
private boolean inService = false;
private boolean daemon = true;
private java.net.MulticastSocket skt;
private DatagramPacket input;
private DiscoveryListener() {
}
public final static DiscoveryListener getInstance() {
return singleton;
}
/**
* Sets the listener as a daemon thread
* @param daemon daemon thread
*/
public void setDaemon( boolean daemon ) {
this.daemon = daemon;
}
/**
* Registers an SSDP response message handler
* @param resultsHandler the SSDP response message handler
* @param searchTarget the search target
* @throws IOException if some errors occurs during SSDP search response messages listener thread startup
*/
public void registerResultsHandler( DiscoveryResultsHandler resultsHandler, String searchTarget ) throws IOException {
synchronized( REGISTRATION_PROCESS ) {
if ( !inService ) startDevicesListenerThread();
Set handlers = (Set)registeredHandlers.get( searchTarget );
if ( handlers == null ) {
handlers = new HashSet();
registeredHandlers.put( searchTarget, handlers );
}
handlers.add( resultsHandler );
}
}
/**
* Unregisters an SSDP response message handler
* @param resultsHandler the SSDP response message handler
* @param searchTarget the search target
*/
public void unRegisterResultsHandler( DiscoveryResultsHandler resultsHandler, String searchTarget ) {
synchronized( REGISTRATION_PROCESS ) {
Set handlers = (Set)registeredHandlers.get( searchTarget );
if ( handlers != null ) {
handlers.remove( resultsHandler );
if ( handlers.size() == 0 ) {
registeredHandlers.remove( searchTarget );
}
}
if ( registeredHandlers.size() == 0 ) {
stopDevicesListenerThread();
}
}
}
private void startDevicesListenerThread() throws IOException {
synchronized( singleton ) {
if ( !inService ) {
this.startMultiCastSocket();
Thread deamon = new Thread( this, "DiscoveryListener daemon" );
deamon.setDaemon( daemon );
deamon.start();
while ( !inService ) {
// wait for the thread to be started let's wait a few ms
try {
Thread.sleep( 2 );
} catch( InterruptedException ex ) {
// don t care
}
}
}
}
}
private void stopDevicesListenerThread() {
synchronized( singleton ) {
inService = false;
}
}
private void startMultiCastSocket() throws IOException {
int bindPort = Discovery.DEFAULT_SSDP_SEARCH_PORT;
String port = System.getProperty( "net.sbbi.upnp.Discovery.bindPort" );
if ( port != null ) {
bindPort = Integer.parseInt( port );
}
skt = new java.net.MulticastSocket( null );
skt.bind( new InetSocketAddress( InetAddress.getByName( "0.0.0.0" ), bindPort ) );
skt.setTimeToLive( Discovery.DEFAULT_TTL );
skt.setSoTimeout( DEFAULT_TIMEOUT );
skt.joinGroup( InetAddress.getByName( Discovery.SSDP_IP ) );
byte[] buf = new byte[2048];
input = new DatagramPacket( buf, buf.length );
}
public void run() {
if ( !Thread.currentThread().getName().equals( "DiscoveryListener daemon" ) ) {
throw new RuntimeException( "No right to call this method" );
}
inService = true;
while ( inService ) {
try {
listenBroadCast();
} catch ( SocketTimeoutException ex ) {
// ignoring
} catch ( IOException ioEx ) {
log.error( "IO Exception during UPNP DiscoveryListener messages listening thread", ioEx );
} catch( Exception ex ) {
log.error( "Fatal Error during UPNP DiscoveryListener messages listening thread, thread will exit", ex );
inService = false;
}
}
try {
skt.leaveGroup( InetAddress.getByName( Discovery.SSDP_IP ) );
skt.close();
} catch ( Exception ex ) {
// ignoring
}
}
private void listenBroadCast() throws IOException {
skt.receive( input );
InetAddress from = input.getAddress();
String received = new String( input.getData(), input.getOffset(), input.getLength() );
HttpResponse msg = null;
try {
msg = new HttpResponse( received );
} catch (IllegalArgumentException ex ) {
// crappy http sent
if ( log.isDebugEnabled() ) log.debug( "Skipping uncompliant HTTP message " + received );
return;
}
String header = msg.getHeader();
if ( header != null && header.startsWith( "HTTP/1.1 200 OK" ) && msg.getHTTPHeaderField( "st" ) != null ) {
// probably a search repsonse !
String deviceDescrLoc = msg.getHTTPHeaderField( "location" );
if( deviceDescrLoc == null || deviceDescrLoc.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'location' field" );
return;
}
URL loc = new URL( deviceDescrLoc );
if ( MATCH_IP ) {
InetAddress locHost = InetAddress.getByName( loc.getHost() );
if ( !from.equals( locHost ) ) {
log.warn( "Discovery message sender IP " + from +
" does not match device description IP " + locHost +
" skipping device, set the net.sbbi.upnp.ddos.matchip system property" +
" to false to avoid this check" );
return;
}
}
if ( log.isDebugEnabled() ) log.debug( "Processing " + deviceDescrLoc + " device description location" );
String st = msg.getHTTPHeaderField( "st" );
if( st == null || st.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'st' field" );
return;
}
String usn = msg.getHTTPHeaderField( "usn" );
if( usn == null || usn.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'usn' field" );
return;
}
String maxAge = msg.getHTTPFieldElement( "Cache-Control", "max-age" );
if( maxAge == null || maxAge.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'max-age' field" );
return;
}
String server = msg.getHTTPHeaderField( "server" );
if( server == null || server.trim().length() == 0 ) {
if ( log.isDebugEnabled() ) log.debug( "Skipping SSDP message, missing HTTP header 'server' field" );
return;
}
String udn = usn;
int index = udn.indexOf( "::" );
if ( index != -1 ) udn = udn.substring( 0, index );
synchronized( REGISTRATION_PROCESS ) {
Set handlers = (Set)registeredHandlers.get( st );
if ( handlers != null ) {
for ( Iterator i = handlers.iterator(); i.hasNext(); ) {
DiscoveryResultsHandler handler = (DiscoveryResultsHandler)i.next();
handler.discoveredDevice( usn, udn, st, maxAge, loc, server );
}
}
}
} else {
if ( log.isDebugEnabled() ) log.debug( "Skipping uncompliant HTTP message " + received );
}
}
}

@ -0,0 +1,71 @@
/*
* ============================================================================
* 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;
/**
* This interface can be use to register against the DiscoveryListener class
* to receive SSDP search responses.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public interface DiscoveryResultsHandler {
/**
* Method called by the DiscoveryListener class when a search response message has been received from the
* network
* @param usn the device USN
* @param udn the device UDN
* @param nt the device NT
* @param maxAge the message max age
* @param location the device location
* @param firmware the device firmware
*/
public void discoveredDevice( String usn, String udn, String nt, String maxAge, java.net.URL location, String firmware );
}

@ -0,0 +1,145 @@
/*
* ============================================================================
* 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;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
/**
* A class to parse an HTTP response message.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class HttpResponse {
private String header;
private Map fields;
private String body;
/**
* Constructor of the response, will try to parse the raw response data
* @param rawHttpResponse the raw response data
* @throws IllegalArgumentException if some error occurs during parsing
*/
protected HttpResponse( String rawHttpResponse ) throws IllegalArgumentException {
if ( rawHttpResponse == null || rawHttpResponse.trim().length() == 0 ) {
throw new IllegalArgumentException( "Empty HTTP response message" );
}
boolean bodyParsing = false;
StringBuffer bodyParsed = new StringBuffer();
fields = new HashMap();
String[] lines = rawHttpResponse.split( "\\r\\n" );
this.header = lines[0].trim();
for ( int i = 1; i < lines.length; i++ ) {
String line = lines[i];
if ( line.length() == 0 ) {
// line break before body
bodyParsing = true;
} else if ( bodyParsing ) {
// we parse the message body
bodyParsed.append( line ).append( "\r\n" );
} else {
// we parse the header
if ( line.length() > 0 ) {
int delim = line.indexOf( ':' );
if ( delim != -1 ) {
String key = line.substring( 0, delim ).toUpperCase();
String value = line.substring( delim + 1 ).trim();
fields.put( key, value );
} else {
throw new IllegalArgumentException( "Invalid HTTP message header :" + line );
}
}
}
}
if ( bodyParsing ) {
body = bodyParsed.toString();
}
}
public String getHeader() {
return header;
}
public String getBody() {
return body;
}
public String getHTTPFieldElement( String fieldName, String elementName ) throws IllegalArgumentException {
String fieldNameValue = getHTTPHeaderField( fieldName );
if ( fieldName!= null ) {
StringTokenizer tokenizer = new StringTokenizer( fieldNameValue.trim(), "," );
while (tokenizer.countTokens() > 0) {
String nextToken = tokenizer.nextToken().trim();
if ( nextToken.startsWith( elementName ) ) {
int index = nextToken.indexOf( "=" );
if ( index != -1 ) {
return nextToken.substring( index + 1 ).trim();
}
}
}
}
throw new IllegalArgumentException( "HTTP element field " + elementName + " is not present" );
}
public String getHTTPHeaderField( String fieldName ) throws IllegalArgumentException {
String field = (String)fields.get( fieldName.toUpperCase() );
if ( field == null ) {
throw new IllegalArgumentException( "HTTP field " + fieldName + " is not present");
}
return field;
}
}

@ -0,0 +1,95 @@
/*
* ============================================================================
* 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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.jxpath.xml.DOMParser;
import org.apache.commons.jxpath.xml.XMLParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Parser to use with JXPath, this is used to fix some problems encountered
* with some UPNP devices returning buggy xml docs...
* This parser acts like a wrapper and make some chars search and replace
* such as 0x0 with 0x20 to produce a valid XML doc.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class JXPathParser implements XMLParser {
private final static Log log = LogFactory.getLog( JXPathParser.class );
private char buggyChar = (char)0;
public Object parseXML( InputStream in ){
StringBuffer xml = new StringBuffer();
try {
byte[] buffer = new byte[512];
int readen = 0;
while ( ( readen = in.read( buffer ) ) != -1 ) {
xml.append( new String( buffer, 0, readen ) );
}
} catch ( IOException ex ) {
log.error( "IOException occured during XML reception", ex );
return null;
}
String doc = xml.toString();
log.debug( "Readen raw xml doc:\n" + doc );
if ( doc.indexOf( buggyChar ) != -1 ) {
doc = doc.replace( buggyChar, ' ' );
}
ByteArrayInputStream in2 = new ByteArrayInputStream( doc.getBytes() );
DOMParser parser = new DOMParser();
return parser.parseXML( in2 ) ;
}
}

@ -0,0 +1,70 @@
/*
* ============================================================================
* 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;
/**
* Interface to implement to receive notifications about state
* variables changes on au UPNP service. The object implementing this interface
* can be used with the ServicesEventing class register method to receive the
* desired notifications.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public interface ServiceEventHandler {
/**
* Handle a var change, called each time a UPNP service fires a
* state variable eventing message.</br>
* The code implemented in this method can block the thread.
* @param varName the state variable name
* @param newValue the new state variable value
*/
public void handleStateVariableEvent( String varName, String newValue );
}

@ -0,0 +1,105 @@
/*
* ============================================================================
* 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;
import java.util.HashMap;
import java.util.Map;
import org.xml.sax.Attributes;
/**
* Simple SAX handler for UPNP service event message parsing, this message is in SOAP format
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServiceEventMessageParser extends org.xml.sax.helpers.DefaultHandler {
private boolean readPropertyName = false;
private String currentPropName = null;
private Map changedStateVars = new HashMap();
protected ServiceEventMessageParser() {
}
public Map getChangedStateVars() {
return changedStateVars;
}
public void characters( char[] ch, int start, int length ) {
if ( currentPropName != null ) {
String origChars = (String)changedStateVars.get( currentPropName );
String newChars = new String( ch, start, length );
if ( origChars == null ) {
changedStateVars.put( currentPropName, newChars );
} else {
changedStateVars.put( currentPropName, origChars + newChars );
}
}
}
public void startElement( String uri, String localName, String qName, Attributes attributes ) {
if ( localName.equals( "property" ) ) {
readPropertyName = true;
} else if ( readPropertyName ) {
currentPropName = localName;
}
}
public void endElement( String uri, String localName, String qName ) {
if ( currentPropName != null && localName.equals( currentPropName ) ) {
readPropertyName = false;
currentPropName = null;
}
}
}

@ -0,0 +1,111 @@
/*
* ============================================================================
* 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;
import java.net.InetAddress;
import java.net.URL;
/**
* This class is used to provide information about a subscription done
* via the ServicesEventing class
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServiceEventSubscription {
private String serviceType = null;
private String serviceId = null;
private URL serviceURL = null;
private String SID = null;
private InetAddress deviceIp = null;
private int durationTime = 0;
public ServiceEventSubscription( String serviceType, String serviceId, URL serviceURL,
String sid, InetAddress deviceIp, int durationTime ) {
this.serviceType = serviceType;
this.serviceId = serviceId;
this.serviceURL = serviceURL;
SID = sid;
this.deviceIp = deviceIp;
this.durationTime = durationTime;
}
public InetAddress getDeviceIp() {
return deviceIp;
}
/**
* Subcription duration in seconds
* @return sub duration time, 0 for an infinite time
*/
public int getDurationTime() {
return durationTime;
}
public String getServiceId() {
return serviceId;
}
public String getServiceType() {
return serviceType;
}
public URL getServiceURL() {
return serviceURL;
}
/**
* The subscription ID returned by the UPNPDevice
* @return subscription id
*/
public String getSID() {
return SID;
}
}

@ -0,0 +1,445 @@
/*
* ============================================================================
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.sbbi.upnp.services.UPNPService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;
/**
* This class can be used with the ServiceEventHandler interface
* to recieve notifications about state variables changes on
* a given UPNP service.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServicesEventing implements Runnable {
private final static Log log = LogFactory.getLog( ServicesEventing.class );
private final static ServicesEventing singleton = new ServicesEventing();
private boolean inService = false;
private boolean daemon = true;
private int daemonPort = 9999;
private ServerSocket server = null;
private List registered = new ArrayList();
private ServicesEventing() {
}
public final static ServicesEventing getInstance() {
return singleton;
}
/**
* Set the listeniner thread as a daemon, default to true.
* Only works when no more objects are registered.
* @param daemon the new thread type.
*/
public void setDaemon( boolean daemon ) {
this.daemon = daemon;
}
/**
* Sets the listener thread port, default to 9999.
* Only works when no more objects are registered.
* @param daemonPort the new listening port
*/
public void setDaemonPort( int daemonPort ) {
this.daemonPort = daemonPort;
}
/**
* Register state variable events notification for a device service
* @param service the service to register with
* @param handler the registrant object
* @param subscriptionDuration subscription time in seconds, -1 for infinite time
* @return the subscription duration returned by the device, 0 for an infinite duration or -1 if no subscription done
* @throws IOException if some IOException error happens during coms with the device
*/
public int register( UPNPService service, ServiceEventHandler handler, int subscriptionDuration ) throws IOException {
ServiceEventSubscription sub = registerEvent( service, handler, subscriptionDuration );
if ( sub != null ) {
return sub.getDurationTime();
}
return -1;
}
/**
* Register state variable events notification for a device service
* @param service the service to register with
* @param handler the registrant object
* @param subscriptionDuration subscription time in seconds, -1 for infinite time
* @return an ServiceEventSubscription object instance containing all the required info or null if no subscription done
* @throws IOException if some IOException error happens during coms with the device
*/
public ServiceEventSubscription registerEvent( UPNPService service, ServiceEventHandler handler, int subscriptionDuration ) throws IOException {
URL eventingLoc = service.getEventSubURL();
if ( eventingLoc != null ) {
if ( !inService ) startServicesEventingThread();
String duration = Integer.toString( subscriptionDuration );
if ( subscriptionDuration == -1 ) {
duration = "infinite";
}
Subscription sub = lookupSubscriber( service, handler );
if ( sub != null ) {
// allready registered let's try to unregister it
unRegister( service, handler );
}
StringBuffer packet = new StringBuffer( 64 );
packet.append( "SUBSCRIBE " ).append( eventingLoc.getFile() ).append( " HTTP/1.1\r\n" );
packet.append( "HOST: " ).append( eventingLoc.getHost() ).append( ":" ).append( eventingLoc.getPort() ).append( "\r\n" );
packet.append( "CALLBACK: <http://" ).append( InetAddress.getLocalHost().getHostAddress() ).append( ":" ).append( daemonPort ).append( "" ).append( eventingLoc.getFile() ).append( ">\r\n" );
packet.append( "NT: upnp:event\r\n" );
packet.append( "Connection: close\r\n" );
packet.append( "TIMEOUT: Second-" ).append( duration ).append( "\r\n\r\n" );
Socket skt = new Socket( eventingLoc.getHost(), eventingLoc.getPort() );
skt.setSoTimeout( 30000 ); // 30 secs timeout according to the specs
if ( log.isDebugEnabled() ) log.debug( packet );
OutputStream out = skt.getOutputStream();
out.write( packet.toString().getBytes() );
out.flush();
InputStream in = skt.getInputStream();
StringBuffer data = new StringBuffer();
int readen = 0;
byte[] buffer = new byte[256];
while ( ( readen = in.read( buffer ) ) != -1 ) {
data.append( new String( buffer, 0, readen ) );
}
in.close();
out.close();
skt.close();
if ( log.isDebugEnabled() ) log.debug( data.toString() );
if ( data.toString().trim().length() > 0 ) {
HttpResponse resp = new HttpResponse( data.toString() );
if ( resp.getHeader().startsWith( "HTTP/1.1 200 OK" ) ) {
String sid = resp.getHTTPHeaderField( "SID" );
String actualTimeout = resp.getHTTPHeaderField( "TIMEOUT" );
int durationTime = 0;
// actualTimeout = Second-xxx or Second-infinite
if ( !actualTimeout.equalsIgnoreCase( "Second-infinite" ) ) {
durationTime = Integer.parseInt( actualTimeout.substring( 7 ) );
}
sub = new Subscription();
sub.handler = handler;
sub.sub = new ServiceEventSubscription( service.getServiceType(), service.getServiceId(),
service.getEventSubURL(), sid, skt.getInetAddress(),
durationTime );
synchronized( registered ) {
registered.add( sub );
}
return sub.sub;
}
}
}
return null;
}
private Subscription lookupSubscriber( UPNPService service, ServiceEventHandler handler ) {
synchronized( registered ) {
for ( Iterator i = registered.iterator(); i.hasNext(); ) {
Subscription sub = (Subscription)i.next();
if ( sub.handler == handler &&
sub.sub.getServiceId().hashCode() == service.getServiceId().hashCode() &&
sub.sub.getServiceType().hashCode() == service.getServiceType().hashCode() &&
sub.sub.getServiceURL().equals( service.getEventSubURL() ) ) {
return sub;
}
}
}
return null;
}
private Subscription lookupSubscriber( String sid, InetAddress deviceIp ) {
synchronized( registered ) {
for ( Iterator i = registered.iterator(); i.hasNext(); ) {
Subscription sub = (Subscription)i.next();
if ( sub.sub.getSID().equals( sid ) && sub.sub.getDeviceIp().equals( deviceIp ) ) {
return sub;
}
}
}
return null;
}
private Subscription lookupSubscriber( String sid ) {
synchronized( registered ) {
for ( Iterator i = registered.iterator(); i.hasNext(); ) {
Subscription sub = (Subscription)i.next();
if ( sub.sub.getSID().equals( sid ) ) {
return sub;
}
}
}
return null;
}
/**
* Unregisters events notifications from a service
* @param service the service that need to be unregistered
* @param handler the handler that registered for this service
* @return true if unregistered false otherwise ( the given handler never registred for the given service )
* @throws IOException if some IOException error happens during coms with the device
*/
public boolean unRegister( UPNPService service, ServiceEventHandler handler ) throws IOException {
URL eventingLoc = service.getEventSubURL();
if ( eventingLoc != null ) {
Subscription sub = lookupSubscriber( service, handler );
if ( sub != null ) {
synchronized( registered ) {
registered.remove( sub );
}
if ( registered.size() == 0 ) {
stopServicesEventingThread();
}
StringBuffer packet = new StringBuffer( 64 );
packet.append( "UNSUBSCRIBE " ).append( eventingLoc.getFile() ).append( " HTTP/1.1\r\n" );
packet.append( "HOST: " ).append( eventingLoc.getHost() ).append( ":" ).append( eventingLoc.getPort() ).append( "\r\n" );
packet.append( "SID: " ).append( sub.sub.getSID() ).append( "\r\n\r\n" );
Socket skt = new Socket( eventingLoc.getHost(), eventingLoc.getPort() );
skt.setSoTimeout( 30000 ); // 30 secs timeout according to the specs
if ( log.isDebugEnabled() ) log.debug( packet );
OutputStream out = skt.getOutputStream();
out.write( packet.toString().getBytes() );
out.flush();
InputStream in = skt.getInputStream();
StringBuffer data = new StringBuffer();
int readen = 0;
byte[] buffer = new byte[256];
while ( ( readen = in.read( buffer ) ) != -1 ) {
data.append( new String( buffer, 0, readen ) );
}
in.close();
out.close();
skt.close();
if ( log.isDebugEnabled() ) log.debug( data.toString() );
if ( data.toString().trim().length() > 0 ) {
HttpResponse resp = new HttpResponse( data.toString() );
if ( resp.getHeader().startsWith( "HTTP/1.1 200 OK" ) ) {
return true;
}
}
}
}
return false;
}
private void startServicesEventingThread() {
synchronized( singleton ) {
if ( !inService ) {
Thread deamon = new Thread( singleton, "ServicesEventing daemon" );
deamon.setDaemon( daemon );
inService = true;
deamon.start();
}
}
}
private void stopServicesEventingThread() {
synchronized( singleton ) {
inService = false;
try {
server.close();
} catch ( IOException ex ) {
// should not happen
}
}
}
public void run() {
// only the deamon thread is allowed to call such method
if ( !Thread.currentThread().getName().equals( "ServicesEventing daemon" ) ) return;
try {
server = new ServerSocket( daemonPort );
} catch ( IOException ex ) {
log.error( "Error during daemon server socket on port " + daemonPort + " creation", ex );
return;
}
while ( inService ) {
try {
Socket skt = server.accept();
new Thread( new RequestProcessor( skt ) ).start();
} catch ( IOException ioEx ) {
if ( inService ) {
log.error( "IO Exception during UPNP messages listening thread", ioEx );
}
}
}
}
private class Subscription {
private ServiceEventSubscription sub = null;
private ServiceEventHandler handler = null;
}
private class RequestProcessor implements Runnable {
private Socket client;
private RequestProcessor( Socket client ) {
this.client = client;
}
public void run() {
try {
client.setSoTimeout( 30000 );
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
int readen = 0;
StringBuffer data = new StringBuffer();
byte[] buffer = new byte[256];
boolean EOF = false;
while ( !EOF && ( readen = in.read( buffer ) ) != -1 ) {
data.append( new String( buffer, 0, readen ) );
// avoid a strange behaviour with some impls.. the -1 is never reached and a sockettimeout occurs
// and a 0 byte is sent as the last byte
if ( data.charAt( data.length()-1 ) == (char)0 ) {
EOF = true;
}
}
String packet = data.toString();
if ( packet.trim().length() > 0 ) {
if ( packet.indexOf( (char)0 ) != -1 ) packet = packet.replace( (char)0, ' ' );
HttpResponse resp = new HttpResponse( packet );
if ( resp.getHeader().startsWith( "NOTIFY" ) ) {
String sid = resp.getHTTPHeaderField( "SID" );
InetAddress deviceIp = client.getInetAddress();
String postURL = resp.getHTTPHeaderField( "SID" );
Subscription subscription = null;
if ( sid != null && postURL != null ) {
subscription = lookupSubscriber( sid, deviceIp );
if ( subscription == null ) {
// not found maybe that the IP is not the same
subscription = lookupSubscriber( sid );
}
}
if ( subscription != null ) {
// respond ok
out.write( "HTTP/1.1 200 OK\r\n".getBytes() );
} else {
// unknown sid respond ko
out.write( "HTTP/1.1 412 Precondition Failed\r\n".getBytes() );
}
out.flush();
in.close();
out.close();
client.close();
if ( subscription != null ) {
// let's parse it
SAXParserFactory saxParFact = SAXParserFactory.newInstance();
saxParFact.setValidating( false );
saxParFact.setNamespaceAware( true );
SAXParser parser = saxParFact.newSAXParser();
ServiceEventMessageParser msgParser = new ServiceEventMessageParser();
StringReader stringReader = new StringReader( resp.getBody() );
InputSource src = new InputSource( stringReader );
parser.parse( src, msgParser );
Map changedStateVars = msgParser.getChangedStateVars();
for ( Iterator i = changedStateVars.keySet().iterator(); i.hasNext(); ) {
String stateVarName = (String)i.next();
String stateVarNewVal = (String)changedStateVars.get( stateVarName );
subscription.handler.handleStateVariableEvent( stateVarName, stateVarNewVal );
}
}
}
}
} catch ( IOException ioEx ) {
log.error( "IO Exception during client processing thread", ioEx );
} catch( Exception ex ) {
log.error( "Unexpected error during client processing thread", ex );
}
}
}
}

@ -0,0 +1,86 @@
/*
* ============================================================================
* 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.devices;
import java.net.*;
/**
* Java Bean for a device icon properties
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class DeviceIcon {
protected String mimeType;
protected int width;
protected int height;
protected int depth;
protected URL url;
public String getMimeType() {
return mimeType;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getDepth() {
return depth;
}
public URL getUrl() {
return url;
}
}

@ -0,0 +1,298 @@
/*
* ============================================================================
* 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.devices;
import net.sbbi.upnp.services.*;
import java.util.*;
import java.net.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class represents an UPNP device, this device contains a set of services
* that will be needed to access the device functionalities.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class UPNPDevice {
private final static Log log = LogFactory.getLog( UPNPDevice.class );
protected String deviceType;
protected String friendlyName;
protected String manufacturer;
protected URL manufacturerURL;
protected URL presentationURL;
protected String modelDescription;
protected String modelName;
protected String modelNumber;
protected String modelURL;
protected String serialNumber;
protected String UDN;
protected String USN;
protected long UPC;
protected List deviceIcons;
protected List services;
protected List childDevices;
protected UPNPDevice parent;
public URL getManufacturerURL() {
return manufacturerURL;
}
/**
* Presentation URL
* @return URL the presenation URL, or null if the device does not provide
* such information
*/
public URL getPresentationURL() {
return presentationURL;
}
public String getModelDescription() {
return modelDescription;
}
public String getModelName() {
return modelName;
}
public String getModelNumber() {
return modelNumber;
}
public String getModelURL() {
return modelURL;
}
public String getSerialNumber() {
return serialNumber;
}
public String getUDN() {
return UDN;
}
public String getUSN(){
return USN;
}
public long getUPC() {
return UPC;
}
public String getDeviceType() {
return deviceType;
}
public String getFriendlyName() {
return friendlyName;
}
public String getManufacturer() {
return manufacturer;
}
public boolean isRootDevice() {
return this instanceof UPNPRootDevice;
}
/**
* Access to the device icons definitions
* @return a list containing DeviceIcon objects or null if no icons defined
*/
public List getDeviceIcons() {
return deviceIcons;
}
/**
* Generates a list of all the child ( not only top level, full childrens hierarchy included )
* UPNPDevice objects for this device.
* @return the generated list or null if no child devices bound
*/
public List getChildDevices() {
if ( childDevices == null ) return null;
List rtrVal = new ArrayList();
for ( Iterator itr = childDevices.iterator(); itr.hasNext(); ) {
UPNPDevice device = (UPNPDevice)itr.next();
rtrVal.add( device );
List found = device.getChildDevices();
if ( found != null ) {
rtrVal.addAll( found );
}
}
return rtrVal;
}
/**
* Generates a list of all the child ( only top level )
* UPNPDevice objects for this device.
* @return the generated list or null if no child devices bound
*/
public List getTopLevelChildDevices() {
if ( childDevices == null ) return null;
List rtrVal = new ArrayList();
for ( Iterator itr = childDevices.iterator(); itr.hasNext(); ) {
UPNPDevice device = (UPNPDevice)itr.next();
rtrVal.add( device );
}
return rtrVal;
}
/**
* Return the parent UPNPDevice, null if the device is an UPNPRootDevice
* @return the parent device instance
*/
public UPNPDevice getDirectParent() {
return parent;
}
/**
* Looks for a child UPNP device definition file,
* the whole devices tree will be searched, starting from the current
* device node.
* @param deviceURI the device URI to search
* @return An UPNPDevice if anything matches or null
*/
public UPNPDevice getChildDevice( String deviceURI ) {
if ( log.isDebugEnabled() ) log.debug( "searching for device URI:" + deviceURI );
if ( getDeviceType().equals( deviceURI ) ) return this;
if ( childDevices == null ) return null;
for ( Iterator itr = childDevices.iterator(); itr.hasNext(); ) {
UPNPDevice device = (UPNPDevice)itr.next();
UPNPDevice found = device.getChildDevice( deviceURI );
if ( found != null ) {
return found;
}
}
return null;
}
/**
* Looks for all UPNP device service definitions objects
* @return A list of all device services
*/
public List getServices() {
if ( services == null ) return null;
List rtrVal = new ArrayList();
rtrVal.addAll( services );
return rtrVal;
}
/**
* Looks for a UPNP device service definition object for the given service URI (Type)
* @param serviceURI the URI of the service
* @return A matching UPNPService object or null
*/
public UPNPService getService( String serviceURI ) {
if ( services == null ) return null;
if ( log.isDebugEnabled() ) log.debug( "searching for service URI:" + serviceURI );
for ( Iterator itr = services.iterator(); itr.hasNext(); ) {
UPNPService service = (UPNPService)itr.next();
if ( service.getServiceType().equals( serviceURI ) ) {
return service;
}
}
return null;
}
/**
* Looks for a UPNP device service definition object for the given service ID
* @param serviceURI the ID of the service
* @return A matching UPNPService object or null
*/
public UPNPService getServiceByID( String serviceID ) {
if ( services == null ) return null;
if ( log.isDebugEnabled() ) log.debug( "searching for service ID:" + serviceID );
for ( Iterator itr = services.iterator(); itr.hasNext(); ) {
UPNPService service = (UPNPService)itr.next();
if ( service.getServiceId().equals( serviceID ) ) {
return service;
}
}
return null;
}
/**
* Looks for the all the UPNP device service definition object for the current
* UPNP device object. This method can be used to retreive multiple same kind
* ( same service type ) of services with different services id on a device
* @param serviceURI the URI of the service
* @return A matching List of UPNPService objects or null
*/
public List getServices( String serviceURI ) {
if ( services == null ) return null;
List rtrVal = new ArrayList();
if ( log.isDebugEnabled() ) log.debug( "searching for services URI:" + serviceURI );
for ( Iterator itr = services.iterator(); itr.hasNext(); ) {
UPNPService service = (UPNPService)itr.next();
if ( service.getServiceType().equals( serviceURI ) ) {
rtrVal.add( service );
}
}
if ( rtrVal.size() == 0 ) {
return null;
}
return rtrVal;
}
/**
* The toString return the device type
* @return the device type
*/
public String toString() {
return getDeviceType();
}
}

@ -0,0 +1,450 @@
/*
* ============================================================================
* 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.devices;
import java.net.*;
import java.util.*;
import java.io.*;
import org.apache.commons.jxpath.*;
import org.apache.commons.jxpath.xml.*;
import org.apache.commons.logging.*;
import net.sbbi.upnp.JXPathParser;
import net.sbbi.upnp.services.*;
/**
* Root UPNP device that is contained in a device definition file.
* Slightly differs from a simple UPNPDevice object.
* This object will contains all the child devices, this is the top
* objet in the UPNP device devices hierarchy.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class UPNPRootDevice extends UPNPDevice {
private final static Log log = LogFactory.getLog( UPNPRootDevice.class );
private int specVersionMajor;
private int specVersionMinor;
private URL URLBase;
private long validityTime;
private long creationTime;
private URL deviceDefLoc;
private String deviceDefLocData;
private String vendorFirmware;
private String discoveryUSN;
private String discoveryUDN;
private DocumentContainer UPNPDevice;
/**
* Constructor for the root device, constructs itself from
* An xml device definition file provided by the UPNP device via http normally.
* @param deviceDefLoc the location of the XML device definition file
* using "the urn:schemas-upnp-org:device-1-0" namespace
* @param maxAge the maximum age of this UPNP device in secs before considered to be outdated
* @param vendorFirmware the vendor firmware
* @param discoveryUSN the discovery USN used to find and create this device
* @param discoveryUDN the discovery UDN used to find and create this device
* @throws MalformedURLException if the location URL is invalid and cannot be used to populate this root object and its child devices
* IllegalStateException if the device has an unsupported version, currently only version 1.0 is supported
*/
public UPNPRootDevice( URL deviceDefLoc, String maxAge, String vendorFirmware, String discoveryUSN, String discoveryUDN ) throws MalformedURLException, IllegalStateException {
this( deviceDefLoc, maxAge );
this.vendorFirmware = vendorFirmware;
this.discoveryUSN = discoveryUSN;
this.discoveryUDN = discoveryUDN;
}
/**
* Constructor for the root device, constructs itself from
* An xml device definition file provided by the UPNP device via http normally.
* @param deviceDefLoc the location of the XML device definition file
* using "the urn:schemas-upnp-org:device-1-0" namespace
* @param maxAge the maximum age of this UPNP device in secs before considered to be outdated
* @param vendorFirmware the vendor firmware
* @throws MalformedURLException if the location URL is invalid and cannot be used to populate this root object and its child devices
* IllegalStateException if the device has an unsupported version, currently only version 1.0 is supported
*/
public UPNPRootDevice( URL deviceDefLoc, String maxAge, String vendorFirmware ) throws MalformedURLException, IllegalStateException {
this( deviceDefLoc, maxAge );
this.vendorFirmware = vendorFirmware;
}
/**
* Constructor for the root device, constructs itself from
* An xml device definition file provided by the UPNP device via http normally.
* @param deviceDefLoc the location of the XML device definition file
* using "the urn:schemas-upnp-org:device-1-0" namespace
* @param maxAge the maximum age in secs of this UPNP device before considered to be outdated
* @throws MalformedURLException if the location URL is invalid and cannot be used to populate this root object and its child devices
* IllegalStateException if the device has an unsupported version, currently only version 1.0 is supported
*/
public UPNPRootDevice( URL deviceDefLoc, String maxAge ) throws MalformedURLException, IllegalStateException {
this.deviceDefLoc = deviceDefLoc;
DocumentContainer.registerXMLParser( DocumentContainer.MODEL_DOM, new JXPathParser() );
UPNPDevice = new DocumentContainer( deviceDefLoc, DocumentContainer.MODEL_DOM );
validityTime = Integer.parseInt( maxAge ) * 1000;
creationTime = System.currentTimeMillis();
JXPathContext context = JXPathContext.newContext( this );
Pointer rootPtr = context.getPointer( "UPNPDevice/root" );
JXPathContext rootCtx = context.getRelativeContext( rootPtr );
specVersionMajor = Integer.parseInt( (String)rootCtx.getValue( "specVersion/major" ) );
specVersionMinor = Integer.parseInt( (String)rootCtx.getValue( "specVersion/minor" ) );
if ( !( specVersionMajor == 1 && specVersionMinor == 0 ) ) {
throw new IllegalStateException( "Unsupported device version (" + specVersionMajor + "." + specVersionMinor + ")" );
}
boolean buildURLBase = true;
String base = null;
try {
base = (String)rootCtx.getValue( "URLBase" );
if ( base != null && base.trim().length() > 0 ) {
URLBase = new URL( base );
if ( log.isDebugEnabled() ) log.debug( "device URLBase " + URLBase );
buildURLBase = false;
}
} catch ( JXPathException ex ) {
// URLBase is not mandatory we assume we use the URL of the device
} catch ( MalformedURLException malformedEx ) {
// crappy urlbase provided
log.warn( "Error occured during device baseURL " + base + " parsing, building it from device default location", malformedEx );
}
if ( buildURLBase ) {
String URL = deviceDefLoc.getProtocol() + "://" + deviceDefLoc.getHost() + ":" + deviceDefLoc.getPort();
String path = deviceDefLoc.getPath();
if ( path != null ) {
int lastSlash = path.lastIndexOf( '/' );
if ( lastSlash != -1 ) {
URL += path.substring( 0, lastSlash );
}
}
URLBase = new URL( URL );
}
Pointer devicePtr = rootCtx.getPointer( "device" );
JXPathContext deviceCtx = rootCtx.getRelativeContext( devicePtr );
fillUPNPDevice( this, null, deviceCtx, URLBase );
}
/**
* The validity time for this device in milliseconds,
* @return the number of milliseconds remaining before the device object that has been build is considered to
* be outdated, after this delay the UPNP device should resend an advertisement message or a negative value
* if the device is outdated
*/
public long getValidityTime() {
long elapsed = System.currentTimeMillis() - creationTime;
return validityTime - elapsed;
}
/**
* Resets the device validity time
* @param newMaxAge the maximum age in secs of this UPNP device before considered to be outdated
*/
public void resetValidityTime( String newMaxAge ) {
validityTime = Integer.parseInt( newMaxAge ) * 1000;
creationTime = System.currentTimeMillis();
}
/**
* Retreives the device description file location
* @return an URL
*/
public URL getDeviceDefLoc() {
return deviceDefLoc;
}
public int getSpecVersionMajor() {
return specVersionMajor;
}
public int getSpecVersionMinor() {
return specVersionMinor;
}
public String getVendorFirmware() {
return vendorFirmware;
}
public String getDiscoveryUSN() {
return discoveryUSN;
}
public String getDiscoveryUDN() {
return discoveryUDN;
}
/**
* URL base acces
* @return URL the URL base, or null if the device does not provide
* such information
*/
public URL getURLBase() {
return URLBase;
}
/**
* Parsing an UPNPdevice description element (<device>) in the description XML file
* @param device the device object that will be populated
* @param parent the device parent object
* @param deviceCtx an XPath context for object population
* @param baseURL the base URL of the UPNP device
* @throws MalformedURLException if some URL provided in the description file is invalid
*/
private void fillUPNPDevice( UPNPDevice device, UPNPDevice parent, JXPathContext deviceCtx, URL baseURL ) throws MalformedURLException {
device.deviceType = getMandatoryData( deviceCtx, "deviceType" );
if ( log.isDebugEnabled() ) log.debug( "parsing device " + device.deviceType );
device.friendlyName = getMandatoryData( deviceCtx, "friendlyName" );
device.manufacturer = getNonMandatoryData( deviceCtx, "manufacturer" );
String base = getNonMandatoryData( deviceCtx, "manufacturerURL" );
try {
if ( base != null ) device.manufacturerURL = new URL( base );
} catch ( java.net.MalformedURLException ex ) {
// crappy data provided, keep the field null
}
try {
device.presentationURL = getURL( getNonMandatoryData( deviceCtx, "presentationURL" ), URLBase );
} catch ( java.net.MalformedURLException ex ) {
// crappy data provided, keep the field null
}
device.modelDescription = getNonMandatoryData( deviceCtx, "modelDescription" );
device.modelName = getMandatoryData( deviceCtx, "modelName" );
device.modelNumber = getNonMandatoryData( deviceCtx, "modelNumber" );
device.modelURL = getNonMandatoryData( deviceCtx, "modelURL" );
device.serialNumber = getNonMandatoryData( deviceCtx, "serialNumber" );
device.UDN = getMandatoryData( deviceCtx, "UDN" );
device.USN = UDN.concat( "::" ).concat( deviceType );
String tmp = getNonMandatoryData( deviceCtx, "UPC" );
if ( tmp != null ) {
try {
device.UPC = Long.parseLong( tmp );
} catch ( Exception ex ) {
// non all numeric field provided, non upnp compliant device
}
}
device.parent = parent;
fillUPNPServicesList( device, deviceCtx );
fillUPNPDeviceIconsList( device, deviceCtx, URLBase );
Pointer deviceListPtr;
try {
deviceListPtr = deviceCtx.getPointer( "deviceList" );
} catch ( JXPathException ex ) {
// no pointers for this device list, this can happen
// if the device has no child devices, simply returning
return;
}
JXPathContext deviceListCtx = deviceCtx.getRelativeContext( deviceListPtr );
Double arraySize = (Double)deviceListCtx.getValue( "count( device )" );
device.childDevices = new ArrayList();
if ( log.isDebugEnabled() ) log.debug( "child devices count is " + arraySize );
for ( int i = 1; i <= arraySize.intValue(); i++ ) {
Pointer devicePtr = deviceListCtx.getPointer( "device[" + i + "]" );
JXPathContext childDeviceCtx = deviceListCtx.getRelativeContext( devicePtr );
UPNPDevice childDevice = new UPNPDevice();
fillUPNPDevice( childDevice, device, childDeviceCtx, baseURL );
if ( log.isDebugEnabled() ) log.debug( "adding child device " + childDevice.getDeviceType() );
device.childDevices.add( childDevice );
}
}
private String getMandatoryData( JXPathContext ctx, String ctxFieldName ) {
String value = (String)ctx.getValue( ctxFieldName );
if ( value != null && value.length() == 0 ) {
throw new JXPathException( "Mandatory field " + ctxFieldName + " not provided, uncompliant UPNP device !!" );
}
return value;
}
private String getNonMandatoryData( JXPathContext ctx, String ctxFieldName ) {
String value = null;
try {
value = (String)ctx.getValue( ctxFieldName );
if ( value != null && value.length() == 0 ) {
value = null;
}
} catch ( JXPathException ex ) {
value = null;
}
return value;
}
/**
* Parsing an UPNPdevice services list element (<device/serviceList>) in the description XML file
* @param device the device object that will store the services list (UPNPService) objects
* @param deviceCtx an XPath context for object population
* @throws MalformedURLException if some URL provided in the description
* file for a service entry is invalid
*/
private void fillUPNPServicesList( UPNPDevice device, JXPathContext deviceCtx ) throws MalformedURLException {
Pointer serviceListPtr = deviceCtx.getPointer( "serviceList" );
JXPathContext serviceListCtx = deviceCtx.getRelativeContext( serviceListPtr );
Double arraySize = (Double)serviceListCtx.getValue( "count( service )" );
if ( log.isDebugEnabled() ) log.debug( "device services count is " + arraySize );
device.services = new ArrayList();
for ( int i = 1; i <= arraySize.intValue(); i++ ) {
Pointer servicePtr = serviceListCtx.getPointer( "service["+i+"]" );
JXPathContext serviceCtx = serviceListCtx.getRelativeContext( servicePtr );
// TODO possibility of bugs if deviceDefLoc contains a file name
URL base = URLBase != null ? URLBase : deviceDefLoc;
UPNPService service = new UPNPService( serviceCtx, base, this );
device.services.add( service );
}
}
/**
* Parsing an UPNPdevice icons list element (<device/iconList>) in the description XML file
* This list can be null
* @param device the device object that will store the icons list (DeviceIcon) objects
* @param deviceCtx an XPath context for object population
* @throws MalformedURLException if some URL provided in the description
* file for an icon URL
*/
private void fillUPNPDeviceIconsList( UPNPDevice device, JXPathContext deviceCtx, URL baseURL ) throws MalformedURLException {
Pointer iconListPtr;
try {
iconListPtr = deviceCtx.getPointer( "iconList" );
} catch ( JXPathException ex ) {
// no pointers for icons list, this can happen
// simply returning
return;
}
JXPathContext iconListCtx = deviceCtx.getRelativeContext( iconListPtr );
Double arraySize = (Double)iconListCtx.getValue( "count( icon )" );
if ( log.isDebugEnabled() ) log.debug( "device icons count is " + arraySize );
device.deviceIcons = new ArrayList();
for ( int i = 1; i <= arraySize.intValue(); i++ ) {
DeviceIcon ico = new DeviceIcon();
ico.mimeType = (String)iconListCtx.getValue( "icon["+i+"]/mimetype" );
ico.width = Integer.parseInt( (String)iconListCtx.getValue( "icon["+i+"]/width" ) );
ico.height = Integer.parseInt( (String)iconListCtx.getValue( "icon["+i+"]/height" ) );
ico.depth = Integer.parseInt( (String)iconListCtx.getValue( "icon["+i+"]/depth" ) );
ico.url = getURL( (String)iconListCtx.getValue( "icon["+i+"]/url" ), baseURL );
if ( log.isDebugEnabled() ) log.debug( "icon URL is " + ico.url );
device.deviceIcons.add( ico );
}
}
/**
* Parsing an URL from the descriptionXML file
* @param url the string representation fo the URL
* @param baseURL the base device URL, needed if the url param is relative
* @return an URL object defining the url param
* @throws MalformedURLException if the url param or baseURL.toExternalForm() + url
* cannot be parsed to create an URL object
*/
public final static URL getURL( String url, URL baseURL ) throws MalformedURLException {
URL rtrVal;
if ( url == null || url.trim().length() == 0 ) return null;
try {
rtrVal = new URL( url );
} catch ( MalformedURLException malEx ) {
// maybe that the url is relative, we add the baseURL and reparse it
// if relative then we take the device baser url root and add the url
if ( baseURL != null ) {
url = url.replace( '\\', '/' );
if ( url.charAt( 0 ) != '/' ) {
// the path is relative to the device baseURL
String externalForm = baseURL.toExternalForm();
if ( !externalForm.endsWith( "/" ) ) {
externalForm += "/";
}
rtrVal = new URL( externalForm + url );
} else {
// the path is not relative
String URLRoot = baseURL.getProtocol() + "://" + baseURL.getHost() + ":" + baseURL.getPort();
rtrVal = new URL( URLRoot + url );
}
} else {
throw malEx;
}
}
return rtrVal;
}
/**
* Retrieves the device definition XML data
* @return the device definition XML data as a String
*/
public String getDeviceDefLocData() {
if ( deviceDefLocData == null ) {
try {
java.io.InputStream in = deviceDefLoc.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();
deviceDefLocData = strBuff.toString();
} catch ( IOException ioEx ) {
return null;
}
}
return deviceDefLocData;
}
/**
* Used for JXPath parsing, do not use this method
* @return a Container object for Xpath parsing capabilities
*/
public Container getUPNPDevice() {
return UPNPDevice;
}
}

@ -0,0 +1,506 @@
/*
* ============================================================================
* 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.impls;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sbbi.upnp.Discovery;
import net.sbbi.upnp.devices.UPNPDevice;
import net.sbbi.upnp.devices.UPNPRootDevice;
import net.sbbi.upnp.messages.ActionMessage;
import net.sbbi.upnp.messages.ActionResponse;
import net.sbbi.upnp.messages.StateVariableMessage;
import net.sbbi.upnp.messages.StateVariableResponse;
import net.sbbi.upnp.messages.UPNPMessageFactory;
import net.sbbi.upnp.messages.UPNPResponseException;
import net.sbbi.upnp.services.UPNPService;
/**
* This class can be used to access some funtionalities on the
* InternetGatewayDevice on your network without having to know
* anything about the required input/output parameters.
* All device functions are not provided.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class InternetGatewayDevice {
private final static Log log = LogFactory.getLog( InternetGatewayDevice.class );
private UPNPRootDevice igd;
private UPNPMessageFactory msgFactory;
public InternetGatewayDevice( UPNPRootDevice igd ) throws UnsupportedOperationException {
this( igd, true, true );
}
private InternetGatewayDevice( UPNPRootDevice igd, boolean WANIPConnection, boolean WANPPPConnection ) throws UnsupportedOperationException {
this.igd = igd;
UPNPDevice myIGDWANConnDevice = igd.getChildDevice( "urn:schemas-upnp-org:device:WANConnectionDevice:1" );
if ( myIGDWANConnDevice == null ) {
throw new UnsupportedOperationException( "device urn:schemas-upnp-org:device:WANConnectionDevice:1 not supported by IGD device " + igd.getModelName() );
}
UPNPService wanIPSrv = myIGDWANConnDevice.getService( "urn:schemas-upnp-org:service:WANIPConnection:1" );
UPNPService wanPPPSrv = myIGDWANConnDevice.getService( "urn:schemas-upnp-org:service:WANPPPConnection:1" );
if ( ( WANIPConnection && WANPPPConnection ) && ( wanIPSrv == null && wanPPPSrv == null ) ) {
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANIPConnection:1 or urn:schemas-upnp-org:service:WANPPPConnection:1 service" );
} else if ( ( WANIPConnection && !WANPPPConnection ) && wanIPSrv == null ) {
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANIPConnection:1 service" );
} else if ( ( !WANIPConnection && WANPPPConnection ) && wanPPPSrv == null ) {
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANPPPConnection:1 service" );
}
if ( wanIPSrv != null && wanPPPSrv == null ) {
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
} else if ( wanPPPSrv != null && wanIPSrv == null ) {
msgFactory = UPNPMessageFactory.getNewInstance( wanPPPSrv );
} else {
// Unable to test the following code since no router implementing both IP and PPP connection on hands..
/*// discover the active WAN interface using the WANCommonInterfaceConfig specs
UPNPDevice wanDevice = igd.getChildDevice( "urn:schemas-upnp-org:device:WANDevice:1" );
UPNPService configService = wanDevice.getService( "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" );
if ( configService != null ) {
// retreive the first active connection
ServiceAction act = configService.getUPNPServiceAction( "GetActiveConnection" );
if ( act != null ) {
UPNPMessageFactory msg = UPNPMessageFactory.getNewInstance( configService );
String deviceContainer = null;
String serviceID = null;
try {
// always lookup for the first index of active connections.
ActionResponse resp = msg.getMessage( "GetActiveConnection" ).setInputParameter( "NewActiveConnectionIndex", 0 ).service();
deviceContainer = resp.getOutActionArgumentValue( "NewActiveConnDeviceContainer" );
serviceID = resp.getOutActionArgumentValue( "NewActiveConnectionServiceID" );
} catch ( IOException ex ) {
// no response returned
} catch ( UPNPResponseException respEx ) {
// should never happen unless the damn thing is bugged
}
if ( deviceContainer != null && deviceContainer.trim().length() > 0 &&
serviceID != null && serviceID.trim().length() > 0 ) {
for ( Iterator i = igd.getChildDevices().iterator(); i.hasNext(); ) {
UPNPDevice dv = (UPNPDevice)i.next();
if ( deviceContainer.startsWith( dv.getUDN() ) &&
dv.getDeviceType().indexOf( ":WANConnectionDevice:" ) != -1 ) {
myIGDWANConnDevice = dv;
break;
}
}
msgFactory = UPNPMessageFactory.getNewInstance( myIGDWANConnDevice.getServiceByID( serviceID ) );
}
}
}*/
// Doing a tricky test with external IP address, the unactive interface should return a null value or none
if ( testWANInterface( wanIPSrv ) ) {
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
} else if( testWANInterface( wanPPPSrv ) ) {
msgFactory = UPNPMessageFactory.getNewInstance( wanPPPSrv );
}
if ( msgFactory == null ) {
// Nothing found using WANCommonInterfaceConfig! IP by default
log.warn( "Unable to detect active WANIPConnection, dfaulting to urn:schemas-upnp-org:service:WANIPConnection:1" );
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
}
}
}
private boolean testWANInterface( UPNPService srv ) {
UPNPMessageFactory tmp = UPNPMessageFactory.getNewInstance( srv );
ActionMessage msg = tmp.getMessage( "GetExternalIPAddress" );
String ipToParse = null;
try {
ipToParse = msg.service().getOutActionArgumentValue( "NewExternalIPAddress" );
} catch ( UPNPResponseException ex ) {
// ok probably not the IP interface
} catch ( IOException ex ) {
// not really normal
log.warn( "IOException occured during device detection", ex );
}
if ( ipToParse != null && ipToParse.length() > 0 && !ipToParse.equals( "0.0.0.0" ) ) {
try {
return InetAddress.getByName( ipToParse ) != null;
} catch ( UnknownHostException ex ) {
// ok a crappy IP provided, definitly the wrong interface..
}
}
return false;
}
/**
* Retreives the IDG UNPNRootDevice object
* @return the UNPNRootDevie object bound to this object
*/
public UPNPRootDevice getIGDRootDevice() {
return igd;
}
/**
* Lookup all the IGD (IP or PPP) devices on the network. If a device implements both
* IP and PPP, the active service will be used for nat mappings.
* @param timeout the timeout in ms to listen for devices response, -1 for default value
* @return an array of devices to play with or null if nothing found.
* @throws IOException if some IO Exception occurs during discovery
*/
public static InternetGatewayDevice[] getDevices( int timeout ) throws IOException {
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, true, true, null );
}
/**
* Lookup all the IGD (IP urn:schemas-upnp-org:service:WANIPConnection:1, or PPP urn:schemas-upnp-org:service:WANPPPConnection:1)
* devices for a given network interface. If a device implements both
* IP and PPP, the active service will be used for nat mappings.
* @param timeout the timeout in ms to listen for devices response, -1 for default value
* @param ttl the discovery ttl such as {@link net.sbbi.upnp.Discovery#DEFAULT_TTL}
* @param mx the discovery mx such as {@link net.sbbi.upnp.Discovery#DEFAULT_MX}
* @param ni the network interface where to lookup IGD devices
* @return an array of devices to play with or null if nothing found.
* @throws IOException if some IO Exception occurs during discovery
*/
public static InternetGatewayDevice[] getDevices( int timeout, int ttl, int mx, NetworkInterface ni ) throws IOException {
return lookupDeviceDevices( timeout, ttl, mx, true, true, ni );
}
/**
* Lookup all the IGD IP devices on the network (urn:schemas-upnp-org:service:WANIPConnection:1 service)
* @param timeout the timeout in ms to listen for devices response, -1 for default value
* @return an array of devices to play with or null if nothing found or if found devices
* do not have the urn:schemas-upnp-org:service:WANIPConnection:1 service
* @deprecated use generic {@link #getDevices(int)} or {@link #getDevices(int, int, int, NetworkInterface)} methods since this one is not
* usable with all IGD devices ( will only work with devices implementing the urn:schemas-upnp-org:service:WANIPConnection:1 service )
*/
public static InternetGatewayDevice[] getIPDevices( int timeout ) throws IOException {
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, true, false, null );
}
/**
* Lookup all the IGD PPP devices on the network (urn:schemas-upnp-org:service:WANPPPConnection:1 service)
* @param timeout the timeout in ms to listen for devices response, -1 for default value
* @return an array of devices to play with or null if nothing found or if found devices
* do not have the urn:schemas-upnp-org:service:WANPPPConnection:1 service
* @deprecated use generic {@link #getDevices(int)} or {@link #getDevices(int, int, int, NetworkInterface)} methods since this one is not
* usable with all IGD devices ( will only work with devices implementing the urn:schemas-upnp-org:service:WANPPPConnection:1 service )
*/
public static InternetGatewayDevice[] getPPPDevices( int timeout ) throws IOException {
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, false, true, null );
}
private static InternetGatewayDevice[] lookupDeviceDevices( int timeout, int ttl, int mx, boolean WANIPConnection, boolean WANPPPConnection, NetworkInterface ni ) throws IOException {
UPNPRootDevice[] devices = null;
InternetGatewayDevice[] rtrVal = null;
if ( timeout == -1 ) {
devices = Discovery.discover( Discovery.DEFAULT_TIMEOUT, ttl, mx, "urn:schemas-upnp-org:device:InternetGatewayDevice:1", ni );
} else {
devices = Discovery.discover( timeout, ttl, mx, "urn:schemas-upnp-org:device:InternetGatewayDevice:1", ni );
}
if ( devices != null ) {
Set valid = new HashSet();
for ( int i = 0; i < devices.length; i++ ) {
try {
valid.add( new InternetGatewayDevice( devices[i], WANIPConnection, WANPPPConnection ) );
} catch ( UnsupportedOperationException ex ) {
// the device is either not IP or PPP
if ( log.isDebugEnabled() ) log.debug( "UnsupportedOperationException during discovery " + ex.getMessage() );
}
}
if ( valid.size() == 0 ) {
return null;
}
rtrVal = new InternetGatewayDevice[valid.size()];
int i = 0;
for ( Iterator itr = valid.iterator(); itr.hasNext(); ) {
rtrVal[i++] = (InternetGatewayDevice)itr.next();
}
}
return rtrVal;
}
/**
* Retreives the external IP address
* @return a String representing the external IP
* @throws UPNPResponseException if the devices returns an error code
* @throws IOException if some error occurs during communication with the device
*/
public String getExternalIPAddress() throws UPNPResponseException, IOException {
ActionMessage msg = msgFactory.getMessage( "GetExternalIPAddress" );
return msg.service().getOutActionArgumentValue( "NewExternalIPAddress" );
}
/**
* Retreives a generic port mapping entry.
* @param newPortMappingIndex the index to lookup in the nat table of the upnp device
* @return an action response Object containing the following fields :
* NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort,
* NewInternalClient, NewEnabled, NewPortMappingDescription, NewLeaseDuration or null if the index does not exists
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if some unexpected error occurs on the UPNP device
*/
public ActionResponse getGenericPortMappingEntry( int newPortMappingIndex ) throws IOException, UPNPResponseException {
ActionMessage msg = msgFactory.getMessage( "GetGenericPortMappingEntry" );
msg.setInputParameter( "NewPortMappingIndex", newPortMappingIndex );
try {
return msg.service();
} catch ( UPNPResponseException ex ) {
if ( ex.getDetailErrorCode() == 714 ) {
return null;
}
throw ex;
}
}
/**
* Retreives information about a specific port mapping
* @param remoteHost the remote host ip to check, null if wildcard
* @param externalPort the port to check
* @param protocol the protocol for the mapping, either TCP or UDP
* @return an action response Object containing the following fields :
* NewInternalPort, NewInternalClient, NewEnabled, NewPortMappingDescription, NewLeaseDuration or
* null if no such entry exists in the device NAT table
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if some unexpected error occurs on the UPNP device
*/
public ActionResponse getSpecificPortMappingEntry( String remoteHost, int externalPort, String protocol ) throws IOException, UPNPResponseException {
remoteHost = remoteHost == null ? "" : remoteHost;
checkPortMappingProtocol( protocol );
checkPortRange( externalPort );
ActionMessage msg = msgFactory.getMessage( "GetSpecificPortMappingEntry" );
msg.setInputParameter( "NewRemoteHost", remoteHost )
.setInputParameter( "NewExternalPort", externalPort )
.setInputParameter( "NewProtocol", protocol );
try {
return msg.service();
} catch ( UPNPResponseException ex ) {
if ( ex.getDetailErrorCode() == 714 ) {
return null;
}
throw ex;
}
}
/**
* Configures a nat entry on the UPNP device.
* @param description the mapping description, null for no description
* @param remoteHost the remote host ip for this entry, null for a wildcard value
* @param internalPort the internal client port where data should be redirected
* @param externalPort the external port to open on the UPNP device an map on the internal client, 0 for a wildcard value
* @param internalClient the internal client ip where data should be redirected
* @param leaseDuration the lease duration in seconds 0 for an infinite time
* @param protocol the protocol, either TCP or UDP
* @return true if the port is mapped false if the mapping is allready done for another internal client
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if the device does not accept some settings :<br/>
* 402 Invalid Args See UPnP Device Architecture section on Control<br/>
* 501 Action Failed See UPnP Device Architecture section on Control<br/>
* 715 WildCardNotPermittedInSrcIP The source IP address cannot be wild-carded<br/>
* 716 WildCardNotPermittedInExtPort The external port cannot be wild-carded <br/>
* 724 SamePortValuesRequired Internal and External port values must be the same<br/>
* 725 OnlyPermanentLeasesSupported The NAT implementation only supports permanent lease times on port mappings<br/>
* 726 RemoteHostOnlySupportsWildcard RemoteHost must be a wildcard and cannot be a specific IP address or DNS name<br/>
* 727 ExternalPortOnlySupportsWildcard ExternalPort must be a wildcard and cannot be a specific port value
*/
public boolean addPortMapping( String description, String remoteHost,
int internalPort, int externalPort,
String internalClient, int leaseDuration,
String protocol ) throws IOException, UPNPResponseException {
remoteHost = remoteHost == null ? "" : remoteHost;
checkPortMappingProtocol( protocol );
if ( externalPort != 0 ) {
checkPortRange( externalPort );
}
checkPortRange( internalPort );
description = description == null ? "" : description;
if ( leaseDuration < 0 ) throw new IllegalArgumentException( "Invalid leaseDuration (" + leaseDuration + ") value" );
ActionMessage msg = msgFactory.getMessage( "AddPortMapping" );
msg.setInputParameter( "NewRemoteHost", remoteHost )
.setInputParameter( "NewExternalPort", externalPort )
.setInputParameter( "NewProtocol", protocol )
.setInputParameter( "NewInternalPort", internalPort )
.setInputParameter( "NewInternalClient", internalClient )
.setInputParameter( "NewEnabled", true )
.setInputParameter( "NewPortMappingDescription", description )
.setInputParameter( "NewLeaseDuration", leaseDuration );
try {
msg.service();
return true;
} catch ( UPNPResponseException ex ) {
if ( ex.getDetailErrorCode() == 718 ) {
return false;
}
throw ex;
}
}
/**
* Deletes a port mapping on the IDG device
* @param remoteHost the host ip for which the mapping was done, null value for a wildcard value
* @param externalPort the port to close
* @param protocol the protocol for the mapping, TCP or UDP
* @return true if the port has been unmapped correctly otherwise false ( entry does not exists ).
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if the devices returns an error message
*/
public boolean deletePortMapping( String remoteHost, int externalPort, String protocol ) throws IOException, UPNPResponseException {
remoteHost = remoteHost == null ? "" : remoteHost;
checkPortMappingProtocol( protocol );
checkPortRange( externalPort );
ActionMessage msg = msgFactory.getMessage( "DeletePortMapping" );
msg.setInputParameter( "NewRemoteHost", remoteHost )
.setInputParameter( "NewExternalPort", externalPort )
.setInputParameter( "NewProtocol", protocol );
try {
msg.service();
return true;
} catch ( UPNPResponseException ex ) {
if ( ex.getDetailErrorCode() == 714 ) {
return false;
}
throw ex;
}
}
/**
* Retreives the current number of mapping in the NAT table
* @return the nat table current number of mappings or null if the device does not allow to query state variables
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if the devices returns an error message with error code other than 404
*/
public Integer getNatMappingsCount() throws IOException, UPNPResponseException {
Integer rtrval = null;
StateVariableMessage natTableSize = msgFactory.getStateVariableMessage( "PortMappingNumberOfEntries" );
try {
StateVariableResponse resp = natTableSize.service();
rtrval = new Integer( resp.getStateVariableValue() );
} catch ( UPNPResponseException ex ) {
// 404 can happen if device do not implement state variables queries
if ( ex.getDetailErrorCode() != 404 ) {
throw ex;
}
}
return rtrval;
}
/**
* Computes the total entries in avaliable in the nat table size, not that this method is not guaranteed to work
* with all upnp devices since it is not an generic IGD command
* @return the number of entries or null if the NAT table size cannot be computed for the device
* @throws IOException if some error occurs during communication with the device
* @throws UPNPResponseException if the devices returns an error message with error code other than 713 or 402
*/
public Integer getNatTableSize() throws IOException, UPNPResponseException {
// first let's look at the first index.. some crappy devices do not start with index 0
// we stop at index 50
int startIndex = -1;
for ( int i = 0; i < 50; i++ ) {
try {
this.getGenericPortMappingEntry( i );
startIndex = i;
break;
} catch ( UPNPResponseException ex ) {
// some devices return the 402 code
if ( ex.getDetailErrorCode() != 713 && ex.getDetailErrorCode() != 402 ) {
throw ex;
}
}
}
if ( startIndex == -1 ) {
// humm nothing found within the first 200 indexes..
// returning null
return null;
}
int size = 0;
while ( true ) {
try {
this.getGenericPortMappingEntry( startIndex++ );
size++;
} catch ( UPNPResponseException ex ) {
if ( ex.getDetailErrorCode() == 713 || ex.getDetailErrorCode() == 402 ) {
/// ok index unknown
break;
}
throw ex;
}
}
return new Integer( size );
}
private void checkPortMappingProtocol( String prot ) throws IllegalArgumentException {
if ( prot == null || ( !prot.equals( "TCP" ) && !prot.equals( "UDP" ) ) )
throw new IllegalArgumentException( "PortMappingProtocol must be either TCP or UDP" );
}
private void checkPortRange( int port ) throws IllegalArgumentException {
if ( port < 1 || port > 65535 )
throw new IllegalArgumentException( "Port range must be between 1 and 65535" );
}
}

@ -0,0 +1,437 @@
/*
* ============================================================================
* 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.messages;
import java.util.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.*;
import net.sbbi.upnp.services.*;
import javax.xml.parsers.*;
/**
* Message object for an UPNP action, simply call setInputParameter() to add
* the required action message params and then service() to receive the ActionResponse
* built with the parsed UPNP device SOAP xml response.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ActionMessage {
private final static Log log = LogFactory.getLog( ActionMessage.class );
private UPNPService service;
private ServiceAction serviceAction;
private List inputParameters;
/**
* Protected constuctor so that only messages factories can build it
* @param service the service for which the
* @param serviceAction
*/
protected ActionMessage( UPNPService service, ServiceAction serviceAction ) {
this.service = service;
this.serviceAction = serviceAction;
if ( serviceAction.getInputActionArguments() != null ) {
inputParameters = new ArrayList();
}
}
/**
* Method to clear all set input parameters so that
* this object can be reused
*/
public void clearInputParameters() {
inputParameters.clear();
}
/**
* Executes the message and retuns the UPNP device response, according to the UPNP specs,
* this method could take up to 30 secs to process ( time allowed for a device to respond to a request )
* @return a response object containing the UPNP parsed response
* @throws IOException if some IOException occurs during message send and reception process
* @throws UPNPResponseException if an UPNP error message is returned from the server
* or if some parsing exception occurs ( detailErrorCode = 899, detailErrorDescription = SAXException message )
*/
public ActionResponse service() throws IOException, UPNPResponseException {
ActionResponse rtrVal = null;
UPNPResponseException upnpEx = null;
IOException ioEx = null;
StringBuffer body = new StringBuffer( 256 );
body.append( "<?xml version=\"1.0\"?>\r\n" );
body.append( "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" );
body.append( " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" );
body.append( "<s:Body>" );
body.append( "<u:" ).append( serviceAction.getName() ).append( " xmlns:u=\"" ).append( service.getServiceType() ).append( "\">" );
if ( serviceAction.getInputActionArguments() != null ) {
// this action requires params so we just set them...
for ( Iterator itr = inputParameters.iterator(); itr.hasNext(); ) {
InputParamContainer container = (InputParamContainer)itr.next();
body.append( "<" ).append( container.name ).append( ">" ).append( container.value );
body.append( "</" ).append( container.name ).append( ">" );
}
}
body.append( "</u:" ).append( serviceAction.getName() ).append( ">" );
body.append( "</s:Body>" );
body.append( "</s:Envelope>" );
if ( log.isDebugEnabled() ) log.debug( "POST prepared for URL " + service.getControlURL() );
URL url = new URL( service.getControlURL().toString() );
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoInput( true );
conn.setDoOutput( true );
conn.setUseCaches( false );
conn.setRequestMethod( "POST" );
HttpURLConnection.setFollowRedirects( false );
//conn.setConnectTimeout( 30000 );
conn.setRequestProperty( "HOST", url.getHost() + ":" + url.getPort() );
conn.setRequestProperty( "CONTENT-TYPE", "text/xml; charset=\"utf-8\"" );
conn.setRequestProperty( "CONTENT-LENGTH", Integer.toString( body.length() ) );
conn.setRequestProperty( "SOAPACTION", "\"" + service.getServiceType() + "#" + serviceAction.getName() + "\"" );
OutputStream out = conn.getOutputStream();
out.write( body.toString().getBytes() );
out.flush();
out.close();
conn.connect();
InputStream input = null;
if ( log.isDebugEnabled() ) log.debug( "executing query :\n" + body );
try {
input = conn.getInputStream();
} catch ( IOException ex ) {
// java can throw an exception if he error code is 500 or 404 or something else than 200
// but the device sends 500 error message with content that is required
// this content is accessible with the getErrorStream
input = conn.getErrorStream();
}
if ( input != null ) {
int response = conn.getResponseCode();
String responseBody = getResponseBody( input );
if ( log.isDebugEnabled() ) log.debug( "received response :\n" + responseBody );
SAXParserFactory saxParFact = SAXParserFactory.newInstance();
saxParFact.setValidating( false );
saxParFact.setNamespaceAware( true );
ActionMessageResponseParser msgParser = new ActionMessageResponseParser( serviceAction );
StringReader stringReader = new StringReader( responseBody );
InputSource src = new InputSource( stringReader );
try {
SAXParser parser = saxParFact.newSAXParser();
parser.parse( src, msgParser );
} catch ( ParserConfigurationException confEx ) {
// should never happen
// we throw a runtimeException to notify the env problem
throw new RuntimeException( "ParserConfigurationException during SAX parser creation, please check your env settings:" + confEx.getMessage() );
} catch ( SAXException saxEx ) {
// kind of tricky but better than nothing..
upnpEx = new UPNPResponseException( 899, saxEx.getMessage() );
} finally {
try {
input.close();
} catch ( IOException ex ) {
// ignore
}
}
if ( upnpEx == null ) {
if ( response == HttpURLConnection.HTTP_OK ) {
rtrVal = msgParser.getActionResponse();
} else if ( response == HttpURLConnection.HTTP_INTERNAL_ERROR ) {
upnpEx = msgParser.getUPNPResponseException();
} else {
ioEx = new IOException( "Unexpected server HTTP response:" + response );
}
}
}
try {
out.close();
} catch ( IOException ex ) {
// ignore
}
conn.disconnect();
if ( upnpEx != null ) {
throw upnpEx;
}
if ( rtrVal == null && ioEx == null ) {
ioEx = new IOException( "Unable to receive a response from the UPNP device" );
}
if ( ioEx != null ) {
throw ioEx;
}
return rtrVal;
}
private String getResponseBody( InputStream in ) throws IOException {
byte[] buffer = new byte[256];
int readen = 0;
StringBuffer content = new StringBuffer( 256 );
while ( ( readen = in.read( buffer ) ) != -1 ) {
content.append( new String( buffer, 0 , readen ) );
}
// some devices add \0 chars at XML message end
// which causes XML parsing errors...
int len = content.length();
while ( content.charAt( len-1 ) == '\0' ) {
len--;
content.setLength( len );
}
return content.toString().trim();
}
/**
* The list of input parameters that should be accepted by the device service for this message
* @return a list of required input parameters ServiceActionArgument objects for this message
* or null if the message does not require any input params
*/
public List getInputParameterNames() {
return serviceAction.getInputActionArgumentsNames();
}
/**
* The list of output parameters that should be returned by the device service
* @return a list of output parameters ServiceActionArgument objects for this message
* or null if the message does not contains any output params.
*/
public List getOutputParameterNames() {
return serviceAction.getOutputActionArgumentsNames();
}
/**
* Set the value of an input parameter before a message service call. If the param name already
* exists, the param value will be overwritten with the new value provided.
* @param parameterName the parameter name
* @param parameterValue the parameter value as an object, primitive object are handled, all other object
* will be assigned with a call to their toString() method call
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, Object parameterValue ) throws IllegalArgumentException {
if ( parameterValue == null ) {
return setInputParameter( parameterName, "" );
} else if ( parameterValue instanceof Date ) {
return setInputParameter( parameterName, (Date)parameterValue );
} else if ( parameterValue instanceof Boolean ) {
return setInputParameter( parameterName, ((Boolean)parameterValue).booleanValue() );
} else if ( parameterValue instanceof Integer ) {
return setInputParameter( parameterName, ((Integer)parameterValue).intValue() );
} else if ( parameterValue instanceof Byte ) {
return setInputParameter( parameterName, ((Byte)parameterValue).byteValue() );
} else if ( parameterValue instanceof Short ) {
return setInputParameter( parameterName, ((Short)parameterValue).shortValue() );
} else if ( parameterValue instanceof Float ) {
return setInputParameter( parameterName, ((Float)parameterValue).floatValue() );
} else if ( parameterValue instanceof Double ) {
return setInputParameter( parameterName, ((Double)parameterValue).doubleValue() );
} else if ( parameterValue instanceof Long ) {
return setInputParameter( parameterName, ((Long)parameterValue).longValue() );
}
return setInputParameter( parameterName, parameterValue.toString() );
}
/**
* Set the value of an input parameter before a message service call. If the param name already
* exists, the param value will be overwritten with the new value provided
* @param parameterName the parameter name
* @param parameterValue the string parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, String parameterValue ) throws IllegalArgumentException {
if ( serviceAction.getInputActionArguments() == null ) throw new IllegalArgumentException( "No input parameters required for this message" );
ServiceActionArgument arg = serviceAction.getInputActionArgument( parameterName );
if ( arg == null ) throw new IllegalArgumentException( "Wrong input argument name for this action:" + parameterName + " available parameters are : " + getInputParameterNames() );
for ( Iterator i = inputParameters.iterator(); i.hasNext(); ) {
InputParamContainer container = (InputParamContainer)i.next();
if ( container.name.equals( parameterName ) ) {
container.value = parameterValue;
return this;
}
}
// nothing found add the new value
InputParamContainer container = new InputParamContainer();
container.name = parameterName;
container.value = parameterValue;
inputParameters.add( container );
return this;
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the date parameter value, will be automatically translated to the correct
* ISO 8601 date format for the given action input param related state variable
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, Date parameterValue ) throws IllegalArgumentException {
if ( serviceAction.getInputActionArguments() == null ) throw new IllegalArgumentException( "No input parameters required for this message" );
ServiceActionArgument arg = serviceAction.getInputActionArgument( parameterName );
if ( arg == null ) throw new IllegalArgumentException( "Wrong input argument name for this action:" + parameterName + " available parameters are : " + getInputParameterNames() );
ServiceStateVariable linkedVar = arg.getRelatedStateVariable();
if ( linkedVar.getDataType().equals( ServiceStateVariable.TIME ) ) {
return setInputParameter( parameterName, ISO8601Date.getIsoTime( parameterValue ) );
} else if ( linkedVar.getDataType().equals( ServiceStateVariable.TIME_TZ ) ) {
return setInputParameter( parameterName, ISO8601Date.getIsoTimeZone( parameterValue ) );
} else if ( linkedVar.getDataType().equals( ServiceStateVariable.DATE ) ) {
return setInputParameter( parameterName, ISO8601Date.getIsoDate( parameterValue ) );
} else if ( linkedVar.getDataType().equals( ServiceStateVariable.DATETIME ) ) {
return setInputParameter( parameterName, ISO8601Date.getIsoDateTime( parameterValue ) );
} else if ( linkedVar.getDataType().equals( ServiceStateVariable.DATETIME_TZ ) ) {
return setInputParameter( parameterName, ISO8601Date.getIsoDateTimeZone( parameterValue ) );
} else {
throw new IllegalArgumentException( "Related input state variable " + linkedVar.getName() + " is not of an date type" );
}
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the boolean parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, boolean parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, parameterValue ? "1" : "0" );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the byte parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, byte parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Byte.toString( parameterValue ) );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the short parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, short parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Short.toString( parameterValue ) );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the integer parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, int parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Integer.toString( parameterValue ) );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the long parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, long parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Long.toString( parameterValue ) );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the float parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, float parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Float.toString( parameterValue ) );
}
/**
* Set the value of an input parameter before a message service call
* @param parameterName the parameter name
* @param parameterValue the double parameter value
* @return the current ActionMessage object instance
* @throws IllegalArgumentException if the provided parameterName is not valid for this message
* or if no input parameters are required for this message
*/
public ActionMessage setInputParameter( String parameterName, double parameterValue ) throws IllegalArgumentException {
return setInputParameter( parameterName, Double.toString( parameterValue ) );
}
/**
* Input params class container
*/
private class InputParamContainer {
private String name;
private String value;
}
}

@ -0,0 +1,160 @@
/*
* ============================================================================
* 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.messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import net.sbbi.upnp.services.*;
/**
* Simple SAX handler for UPNP response message parsing, this message is in SOAP format
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ActionMessageResponseParser extends org.xml.sax.helpers.DefaultHandler {
private final static Log log = LogFactory.getLog( ActionMessageResponseParser.class );
private final static String SOAP_FAULT_EL = "Fault";
private ServiceAction serviceAction;
private String bodyElementName;
private boolean faultResponse = false;
private UPNPResponseException msgEx;
private boolean readFaultCode = false;
private boolean readFaultString = false;
private boolean readErrorCode = false;
private boolean readErrorDescription = false;
private boolean parseOutputParams = false;
private ActionResponse result;
private ServiceActionArgument parsedResultOutArg;
protected ActionMessageResponseParser( ServiceAction serviceAction ) {
this.serviceAction = serviceAction;
bodyElementName = serviceAction.getName() + "Response";
}
protected UPNPResponseException getUPNPResponseException() {
return msgEx;
}
protected ActionResponse getActionResponse() {
return result;
}
public void characters( char[] ch, int start, int length ) {
if ( parseOutputParams ) {
if ( parsedResultOutArg != null ) {
String origChars = result.getOutActionArgumentValue( parsedResultOutArg.getName() );
String newChars = new String( ch, start, length );
if ( origChars == null ) {
result.addResult( parsedResultOutArg, newChars );
} else {
result.addResult( parsedResultOutArg, origChars + newChars );
}
}
} else if ( readFaultCode ) {
msgEx.faultCode = new String( ch, start, length );
readFaultCode = false;
} else if ( readFaultString ) {
msgEx.faultString = new String( ch, start, length );
readFaultString = false;
} else if ( readErrorCode ) {
String code = new String( ch, start, length );
try {
msgEx.detailErrorCode = Integer.parseInt( code );
} catch ( Throwable ex ) {
log.debug( "Error during returned error code " + code + " parsing" );
}
readErrorCode = false;
} else if ( readErrorDescription ) {
msgEx.detailErrorDescription = new String( ch, start, length );
readErrorDescription = false;
}
}
public void startElement( String uri, String localName, String qName, Attributes attributes ) {
if ( parseOutputParams ) {
ServiceActionArgument arg = serviceAction.getActionArgument( localName );
if ( arg != null && arg.getDirection() == ServiceActionArgument.DIRECTION_OUT ) {
parsedResultOutArg = arg;
result.addResult( parsedResultOutArg, null );
} else {
parsedResultOutArg = null;
}
} else if ( faultResponse ) {
if ( localName.equals( "faultcode") ) {
readFaultCode = true;
} else if ( localName.equals( "faultstring" ) ) {
readFaultString = true;
} else if ( localName.equals( "errorCode" ) ) {
readErrorCode = true;
} else if ( localName.equals( "errorDescription" ) ) {
readErrorDescription = true;
}
} else if ( localName.equals( SOAP_FAULT_EL ) ) {
msgEx = new UPNPResponseException();
faultResponse = true;
} else if ( localName.equals( bodyElementName ) ) {
parseOutputParams = true;
result = new ActionResponse();
}
}
public void endElement( String uri, String localName, String qName ) {
if ( parsedResultOutArg != null && parsedResultOutArg.getName().equals( localName ) ) {
parsedResultOutArg = null;
} else if ( localName.equals( bodyElementName ) ) {
parseOutputParams = false;
}
}
}

@ -0,0 +1,101 @@
/*
* ============================================================================
* 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.messages;
import net.sbbi.upnp.services.ServiceActionArgument;
import java.util.*;
/**
* An action respons container Object
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ActionResponse {
private Map outArguments = new HashMap();
private Map outArgumentsVals = new HashMap();
protected ActionResponse() {
}
public ServiceActionArgument getOutActionArgument( String actionArgumentName ) {
return (ServiceActionArgument)outArguments.get( actionArgumentName );
}
public String getOutActionArgumentValue( String actionArgumentName ) {
return (String)outArgumentsVals.get( actionArgumentName );
}
public Set getOutActionArgumentNames() {
return outArguments.keySet();
}
/**
* Adds a result to the response, adding an existing result ServiceActionArgument
* will override the ServiceActionArgument value
* @param arg the service action argument
* @param value the arg value
*/
protected void addResult( ServiceActionArgument arg, String value ) {
outArguments.put( arg.getName(), arg );
outArgumentsVals.put( arg.getName(), value );
}
public String toString() {
StringBuffer rtrVal = new StringBuffer();
for ( Iterator i = outArguments.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
String value = (String)outArgumentsVals.get( name );
rtrVal.append( name ).append( "=" ).append( value );
if ( i.hasNext() ) rtrVal.append( "\n" );
}
return rtrVal.toString();
}
}

@ -0,0 +1,204 @@
/*
* ============================================================================
* 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.messages;
import org.xml.sax.*;
import javax.xml.parsers.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sbbi.upnp.services.*;
/**
* This class is used to create state variable messages to
* comminicate with the device
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class StateVariableMessage {
private final static Log log = LogFactory.getLog( StateVariableMessage.class );
private UPNPService service;
private ServiceStateVariable serviceStateVar;
protected StateVariableMessage( UPNPService service, ServiceStateVariable serviceStateVar ) {
this.service = service;
this.serviceStateVar = serviceStateVar;
}
/**
* Executes the state variable query and retuns the UPNP device response, according to the UPNP specs,
* this method could take up to 30 secs to process ( time allowed for a device to respond to a request )
* @return a state variable response object containing the variable value
* @throws IOException if some IOException occurs during message send and reception process
* @throws UPNPResponseException if an UPNP error message is returned from the server
* or if some parsing exception occurs ( detailErrorCode = 899, detailErrorDescription = SAXException message )
*/
public StateVariableResponse service() throws IOException, UPNPResponseException {
StateVariableResponse rtrVal = null;
UPNPResponseException upnpEx = null;
IOException ioEx = null;
StringBuffer body = new StringBuffer( 256 );
body.append( "<?xml version=\"1.0\"?>\r\n" );
body.append( "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" );
body.append( " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" );
body.append( "<s:Body>" );
body.append( "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">" );
body.append( "<u:varName>" ).append( serviceStateVar.getName() ).append( "</u:varName>" );
body.append( "</u:QueryStateVariable>" );
body.append( "</s:Body>" );
body.append( "</s:Envelope>" );
if ( log.isDebugEnabled() ) log.debug( "POST prepared for URL " + service.getControlURL() );
URL url = new URL( service.getControlURL().toString() );
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoInput( true );
conn.setDoOutput( true );
conn.setUseCaches( false );
conn.setRequestMethod( "POST" );
HttpURLConnection.setFollowRedirects( false );
//conn.setConnectTimeout( 30000 );
conn.setRequestProperty( "HOST", url.getHost() + ":" + url.getPort() );
conn.setRequestProperty( "SOAPACTION", "\"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"" );
conn.setRequestProperty( "CONTENT-TYPE", "text/xml; charset=\"utf-8\"" );
conn.setRequestProperty( "CONTENT-LENGTH", Integer.toString( body.length() ) );
OutputStream out = conn.getOutputStream();
out.write( body.toString().getBytes() );
out.flush();
conn.connect();
InputStream input = null;
if ( log.isDebugEnabled() ) log.debug( "executing query :\n" + body );
try {
input = conn.getInputStream();
} catch ( IOException ex ) {
// java can throw an exception if he error code is 500 or 404 or something else than 200
// but the device sends 500 error message with content that is required
// this content is accessible with the getErrorStream
input = conn.getErrorStream();
}
if ( input != null ) {
int response = conn.getResponseCode();
String responseBody = getResponseBody( input );
if ( log.isDebugEnabled() ) log.debug( "received response :\n" + responseBody );
SAXParserFactory saxParFact = SAXParserFactory.newInstance();
saxParFact.setValidating( false );
saxParFact.setNamespaceAware( true );
StateVariableResponseParser msgParser = new StateVariableResponseParser( serviceStateVar );
StringReader stringReader = new StringReader( responseBody );
InputSource src = new InputSource( stringReader );
try {
SAXParser parser = saxParFact.newSAXParser();
parser.parse( src, msgParser );
} catch ( ParserConfigurationException confEx ) {
// should never happen
// we throw a runtimeException to notify the env problem
throw new RuntimeException( "ParserConfigurationException during SAX parser creation, please check your env settings:" + confEx.getMessage() );
} catch ( SAXException saxEx ) {
// kind of tricky but better than nothing..
upnpEx = new UPNPResponseException( 899, saxEx.getMessage() );
} finally {
try {
input.close();
} catch ( IOException ex ) {
// ignoring
}
}
if ( upnpEx == null ) {
if ( response == HttpURLConnection.HTTP_OK ) {
rtrVal = msgParser.getStateVariableResponse();
} else if ( response == HttpURLConnection.HTTP_INTERNAL_ERROR ) {
upnpEx = msgParser.getUPNPResponseException();
} else {
ioEx = new IOException( "Unexpected server HTTP response:" + response );
}
}
}
try {
out.close();
} catch ( IOException ex ) {
// ignore
}
conn.disconnect();
if ( upnpEx != null ) {
throw upnpEx;
}
if ( rtrVal == null && ioEx == null ) {
ioEx = new IOException( "Unable to receive a response from the UPNP device" );
}
if ( ioEx != null ) {
throw ioEx;
}
return rtrVal;
}
private String getResponseBody( InputStream in ) throws IOException {
byte[] buffer = new byte[256];
int readen = 0;
StringBuffer content = new StringBuffer( 256 );
while ( ( readen = in.read( buffer ) ) != -1 ) {
content.append( new String( buffer, 0 , readen ) );
}
// some devices add \0 chars at XML message end
// which causes XML parsing errors...
int len = content.length();
while ( content.charAt( len-1 ) == '\0' ) {
len--;
content.setLength( len );
}
return content.toString().trim();
}
}

@ -0,0 +1,74 @@
/*
* ============================================================================
* 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.messages;
import net.sbbi.upnp.services.ServiceStateVariable;
/**
* This class contains data returned by a state variable query response
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class StateVariableResponse {
protected ServiceStateVariable stateVar;
protected String stateVariableValue;
protected StateVariableResponse() {
}
public ServiceStateVariable getStateVar() {
return stateVar;
}
public String getStateVariableValue() {
return stateVariableValue;
}
}

@ -0,0 +1,154 @@
/*
* ============================================================================
* 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.messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import net.sbbi.upnp.services.*;
/**
* Simple SAX handler for UPNP state variable query response message parsing, this message is in SOAP format
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class StateVariableResponseParser extends org.xml.sax.helpers.DefaultHandler {
private final static Log log = LogFactory.getLog( StateVariableResponseParser.class );
private final static String SOAP_FAULT_EL = "Fault";
private ServiceStateVariable stateVar;
private boolean faultResponse = false;
private UPNPResponseException msgEx;
private boolean readFaultCode = false;
private boolean readFaultString = false;
private boolean readErrorCode = false;
private boolean readErrorDescription = false;
private boolean parseStateVar = false;
private StateVariableResponse result;
protected StateVariableResponseParser( ServiceStateVariable stateVar ) {
this.stateVar = stateVar;
}
protected UPNPResponseException getUPNPResponseException() {
return msgEx;
}
protected StateVariableResponse getStateVariableResponse() {
return result;
}
public void characters( char[] ch, int start, int length ) {
if ( parseStateVar ) {
String origChars = result.stateVariableValue;
String newChars = new String( ch, start, length );
if ( origChars == null ) {
result.stateVariableValue = newChars;
} else {
result.stateVariableValue = origChars + newChars;
}
} else if ( readFaultCode ) {
msgEx.faultCode = new String( ch, start, length );
readFaultCode = false;
} else if ( readFaultString ) {
msgEx.faultString = new String( ch, start, length );
readFaultString = false;
} else if ( readErrorCode ) {
String code = new String( ch, start, length );
try {
msgEx.detailErrorCode = Integer.parseInt( code );
} catch ( Throwable ex ) {
log.debug( "Error during returned error code " + code + " parsing" );
}
readErrorCode = false;
} else if ( readErrorDescription ) {
msgEx.detailErrorDescription = new String( ch, start, length );
readErrorDescription = false;
}
}
public void startElement( String uri, String localName, String qName, Attributes attributes ) {
if ( faultResponse ) {
if ( localName.equals( "faultcode") ) {
readFaultCode = true;
} else if ( localName.equals( "faultstring" ) ) {
readFaultString = true;
} else if ( localName.equals( "errorCode" ) ) {
readErrorCode = true;
} else if ( localName.equals( "errorDescription" ) ) {
readErrorDescription = true;
}
} else if ( localName.equals( SOAP_FAULT_EL ) ) {
msgEx = new UPNPResponseException();
faultResponse = true;
} else if ( localName.equals( "return" ) || localName.equals( "varName" ) ) {
// some buggy implementations ( intel sample media server )
// do not use the specs compliant return element name but varName ...
parseStateVar = true;
result = new StateVariableResponse();
result.stateVar = stateVar;
}
}
public void endElement( String uri, String localName, String qName ) throws SAXException {
// some buggy implementations ( intel sample media server )
// do not use the specs compliant return element name but varName ...
if ( localName.equals( "return" ) || localName.equals( "varName" ) ) {
parseStateVar = false;
}
}
}

@ -0,0 +1,108 @@
/*
* ============================================================================
* 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.messages;
import net.sbbi.upnp.services.*;
/**
* Factory to create UPNP messages to access and communicate with
* a given UPNPDevice service capabilities
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class UPNPMessageFactory {
private UPNPService service;
/**
* Private constructor since this is a factory.
* @param service the UPNPService that will be used to generate messages by thid factory
*/
private UPNPMessageFactory( UPNPService service ) {
this.service = service;
}
/**
* Generate a new factory instance for a given device service definition object
* @param service the UPNP service definition object for messages creation
* @return a new message factory
*/
public static UPNPMessageFactory getNewInstance( UPNPService service ) {
return new UPNPMessageFactory( service );
}
/**
* Creation of a new ActionMessage to communicate with the UPNP device
* @param serviceActionName the name of a service action, this name is case sensitive
* and matches exactly the name provided by the UPNP device in the XML definition file
* @return a ActionMessage object or null if the action is unknown for this service messages factory
*/
public ActionMessage getMessage( String serviceActionName ) {
ServiceAction serviceAction = service.getUPNPServiceAction( serviceActionName );
if ( serviceAction != null ) {
return new ActionMessage( service, serviceAction );
}
return null;
}
/**
* Creation of a new StateVariableMessage to communicate with the UPNP device, for a service state variable query
* @param serviceStateVariable the name of a service state variable, this name is case sensitive
* and matches exactly the name provided by the UPNP device in the XML definition file
* @return a StateVariableMessage object or null if the state variable is unknown for this service mesages factory
*/
public StateVariableMessage getStateVariableMessage( String serviceStateVariable ) {
ServiceStateVariable stateVar = service.getUPNPServiceStateVariable( serviceStateVariable );
if ( stateVar != null ) {
return new StateVariableMessage( service, stateVar );
}
return null;
}
}

@ -0,0 +1,97 @@
/*
* ============================================================================
* 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.messages;
/**
* An exception throws when parsing a message if a SOAP fault
* exception is returned.
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class UPNPResponseException extends Exception {
private static final long serialVersionUID = 8313107558129180594L;
protected String faultCode;
protected String faultString;
protected int detailErrorCode;
protected String detailErrorDescription;
public UPNPResponseException() {
}
public UPNPResponseException( int detailErrorCode, String detailErrorDescription ) {
this.detailErrorCode = detailErrorCode;
this.detailErrorDescription = detailErrorDescription;
}
public String getFaultCode() {
return faultCode == null ? "Client" : faultCode ;
}
public String getFaultString() {
return faultString == null ? "UPnPError" : faultString;
}
public int getDetailErrorCode() {
return detailErrorCode;
}
public String getDetailErrorDescription() {
return detailErrorDescription;
}
public String getMessage() {
return "Detailed error code :" + detailErrorCode + ", Detailed error description :" + detailErrorDescription;
}
public String getLocalizedMessage() {
return getMessage();
}
}

@ -0,0 +1,331 @@
/*
* ============================================================================
* 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 java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.TimeZone;
/**
* ISO8601 Date implementation taken from org.w3c package and modified
* to work with UPNP date types
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ISO8601Date {
private static boolean check(StringTokenizer st, String token) throws NumberFormatException {
try {
if (st.nextToken().equals(token)) {
return true;
} else {
throw new NumberFormatException("Missing [" + token + "]");
}
} catch (NoSuchElementException ex) {
return false;
}
}
private static Calendar getCalendar( String isodate ) throws NumberFormatException {
// YYYY-MM-DDThh:mm:ss.sTZD or hh:mm:ss.sTZD
// does it contains a date ?
boolean isATime = isodate.indexOf( ':' ) != -1;
boolean isADate = isodate.indexOf( '-' ) != -1 || ( isodate.length() == 4 && !isATime );
if ( isATime && ! isADate ) {
if ( !isodate.toUpperCase().startsWith( "T" ) ) {
isodate = "T" + isodate;
}
}
StringTokenizer st = new StringTokenizer(isodate, "-T:.+Z", true);
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.clear();
if ( isADate ) {
// Year
if (st.hasMoreTokens()) {
int year = Integer.parseInt(st.nextToken());
calendar.set(Calendar.YEAR, year);
} else {
return calendar;
}
// Month
if (check(st, "-") && (st.hasMoreTokens())) {
int month = Integer.parseInt(st.nextToken()) - 1;
calendar.set(Calendar.MONTH, month);
} else {
return calendar;
}
// Day
if (check(st, "-") && (st.hasMoreTokens())) {
int day = Integer.parseInt(st.nextToken());
calendar.set(Calendar.DAY_OF_MONTH, day);
} else {
return calendar;
}
}
// Hour
if ( ( check(st, "T") ) && ( st.hasMoreTokens() ) ) {
int hour = Integer.parseInt(st.nextToken());
calendar.set(Calendar.HOUR_OF_DAY, hour);
} else {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
// Minutes
if (check(st, ":") && (st.hasMoreTokens())) {
int minutes = Integer.parseInt(st.nextToken());
calendar.set(Calendar.MINUTE, minutes);
} else {
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
//
// Not mandatory now
//
// Secondes
if (!st.hasMoreTokens()) {
return calendar;
}
String tok = st.nextToken();
if (tok.equals(":")) { // secondes
if (st.hasMoreTokens()) {
int secondes = Integer.parseInt(st.nextToken());
calendar.set(Calendar.SECOND, secondes);
if (!st.hasMoreTokens()) {
return calendar;
}
// frac sec
tok = st.nextToken();
if (tok.equals(".")) {
// bug fixed, thx to Martin Bottcher
String nt = st.nextToken();
while (nt.length() < 3) {
nt += "0";
}
nt = nt.substring(0, 3); // Cut trailing chars..
int millisec = Integer.parseInt(nt);
// int millisec = Integer.parseInt(st.nextToken()) * 10;
calendar.set(Calendar.MILLISECOND, millisec);
if (!st.hasMoreTokens()) {
return calendar;
}
tok = st.nextToken();
} else {
calendar.set(Calendar.MILLISECOND, 0);
}
} else {
throw new NumberFormatException("No secondes specified");
}
} else {
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
}
// Timezone
if (!tok.equals("Z")) { // UTC
if (!(tok.equals("+") || tok.equals("-"))) {
throw new NumberFormatException("only Z, + or - allowed");
}
boolean plus = tok.equals("+");
if (!st.hasMoreTokens()) {
throw new NumberFormatException("Missing hour field");
}
int tzhour = Integer.parseInt(st.nextToken());
int tzmin = 0;
if (check(st, ":") && (st.hasMoreTokens())) {
tzmin = Integer.parseInt(st.nextToken());
} else {
throw new NumberFormatException("Missing minute field");
}
if (plus) {
calendar.add(Calendar.HOUR, -tzhour);
calendar.add(Calendar.MINUTE, -tzmin);
} else {
calendar.add(Calendar.HOUR, tzhour);
calendar.add(Calendar.MINUTE, tzmin);
}
}
return calendar;
}
/**
* Parse the given string in ISO 8601 format and build a Date object.
* @param isodate the date in ISO 8601 format
* @return a Date instance
* @exception InvalidDateException
* if the date is not valid
*/
public static Date parse(String isodate) throws NumberFormatException {
Calendar calendar = getCalendar(isodate);
return calendar.getTime();
}
private static String twoDigit(int i) {
if (i >= 0 && i < 10) {
return "0" + String.valueOf(i);
}
return String.valueOf(i);
}
/**
* Generate a ISO 8601 date
* @param date a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String getIsoDate(Date date) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
StringBuffer buffer = new StringBuffer();
buffer.append(calendar.get(Calendar.YEAR));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.MONTH) + 1));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH)));
return buffer.toString();
}
/**
* Generate a ISO 8601 date time without timezone
* @param date a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String getIsoDateTime(Date date) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
StringBuffer buffer = new StringBuffer();
buffer.append(calendar.get(Calendar.YEAR));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.MONTH) + 1));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH)));
buffer.append("T");
buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
buffer.append(".");
buffer.append(twoDigit(calendar.get(Calendar.MILLISECOND) / 10));
return buffer.toString();
}
/**
* Generate a ISO 8601 date time with timezone
* @param date a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String getIsoDateTimeZone(Date date) {
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setTime(date);
StringBuffer buffer = new StringBuffer();
buffer.append(calendar.get(Calendar.YEAR));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.MONTH) + 1));
buffer.append("-");
buffer.append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH)));
buffer.append("T");
buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
buffer.append(".");
buffer.append(twoDigit(calendar.get(Calendar.MILLISECOND) / 10));
buffer.append("Z");
return buffer.toString();
}
/**
* Generate a ISO 8601 time
* @param date a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String getIsoTime(Date date) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
StringBuffer buffer = new StringBuffer();
buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
buffer.append(".");
buffer.append(twoDigit(calendar.get(Calendar.MILLISECOND) / 10));
return buffer.toString();
}
/**
* Generate a ISO 8601 time
* @param date a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String getIsoTimeZone(Date date) {
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setTime(date);
StringBuffer buffer = new StringBuffer();
buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
buffer.append(":");
buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
buffer.append(".");
buffer.append(twoDigit(calendar.get(Calendar.MILLISECOND) / 10));
buffer.append("Z");
return buffer.toString();
}
}

@ -0,0 +1,202 @@
/*
* ============================================================================
* 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 java.util.*;
/**
* An object to represent a service action proposed by an UPNP service
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServiceAction {
protected String name;
protected UPNPService parent;
private List orderedActionArguments;
private List orderedInputActionArguments;
private List orderedOutputActionArguments;
private List orderedInputActionArgumentsNames;
private List orderedOutputActionArgumentsNames;
protected ServiceAction() {
}
public UPNPService getParent() {
return parent;
}
/**
* The action in and out arguments ServiceActionArgument objects list
* @return the list with ServiceActionArgument objects or null if the action has no params
*/
public List getActionArguments() {
return orderedActionArguments;
}
/**
* Look for an ServiceActionArgument for a given name
* @param argumentName the argument name
* @return the argument or null if not found or not available
*/
public ServiceActionArgument getActionArgument( String argumentName ) {
if ( orderedActionArguments == null ) return null;
for ( Iterator i = orderedActionArguments.iterator(); i.hasNext(); ) {
ServiceActionArgument arg = (ServiceActionArgument)i.next();
if ( arg.getName().equals( argumentName ) ) return arg;
}
return null;
}
protected void setActionArguments( List orderedActionArguments ) {
this.orderedActionArguments = orderedActionArguments;
orderedInputActionArguments = getListForActionArgument( orderedActionArguments, ServiceActionArgument.DIRECTION_IN );
orderedOutputActionArguments = getListForActionArgument( orderedActionArguments, ServiceActionArgument.DIRECTION_OUT );
orderedInputActionArgumentsNames = getListForActionArgumentNames( orderedActionArguments, ServiceActionArgument.DIRECTION_IN );
orderedOutputActionArgumentsNames = getListForActionArgumentNames( orderedActionArguments, ServiceActionArgument.DIRECTION_OUT );
}
/**
* Return a list containing input ( when a response is sent ) arguments objects
* @return a list containing input arguments ServiceActionArgument objects or null when nothing
* is needed for such operation
*/
public List getInputActionArguments() {
return orderedInputActionArguments;
}
/**
* Look for an input ServiceActionArgument for a given name
* @param argumentName the input argument name
* @return the argument or null if not found or not available
*/
public ServiceActionArgument getInputActionArgument( String argumentName ) {
if ( orderedInputActionArguments == null ) return null;
for ( Iterator i = orderedInputActionArguments.iterator(); i.hasNext(); ) {
ServiceActionArgument arg = (ServiceActionArgument)i.next();
if ( arg.getName().equals( argumentName ) ) return arg;
}
return null;
}
/**
* Return a list containing output ( when a response is received ) arguments objects
* @return a list containing output arguments ServiceActionArgument objects or null when nothing
* returned for such operation
*/
public List getOutputActionArguments() {
return orderedOutputActionArguments;
}
/**
* Look for an output ServiceActionArgument for a given name
* @param argumentName the input argument name
* @return the argument or null if not found or not available
*/
public ServiceActionArgument getOutputActionArgument( String argumentName ) {
if ( orderedOutputActionArguments == null ) return null;
for ( Iterator i = orderedOutputActionArguments.iterator(); i.hasNext(); ) {
ServiceActionArgument arg = (ServiceActionArgument)i.next();
if ( arg.getName().equals( argumentName ) ) return arg;
}
return null;
}
/**
* Return a list containing input ( when a response is sent ) arguments names
* @return a list containing input arguments names as Strings or null when nothing
* is needed for such operation
*/
public List getInputActionArgumentsNames() {
return orderedInputActionArgumentsNames;
}
/**
* Return a list containing output ( when a response is received ) arguments names
* @return a list containing output arguments names as Strings or null when nothing
* returned for such operation
*/
public List getOutputActionArgumentsNames() {
return orderedOutputActionArgumentsNames;
}
/**
* The action name
* @return The action name
*/
public String getName() {
return name;
}
private List getListForActionArgument( List args, String direction ) {
if ( args == null ) return null;
List rtrVal = new ArrayList();
for ( Iterator itr = args.iterator(); itr.hasNext(); ) {
ServiceActionArgument actArg = (ServiceActionArgument)itr.next();
if ( actArg.getDirection() == direction ) {
rtrVal.add( actArg );
}
}
if ( rtrVal.size() == 0 ) rtrVal = null;
return rtrVal;
}
private List getListForActionArgumentNames( List args, String direction ) {
if ( args == null ) return null;
List rtrVal = new ArrayList();
for ( Iterator itr = args.iterator(); itr.hasNext(); ) {
ServiceActionArgument actArg = (ServiceActionArgument)itr.next();
if ( actArg.getDirection() == direction ) {
rtrVal.add( actArg.getName() );
}
}
if ( rtrVal.size() == 0 ) rtrVal = null;
return rtrVal;
}
}

@ -0,0 +1,87 @@
/*
* ============================================================================
* 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;
/**
* An argument for a service action
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServiceActionArgument {
public final static String DIRECTION_IN = "in";
public final static String DIRECTION_OUT = "out";
protected ServiceStateVariable relatedStateVariable;
protected String name;
protected String direction;
/**
* The related service state variable for this ServiceActionArgument
* @return The related service state variable for this ServiceActionArgument
*/
public ServiceStateVariable getRelatedStateVariable() {
return relatedStateVariable;
}
/**
* The argument name
* @return the argument name
*/
public String getName() {
return name;
}
/**
* The argument direction
* @return the argument direction (in|out)
*/
public String getDirection() {
return direction;
}
}

@ -0,0 +1,263 @@
/*
* ============================================================================
* 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 java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.Set;
import net.sbbi.upnp.messages.StateVariableMessage;
import net.sbbi.upnp.messages.UPNPMessageFactory;
import net.sbbi.upnp.messages.UPNPResponseException;
/**
* Class to contain a service state variable definition
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public class ServiceStateVariable implements ServiceStateVariableTypes {
private StateVariableMessage stateVarMsg = null;
protected String name;
protected boolean sendEvents;
protected String dataType;
protected String defaultValue;
protected String minimumRangeValue;
protected String maximumRangeValue;
protected String stepRangeValue;
protected Set allowedvalues;
protected UPNPService parent;
protected ServiceStateVariable() {
}
/**
* Call to the UPNP device to retreive the state variable actual value
* @return the state variable actual value on the device, should be never null, an empty string could be returned by the device
* @throws UPNPResponseException if the device throws an exception during query
* @throws IOException if some IO error with device occurs during query
*/
public String getValue() throws UPNPResponseException, IOException {
if ( stateVarMsg == null ) {
synchronized( this ) {
if ( stateVarMsg == null ) {
UPNPMessageFactory factory = UPNPMessageFactory.getNewInstance( parent );
stateVarMsg = factory.getStateVariableMessage( name );
}
}
}
return stateVarMsg.service().getStateVariableValue();
}
/**
* State variable name
* @return the state variable name
*/
public String getName() {
return name;
}
/**
* The parent UPNPService Object
* @return the parent object instance
*/
public UPNPService getParent() {
return parent;
}
/**
* Boolean to indicate if the variable is sending events when value
* of the var is changing. The events can be subscribed via the {@link net.sbbi.upnp.ServicesEventing}
* class
* @return true if sending events
*/
public boolean isSendEvents() {
return sendEvents;
}
/**
* The default value of the state variable
* @return the default value representation as an string
*/
public String getDefaultValue() {
return defaultValue;
}
/**
* The variable UPNP data type
* @return the data type
*/
public String getDataType() {
return dataType;
}
/**
* The varialbe JAVA data type (using an UPNP->Java mapping)
* @return the class mapped
*/
public Class getDataTypeAsClass() {
return getDataTypeClassMapping( dataType );
}
/**
* A set of allowed values (String objects) for the variable
* @return the allowed values or null if none
*/
public Set getAllowedvalues() {
return allowedvalues;
}
/**
* The minimum value as a string
* @return the minimum value or null if no restriction
*/
public String getMinimumRangeValue() {
return minimumRangeValue;
}
/**
* The maximum value as a string
* @return the maximum value or null if no restriction
*/
public String getMaximumRangeValue() {
return maximumRangeValue;
}
/**
* The value step range as a string
* @return the value step raqnge or null if no restriction
*/
public String getStepRangeValue() {
return stepRangeValue;
}
public static Class getDataTypeClassMapping( String dataType ) {
int hash = dataType.hashCode();
if ( hash == UI1_INT ) return Short.class;
else if ( hash == UI2_INT ) return Integer.class;
else if ( hash == UI4_INT ) return Long.class;
else if ( hash == I1_INT ) return Byte.class;
else if ( hash == I2_INT ) return Short.class;
else if ( hash == I4_INT ) return Integer.class;
else if ( hash == INT_INT ) return Integer.class;
else if ( hash == R4_INT ) return Float.class;
else if ( hash == R8_INT ) return Double.class;
else if ( hash == NUMBER_INT ) return Double.class;
else if ( hash == FIXED_14_4_INT ) return Double.class;
else if ( hash == FLOAT_INT ) return Float.class;
else if ( hash == CHAR_INT) return Character.class;
else if ( hash == STRING_INT ) return String.class;
else if ( hash == DATE_INT ) return Date.class;
else if ( hash == DATETIME_INT ) return Date.class;
else if ( hash == DATETIME_TZ_INT ) return Date.class;
else if ( hash == TIME_INT ) return Date.class;
else if ( hash == TIME_TZ_INT ) return Date.class;
else if ( hash == BOOLEAN_INT ) return Boolean.class;
else if ( hash == BIN_BASE64_INT ) return String.class;
else if ( hash == BIN_HEX_INT ) return String.class;
else if ( hash == URI_INT ) return URI.class;
else if ( hash == UUID_INT ) return String.class;
return null;
}
public static String getUPNPDataTypeMapping( String className ) {
if ( className.equals( Short.class.getName() ) || className.equals( "short" )) return I2;
else if ( className.equals( Byte.class.getName() ) || className.equals( "byte" )) return I1;
else if ( className.equals( Integer.class.getName() ) || className.equals( "int" ) ) return INT;
else if ( className.equals( Long.class.getName() ) || className.equals( "long" ) ) return UI4;
else if ( className.equals( Float.class.getName() ) || className.equals( "float" )) return FLOAT;
else if ( className.equals( Double.class.getName() ) || className.equals( "double" ) ) return NUMBER;
else if ( className.equals( Character.class.getName() ) || className.equals( "char" ) ) return CHAR;
else if ( className.equals( String.class.getName() ) || className.equals( "string" ) ) return STRING;
else if ( className.equals( Date.class.getName() ) ) return DATETIME;
else if ( className.equals( Boolean.class.getName() ) || className.equals( "boolean" ) ) return BOOLEAN;
else if ( className.equals( URI.class.getName() ) ) return URI;
return null;
}
public static Object UPNPToJavaObject( String dataType, String value ) throws Throwable {
if ( value == null ) throw new Exception( "null value" );
if ( dataType == null ) throw new Exception( "null dataType" );
int hash = dataType.hashCode();
if ( hash == UI1_INT ) return new Short( value );
else if ( hash == UI2_INT ) return new Integer( value );
else if ( hash == UI4_INT ) return new Long( value );
else if ( hash == I1_INT ) return new Byte( value );
else if ( hash == I2_INT ) return new Short( value );
else if ( hash == I4_INT ) return new Integer( value );
else if ( hash == INT_INT ) return new Integer( value );
else if ( hash == R4_INT ) return new Float( value );
else if ( hash == R8_INT ) return new Double( value );
else if ( hash == NUMBER_INT ) return new Double( value );
else if ( hash == FIXED_14_4_INT ) return new Double( value );
else if ( hash == FLOAT_INT ) return new Float( value );
else if ( hash == CHAR_INT) return new Character( value.charAt( 0 ) );
else if ( hash == STRING_INT ) return value;
else if ( hash == DATE_INT ) return ISO8601Date.parse( value );
else if ( hash == DATETIME_INT ) return ISO8601Date.parse( value );
else if ( hash == DATETIME_TZ_INT ) return ISO8601Date.parse( value );
else if ( hash == TIME_INT ) return ISO8601Date.parse( value );
else if ( hash == TIME_TZ_INT ) return ISO8601Date.parse( value );
else if ( hash == BOOLEAN_INT ) {
if ( value.equals( "1" ) || value.equalsIgnoreCase( "yes" ) || value.equals( "true" ) ) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
else if ( hash == BIN_BASE64_INT ) return value;
else if ( hash == BIN_HEX_INT ) return value;
else if ( hash == URI_INT ) return new URI( value );
else if ( hash == UUID_INT ) return value;
throw new Exception( "Unhandled data type " + dataType );
}
}

@ -0,0 +1,188 @@
/*
* ============================================================================
* 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;
/**
* Interface to defined allowed values for service state variables data types
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
* @version 1.0
*/
public interface ServiceStateVariableTypes {
/**
* Unsigned 1 Byte int. Same format as int without leading sign.
*/
public final static String UI1 = "ui1";
/**
* Unsigned 2 Byte int. Same format as int without leading sign.
*/
public final static String UI2 = "ui2";
/**
* Unsigned 4 Byte int. Same format as int without leading sign.
*/
public final static String UI4 = "ui4";
/**
* 1 Byte int. Same format as int.
*/
public final static String I1 = "i1";
/**
* 2 Byte int. Same format as int.
*/
public final static String I2 = "i2";
/**
* 4 Byte int. Same format as int.
*/
public final static String I4 = "i4";
/**
* Fixed point, integer number. May have leading sign. May have leading zeros.
* (No currency symbol.) (No grouping of digits to the left of the decimal, e.g., no commas.)
*/
public final static String INT = "int";
/**
* 4 Byte float. Same format as float. Must be between 3.40282347E+38 to 1.17549435E-38.
*/
public final static String R4 = "r4";
/**
* 8 Byte float. Same format as float. Must be between -1.79769313486232E308 and -4.94065645841247E-324
* for negative values, and between 4.94065645841247E-324 and 1.79769313486232E308 for positive values,
* i.e., IEEE 64-bit (8-Byte) double.
*/
public final static String R8 = "r8";
/**
* Same as r8.
*/
public final static String NUMBER = "number";
/**
* Same as r8 but no more than 14 digits to the left of the decimal point and no more than 4 to the right.
*/
public final static String FIXED_14_4 = "fixed.14.4";
/**
* Floating point number. Mantissa (left of the decimal) and/or exponent may have a leading sign.
* Mantissa and/or exponent may have leading zeros. Decimal character in mantissa is a period, i.e.,
* whole digits in mantissa separated from fractional digits by period. Mantissa separated from exponent
* by E. (No currency symbol.) (No grouping of digits in the mantissa, e.g., no commas.)
*/
public final static String FLOAT = "float";
/**
* Unicode string. One character long.
*/
public final static String CHAR = "char";
/**
* Unicode string. No limit on length.
*/
public final static String STRING = "string";
/**
* Date in a subset of ISO 8601 format without time data.
*/
public final static String DATE = "date";
/**
* Date in ISO 8601 format with optional time but no time zone.
*/
public final static String DATETIME = "dateTime";
/**
* Date in ISO 8601 format with optional time and optional time zone.
*/
public final static String DATETIME_TZ = "dateTime.tz";
/**
* Time in a subset of ISO 8601 format with no date and no time zone.
*/
public final static String TIME = "time";
/**
* Time in a subset of ISO 8601 format with optional time zone but no date.
*/
public final static String TIME_TZ = "time.tz";
/**
* 0, false, or no for false; 1, true, or yes for true.
*/
public final static String BOOLEAN = "boolean";
/**
* MIME-style Base64 encoded binary BLOB. Takes 3 Bytes, splits them into 4 parts,
* and maps each 6 bit piece to an octet. (3 octets are encoded as 4.) No limit on size.
*/
public final static String BIN_BASE64 = "bin.base64";
/**
* Hexadecimal digits representing octets. Treats each nibble as a hex digit and encodes
* as a separate Byte. (1 octet is encoded as 2.) No limit on size.
*/
public final static String BIN_HEX = "bin.hex";
/**
* Universal Resource Identifier.
*/
public final static String URI = "uri";
/**
* Universally Unique ID. Hexadecimal digits representing octets.
* Optional embedded hyphens are ignored.
*/
public final static String UUID = "uuid";
public final static int UI1_INT = "ui1".hashCode();
public final static int UI2_INT = "ui2".hashCode();
public final static int UI4_INT = "ui4".hashCode();
public final static int I1_INT = "i1".hashCode();
public final static int I2_INT = "i2".hashCode();
public final static int I4_INT = "i4".hashCode();
public final static int INT_INT = "int".hashCode();
public final static int R4_INT = "r4".hashCode();
public final static int R8_INT = "r8".hashCode();
public final static int NUMBER_INT = "number".hashCode();
public final static int FIXED_14_4_INT = "fixed.14.4".hashCode();
public final static int FLOAT_INT = "float".hashCode();
public final static int CHAR_INT = "char".hashCode();
public final static int STRING_INT = "string".hashCode();
public final static int DATE_INT = "date".hashCode();
public final static int DATETIME_INT = "dateTime".hashCode();
public final static int DATETIME_TZ_INT = "dateTime.tz".hashCode();
public final static int TIME_INT = "time".hashCode();
public final static int TIME_TZ_INT = "time.tz".hashCode();
public final static int BOOLEAN_INT = "boolean".hashCode();
public final static int BIN_BASE64_INT = "bin.base64".hashCode();
public final static int BIN_HEX_INT = "bin.hex".hashCode();
public final static int URI_INT = "uri".hashCode();
public final static int UUID_INT = "uuid".hashCode();
}

@ -0,0 +1,326 @@
/*
* ============================================================================
* 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;
}
}

@ -0,0 +1,8 @@
# please make sur to update those values
# when crating a new release
# especially to create cvs changelogs
release.version=1.0.4
release.version.cvs.tag=V1_0_4
release.version.publish.date=20 Nov 2006
last.release.version.cvs.tag=V1_0_3
last.release.version.publish.date=14 Feb 2006
Loading…
Cancel
Save