blob: a0a1141056cd126de5489d887d90fd2251be7520 [file] [log] [blame]
Francesco Furfarid8bdb642006-04-04 23:33:40 +00001/*
2 * Copyright 2006 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.felix.upnp.basedriver.export;
19
20
21import java.io.File;
22import java.util.Enumeration;
23import java.util.Hashtable;
24import java.util.Properties;
25import java.util.Vector;
26
27import org.cybergarage.upnp.Device;
28import org.cybergarage.upnp.DeviceList;
29import org.cybergarage.upnp.ServiceList;
30import org.cybergarage.upnp.UPnP;
31
32import org.osgi.framework.BundleException;
33import org.osgi.framework.Constants;
34import org.osgi.framework.Filter;
35import org.osgi.framework.InvalidSyntaxException;
36import org.osgi.framework.ServiceEvent;
37import org.osgi.framework.ServiceListener;
38import org.osgi.framework.ServiceReference;
39import org.osgi.framework.ServiceRegistration;
40import org.osgi.service.upnp.UPnPDevice;
41import org.osgi.service.upnp.UPnPEventListener;
42
43import org.apache.felix.upnp.basedriver.Activator;
44
45/**
46 * @author Stefano "Kismet" Lenzi
47 *
48 */
49public class ThreadExporter implements Runnable,ServiceListener {
50
51 private boolean end;
52
53 private RootDeviceExportingQueue queueRootDevice;
54
55// private String basePath; twa: redundant
56
57// private File baseFile; twa: redundant
58
59 private Hashtable exportedDevices;
60
61 private boolean listening;
62
63 private class ExportedDeviceInfo{
64 private Device device;
65 private ServiceRegistration serviceRegistration;
66 //private DeviceNode deviceNode;
67
68
69 /**
70 * @param device
71 * @param serviceRegistration
72 * @param deviceNode
73 */
74 private ExportedDeviceInfo(Device device,
75 ServiceRegistration serviceRegistration,
76 DeviceNode deviceNode) {
77 super();
78 this.device = device;
79 this.serviceRegistration = serviceRegistration;
80 //this.deviceNode = deviceNode;
81 }
82
83
84 private Device getDevice() {
85 return this.device;
86 }
87 private ServiceRegistration getServiceRegistration() {
88 return this.serviceRegistration;
89 }
90 /*private DeviceNode getDeviceNode(){
91 return this.deviceNode;
92 }*/
93
94
95 }
96
97 /**
98 *
99 */
100 public ThreadExporter(RootDeviceExportingQueue queue) throws InvalidSyntaxException {
101 end=false;
102 queueRootDevice=queue;
103// basePath="./tmp/device"; twa: redundant
104// baseFile = Activator.bc.getDataFile("./tmp/device"); twa: redundant
105// if(!baseFile.exists()) baseFile.mkdirs(); twa: redundant
106 this.exportedDevices=new Hashtable();
107 setListening(false);
108 UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
109 }
110 public void run() {
111
112 File osgiRoot = Activator.bc.getDataFile("");
113 if (osgiRoot == null) {
114 Activator.logger.ERROR("Unable to use filesystem");
115 while (true) {
116 try {
117 Activator.bc.getBundle().stop();
118 break;
119 } catch (BundleException e) {
120 e.printStackTrace();
121 try {
122 Thread.sleep(1000);
123 } catch (InterruptedException ex) {
124 ex.printStackTrace();
125 }
126 }
127 }
128 return;
129 }
130
131 ServiceReference rootDevice = null;
132 while (!shouldEnd()) {
133 DeviceNode dn = queueRootDevice.getRootDevice();
134 if (dn == null)
135 continue;
136 rootDevice = dn.getReference();
137 if(!getListening())
138 setListen();
139 Activator.logger.INFO("[Exporter] Exporting device "+ rootDevice.getProperty(UPnPDevice.FRIENDLY_NAME));
140 /*
141 File xml = new File(baseFile,
142 Converter.sanitizeFilename(
143 (String) rootDevice.getProperty(UPnPDevice.UDN)
144 )
145 );
146 if (xml == null)
147 continue;
148 if (!xml.exists())
149 xml.mkdir();
150
151 FileOutputStream fos = null;
152 try {
153 fos = new FileOutputStream(xml.getAbsolutePath()
154 + File.separator + "desc.xml");
155 } catch (FileNotFoundException e) {
156 Activator.logger.log(LogService.LOG_ERROR,"Unable to write:" + xml.getAbsolutePath(), e);
157 continue;
158 }
159 if (fos == null)
160 continue;*/
161 /*
162 * I don't know if the exporting should be make default language of the framework
163 * or without any lanuguages
164 Root r = new Root(rootDevice, context, context
165 .getProperty(Constants.FRAMEWORK_LANGUAGE));
166 */
167
168 synchronized (this) {
169 //Root r = new Root(rootDevice, Activator.bc, null);
170 /*
171 * Now that I have XML I'm going to exporting device
172 * so I have to avoid rece condition with deregistration
173 * of the same device
174 */
175 /*
176 try {
177 r.writeXML(fos);
178 } catch (IOException e) {
179 e.printStackTrace();
180 continue;
181 }
182 if(writeXMLService(xml.getAbsolutePath(),r.getRootDevice())){
183 Device d = null;
184 try {
185 d = new Device(xml.getAbsolutePath()
186 + File.separator + "desc.xml");
187 } catch (InvalidDescriptionException e) {
188 e.printStackTrace();
189 }*/
190 Device d = BuildDevice.createCyberLinkDevice(dn.getReference());
191 if (d != null) {
192 if(!bindInvokes(d,rootDevice)){
193 Activator.logger.DEBUG("Unable to find all the sub device or to set action listener");
194 continue;
195 }
196 ServiceRegistration listenReg = bindSubscribe(d);
197 if(listenReg==null){
198 Activator.logger.DEBUG("Unable to set action listener event listener");
199 continue;
200 }
201 //makeIcons(r.getRootDevice(),xml.getAbsolutePath());
202 d.start();
203 exportedDevices.put(
204 rootDevice.getProperty(UPnPDevice.UDN),
205 new ExportedDeviceInfo(d,listenReg,dn)
206 );
207 }
208 //}
209 }
210 }
211 }
212
213 /**
214 *
215 */
216 private void setListen() {
217 {
218 try {
219 Activator.bc.addServiceListener(
220 this,
221 // fixed by Matteo and Francesco 21/9/04
222 "(&("+Constants.OBJECTCLASS+"="+UPnPDevice.class.getName()+")"
223 + "(" + UPnPDevice.UPNP_EXPORT +"=*))"
224 );
225 } catch (InvalidSyntaxException ingnore) {}
226 }
227 }
228 /*
229 /**
230 * @param upnpDev
231 * @param refDev
232 *
233 private void makeIcons(
234 org.apache.felix.upnpbase.export.xml.Device upnpDev,
235 String path) {
236 Icon[] icons = upnpDev.getIcons();
237 if(icons!=null){
238 byte[] buf = new byte[512];
239 for (int i = 0; i < icons.length; i++) {
240 try {
241 String icoPath = path+icons[i].getUrl().replace('/',File.separatorChar);
242 InputStream is = icons[i].getInputStream();
243 Converter.makeParentPath(icoPath);
244 FileOutputStream fos = new FileOutputStream(icoPath);
245 int n=is.read(buf,0,buf.length);
246 while(n>0){
247 fos.write(buf,0,n);
248 n=is.read(buf,0,buf.length);
249 }
250 } catch (IOException e) {
251 e.printStackTrace();
252 }
253 }
254 }
255 org.apache.felix.upnpbase.export.xml.Device[] devs = upnpDev.getDevices();
256 if(devs==null)
257 return;
258 for (int i = 0; i < devs.length; i++) {
259 makeIcons(devs[i],path);
260 }
261 }*/
262 /**
263 * This method is used to connect all the Action that are shown to UPnP world
264 * by CyberLink UPnP Device to the real implementation that is conatined iniside
265 * the OSGi service.
266 * This method will connect even all the subdevice of te given OSGi device.
267 *
268 * @param d CyberLink Device that will be used associated to the OSGi Device
269 * @param rootDevice ServiceReference to the OSGi Device that will be used as
270 * implementation of the CyberLink Device
271 * @return true if and only if the binding off all the action of all the children
272 * device is done succesfully
273 */
274 private boolean bindInvokes(Device d, ServiceReference rootDevice) {
275 bindInvoke(d,rootDevice);
276 //Activator.bc.ungetService(rootDevice);
277 ServiceReference[] childs = null;
278 try {
279 childs = Activator.bc.getServiceReferences(
280 UPnPDevice.class.getName(),
281 "("+UPnPDevice.PARENT_UDN+"="+rootDevice.getProperty(UPnPDevice.UDN)+")"
282 );
283 } catch (InvalidSyntaxException e) {
284 e.printStackTrace();
285 }
286 String[] childsUDN = (String[]) rootDevice.getProperty(UPnPDevice.CHILDREN_UDN);
287 if((childs==null)&&(childsUDN==null)){
288 return true;
289 }else if((childs==null)||(childsUDN==null)){
290 return false;
291 }else if(childs.length==childsUDN.length){
292 /*--- your code ---
293 for (int i = 0; i < childs.length; i++) {
294 if(!bindInvokes(d,childs[i]))
295 return false;
296 }
297 ----- your code end ---*/
298 /*--- twa code ---*/
299 DeviceList dl = d.getDeviceList();
300 for (int i = 0; i < childs.length; i++) {
301 Device dev = (Device)dl.elementAt(i);
302 if(!bindInvokes(dev,childs[i]))
303 return false;
304 }
305 /*----- twa code end ---*/
306
307 return true;
308 }else{
309 return false;
310 }
311
312 }
313 /*
314 /**
315 * @param path
316 *
317 private boolean writeXMLService(String path,org.apache.felix.upnpbase.export.xml.Device d) {
318 Vector v = new Vector();
319 v.add(d);
320 while(v.size()!=0){
321 d=(org.apache.felix.upnpbase.export.xml.Device) v.elementAt(0);
322 v.remove(0);
323 Service[] servs = d.getServices();
324 if(servs==null)
325 continue;
326 for (int i = 0; i < servs.length; i++) {
327 FileOutputStream fos;
328 String xmlPath=path+servs[i].getScpdURL().replace('/',File.separatorChar);
329 if(!Converter.makeParentPath(xmlPath)) return false;
330 try {
331 fos = new FileOutputStream(xmlPath);
332 } catch (FileNotFoundException e) {
333 e.printStackTrace();
334 return false;
335 }
336 try {
337 servs[i].writeXML(fos);
338 fos.close();
339 } catch (IOException e) {
340 e.printStackTrace();
341 }
342 }
343
344 org.apache.felix.upnpbase.export.xml.Device[] subs = d.getDevices();
345 if(subs==null){
346 return true;
347 }else{
348 for(int i = 0; i < subs.length;i++){
349 v.add(subs[i]);
350 }
351 }
352 }
353 return true;
354 }*/
355 /**
356 * This method add an UPnPEventListener Service to the OSGi Framework so that
357 * the Base Driver can notify all the event listener registered on the CyberLink
358 * UPnP device from the UPnP World.
359 *
360 * @param d Device of CyberLink that will be notified by the changing of the StateVariable
361 * that happen on the OSGi World
362 * @return ServiceRegistration of the new registered service.
363 */
364 private ServiceRegistration bindSubscribe(Device d) {
365 ExporterUPnPEventListener eventer = new ExporterUPnPEventListener(d);
366 Properties p = new Properties();
367
368 StringBuffer sb = new StringBuffer("(|");
369 Vector v = new Vector();
370 v.add(d);
371 Device current;
372 while (v.size() != 0) {
373 current = (Device) v.elementAt(0);
374 v.remove(0);
375 DeviceList dl = current.getDeviceList();
376 for (int i = 0; i < dl.size(); i++) {
377 v.add(dl.elementAt(i));
378 }
379 sb.append("(").append(UPnPDevice.ID).append("=").append(
380 current.getUDN()).append(")");
381 }
382 sb.append(")");
383 Filter f = null;
384 try {
385 f = Activator.bc.createFilter(sb.toString());
386 } catch (InvalidSyntaxException e) {
387 e.printStackTrace();
388 return null;
389 }
390 if (f != null) p.put(UPnPEventListener.UPNP_FILTER, f);
391
392 return Activator.bc.registerService(UPnPEventListener.class.getName(), eventer, p);
393 }
394
395 /**
396 * This method do the real connection between OSGi UPnP Service action and
397 * CyberLink UPnP Device action
398 *
399 * @param upnpDev the CyberLink UPnP Device object that will be connected
400 * @param osgiDev the ServiceReference to OSGi UPnP Service that will be connected to
401 * CyberLink UPnP as implementation of the Action
402 * @return true if and only if the binding off all the action is done succesfully
403 */
404 private boolean bindInvoke(Device upnpDev,ServiceReference osgiDev) {
405 ServiceList sl = upnpDev.getServiceList();
406 int l=sl.size();
407 for (int i = 0; i < l; i++) {
408 sl.getService(i).setActionListener(
409 new GeneralActionListener(
410 osgiDev,
411 sl.getService(i).getServiceID()
412 )
413 );
414 }
415 return true;
416 }
417
418 /**
419 *
420 */
421 public synchronized void cleanUp() {
422 Activator.logger.INFO("Cleaning...");
423
424 Enumeration keys;
425
426 Activator.logger.INFO("Removing temporary listener....");
427 keys=exportedDevices.keys();
428 while (keys.hasMoreElements()) {
429 ServiceRegistration sr = ((ExportedDeviceInfo)
430 exportedDevices.get(keys.nextElement())).getServiceRegistration();
431 sr.unregister();
432 }
433 Activator.logger.INFO("Done");
434
435 Activator.logger.INFO("Removing device....");
436 keys=exportedDevices.keys();
437 while (keys.hasMoreElements()) {
438 Device dev = ((ExportedDeviceInfo)
439 exportedDevices.get(keys.nextElement())).getDevice();
440 dev.stop();
441 }
442 Activator.logger.INFO("Done");
443 }
444
445 private synchronized boolean shouldEnd() {
446 return end;
447 }
448
449 public synchronized void end() {
450 end = true;
451 queueRootDevice.addRootDevice(null);
452 }
453 /**
454 * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
455 */
456 public void serviceChanged(ServiceEvent event) {
457 switch(event.getType()){
458 case ServiceEvent.REGISTERED:break;
459
460 case ServiceEvent.MODIFIED:
461 case ServiceEvent.UNREGISTERING:{
462 this.unexportDevice(event.getServiceReference());
463 if(exportedDevices.size()==0){
464 Activator.bc.removeServiceListener(this);
465 setListening(false);
466 }
467 }break;
468 }
469 }
470 /**
471 * @param b
472 */
473 private synchronized void setListening(boolean b) {
474 listening=b;
475 }
476
477 private synchronized boolean getListening(){
478 return listening;
479 }
480
481 /**
482 * @param property
483 */
484 private synchronized void unexportDevice(ServiceReference dev) {
485 String udn=(String) dev.getProperty(UPnPDevice.PARENT_UDN);
486 if(udn==null){
487 ExportedDeviceInfo edi =
488 (ExportedDeviceInfo) exportedDevices.get(
489 dev.getProperty(UPnPDevice.UDN)
490 );
491 Device d = edi.getDevice();
492 if(d!=null) {
493 Activator.logger.INFO("[Exporter] removing device:" +d.getFriendlyName());
494 d.stop();
495 exportedDevices.remove(d.getUDN());
496 }
497 ServiceRegistration srListener=edi.getServiceRegistration();
498 if(srListener!=null) srListener.unregister();
499
500 }else{
501 ServiceReference[] servs=null;
502 try {
503 servs = Activator.bc.getServiceReferences(
504 UPnPDevice.class.getName(),
505 "("+UPnPDevice.UDN+"="+udn+")"
506 );
507 } catch (InvalidSyntaxException ignored) {}
508 if(servs==null) return;
509 this.unexportDevice(servs[0]);
510 }
511 }
512}