Committed the initial version of the UPnP TV bundle (FELIX-52).
It requires org.osgi.core, org.osgi.compendium 
A first sample to test the BaseDriver on the OSGi side.
You can use the Intel UpnP Toolkit to listen real devices on the UPnP network.
http://www.intel.com/cd/ids/developer/asmo-na/eng/downloads/upnp/tools/index.htm


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@391458 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/Activator.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/Activator.java
new file mode 100644
index 0000000..1184908
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/Activator.java
@@ -0,0 +1,66 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+
+package org.apache.felix.upnp.sample.tv;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.upnp.UPnPDevice;
+
+/**
+ * @author Francesco Furfari
+ */
+
+public class Activator implements BundleActivator {
+
+	static BundleContext context;
+
+	private ServiceRegistration serviceRegistration;
+
+	private TvFrame tvFrame;
+
+	private void doServiceRegistration() {
+		tvFrame = new TvFrame();
+		TvDevice tvDev = tvFrame.getTvDevice();
+		Dictionary dict = tvDev.getDescriptions(null);
+		serviceRegistration = context.registerService(UPnPDevice.class
+				.getName(), tvDev, dict);
+
+		tvFrame.start();
+		
+
+	}
+
+	/**
+	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+	 */
+	public void start(BundleContext context) throws Exception {
+		Activator.context = context;
+		doServiceRegistration();
+	}
+
+	/**
+	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+	 */
+	public void stop(BundleContext context) throws Exception {
+		serviceRegistration.unregister();
+		tvFrame.stop();
+	}
+}
\ No newline at end of file
diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/EventSource.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/EventSource.java
new file mode 100644
index 0000000..3a21d23
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/EventSource.java
@@ -0,0 +1,31 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.beans.PropertyChangeListener;

+/**

+ * @author Francesco Furfari

+ */

+

+public interface EventSource {

+	void addPropertyChangeListener(PropertyChangeListener listener);

+	void addPropertyChangeListener(String propertyName, PropertyChangeListener listener);

+	void removePropertyChangeListener(PropertyChangeListener listener);

+	void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);

+}

+

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/GetPowerAction.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/GetPowerAction.java
new file mode 100644
index 0000000..61f9ec0
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/GetPowerAction.java
@@ -0,0 +1,86 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+

+import org.osgi.service.upnp.UPnPAction;

+import org.osgi.service.upnp.UPnPStateVariable;

+/**

+ * @author Francesco Furfari

+ */

+

+public class GetPowerAction implements UPnPAction {

+

+	final private String NAME = "GetPower";

+	final private String RESULT_STATUS = "Power";

+	final private String[] OUT_ARG_NAMES = new String[]{RESULT_STATUS};

+	private PowerStateVariable power;

+	

+	

+	public GetPowerAction(PowerStateVariable power){

+		this.power = power;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getName() {

+		return NAME;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getReturnArgumentName()

+	 */

+	public String getReturnArgumentName() {

+		return null;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getInputArgumentNames()

+	 */

+	public String[] getInputArgumentNames() {

+		

+		return null;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getOutputArgumentNames()

+	 */

+	public String[] getOutputArgumentNames() {

+		return OUT_ARG_NAMES;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getStateVariable(java.lang.String)

+	 */

+	public UPnPStateVariable getStateVariable(String argumentName) {

+		return power;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#invoke(java.util.Dictionary)

+	 */

+	public Dictionary invoke(Dictionary args) throws Exception {

+		Boolean value = power.getCurrentPower();

+		Hashtable result = new Hashtable();

+		result.put(RESULT_STATUS,value);

+		return result;

+	}

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerService.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerService.java
new file mode 100644
index 0000000..ce37959
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerService.java
@@ -0,0 +1,104 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.util.HashMap;

+

+import org.osgi.service.upnp.UPnPAction;

+import org.osgi.service.upnp.UPnPService;

+import org.osgi.service.upnp.UPnPStateVariable;

+/**

+ * @author Francesco Furfari

+ */

+

+public class PowerService implements UPnPService {

+	final private String SERVICE_ID = "urn:schemas-upnp-org:serviceId:power:1";

+	final private String SERVICE_TYPE = "urn:schemas-upnp-org:service:power:1";

+	final private String VERSION ="1";

+

+	private PowerStateVariable power;

+	private ResultStateVariable result;

+	private UPnPStateVariable[] states;

+	private HashMap actions = new HashMap();

+	

+	

+	public PowerService(){

+		power = new PowerStateVariable();

+		result = new ResultStateVariable();

+		this.states = new UPnPStateVariable[]{power,result};

+		

+		UPnPAction setPower= new SetPowerAction(power,result);

+		UPnPAction getPower = new GetPowerAction(power);

+		actions.put(setPower.getName(),setPower);

+		actions.put(getPower.getName(),getPower);

+		

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getId()

+	 */

+	public String getId() {

+		return SERVICE_ID;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getType()

+	 */

+	public String getType() {

+		return SERVICE_TYPE;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getVersion()

+	 */

+	public String getVersion() {

+		return VERSION;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getAction(java.lang.String)

+	 */

+	public UPnPAction getAction(String name) {

+		return (UPnPAction)actions.get(name);

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getActions()

+	 */

+	public UPnPAction[] getActions() {

+		return (UPnPAction[])(actions.values()).toArray(new UPnPAction[]{});

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getStateVariables()

+	 */

+	public UPnPStateVariable[] getStateVariables() {

+		return states;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPService#getStateVariable(java.lang.String)

+	 */

+	public UPnPStateVariable getStateVariable(String name) {

+		if (name.equals("Power"))

+			return power;

+		else if (name.equals("Result"))

+			return result;

+		else return null;

+	}

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerStateVariable.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerStateVariable.java
new file mode 100644
index 0000000..c84566f
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/PowerStateVariable.java
@@ -0,0 +1,120 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+import java.beans.PropertyChangeEvent;

+/**

+ * @author Francesco Furfari

+ */

+

+public class PowerStateVariable implements UPnPStateVariableDescriptor {

+	

+	final private String NAME = "Power";

+	final private Boolean DEFAULT_VALUE = Boolean.FALSE;

+	private UPnPEventNotifier notifier;

+	private Boolean power = Boolean.FALSE;

+	

+	public PowerStateVariable(){

+	}

+	

+	/* (non-Javadoc)

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

+	 */

+	public String getName() {

+		return NAME;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Class getJavaDataType() {

+		return Boolean.class;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getUPnPDataType() {

+		return TYPE_BOOLEAN;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Object getDefaultValue() {

+		return DEFAULT_VALUE;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String[] getAllowedValues() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getMinimum() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getMaximum() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getStep() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public boolean sendsEvents() {

+		return true;

+	}

+	

+	public Boolean getCurrentPower(){

+		return power;

+	}

+	

+	public void setPower(Boolean value){

+		if (!value.equals(power)) {

+			Boolean oldValue = power;

+			power = value;

+			if (notifier != null)

+			notifier.propertyChange(new PropertyChangeEvent(this,"Power",oldValue,value));

+		}

+	}

+

+	public void setNotifier(UPnPEventNotifier notifier){

+		this.notifier = notifier;

+	}

+

+    /* (non-Javadoc)

+     * @see it.cnr.isti.niche.osgi.sample.tv.UPnPStateVariableDescriptor#getValue()

+     */

+    public Object getValue() {

+         return power;

+    }

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/ResultStateVariable.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/ResultStateVariable.java
new file mode 100644
index 0000000..430fbf3
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/ResultStateVariable.java
@@ -0,0 +1,96 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+import org.osgi.service.upnp.UPnPStateVariable;

+/**

+ * @author Francesco Furfari

+ */

+

+public class ResultStateVariable implements UPnPStateVariable{

+	

+	final private String NAME = "Result";

+	final private Boolean DEFAULT_VALUE = Boolean.FALSE;

+	

+	

+	public ResultStateVariable(){

+	}

+	

+	/* (non-Javadoc)

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

+	 */

+	public String getName() {

+		return NAME;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Class getJavaDataType() {

+		return Boolean.class;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getUPnPDataType() {

+		return TYPE_BOOLEAN;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Object getDefaultValue() {

+		return DEFAULT_VALUE;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String[] getAllowedValues() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getMinimum() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getMaximum() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Number getStep() {

+		return null;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public boolean sendsEvents() {

+		return false;

+	}

+	

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/SetPowerAction.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/SetPowerAction.java
new file mode 100644
index 0000000..944516f
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/SetPowerAction.java
@@ -0,0 +1,92 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+

+import org.osgi.service.upnp.UPnPAction;

+import org.osgi.service.upnp.UPnPStateVariable;

+/**

+ * @author Francesco Furfari

+ */

+

+public class SetPowerAction implements UPnPAction {

+

+	final private String NAME = "SetPower";

+	final private String NEW_TIME_VALUE = "Power";

+	final private String NEW_RESULT_VALUE = "Result";

+	final private String[] IN_ARG_NAMES = new String[]{NEW_TIME_VALUE};

+	final private String[] OUT_ARG_NAMES = new String[]{NEW_RESULT_VALUE};

+	private PowerStateVariable power;

+	private ResultStateVariable result;

+	

+	

+	public SetPowerAction(PowerStateVariable power,ResultStateVariable result){

+		this.power = power;

+		this.result=result;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getName() {

+		return NAME;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getReturnArgumentName()

+	 */

+	public String getReturnArgumentName() {

+		return "Result";

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getInputArgumentNames()

+	 */

+	public String[] getInputArgumentNames() {

+		return IN_ARG_NAMES;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getOutputArgumentNames()

+	 */

+	public String[] getOutputArgumentNames() {

+		return OUT_ARG_NAMES;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#getStateVariable(java.lang.String)

+	 */

+	public UPnPStateVariable getStateVariable(String argumentName) {

+		if (argumentName.equals("Power")) return power;

+		else if (argumentName.equals("Result")) return result;

+		else return null;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPAction#invoke(java.util.Dictionary)

+	 */

+	public Dictionary invoke(Dictionary args) throws Exception {

+		Boolean value = (Boolean) args.get(NEW_TIME_VALUE);

+		power.setPower(value);

+		Hashtable result = new Hashtable();

+		result.put(NEW_RESULT_VALUE,Boolean.TRUE);

+		return result;

+	}

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvDevice.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvDevice.java
new file mode 100644
index 0000000..c8c20ec
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvDevice.java
@@ -0,0 +1,313 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.awt.Component;

+import java.util.ArrayList;

+import java.util.Dictionary;

+import java.util.Properties;

+

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.Constants;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceEvent;

+import org.osgi.framework.ServiceListener;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.upnp.UPnPDevice;

+import org.osgi.service.upnp.UPnPEventListener;

+import org.osgi.service.upnp.UPnPIcon;

+import org.osgi.service.upnp.UPnPService;

+/**

+ * @author Francesco Furfari

+ */

+

+public class TvDevice implements UPnPDevice,UPnPEventListener,ServiceListener  {

+	

+	final private String DEVICE_ID = "uuid:DomoWare-TV";

+	private final static String CLOCK_DEVICE_TYPE = "urn:schemas-upnp-org:device:clock:1";

+	private final static String CLOCK_SERVICE_TYPE = "urn:schemas-upnp-org:service:timer:1";

+	

+	private final static String LIGHT_DEVICE_TYPE = "urn:schemas-upnp-org:device:light:1";

+	private final static String LIGHT_SERVICE_TYPE = "urn:schemas-upnp-org:service:power:1";

+

+	private final static String AIRCON_DEVICE_TYPE = "urn:schemas-upnp-org:device:aircon:1";

+	private final static String AIRCON_SERVICE_TYPE = "urn:schemas-upnp-org:service:temp:1";

+	

+	private final static String WASHER_DEVICE_TYPE = "urn:schemas-upnp-org:device:washer:1";

+	private final static String WASHER_SERVICE_TYPE = "urn:schemas-upnp-org:service:state:1";

+

+	private final String devicesFilter = 

+		"(&"+

+			"("+Constants.OBJECTCLASS+"="+UPnPDevice.class.getName()+"))";

+			/*"(|("+UPnPDevice.TYPE+"="+ CLOCK_SERVICE_TYPE+")"+

+				"("+UPnPDevice.TYPE+"="+ LIGHT_SERVICE_TYPE+")"+

+				"("+UPnPDevice.TYPE+"="+ AIRCON_SERVICE_TYPE+")"+

+				"("+UPnPDevice.TYPE+"="+ WASHER_SERVICE_TYPE+")))";*/

+

+	private BundleContext context;

+	private PowerService powerService;

+	private UPnPService[] services;

+	private Dictionary dictionary;

+	private UPnPEventNotifier notifier;

+	private PowerStateVariable powerState;

+

+	public TvDevice() {

+		powerService = new PowerService();

+		services = new UPnPService[]{powerService};

+		powerState = (PowerStateVariable) powerService.getStateVariable("Power");

+		setupDeviceProperties();

+		buildEventNotifyer();

+		try {

+			Activator.context.addServiceListener(this,devicesFilter);

+		} catch (InvalidSyntaxException e) {

+			System.out.println(e);		

+		}

+	}

+

+	/**

+	 * 

+	 */

+	private void buildEventNotifyer() {

+		notifier = new UPnPEventNotifier(Activator.context,this,powerService,null);

+		powerState.setNotifier(notifier);

+	}

+

+	private void setupDeviceProperties(){

+		dictionary = new Properties();

+		dictionary.put(UPnPDevice.UPNP_EXPORT,"");

+		//org.osgi.service.device.Constants.DEVICE_CATEGORY

+		//dictionary.put("DEVICE_CATEGORY","UPnP");

+		dictionary.put(

+	        org.osgi.service

+	        	.device.Constants.DEVICE_CATEGORY,

+        	new String[]{UPnPDevice.DEVICE_CATEGORY}

+        );

+		dictionary.put(UPnPDevice.FRIENDLY_NAME,"Domoware Sample Tv");

+		dictionary.put(UPnPDevice.MANUFACTURER,"ISTI-CNR");

+		dictionary.put(UPnPDevice.MANUFACTURER_URL,"http://domoware.isti.cnr.it");

+		dictionary.put(UPnPDevice.MODEL_DESCRIPTION,"A CyberLink Tv device clone to test OSGi to UPnP service import");

+		dictionary.put(UPnPDevice.MODEL_NAME,"BimbiTv");

+		dictionary.put(UPnPDevice.MODEL_NUMBER,"1.0");

+		dictionary.put(UPnPDevice.MODEL_URL,"http://domoware.isti.cnr.it/BimbiTv");

+		dictionary.put(UPnPDevice.PRESENTATION_URL,"http://domoware.isti.cnr.it/BimbiTv/presentation");

+		dictionary.put(UPnPDevice.SERIAL_NUMBER,"123456789");

+		dictionary.put(UPnPDevice.TYPE,"urn:schemas-upnp-org:device:tv:1");

+		dictionary.put(UPnPDevice.UDN,DEVICE_ID);

+		//dictionary.put(UPnPDevice.ID,dictionary.get(UPnPDevice.UDN));

+		dictionary.put(UPnPDevice.UPC,"1213456789");

+	}

+	

+	

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPDevice#getService(java.lang.String)

+	 */

+	public UPnPService getService(String serviceId) {

+		if  (serviceId.equals(powerService.getId())) return powerService;

+		return null;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPDevice#getServices()

+	 */

+	public UPnPService[] getServices() {

+		return services;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPDevice#getIcons(java.lang.String)

+	 */

+	public UPnPIcon[] getIcons(String locale) {

+		UPnPIcon icon = new TvIcon();

+		return new UPnPIcon[]{icon} ;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPDevice#getDescriptions(java.lang.String)

+	 */

+	public Dictionary getDescriptions(String locale) {

+		return dictionary;

+	}

+

+

+

+	////////////////////////////////////////////////

+	//	Component

+	////////////////////////////////////////////////

+

+	private Component comp;

+	

+	public void setComponent(Component comp)

+	{

+		this.comp = comp;	

+	}

+	

+	public Component getComponent()

+	{

+		return comp;

+	}

+	

+	////////////////////////////////////////////////

+	//	on/off

+	////////////////////////////////////////////////

+

+	private boolean onFlag = false;

+	

+	public void on()

+	{

+		powerState.setPower(Boolean.TRUE);

+		doSubscribe();

+	}

+

+	public boolean isOn()

+	{ 

+		return powerState.getCurrentPower().booleanValue();

+	}

+	public void off()

+	{

+		powerState.setPower(Boolean.FALSE);

+		undoSubscribe();

+	}

+

+

+	////////////////////////////////////////////////

+	//	Clock

+	////////////////////////////////////////////////

+

+	private String clockTime = ""; 

+	

+	public String getClockTime()

+	{

+		return clockTime;	

+	}

+	

+	////////////////////////////////////////////////

+	//	Aircon

+	////////////////////////////////////////////////

+

+	private String airconTemp = ""; 

+	

+	public String getAirconTempture()

+	{

+		return airconTemp;	

+	}

+

+	////////////////////////////////////////////////

+	//	Message

+	////////////////////////////////////////////////

+

+	private String message = ""; 

+	

+	public void setMessage(String msg)

+	{

+		message = msg;

+	}

+	

+	public String getMessage()

+	{

+		return message;

+	}

+	

+

+	////////////////////////////////////////////////

+	//	Subscribe

+	////////////////////////////////////////////////

+	

+	private UPnPSubscriber subscriber;

+	

+	public void doSubscribe()

+	{

+		subscriber = new UPnPSubscriber(Activator.context,this);

+		subscriber.subscribe(CLOCK_DEVICE_TYPE, CLOCK_SERVICE_TYPE);

+		subscriber.subscribe(AIRCON_DEVICE_TYPE, AIRCON_SERVICE_TYPE);

+		subscriber.subscribe(LIGHT_DEVICE_TYPE, LIGHT_SERVICE_TYPE);

+		subscriber.subscribe(WASHER_DEVICE_TYPE, WASHER_SERVICE_TYPE);

+	}

+	

+	public void undoSubscribe(){

+		subscriber.unsubscribe();

+	}

+	

+	ArrayList LinkedDevices = new ArrayList();

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPEventListener#notifyUPnPEvent(java.lang.String, java.lang.String, java.util.Dictionary)

+	 */

+	public void notifyUPnPEvent(String deviceId, String serviceId, Dictionary events) {

+		if( !LinkedDevices.contains(deviceId))

+			LinkedDevices.add(deviceId);

+		if (deviceId.indexOf("Clock") != -1)

+				clockTime = (String) events.get("Time");

+		else if (deviceId.indexOf("AirCon") != -1)

+				airconTemp = (String) events.get("Temp");

+		else if (deviceId.indexOf("Washer") != -1)

+				message = (String) events.get("State");

+		else if (deviceId.indexOf("Light") != -1)

+				message = (String) events.get("Power");

+ 

+		comp.repaint();

+	}

+

+

+	////////////////////////////////////////////////

+	//	start/stop

+	////////////////////////////////////////////////

+	

+	public void start()

+	{

+		on();

+	}

+

+	public void stop()

+	{

+		((PowerStateVariable) powerService.getStateVariable("Power")).setNotifier(null);

+		notifier.destroy();

+		off();

+	}

+

+	/* (non-Javadoc)

+	 * @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:{				

+			};break;

+			

+			case ServiceEvent.UNREGISTERING:{	

+				ServiceReference sr = event.getServiceReference();

+				String UDN = (String)sr.getProperty(UPnPDevice.ID);

+				if (UDN != null){

+					if (LinkedDevices.contains(UDN)) {

+						if (UDN.indexOf("Clock") != -1)

+								clockTime = "";

+						else if (UDN.indexOf("AirCon") != -1)

+								airconTemp = "";

+						else if (UDN.indexOf("Washer") != -1)

+								message = "";

+						else if (UDN.indexOf("Light") != -1)

+								message = "";

+					}

+				}

+				comp.repaint();

+		 	};break;

+		}

+	}

+

+}

+

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvFrame.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvFrame.java
new file mode 100644
index 0000000..60a7352
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvFrame.java
@@ -0,0 +1,113 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.awt.BorderLayout;

+import java.awt.event.WindowAdapter;

+import java.awt.event.WindowEvent;

+

+import javax.swing.JFrame;

+

+import org.osgi.framework.BundleException;

+/**

+ * @author Francesco Furfari

+ */

+

+public class TvFrame extends JFrame implements Runnable 

+{

+	private final static String TITLE = "DomoWare Sample TV";

+	

+	private TvDevice tvDev;

+	private TvPane tvPane;

+	

+	public TvFrame()

+	{

+		super(TITLE);

+

+		tvDev = new TvDevice();

+

+		getContentPane().setLayout(new BorderLayout());

+

+		tvPane = new TvPane();

+		tvDev.setComponent(tvPane);

+		tvPane.setDevice(tvDev);

+		getContentPane().add(tvPane, BorderLayout.CENTER);

+

+		addWindowListener(new WindowAdapter(){

+			public void windowClosing(WindowEvent e) 

+			{

+				try {

+					Activator.context.getBundle().stop();

+				} catch (BundleException ex) {

+					ex.printStackTrace();

+				}

+			}

+		});			

+		

+		pack();

+		setVisible(true);

+	}

+

+	public TvPane getTvPanel()

+	{

+		return tvPane;

+	}

+

+	public TvDevice getTvDevice()

+	{

+		return tvDev;

+	}

+		

+	////////////////////////////////////////////////

+	//	run	

+	////////////////////////////////////////////////

+

+	private Thread timerThread = null;

+		

+	public void run()

+	{

+		Thread thisThread = Thread.currentThread();

+

+		while (timerThread == thisThread) {

+			tvDev.setMessage("");

+			tvPane.repaint();

+			try {

+				Thread.sleep(1000*5);

+			}

+			catch(InterruptedException e) {}

+		}

+	}

+	

+	public void start()

+	{

+		tvDev.start();

+		

+		timerThread = new Thread(this);

+		timerThread.start();

+	}

+	

+	public void stop()

+	{

+		tvDev.stop();

+		timerThread = null;

+		dispose();

+	}

+

+

+}

+

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvIcon.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvIcon.java
new file mode 100644
index 0000000..22de04d
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvIcon.java
@@ -0,0 +1,70 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+import java.io.IOException;

+import java.io.InputStream;

+

+import org.osgi.service.upnp.UPnPIcon;

+/**

+ * @author Francesco Furfari

+ */

+

+public class TvIcon implements UPnPIcon {

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getMimeType()

+	 */

+	public String getMimeType() {

+		return "image/gif";

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getWidth()

+	 */

+	public int getWidth() {

+		return 32;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getHeight()

+	 */

+	public int getHeight() {

+		return 32;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getSize()

+	 */

+	public int getSize() {

+		return 0;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getDepth()

+	 */

+	public int getDepth() {

+		return 16;

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.service.upnp.UPnPIcon#getInputStream()

+	 */

+	public InputStream getInputStream() throws IOException {

+		return TvIcon.class.getResourceAsStream("images/tv.gif");

+	}

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvPane.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvPane.java
new file mode 100644
index 0000000..0787f69
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/TvPane.java
@@ -0,0 +1,215 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.awt.Color;

+import java.awt.Dimension;

+import java.awt.Font;

+import java.awt.FontMetrics;

+import java.awt.Graphics;

+import java.awt.geom.Rectangle2D;

+import java.awt.image.BufferedImage;

+

+import javax.imageio.ImageIO;

+import javax.swing.JPanel;

+/**

+ * @author Francesco Furfari

+ */

+

+public class TvPane extends JPanel  // MouseListener

+{

+	private final static int IMAGE_BORDER_SIZE = 20;

+	private final static int IMAGE_BOTTOM_BORDER_SIZE = 30;

+	

+	////////////////////////////////////////////////

+	//	Constructor

+	////////////////////////////////////////////////

+

+	public TvPane()

+	{

+		loadImage();

+		initPanel();

+	}

+

+	////////////////////////////////////////////////

+	//	TvDevice

+	////////////////////////////////////////////////

+

+	private TvDevice tvDev = null;

+	

+	public void setDevice(TvDevice dev)

+	{

+		tvDev = dev;

+	}

+

+	public TvDevice getDevice()

+	{

+		return tvDev;

+	}

+	

+	////////////////////////////////////////////////

+	//	Background

+	////////////////////////////////////////////////

+

+	private BufferedImage panelmage;

+	private BufferedImage tvOnImage;

+	

+	private void loadImage()

+	{

+		try {

+			panelmage = ImageIO.read(TvPane.class.getResourceAsStream("images/tv.jpg"));

+			tvOnImage = ImageIO.read(TvPane.class.getResourceAsStream("images/tvon.jpg"));

+		}

+		catch (Exception e) {

+			System.out.println(e);

+		}

+	}

+

+	////////////////////////////////////////////////

+	//	Background

+	////////////////////////////////////////////////

+

+	private void initPanel()

+	{

+		setPreferredSize(new Dimension(panelmage.getWidth(), panelmage.getHeight()));

+	}

+

+	////////////////////////////////////////////////

+	//	Font

+	////////////////////////////////////////////////

+

+	private final static String DEFAULT_FONT_NAME = "Lucida Console";

+	private final static int DEFAULT_TIME_FONT_SIZE = 10;

+

+	private Font timeFont = null;

+

+	private Font getFont(Graphics g, int size)

+	{

+		Font font = new Font(DEFAULT_FONT_NAME, Font.BOLD, size);

+		if (font != null)

+			return font;

+		return g.getFont();

+	}

+		

+	private Font getFont(Graphics g)

+	{

+		if (timeFont == null)

+			timeFont = getFont(g, DEFAULT_TIME_FONT_SIZE);

+		return timeFont;

+	}

+

+	////////////////////////////////////////////////

+	//	paint

+	////////////////////////////////////////////////

+

+	private void drawClockInfo(Graphics g)

+	{

+		TvDevice tvDev = getDevice();

+

+		int winWidth = getWidth();

+		int winHeight = getHeight();

+		

+		Font font = getFont(g);

+		g.setFont(font);

+		FontMetrics fontMetric = g.getFontMetrics();

+		g.setColor(Color.RED);

+

+		Rectangle2D strBounds;

+		int strWidth;

+		int strHeight;

+		int strX;

+		int strY;

+		

+		//// Time String ////

+		

+		String timeStr = tvDev.getClockTime();

+		if (timeStr != null && 0 < timeStr.length()) {

+			strBounds = fontMetric.getStringBounds(timeStr, g);

+			strWidth = (int)strBounds.getWidth();		

+			strHeight = (int)strBounds.getHeight();

+			strX = IMAGE_BORDER_SIZE;

+			strY = IMAGE_BORDER_SIZE + strHeight;

+			g.drawString(

+				timeStr,

+				strX,

+				strY);

+		}

+		

+		//// Tempture String ////

+		

+		String tempStr = tvDev.getAirconTempture();

+		if (tempStr != null && 0 < tempStr.length()) {

+			tempStr += "C";

+			strBounds = fontMetric.getStringBounds(tempStr, g);

+			strWidth = (int)strBounds.getWidth();		

+			strHeight = (int)strBounds.getHeight();

+			strX = winWidth - IMAGE_BORDER_SIZE - strWidth;

+			strY = IMAGE_BORDER_SIZE + strHeight;

+			g.drawString(

+				tempStr,

+				strX,

+				strY);

+		}

+

+		//// Message String ////

+		

+		String msgStr = tvDev.getMessage();

+		if (msgStr != null && 0 < msgStr.length()) {

+			strBounds = fontMetric.getStringBounds(msgStr, g);

+			strWidth = (int)strBounds.getWidth();		

+			strHeight = (int)strBounds.getHeight();

+			strX = IMAGE_BORDER_SIZE;

+			strY = getHeight()-IMAGE_BOTTOM_BORDER_SIZE-2;

+			g.drawString(

+				msgStr,

+				strX,

+				strY);

+		}

+	}

+

+	private void drawTvImage(Graphics g)

+	{

+		//g.setColor(Color.WHITE);

+		//g.clearRect(IMAGE_BORDER_SIZE, IMAGE_BORDER_SIZE, getWidth()-IMAGE_BORDER_SIZE*2, getHeight()-IMAGE_BORDER_SIZE-IMAGE_BOTTOM_BORDER_SIZE);

+		g.drawImage(tvOnImage, IMAGE_BORDER_SIZE, IMAGE_BORDER_SIZE, null);

+	}

+	

+	private void clear(Graphics g)

+	{

+		g.setColor(Color.GRAY);

+		g.clearRect(0, 0, getWidth(), getHeight());

+	}

+	

+

+	private void drawPanelImage(Graphics g)

+	{

+		g.drawImage(panelmage, 0, 0, null);

+	}

+		

+	public void paint(Graphics g)

+	{

+		clear(g);

+		drawPanelImage(g);

+		TvDevice tvDev = getDevice();

+		if (tvDev.isOn() == true) {

+			drawTvImage(g);

+			drawClockInfo(g);

+		}

+	}

+}

+

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPEventNotifier.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPEventNotifier.java
new file mode 100644
index 0000000..58a48a5
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPEventNotifier.java
@@ -0,0 +1,195 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+import java.util.Dictionary;

+import java.util.Iterator;

+import java.util.Properties;

+import java.util.Vector;

+

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.Constants;

+import org.osgi.framework.Filter;

+import org.osgi.framework.ServiceEvent;

+import org.osgi.framework.ServiceListener;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.upnp.UPnPDevice;

+import org.osgi.service.upnp.UPnPEventListener;

+import org.osgi.service.upnp.UPnPService;

+import org.osgi.service.upnp.UPnPStateVariable;

+

+/**

+ * @author Francesco Furfari

+ */

+

+public class UPnPEventNotifier implements PropertyChangeListener,ServiceListener {

+	BundleContext context;

+	UPnPDevice device;

+	UPnPService service;

+	EventSource source;

+	

+	Properties UPnPTargetListener;

+	String deviceId;

+	String serviceId;

+	Vector upnpListeners = new Vector();

+	

+	public UPnPEventNotifier(BundleContext context,UPnPDevice device,UPnPService service,EventSource source){

+		this.context=context;

+		this.device=device;

+		this.service=service;

+		this.source=source;

+		this.serviceId=service.getId();

+		setupUPnPListenerHouseKeeping(device);

+	}

+	

+	/**

+	 * @param deviceId

+	 */

+	private void setupUPnPListenerHouseKeeping(UPnPDevice device) {

+		UPnPTargetListener = new Properties();

+		Dictionary dict = device.getDescriptions(null);

+		deviceId = (String) dict.get(UPnPDevice.ID);

+		UPnPTargetListener.put(UPnPDevice.ID,deviceId);

+		UPnPTargetListener.put(UPnPService.ID,serviceId);

+		UPnPTargetListener.put(UPnPDevice.TYPE,dict.get(UPnPDevice.TYPE));

+		UPnPTargetListener.put(UPnPService.TYPE,service.getType());

+		String ANY_UPnPEventListener = "("+Constants.OBJECTCLASS+"="+UPnPEventListener.class.getName()+")";

+		

+		ServiceReference[] listeners = null; 

+		try {

+			listeners = context.getServiceReferences(UPnPEventListener.class.getName(),null);

+			if (listeners != null){

+				for (int i = 0;i<listeners.length;i++){

+					ServiceReference sr = listeners[i];

+					Filter filter = (Filter) sr.getProperty(UPnPEventListener.UPNP_FILTER);

+					if (filter == null) addNewListener(sr);

+					else {				

+						if (filter.match(UPnPTargetListener))

+							addNewListener(sr);

+					}

+				}

+			}

+		} catch (Exception ex) {

+			System.out.println(ex);

+		}

+		

+	    try {

+	    	//String filter = "(&" + ANY_UPnPEventListener + deviceId_Filter + ")";

+	    	String filter = ANY_UPnPEventListener;

+			context.addServiceListener(this,filter);

+		} catch (Exception ex) {

+			System.out.println(ex);

+		}

+		

+		if (source!=null){

+			UPnPStateVariable[] vars = service.getStateVariables();

+			if (vars != null){

+				for (int i=0;i<vars.length;i++)

+					if(vars[i].sendsEvents())

+						source.addPropertyChangeListener(vars[i].getName(),this);

+			}

+		}

+		

+		

+	}

+

+	/* (non-Javadoc)

+	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)

+	 */

+	public void propertyChange(PropertyChangeEvent evt) {

+		Iterator list = upnpListeners.iterator();

+		String property = evt.getPropertyName();

+		Object value = evt.getNewValue();

+		String valueString = value.toString();

+		final Properties events = new Properties();

+		events.put(property,valueString);

+		while (list.hasNext()){

+			final ServiceReference sr = (ServiceReference)list.next();

+			String[] props =sr.getPropertyKeys();

+			new Thread(null,null,"Notifier"){

+				public void run(){

+					try {

+					    UPnPEventListener listener = (UPnPEventListener) context.getService(sr);

+						listener.notifyUPnPEvent(deviceId,serviceId,events);

+						context.ungetService(sr);

+					} catch (Exception ex){

+						System.out.println("TV UPnPEventNotifier Err: " +ex);

+						System.out.println("context: " +context);

+						System.out.println("listener: " +context.getService(sr));

+						System.out.println("sr: " +sr);

+					}

+				}

+			}.start();						

+		}

+	}

+

+	/* (non-Javadoc)

+	 * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)

+	 */

+	public void serviceChanged(ServiceEvent e) {

+		switch(e.getType()){

+			case ServiceEvent.REGISTERED:{

+				ServiceReference sr = e.getServiceReference();

+				Filter filter = (Filter) sr.getProperty(UPnPEventListener.UPNP_FILTER);

+				if (filter == null) addNewListener(sr);

+				else {				

+					if (filter.match(UPnPTargetListener))

+						addNewListener(sr);

+				}

+			};break;

+			

+			case ServiceEvent.MODIFIED:{				

+			};break;

+			

+			case ServiceEvent.UNREGISTERING:{	

+				removeListener(e.getServiceReference());

+			};break;

+				

+		}

+		

+	}

+

+	/**

+	 * @param reference

+	 */

+	private void removeListener(ServiceReference reference) {

+		upnpListeners.remove(reference);		

+	}

+

+	/**

+	 * @param reference

+	 */

+	private void addNewListener(ServiceReference reference) {

+		upnpListeners.add(reference);	

+		UPnPStateVariable[] vars = service.getStateVariables();

+		if (vars != null){

+			for (int i=0;i<vars.length;i++)

+				if(vars[i].sendsEvents()){

+				    Object value = ((UPnPStateVariableDescriptor) vars[i]).getValue();

+				    propertyChange(new PropertyChangeEvent(this,vars[i].getName(),value,value));

+				}

+		}

+	}

+	

+	public void destroy(){

+		context.removeServiceListener(this);

+		upnpListeners.removeAllElements();

+	}

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPStateVariableDescriptor.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPStateVariableDescriptor.java
new file mode 100644
index 0000000..b08d4bc
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPStateVariableDescriptor.java
@@ -0,0 +1,29 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import org.osgi.service.upnp.UPnPStateVariable;

+

+/**

+ * UPnPStateVariableDescriptor.java 22-gen-2005

+ * @author Francesco Furfari

+ */

+public interface UPnPStateVariableDescriptor extends UPnPStateVariable {

+    Object getValue();

+

+}

diff --git a/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPSubscriber.java b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPSubscriber.java
new file mode 100644
index 0000000..34f5313
--- /dev/null
+++ b/org.apache.felix.upnp.sample.tv/src/main/java/org/apache/felix/upnp/sample/tv/UPnPSubscriber.java
@@ -0,0 +1,61 @@
+/*

+ *   Copyright 2006 The Apache Software Foundation

+ *

+ *   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.

+ *

+ */

+

+package org.apache.felix.upnp.sample.tv;

+

+import java.util.Properties;

+

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.Filter;

+import org.osgi.framework.ServiceRegistration;

+import org.osgi.service.upnp.UPnPDevice;

+import org.osgi.service.upnp.UPnPEventListener;

+import org.osgi.service.upnp.UPnPService;

+

+/**

+ * @author Francesco Furfari

+ */

+

+public class UPnPSubscriber {

+	ServiceRegistration registration = null;

+	BundleContext context;

+	UPnPEventListener listener;

+	

+	public UPnPSubscriber(BundleContext context,UPnPEventListener listener){

+		this.context = context;

+		this.listener = listener;

+	}

+	

+	public void subscribe(String deviceType, String serviceType){

+		String keys = "(&(" + UPnPDevice.TYPE + "="+ deviceType + ")(" + UPnPService.TYPE + "=" + serviceType + "))";

+		try {

+			Filter filter = context.createFilter(keys);

+			Properties props = new Properties();

+			props.put(UPnPEventListener.UPNP_FILTER, filter);

+			registration = context.registerService(UPnPEventListener.class.getName(), listener, props);

+		}catch (Exception ex){

+			System.out.println(ex);

+		}

+	}

+	

+	public void unsubscribe(){

+		registration.unregister();

+		registration = null;

+	}

+	

+

+}