added Clock and BinaryLight UPnP examples

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@399191 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Activator.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Activator.java
new file mode 100644
index 0000000..5b92db7
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Activator.java
@@ -0,0 +1,65 @@
+/*
+ *   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.clock;
+
+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 <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+*/
+public class Activator implements BundleActivator {
+
+	private ServiceRegistration serviceRegistration;
+	private BundleContext context;
+	private ClockFrame clockFrame;
+	
+	/**
+	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+	 */
+	public void start(BundleContext context) throws Exception {
+		this.context = context;
+		doServiceRegistration();
+	}
+
+	private void doServiceRegistration() {
+		clockFrame = new ClockFrame(context);
+		Dictionary dict = clockFrame.getClockDevice().getDescriptions(null);
+		
+		serviceRegistration = context.registerService(
+				UPnPDevice.class.getName(),
+				clockFrame.getClockDevice(),
+				dict
+			);
+		
+		clockFrame.start();
+
+	}
+
+	/**
+	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+	 */
+	public void stop(BundleContext context) throws Exception {
+		serviceRegistration.unregister();
+		clockFrame.stop();
+	}
+}
diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Clock.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Clock.java
new file mode 100644
index 0000000..294e637
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/Clock.java
@@ -0,0 +1,156 @@
+/*

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

+

+import java.util.Calendar;

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class Clock

+{

+	private Calendar cal;

+

+	public Clock(Calendar cal)

+	{

+		this.cal = cal;

+	}

+

+	public Calendar getCalendar()

+	{

+		return cal;

+	}

+

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

+	//	Time

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

+

+	public int getHour()

+	{

+		return getCalendar().get(Calendar.HOUR);

+	}

+

+	public int getMinute()

+	{

+		return getCalendar().get(Calendar.MINUTE);

+	}

+

+	public int getSecond()

+	{

+		return getCalendar().get(Calendar.SECOND);

+	}

+	

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

+	//	paint

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

+

+	public final static Clock getInstance()

+	{

+		return new Clock(Calendar.getInstance());

+	}

+

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

+	//	getDateString

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

+

+	public final static String toClockString(int value)

+	{

+		if (value < 10)

+			return "0" + Integer.toString(value);

+		return Integer.toString(value);

+	}

+

+	private final static String MONTH_STRING[] = {

+		"Jan",

+		"Feb",

+		"Mar",

+		"Apr",

+		"May",

+		"Jun",

+		"Jul",

+		"Aug",

+		"Sep",

+		"Oct",

+		"Nov",

+		"Dec",

+	};

+

+	public final static String toMonthString(int value)

+	{

+		value -= Calendar.JANUARY;

+		if (0 <= value && value < 12)

+			return MONTH_STRING[value];

+		return "";

+	}

+	

+	private final static String WEEK_STRING[] = {

+		"Sun",

+		"Mon",

+		"Tue",

+		"Wed",

+		"Thu",

+		"Fri",

+		"Sat",

+	};

+

+	public final static String toWeekString(int value)

+	{

+		value -= Calendar.SUNDAY;

+		if (0 <= value && value < 7)

+			return WEEK_STRING[value];

+		return "";

+	}

+	

+	public String getDateString()

+	{

+		Calendar cal = getCalendar();

+		return

+			toWeekString(cal.get(Calendar.DAY_OF_WEEK)) +", " + 

+			toMonthString(cal.get(Calendar.MONTH)) + " " +

+			Integer.toString(cal.get(Calendar.DATE)) + ", " +

+			toClockString(cal.get(Calendar.YEAR) % 100);

+	}

+

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

+	//	getTimeString

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

+	

+	public String getTimeString()

+	{

+		Calendar cal = getCalendar();

+		return

+			toClockString(cal.get(Calendar.HOUR)) +

+			(((cal.get(Calendar.SECOND) % 2) == 0) ? ":" : " ") +

+			toClockString(cal.get(Calendar.MINUTE));

+	}

+

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

+	//	toString

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

+

+	public String toString()

+	{

+		Calendar cal = getCalendar();

+		return

+			getDateString() + ", " +

+			toClockString(cal.get(Calendar.HOUR)) + ":" +

+			toClockString(cal.get(Calendar.MINUTE)) + ":" +

+			toClockString(cal.get(Calendar.SECOND));

+	}

+		

+}

+

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockDevice.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockDevice.java
new file mode 100644
index 0000000..677975b
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockDevice.java
@@ -0,0 +1,135 @@
+/*

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

+

+

+import java.beans.PropertyChangeEvent;

+import java.util.Dictionary;

+import java.util.Properties;

+

+import org.osgi.framework.BundleContext;

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

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

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

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class ClockDevice implements UPnPDevice {

+

+	final private String DEVICE_ID = "uuid:Felix-Clock";

+	private BundleContext context;

+	private TimerService timerService;

+	private UPnPService[] services;

+	private Dictionary dictionary;

+	UPnPEventNotifier notifier;

+	

+	public ClockDevice(BundleContext context) {

+		this.context=context;

+		timerService = new TimerService();

+		services = new UPnPService[]{timerService};

+		setupDeviceProperties();

+		buildEventNotifyer();

+	}

+

+	/**

+	 * 

+	 */

+	private void buildEventNotifyer() {

+		 notifier = new UPnPEventNotifier(context,this,timerService,null);

+	}

+

+	private void setupDeviceProperties(){

+		dictionary = new Properties();

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

+		dictionary.put(

+		        org.osgi.service

+		        	.device.Constants.DEVICE_CATEGORY,

+	        	new String[]{UPnPDevice.DEVICE_CATEGORY}

+	        );

+		//dictionary.put(UPnPDevice.DEVICE_CATEGORY,new String[]{UPnPDevice.DEVICE_CATEGORY});

+		dictionary.put(UPnPDevice.FRIENDLY_NAME,"Felix OSGi-UPnP Clock");

+		dictionary.put(UPnPDevice.MANUFACTURER,"Apache Software Foundation");

+		dictionary.put(UPnPDevice.MANUFACTURER_URL,"http://incubator.apache.org/felix/");

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

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

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

+		dictionary.put(UPnPDevice.MODEL_URL,"http://incubator.apache.org/felix/DolceDormire");

+		dictionary.put(UPnPDevice.PRESENTATION_URL,"http://incubator.apache.org/felix/dolceDormire/presentation");

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

+		dictionary.put(UPnPDevice.TYPE,"urn:schemas-upnp-org:device:clock: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(timerService.getId())) return timerService;

+		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 ClockIcon();

+		return new UPnPIcon[]{icon} ;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Dictionary getDescriptions(String locale) {

+		return dictionary;

+	}

+

+	/**

+	 * 

+	 */

+	public void start() {

+		// TODO Auto-generated method stub

+		

+	}

+

+	/**

+	 * 

+	 */

+	public void stop() {

+		notifier.destroy();	

+	}

+

+	/**

+	 * 

+	 */

+	public void update() {

+		Clock clock = Clock.getInstance();

+		String timeStr = clock.toString();

+		notifier.propertyChange(new PropertyChangeEvent(this,"Time","",timeStr));

+	}

+	

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockFrame.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockFrame.java
new file mode 100644
index 0000000..a35dc38
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockFrame.java
@@ -0,0 +1,123 @@
+/*

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

+

+import java.awt.BorderLayout;

+import java.awt.event.WindowAdapter;

+import java.awt.event.WindowEvent;

+import java.net.URL;

+

+import javax.swing.ImageIcon;

+import javax.swing.JFrame;

+

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.BundleException;

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class ClockFrame extends JFrame implements Runnable 

+{

+	private final static String TITLE = "Felix UPnP Clock";

+	private ClockDevice clockDev;

+	private ClockPane clockPane;

+	

+	public ClockFrame(final BundleContext context)

+	{

+		super(TITLE);

+		try {

+			clockDev = new ClockDevice( context);

+		}

+		catch (Exception e) {

+			System.out.println(e);

+		}

+				

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

+

+		clockPane = new ClockPane();		

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

+

+		addWindowListener(new WindowAdapter(){

+			public void windowClosing(WindowEvent e) 

+			{

+				try {

+					context.getBundle().stop();

+				} catch (BundleException ex) {

+					ex.printStackTrace();

+				}

+			}

+		});			

+	       try {

+	            URL eventIconUrl = ClockFrame.class.getResource("images/logo.gif");           

+	            ImageIcon icon=  new ImageIcon(eventIconUrl,"logo");

+	            setIconImage(icon.getImage());

+	       }

+	        catch (Exception ex){

+	                System.out.println("Resource: IMAGES/logo.gif not found : " + ex.toString());

+	        }

+		

+		pack();

+		setVisible(true);

+	}

+

+	public ClockPane getClockPanel()

+	{

+		return clockPane;

+	}

+

+	public ClockDevice getClockDevice()

+	{

+		return clockDev;

+	}

+		

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

+	//	run	

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

+

+	private Thread timerThread = null;

+		

+	public void run()

+	{

+		Thread thisThread = Thread.currentThread();

+

+		while (timerThread == thisThread) {

+			getClockDevice().update();

+			getClockPanel().repaint();

+			try {

+				Thread.sleep(1000);

+			}

+			catch(InterruptedException e) {}

+		}

+	}

+	

+	public void start()

+	{

+		clockDev.start();

+		

+		timerThread = new Thread(this);

+		timerThread.start();

+	}

+	

+	public void stop()

+	{

+		clockDev.stop();

+		timerThread = null;

+		dispose();

+	}

+

+}

+

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockIcon.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockIcon.java
new file mode 100644
index 0000000..53586be
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockIcon.java
@@ -0,0 +1,69 @@
+/*

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

+import java.io.IOException;

+import java.io.InputStream;

+

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

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class ClockIcon 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 ClockIcon.class.getResourceAsStream("images/clock.gif");

+	}

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockPane.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockPane.java
new file mode 100644
index 0000000..3a9e232
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ClockPane.java
@@ -0,0 +1,209 @@
+/*

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

+

+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 <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class ClockPane extends JPanel  // MouseListener

+{

+	public ClockPane()

+	{

+		loadImage();

+		initPanel();

+	}

+

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

+	//	Background

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

+	

+

+	private BufferedImage panelmage;

+	

+	private void loadImage()

+	{

+		

+		try {

+			panelmage = ImageIO.read(ClockPane.class.getResourceAsStream("images/clock.jpg"));

+		}

+		catch (Exception e) {

+			System.out.println(e);

+		}

+	}

+

+	private BufferedImage getPaneImage()

+	{

+		return panelmage;

+	}

+

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

+	//	Background

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

+

+	private void initPanel()

+	{

+		BufferedImage panelmage = getPaneImage();

+		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 = 60;

+	private final static int DEFAULT_DATE_FONT_SIZE = 18;

+	private final static int DEFAULT_SECOND_BLOCK_HEIGHT = 8;

+	private final static int DEFAULT_SECOND_BLOCK_FONT_SIZE = 10;

+

+	private Font timeFont = null;

+	private Font dateFont = null;

+	private Font secondFont = null;

+

+	private Font getFont(Graphics g, int size)

+	{

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

+		if (font != null)

+			return font;

+		return g.getFont();

+	}

+		

+	private Font getTimeFont(Graphics g)

+	{

+		if (timeFont == null)

+			timeFont = getFont(g, DEFAULT_TIME_FONT_SIZE);

+		return timeFont;

+	}

+

+	private Font getDateFont(Graphics g)

+	{

+		if (dateFont == null)

+			dateFont = getFont(g, DEFAULT_DATE_FONT_SIZE);

+		return dateFont;

+	}

+

+	private Font getSecondFont(Graphics g)

+	{

+		if (secondFont == null)

+			secondFont = getFont(g, DEFAULT_SECOND_BLOCK_FONT_SIZE);

+		return secondFont;

+	}

+

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

+	//	paint

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

+

+	private void drawClockInfo(Graphics g)

+	{

+		Clock clock = Clock.getInstance();

+		

+		int winWidth = getWidth();

+		int winHeight = getHeight();

+		

+		g.setColor(Color.BLACK);

+		

+		//// Time String ////

+		

+		String timeStr = clock.getTimeString();

+

+		Font timeFont = getTimeFont(g);

+		g.setFont(timeFont);

+

+		FontMetrics timeFontMetric = g.getFontMetrics();

+		Rectangle2D timeStrBounds = timeFontMetric.getStringBounds(timeStr, g);

+

+		int timeStrWidth = (int)timeStrBounds.getWidth();		

+		int timeStrHeight = (int)timeStrBounds.getHeight();

+		int timeStrX = (winWidth-timeStrWidth)/2;

+		int timeStrY = (winHeight+timeStrHeight)/2;

+		int timeStrOffset = timeStrHeight/8/2;

+		g.drawString(

+			timeStr,

+			timeStrX,

+			timeStrY);

+

+		//// Date String ////

+

+		String dateStr = clock.getDateString();

+

+		Font dateFont = getDateFont(g);

+		g.setFont(dateFont);

+

+		FontMetrics dateFontMetric = g.getFontMetrics();

+		Rectangle2D dateStrBounds = dateFontMetric.getStringBounds(dateStr, g);

+

+		g.drawString(

+			dateStr,

+			(winWidth-(int)dateStrBounds.getWidth())/2,

+			timeStrY-timeStrHeight-timeStrOffset);

+

+		//// Second Bar ////

+		

+		Font secFont = getSecondFont(g);

+		g.setFont(secFont);

+		int sec = clock.getSecond();

+		int secBarBlockSize = timeStrWidth / 60;

+		int secBarBlockY = timeStrY + timeStrOffset;

+		for (int n=0; n<sec; n++) {

+			int x = timeStrX + (secBarBlockSize*n);

+			g.fillRect(

+				x,

+				secBarBlockY,

+				secBarBlockSize-1,

+				DEFAULT_SECOND_BLOCK_HEIGHT);

+		}

+		if (sec != 0 && (sec % 10) == 0) {

+			int x = timeStrX + (secBarBlockSize*sec);

+			g.drawString(

+				Integer.toString(sec),

+				x + secBarBlockSize,

+				secBarBlockY + DEFAULT_SECOND_BLOCK_HEIGHT);

+		}

+	}

+

+	private void clear(Graphics g)

+	{

+		g.setColor(Color.GRAY);

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

+	}

+	

+

+	private void drawPanelImage(Graphics g)

+	{

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

+	}

+		

+	public void paint(Graphics g)

+	{

+		clear(g);

+		drawPanelImage(g);

+		drawClockInfo(g);

+	}

+}

+

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/EventSource.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/EventSource.java
new file mode 100644
index 0000000..62fd7b1
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/EventSource.java
@@ -0,0 +1,30 @@
+/*

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

+

+import java.beans.PropertyChangeListener;

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+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.clock/src/main/java/org/apache/felix/upnp/sample/clock/GetTimeAction.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/GetTimeAction.java
new file mode 100644
index 0000000..d3d8728
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/GetTimeAction.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.clock;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+

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

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

+

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class GetTimeAction implements UPnPAction {

+

+	final private String NAME = "GetTime";

+	final private String RESULT_STATUS = "CurrentTime";

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

+	private TimeStateVariable time;

+	

+	

+	public GetTimeAction(TimeStateVariable time){

+		this.time = time;

+	}

+

+	/* (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 time;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public Dictionary invoke(Dictionary args) throws Exception {

+		String value = time.getCurrentTime();

+		Hashtable result = new Hashtable();

+		result.put(RESULT_STATUS,value);

+		return result;

+	}

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ResultStateVariable.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/ResultStateVariable.java
new file mode 100644
index 0000000..45983b8
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/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.clock;

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

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class ResultStateVariable implements UPnPStateVariable{

+	

+	final private String NAME = "Result";

+	final private String DEFAULT_VALUE = "";

+	private Clock clock;

+	

+	

+	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 String.class;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getUPnPDataType() {

+		return TYPE_STRING;

+	}

+

+	/* (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.clock/src/main/java/org/apache/felix/upnp/sample/clock/SetTimeAction.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/SetTimeAction.java
new file mode 100644
index 0000000..6312e02
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/SetTimeAction.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.clock;

+

+import java.util.Dictionary;

+

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

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

+

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class SetTimeAction implements UPnPAction {

+

+	final private String NAME = "SetTime";

+	final private String NEW_TIME_VALUE = "NewTime";

+	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 UPnPStateVariable time,result;

+	

+	

+	public SetTimeAction(UPnPStateVariable time,UPnPStateVariable result){

+		this.time = time;

+		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("NewTime")) return time;

+		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 {

+		//Date value = (Date) args.get(NEW_TIME_VALUE);

+		long l = ((Long) args.get(NEW_TIME_VALUE)).longValue();

+		((TimeStateVariable) time).setCurrentTime(l);

+		args.remove(NEW_TIME_VALUE);

+		args.put(NEW_RESULT_VALUE,((TimeStateVariable) time).getCurrentTime());

+		return args;

+	}

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimeStateVariable.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimeStateVariable.java
new file mode 100644
index 0000000..8ee30b5
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimeStateVariable.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.clock;

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

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class TimeStateVariable implements UPnPStateVariable{

+	

+	final private String NAME = "Time";

+	final private String DEFAULT_VALUE = "";

+	private Clock clock;

+	

+	

+	public TimeStateVariable(){

+		clock = Clock.getInstance();

+	}

+	

+	/* (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 Long.class;

+	}

+

+	/* (non-Javadoc)

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

+	 */

+	public String getUPnPDataType() {

+		return TYPE_TIME;

+	}

+

+	/* (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 String getCurrentTime(){

+		return clock.getTimeString();

+	}

+	

+	public void setCurrentTime(long milliseconds){

+		clock.getCalendar().setTimeInMillis(milliseconds);

+	}

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimerService.java b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimerService.java
new file mode 100644
index 0000000..57927cf
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/java/org/apache/felix/upnp/sample/clock/TimerService.java
@@ -0,0 +1,103 @@
+/*

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

+

+import java.util.HashMap;

+

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

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

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

+

+/* 

+* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+public class TimerService implements UPnPService {

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

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

+	final private String VERSION ="1";

+

+	private UPnPStateVariable time,result;

+	private UPnPStateVariable[] states;

+	private HashMap actions = new HashMap();

+	

+	

+	public TimerService(){

+		time = new TimeStateVariable();

+		result = new ResultStateVariable();

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

+		

+		UPnPAction setTime= new SetTimeAction(time,result);

+		UPnPAction getTime = new GetTimeAction((TimeStateVariable)time);

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

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

+		

+	}

+

+	/* (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("Time"))

+			return time;

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

+			return result;

+		else return null;

+	}

+}

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

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

+

+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 <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>

+*/

+

+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) upnpListeners.add(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(){

+				public void run(){

+					try {

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

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

+						context.ungetService(sr);

+					} catch (Exception ex){

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

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

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

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

+						System.out.println();

+					}

+				}

+			}.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);	

+	}

+	

+	public void destroy(){

+		context.removeServiceListener(this);

+		upnpListeners.removeAllElements();

+	}

+}

diff --git a/org.apache.felix.upnp.sample.clock/src/main/resources/META-INF/Manifest.mf b/org.apache.felix.upnp.sample.clock/src/main/resources/META-INF/Manifest.mf
new file mode 100644
index 0000000..69ff757
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/resources/META-INF/Manifest.mf
@@ -0,0 +1,4 @@
+Bundle-Author: Matteo Demuru <demuru@apache.org>,Francesco Furfari <furfari@apache.org>,Satoshi Konno <konno@users.sourceforge.

+ net>,Stefano "Kismet" Lenzi <lenzi@apache.org>

+Bundle-ClassPath: .

+

diff --git a/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.gif b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.gif
new file mode 100644
index 0000000..21327fc
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.gif
Binary files differ
diff --git a/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.jpg b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.jpg
new file mode 100644
index 0000000..f7fcea0
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/clock.jpg
Binary files differ
diff --git a/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/logo.gif b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/logo.gif
new file mode 100644
index 0000000..ef39d61
--- /dev/null
+++ b/org.apache.felix.upnp.sample.clock/src/main/resources/org/apache/felix/upnp/sample/clock/images/logo.gif
Binary files differ