blob: 23d3e9d7ec1ce3b347f01526160438fbee36600b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.felix.upnp.basedriver.export;
import java.io.File;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import org.apache.felix.upnp.basedriver.Activator;
import org.cybergarage.upnp.Device;
import org.cybergarage.upnp.DeviceList;
import org.cybergarage.upnp.ServiceList;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.upnp.UPnPDevice;
import org.osgi.service.upnp.UPnPEventListener;
/*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ThreadExporter implements Runnable,ServiceListener {
private boolean end;
private RootDeviceExportingQueue queueRootDevice;
// private String basePath; twa: redundant
// private File baseFile; twa: redundant
private Hashtable exportedDevices;
private boolean listening;
private class ExportedDeviceInfo{
private Device device;
private ServiceRegistration serviceRegistration;
//private DeviceNode deviceNode;
/**
* @param device
* @param serviceRegistration
* @param deviceNode
*/
private ExportedDeviceInfo(Device device,
ServiceRegistration serviceRegistration,
DeviceNode deviceNode) {
super();
this.device = device;
this.serviceRegistration = serviceRegistration;
//this.deviceNode = deviceNode;
}
private Device getDevice() {
return this.device;
}
private ServiceRegistration getServiceRegistration() {
return this.serviceRegistration;
}
/*private DeviceNode getDeviceNode(){
return this.deviceNode;
}*/
}
/**
*
*/
public ThreadExporter(RootDeviceExportingQueue queue) throws InvalidSyntaxException {
end=false;
queueRootDevice=queue;
this.exportedDevices=new Hashtable();
setListening(false);
}
public void run() {
File osgiRoot = Activator.bc.getDataFile("");
if (osgiRoot == null) {
Activator.logger.ERROR("Unable to use filesystem");
while (true) {
try {
Activator.bc.getBundle().stop();
break;
} catch (BundleException e) {
e.printStackTrace();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
return;
}
ServiceReference rootDevice = null;
while (!shouldEnd()) {
DeviceNode dn = queueRootDevice.getRootDevice();
if (dn == null)
continue;
rootDevice = dn.getReference();
if(!getListening())
setListen();
Activator.logger.INFO("[Exporter] Exporting device "+ rootDevice.getProperty(UPnPDevice.FRIENDLY_NAME));
/*
* I don't know if the exporting should be make default language of the framework
* or without any lanuguages
Root r = new Root(rootDevice, context, context
.getProperty(Constants.FRAMEWORK_LANGUAGE));
*/
synchronized (this) {
Device d = BuildDevice.createCyberLinkDevice(dn.getReference());
if (d != null) {
if(!bindInvokes(d,rootDevice)){
Activator.logger.DEBUG("Unable to find all the sub device or to set action listener");
continue;
}
ServiceRegistration listenReg = bindSubscribe(d);
if(listenReg==null){
Activator.logger.DEBUG("Unable to set action listener event listener");
continue;
}
//makeIcons(r.getRootDevice(),xml.getAbsolutePath());
d.start();
exportedDevices.put(
rootDevice.getProperty(UPnPDevice.UDN),
new ExportedDeviceInfo(d,listenReg,dn)
);
}
}
}
}
/**
*
*/
private void setListen() {
{
try {
Activator.bc.addServiceListener(
this,
// fixed by Matteo and Francesco 21/9/04
"(&("+Constants.OBJECTCLASS+"="+UPnPDevice.class.getName()+")"
+ "(" + UPnPDevice.UPNP_EXPORT +"=*))"
);
} catch (InvalidSyntaxException ingnore) {}
}
}
/*
/**
* @param upnpDev
* @param refDev
*
private void makeIcons(
org.apache.felix.upnpbase.export.xml.Device upnpDev,
String path) {
Icon[] icons = upnpDev.getIcons();
if(icons!=null){
byte[] buf = new byte[512];
for (int i = 0; i < icons.length; i++) {
try {
String icoPath = path+icons[i].getUrl().replace('/',File.separatorChar);
InputStream is = icons[i].getInputStream();
Converter.makeParentPath(icoPath);
FileOutputStream fos = new FileOutputStream(icoPath);
int n=is.read(buf,0,buf.length);
while(n>0){
fos.write(buf,0,n);
n=is.read(buf,0,buf.length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
org.apache.felix.upnpbase.export.xml.Device[] devs = upnpDev.getDevices();
if(devs==null)
return;
for (int i = 0; i < devs.length; i++) {
makeIcons(devs[i],path);
}
}*/
/**
* This method is used to connect all the Action that are shown to UPnP world
* by CyberLink UPnP Device to the real implementation that is conatined iniside
* the OSGi service.
* This method will connect even all the subdevice of te given OSGi device.
*
* @param d CyberLink Device that will be used associated to the OSGi Device
* @param rootDevice ServiceReference to the OSGi Device that will be used as
* implementation of the CyberLink Device
* @return true if and only if the binding off all the action of all the children
* device is done succesfully
*/
private boolean bindInvokes(Device d, ServiceReference rootDevice) {
bindInvoke(d,rootDevice);
ServiceReference[] childs = null;
try {
childs = Activator.bc.getServiceReferences(
UPnPDevice.class.getName(),
"("+UPnPDevice.PARENT_UDN+"="+rootDevice.getProperty(UPnPDevice.UDN)+")"
);
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
String[] childsUDN = (String[]) rootDevice.getProperty(UPnPDevice.CHILDREN_UDN);
if((childs==null)&&(childsUDN==null)){
return true;
}else if((childs==null)||(childsUDN==null)){
return false;
}else if(childs.length==childsUDN.length){
DeviceList dl = d.getDeviceList();
for (int i = 0; i < childs.length; i++) {
Device dev = (Device)dl.elementAt(i);
if(!bindInvokes(dev,childs[i]))
return false;
}
return true;
}else{
return false;
}
}
/**
* This method add an UPnPEventListener Service to the OSGi Framework so that
* the Base Driver can notify all the event listener registered on the CyberLink
* UPnP device from the UPnP World.
*
* @param d Device of CyberLink that will be notified by the changing of the StateVariable
* that happen on the OSGi World
* @return ServiceRegistration of the new registered service.
*/
private ServiceRegistration bindSubscribe(Device d) {
ExporterUPnPEventListener eventer = new ExporterUPnPEventListener(d);
Properties p = new Properties();
StringBuffer sb = new StringBuffer("(|");
Vector v = new Vector();
v.add(d);
Device current;
while (v.size() != 0) {
current = (Device) v.elementAt(0);
v.remove(0);
DeviceList dl = current.getDeviceList();
for (int i = 0; i < dl.size(); i++) {
v.add(dl.elementAt(i));
}
sb.append("(").append(UPnPDevice.ID).append("=").append(
current.getUDN()).append(")");
}
sb.append(")");
Filter f = null;
try {
f = Activator.bc.createFilter(sb.toString());
} catch (InvalidSyntaxException e) {
e.printStackTrace();
return null;
}
if (f != null) p.put(UPnPEventListener.UPNP_FILTER, f);
return Activator.bc.registerService(UPnPEventListener.class.getName(), eventer, p);
}
/**
* This method do the real connection between OSGi UPnP Service action and
* CyberLink UPnP Device action
*
* @param upnpDev the CyberLink UPnP Device object that will be connected
* @param osgiDev the ServiceReference to OSGi UPnP Service that will be connected to
* CyberLink UPnP as implementation of the Action
* @return true if and only if the binding off all the action is done succesfully
*/
private boolean bindInvoke(Device upnpDev,ServiceReference osgiDev) {
ServiceList sl = upnpDev.getServiceList();
int l=sl.size();
for (int i = 0; i < l; i++) {
sl.getService(i).setActionListener(
new GeneralActionListener(
osgiDev,
sl.getService(i).getServiceID()
)
);
}
return true;
}
/**
*
*/
public synchronized void cleanUp() {
Activator.logger.INFO("Cleaning...");
Enumeration keys;
Activator.logger.INFO("Removing temporary listener....");
keys=exportedDevices.keys();
while (keys.hasMoreElements()) {
ServiceRegistration sr = ((ExportedDeviceInfo)
exportedDevices.get(keys.nextElement())).getServiceRegistration();
sr.unregister();
}
Activator.logger.INFO("Done");
Activator.logger.INFO("Removing device....");
keys=exportedDevices.keys();
while (keys.hasMoreElements()) {
Device dev = ((ExportedDeviceInfo)
exportedDevices.get(keys.nextElement())).getDevice();
dev.stop();
}
Activator.logger.INFO("Done");
}
private synchronized boolean shouldEnd() {
return end;
}
public synchronized void end() {
end = true;
queueRootDevice.addRootDevice(null);
}
/**
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent event) {
switch(event.getType()){
case ServiceEvent.REGISTERED:break;
case ServiceEvent.MODIFIED:
case ServiceEvent.UNREGISTERING:{
this.unexportDevice(event.getServiceReference());
if(exportedDevices.size()==0){
Activator.bc.removeServiceListener(this);
setListening(false);
}
}break;
}
}
/**
* @param b
*/
private synchronized void setListening(boolean b) {
listening=b;
}
private synchronized boolean getListening(){
return listening;
}
/**
* @param property
*/
private synchronized void unexportDevice(ServiceReference dev) {
String udn=(String) dev.getProperty(UPnPDevice.PARENT_UDN);
if(udn==null){
ExportedDeviceInfo edi =
(ExportedDeviceInfo) exportedDevices.get(
dev.getProperty(UPnPDevice.UDN)
);
Device d = edi.getDevice();
if(d!=null) {
Activator.logger.INFO("[Exporter] removing device:" +d.getFriendlyName());
d.stop();
exportedDevices.remove(d.getUDN());
}
ServiceRegistration srListener=edi.getServiceRegistration();
if(srListener!=null) srListener.unregister();
}else{
ServiceReference[] servs=null;
try {
servs = Activator.bc.getServiceReferences(
UPnPDevice.class.getName(),
"("+UPnPDevice.UDN+"="+udn+")"
);
} catch (InvalidSyntaxException ignored) {}
if(servs==null) return;
this.unexportDevice(servs[0]);
}
}
}