* 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-ef5d649a1542pull/1/head
parent
049fb23a8d
commit
477807e0e6
@ -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…
Reference in new issue