Context Toolkit Primer – Part 3: Services

In Part 2 of this primer, we described how to model logic using Enactors. In this post, we will cover the third step of modeling behavior with Services attached to Widgets.

Now that we have modeled how our context-aware application makes decisions, we would like to model how it behaves (or what it does) after it decides. We do this using Services. Services would be coupled to widgets so that they can be executed. They can be consideredactuators of widgets, and allow behaviors such as actually turning the lamp on, rather than just indicating a state. Note that widgets do not need services if they just store context state, and do not need any behavior functionality. Services can also be requested to be executed remotely, so the caller (usually an enactor) does not need to be on the same machine.

For our smart living room example, we would like the LightWidget to have a service, so that we can change the light level. We need to subclass Service to define LightService to be executed to do these:

public class LightService extends Service {

	public static final String LIGHT_ON = "LightOn";
	public static final String LIGHT_OFF = "LightOff";

	private RoomApplication application;

	@SuppressWarnings("serial")
	public LightService(final Widget widget, RoomApplication application) {
		super(widget, // widget that contains this service
			"LightService", // service name
			new FunctionDescriptions() { // list of FunctionDescriptions
				{ // constructor
					/*
					 * define functions for the service
					 */
					// light on and vary brightness
					add(new FunctionDescription(
							LIGHT_ON, // function name
							"Sets the light level of the lamp", // descriptive text
							widget.getNonConstantAttributes(), // input attributes
							FunctionDescription.FUNCTION_SYNC)); // synchronicity
					// light off
					add(new FunctionDescription(
							LIGHT_OFF,
							"Sets the light of the lamp to Off",
							null, // no input attributes
							FunctionDescription.FUNCTION_SYNC));
				}
			});
		this.application = application;
	}

	...

}

In the constructor, we need a pointer to the widget that will contain this Service. In this case, the LightService is contained (encapsulated) by the LightWidget. We also need to provide a name for the service. The name would be used by other components to be able to execute the service remotely (e.g., an Enactor instantiated on a different machine than the LightWidget can execute the service).

The third argument passed to the superclass constructor is a FunctionDescriptions which is essentially a list of FunctionDescriptions. For each FunctionDescription, we specify the function name, description, input attributes (takes in Attributes like arguments to a function), and the synchronicity for executing the service (FUNCTION_SYNC or FUNCTION_ASYNC).

We have a pointer to RoomApplication that handles the application-specific GUI code to render the light level accordingly. More of this would be discussed in the next tutorial.

Since LightService subclasses Service, it needs to implement the execute(ServiceInput) method which would be called when the service is executed. It is supplied with a ServiceInput argument that specifies service name, function name, and input attributes. These are typically passed from the Enactor (or other component) that is invoking the service remotely. When the function name is “lightOn”, LightService would set the light level to what was supplied in the ServiceInput; if the function name is “lightOff”, it would set the light level to 0.

@Override
public DataObject execute(ServiceInput serviceInput) {
	String functionName = serviceInput.getFunctionName();

	if (functionName.equals(LIGHT_ON)) {
		int light = serviceInput.getInput().getAttributeValue("light");
		application.setLight(light);
	}

	else if (functionName.equals(LIGHT_OFF)) {
		application.setLight(0);
	}

	return new DataObject(); // no particular info to return
}

Finally, we attach the service to the LightWidget instance we had instantiated earlier (Part 1 or Part 1b).

LightService lightService = new LightService(lightWidget, application);
lightWidget.addService(lightService);

ServiceInputs in Enactors

Recall in Part 2 (or Part 2b), Enactors could have their EnactorReferences associated with ServiceInputs. By specifying the service name, function name, and input Attributes there, the corresponding Service’s FunctionDescription would be identified and the service executed when the EnactorReference’s query condition is true. We can add a ServiceInput to an EnactorReference in Java code through the constructor:

EnactorReference(AbstractQueryItem<?,?> conditionQuery, String outcomeValue, List<ServiceInput> serviceInputs)

Or in XML using one or more ServiceInput tags within the Reference element:

<Reference name="Off">
	...
	<ServiceInput service="LightService" function="LightOff" />
</Reference>

In Part 4, we will cover the fourth step of combining Widgets, and Enactors into a Context-Aware Application.

This entry was posted in Tutorial. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">