UPnP programming

UPnP programming

Cyberlink Java

Cyberlink Programmer's Guide

Echo

Echo device description


<?xml version="1.0" ?> 
<root xmlns="urn:schemas-upnp-org:device-1-0">
	<specVersion>
		<major>1</major> 
		<minor>0</minor> 
	</specVersion>
	<device>
		<deviceType>urn:schemas-upnp-org:device:echo:1</deviceType> 
		<friendlyName>Echo device</friendlyName> 
		<manufacturer>Jan Newmarch</manufacturer> 
		<manufacturerURL>http://jan.newmarch.name</manufacturerURL> 
		<modelDescription>Jan's Simple Echo Device</modelDescription> 
		<modelName>Echo</modelName> 
		<modelNumber>1.0</modelNumber> 
		<modelURL>http://jan.newmarch.name</modelURL> 
		<serialNumber>1234567890</serialNumber> 
		<UDN>uuid:jan_echo_server_1</UDN> 
		<UPC>123456789012</UPC> 
		<iconList>
			<icon>
				<mimetype>image/gif</mimetype> 
				<width>48</width> 
				<height>32</height> 
				<depth>8</depth> 
				<url>/description/icon.gif</url> 
			</icon>
		</iconList>
		<serviceList>
			<service>
				<serviceType>urn:schemas-upnp-org:service:echo:1</serviceType> 
				<serviceId>urn:schemas-upnp-org:serviceId:echo:1</serviceId> 
				<SCPDURL>/service_description.xml</SCPDURL> 
				<controlURL>/service/timer/control</controlURL> 
				<eventSubURL>/service/timer/eventSub</eventSubURL> 
			</service>
		</serviceList>
		<presentationURL>/presentation</presentationURL> 
	</device>
</root>

Echo service description


<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0" >
    <specVersion>
	<major>1</major>
	<minor>0</minor>
    </specVersion>
    <actionList>
	<action>
	    <name>Echo</name>
	    <argumentList>
		<argument>
		    <name>InText</name>
  		    <relatedStateVariable>Text</relatedStateVariable>
		    <direction>in</direction>
		</argument>
		<argument>
		    <name>ReturnText</name>
		    <relatedStateVariable>Text</relatedStateVariable>
		    <direction>out</direction>
		</argument>
	    </argumentList>
	</action>
    </actionList>
    <serviceStateTable>
	<stateVariable sendEvents="no">
	    <name>Text</name>
	    <dataType>string</dataType>
	</stateVariable>
    </serviceStateTable>
</scpd>

Echo device



/******************************************************************
 *
 *	Echo
 *
 *	Copyright (C) Jan Newmarch
 *
 *	File : EchoDevice.java
 *
 ******************************************************************/

package device;

// import service.*;

import java.io.*;

import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;

public class EchoDevice extends Device implements ActionListener
{
    private final static String DESCRIPTION_FILE_NAME = "description/description.xml";

    private StateVariable textVar;

    public static void main(String[] args) {

	UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
        org.cybergarage.util.Debug.on();


	EchoDevice dev = null;
	try {
	    dev = new EchoDevice();
	} catch(InvalidDescriptionException e) {
	    e.printStackTrace();
	    System.exit(1);
	}
	System.out.println("Running...");

	// This is a hack to keep the server alive.
	// If the device had a GUI, the AWT/Swing would keep it alive
	Object keepAlive = new Object();
	synchronized(keepAlive) {
	    try {
		// Wait for a "notify" from another thread
		// that will never be sent.
		// So we stay alive for ever
		keepAlive.wait();
	    } catch(java.lang.InterruptedException e) {
		// do nothing
	    }
	}
    }

    public EchoDevice() throws InvalidDescriptionException
    {
	super(new File(DESCRIPTION_FILE_NAME));

	Action getEchoAction = getAction("Echo");
	getEchoAction.setActionListener(this);
	
	ServiceList serviceList = getServiceList();
	// there should only be an echo service
	Service service = serviceList.getService(0);
	// service.setQueryListener(this);

	// we don't use this	
	textVar = getStateVariable("Text");
	
	setLeaseTime(60);
	start();
    }

    public String echo(String txt) {
	return("Echo from \"" + getFriendlyName() + "\": "  + txt);
    }
    
    public boolean actionControlReceived(Action action)
    {
	String actionName = action.getName();
	if (actionName.equals("Echo")) {
	    System.out.println("Echo action control");

	    Argument inArg = action.getArgument("InText");
	    String txt = inArg.getValue();

	    Argument returnArg = action.getArgument("ReturnText");
	    String returnTxt = echo(txt);
	    returnArg.setValue(returnTxt);
	    return true;
	}
	return false;
    }
}

Echo client



/******************************************************************
 *
 *	CyberUPnP for Java
 *
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File : ClockDevice.java
 *
 ******************************************************************/

package controlPoint;

import java.io.*;

import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;

public class Client extends ControlPoint implements NotifyListener {

    public static void main(String[] args) {
        UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
	org.cybergarage.util.Debug.on();

	new Client();
    }

    public Client()
    {
	addNotifyListener(this);
	start();
	loop();
    }

    public void loop() {
        for (int n = 0; n < 100; n++) {
            System.out.println("Sending: hello " + n);
            echoToAll("hello " +  n);
            try {
                Thread.currentThread().sleep(1000);
            } catch(Exception e) {
            }
        }
        System.exit(0);
    }

    private void echoToAll(String txt) {
	DeviceList devList = getDeviceList();
	// System.out.println("Discovered devices: " + devList.size());

	Device dev = null;
	for (int n = 0; n < devList.size(); n++) {
	    dev = devList.getDevice(n);
	    // System.out.println("  Type: " +  dev.getDeviceType());
	    if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:echo:1")) {
		ServiceList services = dev.getServiceList();
		for (int m = 0; m < services.size(); m++) {
		    Service svc = services.getService(m);
		    if (svc.getServiceType().equals("urn:schemas-upnp-org:service:echo:1")) {
			echoToOne(svc, txt);;
		    }
		}
	    }
	}
    }	    

    private void echoToOne(Service svc, String txt) {
	Action echoAction = svc.getAction("Echo");
	Argument inArg = echoAction.getArgument("InText");
	inArg.setValue(txt);
	if (echoAction.postControlAction()) {
	    Argument arg = echoAction.getArgument("ReturnText");
	    String value = arg.getValue();
	    System.out.println(value);
	}
    }

    public void deviceNotifyReceived(SSDPPacket ssdpPacket) { 
	// uncomment this to see all discovery packets
	// System.out.println("New SSDPPacket, all " + ssdpPacket);
    } 
}

Ant file for echo

A build.xml is


<project name="UPnP demos" default="usage" basedir=".">

    <!-- CONFIGURABLE STUFF HERE -->
    <!-- where this directory is -->
    <property name="top" value="/home/httpd/html/internetdevices/upnp/java/echo"/>

    <property name="upnp_lib" value="${top}/lib/clink132a.jar"/>
    <property name="xerces_lib" value="${top}/lib/xercesImpl.jar"/>
    <property name="xml_lib" value="${top}/lib/xml-apis.jar"/>

    <!-- END CONFIGURABLE STUFF -->

    <!-- Directories -->

  <!-- Show the usage options to the user -->
  <target name="usage" >
    <echo message=""/>
    <echo message=""/>
    <echo message="-------------------------------------------------------------"/>
    <echo message=""/>
    <echo message="available targets are:"/>
    <echo message=""/>
    <echo message=" compile"/>
    <echo message=" rundevice"/>
    <echo message=" runclient"/>
    <echo message=" clean"/>
    <echo message=" usage"/>
    <echo message=""/>
    <echo message="See comments inside the build.xml file for more details."/>
    <echo message="-------------------------------------------------------------"/>
    <echo message=""/>
    <echo message=""/>
  </target>

  <target name="all" depends="init,compile"/> 

    <!-- CLEAN -->
    <target name="clean">
 	<!-- Delete our the ${build}, and ${deploy} directory trees -->
	<delete dir="classes"/>
    </target>


    <target name="init">
	<!-- Create the build directory structure used by compile N deploy -->
    </target>

    <target name="compile">
	<mkdir dir="classes"/>
	<javac destdir="classes" srcdir="src" 
	       classpath="${upnp_lib}"
               target="1.1"/>
    </target>

    <target name="runclient" depends="compile">
	<java
            classpath="${upnp_lib}:${xerces_lib}:${xml_lib}:classes"
	    classname="controlPoint.Client"/>
    </target>

    <target name="rundevice" depends="compile">
	<exec executable="java">
            <env key="CLASSPATH" 
                 path="${upnp_lib}:${xerces_lib}:${xml_lib}:classes"/>
	    <arg line="device.EchoDevice"/>
	</exec>
    </target>
</project>

Compile and run

Flashing clocks

Timer service

Java classes for timer service

Code is available from here

Running the example

All the source code is available from here . This includes an ant build.xml file. You can use this to build/run the examples by


ant compileclock
ant runtickerclock
ant runcomputerclock
If you don't want to do it this way, you can run any Java compiler/runtime or IDE with the classpath set to include the files

clink123a.jar
xercesImpl.jar
xml-apis.jar
These files are in the zip file with the source code

Time

A convenience class to handle ISO 8601 time format




package xmltypes;

public class Time {

    private int hours = 0;
    private int mins = 0;
    private int secs = 0;

    public Time(String str) {
	setTime(str);
    }

    public Time(int h, int m) {
	hours = h;
	mins = m;
	secs = 0;
    }

    public Time(int h, int m, int s) {
	hours = h;
	mins = m;
	secs = s;
    }	

    private void setTime(String str) {
	String[] parts = str.split(":");
	if (parts.length == 2) {
	    try {
		hours = Integer.parseInt(parts[0]);
		mins = Integer.parseInt(parts[1]);
		secs = 0;
	    } catch (NumberFormatException e) {
	    }
	} else if (parts.length == 3) {
	    try {
		hours = Integer.parseInt(parts[0]);
		mins = Integer.parseInt(parts[1]);
		secs = Integer.parseInt(parts[2]);
	    } catch(NumberFormatException e) {
	    }
	}
    }

    private void setTime(int hours, int mins, int secs) {
	this.hours = hours;
	this.mins = mins;
	this.secs = secs;
    }

    public void increment() {
	if (++secs == 60) {
	    secs = 0;
	    if (++mins == 60) {
		mins = 0;
		if (++hours == 24) {
		    hours = 0;
		}
	    }
	}
    }

    public String toString() {
	return paddedString(hours) + ":"  + paddedString(mins) + ":" + paddedString(secs);
    }

    private String paddedString(int n) {
	if (n <= 9) {
	    return "0" + n;
	} else {
	    return "" + n;
	}
    }
}

Timer

Interface for different implementations of timers, matches the methods defined for the timer service


/**
 * Timer service as per Timer XML description
 * This may be subclassed to provide functionality
 */
package service;

import xmltypes.*;

public interface Timer {
 
    public void setTime(Time t);

    public Time getTime();

    public boolean isValidTime();
}

Computer Timer

Implementation of timer that gets its time from the computer clock using the Calendar class






package service;

import java.util.Calendar;
import xmltypes.*;

public class ComputerTimer implements Timer {

    public void setTime(Time t) {
	// void
    }

    public Time getTime() {
	Calendar now = Calendar.getInstance();
	return new Time(now.get(Calendar.HOUR_OF_DAY),
			now.get(Calendar.MINUTE),
			now.get(Calendar.SECOND));
    }

    public boolean isValidTime() {
	return true;
    }
}

Ticker Timer

Another implementation that runs a "ticker" in a thread to update the time. This timer is invalid until something else sets its time


/**
 * Timer service as per Timer XML description
 * This may be subclassed to provide functionality
 */
package service;

import xmltypes.*;

public class TickerTimer implements Timer {
    private Time time;
    private boolean isValid;
    private Ticker ticker;

    /**
     * Constructor with no starting time has
     * invalid timer and any time
     */
    public TickerTimer() {
	time = new Time("12:00:00");
	isValid = false;
	new Ticker(time).start();
    }

    public TickerTimer(Time t) {
	time = t;
	isValid = true;
	ticker = new Ticker(time);
	ticker.start();
    }
 
    public void setTime(Time t) {
	System.out.println("Setting time to " + t);
	time = t;
	isValid = true;
	if (ticker != null) {
	    ticker.stopRunning();
	}
	ticker = new Ticker(time);
	ticker.start();
    }

    public Time getTime() {
	return time;
    }

    public boolean isValidTime() {
	if (isValid) {
	    return true;
	} else {
	    return false;
	}
    }
}

class Ticker extends Thread {
    private Time time;
    private boolean keepRunning = true;

    public Ticker(Time t) {
	time = t;
    }

    public void run() {
	while (keepRunning) {
	    try {
		sleep(1000);
	    } catch(InterruptedException e) {
	    }
	    time.increment();
	}
    }

    public void stopRunning() {
	keepRunning = false;
    }
}

Clock device


/******************************************************************
 *
 *	CyberUPnP for Java
 *
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File : ClockDevice.java
 *
 ******************************************************************/

package device;

import service.*;
import xmltypes.*;

import java.io.*;

import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;

public class ClockDevice extends Device implements ActionListener, QueryListener, NotifyListener 
{
    private final static String DESCRIPTION_FILE_NAME = "description/description.xml";
    private final static String PRESENTATION_URI = "clock/presentation";

    private ControlPoint ctrlPoint = new ControlPoint();
    
    private Timer timer;
    private Service timerService;

    private Device lastDeviceSeen;

    public ClockDevice() throws InvalidDescriptionException
    {
	super(DESCRIPTION_FILE_NAME);
	// super(new File(DESCRIPTION_FILE_NAME));

	UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);

	org.cybergarage.util.Debug.on();
	ctrlPoint.addNotifyListener(this);
	ctrlPoint.start();
	
	Action getTimeAction = getAction("GetTime");
	getTimeAction.setActionListener(this);
	
	Action setTimeAction = getAction("SetTime");
	setTimeAction.setActionListener(this);
	
	Action timeValidAction = getAction("TimeValid");
	timeValidAction.setActionListener(this);
	
	ServiceList serviceList = getServiceList();
	// there should only be a timer service
	timerService = serviceList.getService(0);
	timerService.setQueryListener(this);
	
	setLeaseTime(60);
    }

    public void setTimer(Timer t) {
	timer = t;
	System.out.println("Our timer service is " + t);
    }
    
    ////////////////////////////////////////////////
    // ActionListener
    ////////////////////////////////////////////////
    
    public boolean actionControlReceived(Action action)
    {
	String actionName = action.getName();
	if (actionName.equals("GetTime") == true) {
	    System.out.println("GetTime action control");

	    Time now = timer.getTime();
	    Argument timeArg = action.getArgument("CurrentTime");
	    timeArg.setValue(now.toString());
	    return true;
	}
	if (actionName.equals("SetTime") == true) {
	    System.out.println("SetTime action control");
	    Argument timeArg = action.getArgument("NewTime");
	    String newTime = timeArg.getValue();
	    timer.setTime(new Time(newTime));
	    System.out.println("SetTime action to " + newTime);
	    return true;
	}
	if (actionName.equals("TimeValid") == true) {
	    System.out.println("TimeValid action control");
	    boolean timeValid = timer.isValidTime();

	    Argument timeArg = action.getArgument("Valid");
	    timeArg.setValue("" + timeValid);
	    return true;
	}
	return false;
    }
    
    ////////////////////////////////////////////////
    // QueryListener
    ////////////////////////////////////////////////
    
    public boolean queryControlReceived(StateVariable stateVar)
    {
	System.out.println("query control");
	if (stateVar.getName().equals("Time")) {
	    stateVar.setValue(getTime().toString());
	    return true;
	} else if (stateVar.getName().equals("TimeValid")) {
	    stateVar.setValue("" + isValidTime());
	    return true;
	}
	return false;
    }
    
    ////////////////////////////////////////////////
    // HttpRequestListner
    ////////////////////////////////////////////////

    public void httpRequestRecieved(HTTPRequest httpReq)
    {
	System.out.println("HTTP request: " + httpReq);
	
	String uri = httpReq.getURI();
	if (uri.startsWith(PRESENTATION_URI) == false) {
	    super.httpRequestRecieved(httpReq);
	    return;
	}
    }

    public void setTime(Time time) {
	System.out.println("setTime in clock device to " + time);
	timer.setTime(time);
    }

    public Time getTime() {
	return timer.getTime();
    }

    public boolean isValidTime() {
	return timer.isValidTime();
    }

    public void deviceNotifyReceived(SSDPPacket ssdpPacket) { 

	System.out.println("New SSDPPacket, all " + ssdpPacket);
	/* There doesn;t seem to be a simple way of getting the new
	 * device from the packet, even though CyberLink creates a
	 * new device from it - and adds it to a list. Looks like we
	 * have to get the whole list and search through it for the
	 * device created by this packet :-(
	 */
	DeviceList devList = ctrlPoint.getDeviceList();
	Device dev = null;
	System.out.println("Devices: " + devList.size());
	for (int n = 0; n < devList.size(); n++) {
	    if (devList.getDevice(n).getSSDPPacket() == ssdpPacket) {
		System.out.println("Found the new device matching SSDP packet");
		dev = devList.getDevice(n);
		System.out.println("  Presentation URI " + dev.getPresentationURL());
		System.out.println("  Presentation base " + dev.getURLBase());
		break;
	    }
	}
	if (dev == null) {
	    // couldn't find it? That shouldn't happen...
	    return;
	}

	/* this doesn't work :-( device changes each time
	
	if (dev == lastDeviceSeen) {
	    // UPnP sends repeat messages to try to make sure things
	    // get through. But we don't want to repeat work
	    System.out.println("Seen this device before");
	    lastDeviceSeen = dev;
	    return;
	}
	*/

	ServiceList timerServices = new ServiceList();

	if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
	    ServiceList services = dev.getServiceList();
	    for (int m = 0; m < services.size(); m++) {
		Service svc = services.getService(m);
		if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
		    timerServices.add(svc);
		}
	    }
	}

	tryClockValidation(timerServices);
    } 

    private ServiceList getTimerServices() {
	ServiceList timerServices = new ServiceList();

	DeviceList devList = ctrlPoint.getDeviceList();
	System.out.println("Devices: " + devList.size());
	for (int n = 0; n < devList.size(); n++) {
	    Device dev = devList.getDevice(n);
	    if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
		ServiceList services = dev.getServiceList();
		for (int m = 0; m < services.size(); m++) {
		    Service svc = services.getService(m);
		    if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
			timerServices.add(svc);
		    }
		}
	    }
	}
	return timerServices;
    }

    private void tryClockValidation(ServiceList services) {
	org.cybergarage.util.Debug.on();
	for (int n = 0; n < services.size(); n++) {
	    Service svc = (Service) services.elementAt(n);

	    System.out.println("Checking service " + svc);
	    Action validAction = svc.getAction("TimeValid");
	    if (validAction.postControlAction()) {
		System.out.println("Checking TimeValid succeeded");
		Argument arg = validAction.getArgument("Valid");
		String value = arg.getValue();
		System.out.println("valid time? " + value);
		if (isValidTime() && value.equals("false")) {
		    // other clock needs to be set
		    Action setTimeAction = svc.getAction("SetTime");
		    setTimeAction.setArgumentValue("NewTime",
						    getTime().toString());
		    setTimeAction.postControlAction();
		} else if (! isValidTime() && value.equals("true")) {

		    Action getTimeAction = svc.getAction("GetTime");
		    if ( ! getTimeAction.postControlAction()) {
			System.out.println("GetTime post action failed");
		    }

		    /*
		    ArgumentList outArgList = getTimeAction.getOutputArgumentList(); 
		    // there should only be one output arg, the CurrentTime
		    Argument currentTimeArg = outArgList.getArgument(n); 
		    String currTime = currentTimeArg.getValue();
		    System.out.println("Current time from other: " + currTime);
		    setTime(new Time(currTime));
		    */

		
		    Argument arg2 = getTimeAction.getArgument("CurrentTime");
		    Object val = arg2.getValue();
		    System.out.println("Arg type " +  val.getClass());
		    System.out.println("setTime in clock validation to " + arg2.getValue());
		    setTime(new Time(arg2.getValue()));
		}
	    }
	}
    }
}

TickerClock

This creates a clock device, sets a ticker timer in it and starts the frame



package clock;

import device.*;
import service.*;

import org.cybergarage.util.*;
import org.cybergarage.upnp.device.*;

public class TickerClock {
    
    ////////////////////////////////////////////////
    //	main
    ////////////////////////////////////////////////
    
    public static void main(String args[]) 
    {
	ClockDevice clockDev = null;
	try {
	    clockDev = new ClockDevice();
	}
	catch (InvalidDescriptionException e) {
	    e.printStackTrace();
	    System.exit(1);
	}

	clockDev.setTimer(new TickerTimer());

	ClockFrame clock;
	if (args.length > 0) {
	    clock= new ClockFrame(clockDev, args[0]);
	} else {
	    clock = new ClockFrame(clockDev);
	}
	clock.start();
    }
}

ComputerClock

This creates a clock device, sets a computer timer in it and starts the frame



package clock;

import device.*;
import service.*;

import org.cybergarage.util.*;
import org.cybergarage.upnp.device.*;

public class ComputerClock {
    
    ////////////////////////////////////////////////
    //	main
    ////////////////////////////////////////////////
    
    public static void main(String args[]) 
    {
	ClockDevice clockDev = null;
	try {
	    clockDev = new ClockDevice();
	}
	catch (InvalidDescriptionException e) {
	    e.printStackTrace();
	    System.exit(1);
	}
	
	clockDev.setTimer(new ComputerTimer());

	ClockFrame clock;
	if (args.length > 0) {
	    clock= new ClockFrame(clockDev, args[0]);
	} else {
	    clock = new ClockFrame(clockDev);
	}
	clock.start();    }
}

Clock Frame

A JFrame to display the clock


/******************************************************************
 *
 *	CyberUPnP for Java
 *
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File : SampleClock.java
 *
 ******************************************************************/

package clock;

import device.*;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import org.cybergarage.util.*;
import org.cybergarage.upnp.*;

import org.cybergarage.upnp.device.*;

public class ClockFrame extends JFrame implements Runnable, WindowListener
{
    private final static String DEFAULT_TITLE = "CyberLink Sample Clock";
    private ClockDevice clockDev;
    private ClockPane clockPane;

    public ClockFrame(ClockDevice clockDev) {
	this(clockDev, DEFAULT_TITLE);
    }

    public ClockFrame(ClockDevice clockDev, String title)
    {
	super(title);

	this.clockDev = clockDev;
	
	getContentPane().setLayout(new BorderLayout());
	
	clockPane = new ClockPane(clockDev);		
	getContentPane().add(clockPane, BorderLayout.CENTER);
	
	addWindowListener(this);
	
	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;
    }
    
    ////////////////////////////////////////////////
    //	main
    ////////////////////////////////////////////////
    
    public void windowActivated(WindowEvent e) 
    {
    }
    
    public void windowClosed(WindowEvent e) 
    {
    }
    
    public void windowClosing(WindowEvent e) 
    {
	stop();
	System.exit(0);
    }
    
    public void windowDeactivated(WindowEvent e) 
    {
    }
    
    public void windowDeiconified(WindowEvent e) 
    {
    }
    
    public void windowIconified(WindowEvent e) 
    {
    }
    
    public void windowOpened(WindowEvent e)
    {
    }

}

Clock Pane

And a JPane to display the clock


/******************************************************************
 *
 *	CyberUPnP for Java
 *
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File : SampleClockPane.java
 *
 ******************************************************************/

package clock;

import device.*;
import xmltypes.*;

import java.io.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;

import javax.swing.*;
import javax.imageio.ImageIO;

import org.cybergarage.util.*;

public class ClockPane extends JPanel
{
    private ClockDevice clockDev;
    private Color lastBlink = Color.BLACK;

    public ClockPane(ClockDevice clockDev)
    {
	this.clockDev = clockDev;
	loadImage();
	initPanel();
    }
    
    ////////////////////////////////////////////////
    //	Background
    ////////////////////////////////////////////////
    
    private final static String CLOCK_PANEL_IMAGE = "images/clock.jpg";
    
    private BufferedImage panelmage;
    
    private void loadImage()
    {
	File f = new File(CLOCK_PANEL_IMAGE);
	try {
	    panelmage = ImageIO.read(f);
	}
	catch (Exception e) {
	    Debug.warning(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 = 48;
    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)
    {
	int winWidth = getWidth();
	int winHeight = getHeight();
	
	if (clockDev.isValidTime()) {
	    g.setColor(Color.BLACK);
	} else {
	    if (lastBlink == Color.WHITE) {
		g.setColor(Color.BLACK);
		lastBlink = Color.BLACK;
	    } else {
		g.setColor(Color.WHITE);
		lastBlink = Color.WHITE;
	    }
	}
	
	//// Time String ////
	Time now = clockDev.getTime();
	String timeStr = now.toString();
	
	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 = "Time";
	
	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);
	

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


Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Wed May 26 10:45:31 EST 2004
Copyright ©Jan Newmarch
Copyright © Jan Newmarch, Monash University, 2007
Creative Commons License This work is licensed under a Creative Commons License
The moral right of Jan Newmarch to be identified as the author of this page has been asserted.