Fixed bug #FELIX-460 in BuildDevice
Removed unused code and avoiding overriding of UPnP configuration in ThreadExporter
Added caching and better control on constraint defined for imported Device in UPnPStateVariableImpl



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@610454 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/BuildDevice.java b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/BuildDevice.java
index 9fd4ca7..65e431a 100644
--- a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/BuildDevice.java
+++ b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/BuildDevice.java
@@ -171,7 +171,7 @@
 		UPnPDevice devOSGi = (UPnPDevice) Activator.bc.getService(sr);

 

 		if( devOSGi == null) {	//added by twa to prevent a null pointer exception

-			Activator.logger.WARNING("UPnP Device taht cotains serviceId="

+			Activator.logger.WARNING("UPnP Device that cotains serviceId="

 					+id+" is deregistered from the framework while is exported");

 			return;

 		}

@@ -194,6 +194,7 @@
 

 			UPnPAction[] actions = services[i].getActions();

 			for (int j = 0; j < actions.length; j++) {

+                boolean valid=true;

 				Action act = new Action(ser.getServiceNode());

 				act.setName(actions[j].getName());

 				ArgumentList al = new ArgumentList();

@@ -201,29 +202,51 @@
 				String[] names=actions[j].getInputArgumentNames();				

 				if(names!=null){

 					for (int k = 0; k < names.length; k++) {

-						Argument a = new Argument();

+                        UPnPStateVariable variable = actions[j].getStateVariable(names[k]);

+                        if(variable==null){

+                            /*

+                             * //TODO Create a stict and relaxed behavior of the base driver which 

+                             * export as much it can or export only 100% complaint UPnPDevice service 

+                             */

+                            Activator.logger.WARNING(

+                                "UPnP Device that cotains serviceId="+id+" contains the action "

+                                +actions[j].getName()+" with the Input argument "+names[k]

+                                +" not related to any UPnPStateVariable. Thus this action won't be exported");

+                            valid=false;

+                            break;

+                        }

+                        Argument a = new Argument();

 						a.setDirection(Argument.IN);

 						a.setName(names[k]);

-						a.setRelatedStateVariableName(

-								actions[j].getStateVariable(names[k]).getName()

-						);						

+						a.setRelatedStateVariableName(variable.getName());						

 						al.add(a);						

 					}

 				}

 				names=actions[j].getOutputArgumentNames();

-				if(names!=null){

+				if(names!=null && valid){

 					for (int k = 0; k < names.length; k++) {

+                        UPnPStateVariable variable = actions[j].getStateVariable(names[k]);

+                        if(variable==null){

+                            /*

+                             * //TODO Create a stict and relaxed behavior of the base driver which 

+                             * export as much it can or export only 100% complaint UPnPDevice service 

+                             */

+                            Activator.logger.WARNING(

+                                "UPnP Device that cotains serviceId="+id+" contains the action "

+                                +actions[j].getName()+" with the Output argument "+names[k]

+                                +" not related to any UPnPStateVariable. Thus this action won't be exported");                            

+                        }

 						Argument a = new Argument();

 						a.setDirection(Argument.OUT);

 						a.setName(names[k]);

-						a.setRelatedStateVariableName(

-								actions[j].getStateVariable(names[k]).getName()

-						);						

+						a.setRelatedStateVariableName(variable.getName());						

 						al.add(a);						

 					}

 				}

-				act.setArgumentList(al);

-				ser.addAction(act);

+                if(valid) {

+    				act.setArgumentList(al);

+    				ser.addAction(act);

+                }

 			}			

 			

 			UPnPStateVariable[] vars = services[i].getStateVariables();

diff --git a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/ThreadExporter.java b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/ThreadExporter.java
index 281abba..1c0c971 100644
--- a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/ThreadExporter.java
+++ b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/export/ThreadExporter.java
@@ -101,13 +101,10 @@
 	public ThreadExporter(RootDeviceExportingQueue queue) throws InvalidSyntaxException {
 	    end=false;
 	    queueRootDevice=queue;
-//		basePath="./tmp/device"; twa: redundant
-//		baseFile = Activator.bc.getDataFile("./tmp/device"); twa: redundant
-//		if(!baseFile.exists())	baseFile.mkdirs(); twa: redundant
 		this.exportedDevices=new Hashtable();
 		setListening(false);
-		UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);		
 	}
+    
 	public void run() {
 		
 		File osgiRoot = Activator.bc.getDataFile("");
@@ -138,75 +135,33 @@
 			if(!getListening()) 
 				setListen();
 			Activator.logger.INFO("[Exporter] Exporting device "+ rootDevice.getProperty(UPnPDevice.FRIENDLY_NAME));
-			/*
-			File xml = new File(baseFile, 
-					Converter.sanitizeFilename(
-						(String) rootDevice.getProperty(UPnPDevice.UDN)
-					)
-				);
-			if (xml == null)
-				continue;			
-			if (!xml.exists())
-				xml.mkdir();
-
-			FileOutputStream fos = null;
-			try {
-				fos = new FileOutputStream(xml.getAbsolutePath()
-						+ File.separator + "desc.xml");
-			} catch (FileNotFoundException e) {				
-				Activator.logger.log(LogService.LOG_ERROR,"Unable to write:" + xml.getAbsolutePath(), e);
-				continue;
-			}
-			if (fos == null)
-				continue;*/
-			/*
+			
+            /*
 			 * 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) {
-				//Root r = new Root(rootDevice, Activator.bc, null);
-				/*
-				 * Now that I have XML I'm going to exporting device
-				 * so I have to avoid rece condition with deregistration 
-				 * of the same device
-				 */
-				/*
-				try {
-					r.writeXML(fos);
-				} catch (IOException e) {
-					e.printStackTrace();
-					continue;
-				}
-				if(writeXMLService(xml.getAbsolutePath(),r.getRootDevice())){
-					Device d = null;
-					try {
-						d = new Device(xml.getAbsolutePath()
-								+ File.separator + "desc.xml");
-					} catch (InvalidDescriptionException e) {
-						e.printStackTrace();
-					}*/
-					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)
-						);
+				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)
+					);
+				}
 			}
 		}
 	}
@@ -274,7 +229,6 @@
 	 */
 	private boolean bindInvokes(Device d, ServiceReference rootDevice) {
 		bindInvoke(d,rootDevice);
-		//Activator.bc.ungetService(rootDevice);
 		ServiceReference[] childs = null;
 		try {
 			childs = Activator.bc.getServiceReferences(
@@ -290,20 +244,12 @@
 		}else if((childs==null)||(childsUDN==null)){
 			return false;
 		}else if(childs.length==childsUDN.length){
-            /*--- your code ---            
-            for (int i = 0; i < childs.length; i++) {
-                if(!bindInvokes(d,childs[i]))
-                    return false;
-            }           
-            ----- your code end ---*/
-            /*--- twa code ---*/
             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;
             }
-            /*----- twa code end ---*/
 
 			return true;
 		}else{
@@ -311,48 +257,7 @@
 		}
 			
 	}
-	/*
-	/**
-	 * @param path
-	 *
-	private boolean writeXMLService(String path,org.apache.felix.upnpbase.export.xml.Device d) {
-		Vector v = new Vector();
-		v.add(d);
-		while(v.size()!=0){
-			d=(org.apache.felix.upnpbase.export.xml.Device) v.elementAt(0);
-			v.remove(0);
-			Service[] servs = d.getServices();
-			if(servs==null)
-				continue;
-			for (int i = 0; i < servs.length; i++) {
-				FileOutputStream fos;
-				String xmlPath=path+servs[i].getScpdURL().replace('/',File.separatorChar);
-				if(!Converter.makeParentPath(xmlPath)) return false;
-				try {
-					fos = new FileOutputStream(xmlPath);
-				} catch (FileNotFoundException e) {
-					e.printStackTrace();
-					return false;
-				}
-				try {
-					servs[i].writeXML(fos);
-					fos.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-			
-			org.apache.felix.upnpbase.export.xml.Device[] subs = d.getDevices();
-			if(subs==null){
-				return true;
-			}else{
-				for(int i = 0; i < subs.length;i++){
-					v.add(subs[i]);
-				}
-			}
-		}
-		return true;
-	}*/
+    
 	/**
 	 * 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
diff --git a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/importer/core/upnp/UPnPStateVariableImpl.java b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/importer/core/upnp/UPnPStateVariableImpl.java
index e5b5f8d..f64b995 100644
--- a/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/importer/core/upnp/UPnPStateVariableImpl.java
+++ b/upnp/basedriver/src/main/java/org/apache/felix/upnp/basedriver/importer/core/upnp/UPnPStateVariableImpl.java
@@ -29,6 +29,7 @@
 

 import org.osgi.service.upnp.UPnPStateVariable;

 

+import org.apache.felix.upnp.basedriver.Activator;

 import org.apache.felix.upnp.basedriver.util.Converter;

 

 /* 

@@ -37,6 +38,16 @@
 public class UPnPStateVariableImpl implements UPnPStateVariable {

 

 	private StateVariable variable;

+    

+    private Number max = null;

+    private Number min = null;

+    private Number step = null;

+

+    private String[] values = null;    

+    

+    private Boolean hasMaxMinStep = null;

+    private Boolean hasRangeValues = null;    

+

 	private static Hashtable upnp2javaTable = null;

 	

 	static{

@@ -96,37 +107,30 @@
 	public UPnPStateVariableImpl(StateVariable variable) {

 

 		this.variable = variable;

-	} /*

-	   * (non-Javadoc)

-	   * 

-	   * @see org.osgi.service.upnp.UPnPStateVariable#getName()

-	   */

+	} 

 

+    /**

+     * @see org.osgi.service.upnp.UPnPStateVariable#getName()

+	 */

 	public String getName() {

 		return variable.getName();

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getJavaDataType()

 	 */

 	public Class getJavaDataType() {

 		return (Class) upnp2javaTable.get(variable.getDataType());

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getUPnPDataType()

 	 */

 	public String getUPnPDataType() {

 		return variable.getDataType();

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getDefaultValue()

 	 */

 	public Object getDefaultValue() {

@@ -134,100 +138,132 @@
 		return null;

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getAllowedValues()

 	 */

 	public String[] getAllowedValues() {

-		if (variable.getDataType().equals("string")) {

-			AllowedValueList allowedvalue = variable.getAllowedValueList();

-            if (allowedvalue == null) return null;

-			if(allowedvalue.size()==0){

-				return null;

-			} 

-			String[] values = new String[allowedvalue.size()];

-			for (int i = 0; i < allowedvalue.size(); i++) {

-				values[i] = allowedvalue.getAllowedValue(i).getValue();

-			}

-			return values;

-		}

-

-		return null;

+        if(hasRangeValues == null)

+            initValueConstraint();

+        

+        return values;

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getMinimum()

 	 */

 	public Number getMinimum() {

-		//TODO the same thing for getMaximum

-		AllowedValueRange allowedValueRange = variable.getAllowedValueRange();

-		if(allowedValueRange==null){

-			return null;

-		}

-		String min=allowedValueRange.getMinimum();

-        //francesco 22/10/2005

-        if (min.equals("")) return null;

-		try {

-			return (Number)Converter.parseString(min,getUPnPDataType());

-		} catch (Exception e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-			return null;

-		}

+        if(hasMaxMinStep == null)

+            initValueConstraint();

+        

+        return min;

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getMaximum()

 	 */

 	public Number getMaximum() {

-		//TODO I think that this method will be invoked from people that know what is doing

-		AllowedValueRange allowedValueRange = variable.getAllowedValueRange();

-		if(allowedValueRange==null){

-			return null;

-		}

-		String max = allowedValueRange.getMaximum();

-        //francesco 22/10/2005

-        if (max.equals("")) return null;

-		try {

-			return (Number)Converter.parseString(max,getUPnPDataType());

-		} catch (Exception e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-			return null;

-		}

+        if(hasMaxMinStep == null)

+            initValueConstraint();

+        

+        return max;

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+    /**

+     * <b>NOTE:</b>  This type of control caches the value recieved by the Device so if XML changes it doesn't affect the OSGi service

+     * 

+     * @since 0.3

+     */

+    private void initValueConstraint(){

+        if(hasRangeValues != null || hasMaxMinStep != null)

+            return;

+

+        hasRangeValues = Boolean.FALSE;

+        hasMaxMinStep = Boolean.FALSE;

+        

+        final AllowedValueRange allowedValueRange = variable.getAllowedValueRange();

+        final AllowedValueList allowedValueList = variable.getAllowedValueList();

+        

+        if(allowedValueRange != null && allowedValueList != null){

+            Activator.logger.WARNING("Imported device with StateVariable "

+                                     +variable.getName()+" contains either AllowedValueRange and AllowedValueList UPnP doesn't allow it because it. Neither of the restriction will be applied");

+            

+        }else if( allowedValueRange != null ){

+            

+            initMaxMinStep(allowedValueRange);

+            

+        }else if( allowedValueList != null ){

+            

+            initAllowedValues(allowedValueList);

+            

+        }

+    }

+    

+    /**

+     * @param allowedValueList

+     * @since 0.3

+     */

+    private void initAllowedValues(AllowedValueList allowedValueList){

+        //PRE:invoked only by initValueConstraint() thus allowedValueList must not null

+        if (String.class != getJavaDataType()) {

+            Activator.logger.WARNING("Imported device with StateVariable "

+                                     +variable.getName()+" contains AllowedValueList but its UPnP type doesn't allow it because it is +"+getUPnPDataType());            

+            return;

+        }

+

+        if(allowedValueList.size() == 0){

+            return ;

+        } 

+

+        values = new String[allowedValueList.size()];

+        for (int i = 0; i < allowedValueList.size(); i++) {

+            values[i] = allowedValueList.getAllowedValue(i).getValue();

+        }

+    }

+

+    /**

+     * @param allowedValueRange

+     * @since 0.3

+     */

+    private void initMaxMinStep(AllowedValueRange allowedValueRange){

+        //PRE:invoked only by initValueConstraint() thus allowedValueRange must not  be null

+        if(allowedValueRange==null){

+            return;

+        }

+

+        if(!Number.class.isAssignableFrom(getJavaDataType())){

+            Activator.logger.WARNING("Imported device with StateVariable "

+                                     +variable.getName()+" contains AllowedValueRange but its UPnP type doesn't allow it because it is +"+getUPnPDataType());            

+            return;

+        }

+        

+        final String maxStr = allowedValueRange.getMaximum();

+        final String minStr = allowedValueRange.getMinimum();

+        final String stepStr = allowedValueRange.getStep();

+        

+        try{

+            final String type = getUPnPDataType();

+            max = (Number)Converter.parseString(maxStr,type);

+            min = (Number)Converter.parseString(minStr,type);

+            step = (Number)Converter.parseString(stepStr,type);

+        }catch(Exception ex){

+            Activator.logger.WARNING("Imported device with StateVariable "

+                +variable.getName()+" contains an invalid definition for AllowedValueRange");

+        }

+        hasMaxMinStep = Boolean.TRUE;

+    }

+    

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#getStep()

 	 */

 	public Number getStep() {

-		//TODO same things of getMaxium

-		AllowedValueRange allowedValueRange = variable.getAllowedValueRange();

-		if(allowedValueRange==null){

-			return null;

-		}

-		String step = allowedValueRange.getStep();

-        //francesco 22/10/2005

-        if (step.equals("")) return null;

-		try {

-			return (Number)Converter.parseString(step,getUPnPDataType());

-		} catch (Exception e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-			return null;

-		}

+        if(hasMaxMinStep == null)

+            initValueConstraint();

+        

+        return step;        

+

 	}

 

-	/*

-	 * (non-Javadoc)

-	 * 

+	/**

 	 * @see org.osgi.service.upnp.UPnPStateVariable#sendsEvents()

 	 */

 	public boolean sendsEvents() {