* 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