You are viewing outdated content for BUG. If you have a BUG Y.T. edition or 2.0 series device, please visit our updated wiki: http://wiki.buglabs.net



Getting Started Guide for OSGi Developers

From BUG Wiki

Jump to: navigation, search

Contents

Introduction

The BUG platform consists of four integrated projects.

  • Hardware - the BUGbase and dependent modules
  • JVM/OSGi Stack - the primary software stack on the BUG
  • Dragonfly (the BUG SDK)/Virtual BUG - a plugin for the popular Eclipse IDE and emulator for the BUG platform
  • BUGnet - online service for applications

Hardware

The BUG platform consists of the BUGbase, which is a standard mobile GNU/Linux computer, and the associated BUGmodules. Each BUGmodule provides one or more services to the applications running on the BUGbase. All of this hardware is open source, schematics and detailed documentation are available on the wiki. Starting out the most important thing for the developer to know are the capabilities of the BUGbase and each module. Note that in contrast to most mobile platforms, applications need not be accessed through a GUI. Though the BUGview module provides a touchscreen LCD service, the base itself allows for users to interact with applications via four hotkeys, a four-way joystick, and two buttons, while information can be output viathe base's status bar and speaker. The list of expansion modules is growing, currently this includes GPS, camera, motion sensing, accelerometer, touchscreen LCD, audio, wifi, and low-level input/output (Von Hippel). The base also provides a mini-USB port for other peripherals, and there is a breakout cable for an Ethernet connection for internet connectivity. The entire file system including kernel is on a removable flash memory card for flexibility, on a standard ext3 filesystem.

JVM/OSGi Stack

Image:JVM_OSGi_stack.jpg‎

The recommended platform for BUG applications is the JVM, running with OSGi on top. The stack currently consists of the Java PhoneME Advanced Personal Profile (Java 1.4 based) JVM, and the Concierge OSGi framework (OSGi Spec R3). Though developers are not forced to stay within the JVM/OSGi framework, it provides numerous advantages to writing outside of the BUG stack. First and foremost, all of the device drivers are abstracted out to standard Java interfaces. This allows the details of implementation to be hidden from the application writer. For example, no knowledge is needed of GPS protocols to gain a position fix, simply use the getLatitudeLongitude() method of the IPositionProvider interface to receive the coordinates at your current position.

This is fairly standard for a Java based mobile application. However when coupled with OSGi, things start to get interesting. The OSGi framework allows for the loading and unloading of bundles during runtime. These bundles provide and consume various services. This technology enables the modular nature of the BUG. In order to be able to hot plug modules when the BUG is running, you need a way to hold programs from trying to execute before modules are available - and to stop when modules are removed. OSGi handles this life cycle management seamlessly from the application developer. OSGi also enables several applications to use one service, such as the LogService or HttpService, without having to load multiple instances of the service into memory. Perhaps the most important advantage of this approach is that it makes the application dependent on services rather than specific modules. You write an application to use an interface, not any specific module. Today there is the BUGlocate GPS module, but in the future GPS may be integrated to the base, or several service may be integrated into a single module, or an entirely different technology may be used to provide position information. So long as your application is written to the IPositionProvider interface, your application can execute getLatitudeLongitude() and the OSGi framework handles finding the right device. This also allows software and hardware to be accessed on an equal footing. Your IPositionProvider can be implemented by a hardware module, a piece of hardware on the base, or a bundle that reads a log file of positions and streams them out - and the OSGi framework will provide it to the application seamlessly.

Finally, the JVM stack allows for multiple languages. Most of our code is in Java, but Jython, JRuby, Scala, Groovy, and any other language for the JVM should be available with minimum fuss as well. Additionally C/C++ code can be accessed via JNI as usual.

Dragonfly (the BUG SDK)/Virtual BUG

Dragonfly (the BUG SDK) is a plugin for the popular Eclipse IDE, with useful features for both end users and developers. It provides a perspective in Eclipse, which is a default set of views and toolbars for BUG application development. All the regular tools in Eclipse are present, such as the Debugger and CVS repository browsing. For more information on Eclipse look at this tutorial (link).

  • Virtual BUG - an emulator for the BUG platform. Currently it runs from the VM up, although shortly it will provide full virtualization of BUG via QEMU. It allows for the development of applications without the target hardware, or to debug applications using the Eclipse IDE. It also allows end users to try applications without having to copy them over to their BUG.
  • BUGnet view - integrates the BUGnet web service with the BUG SDK. This allows users and developers to download, upload, and search for applications on BUGnet through Eclipse directly, without having to import or export archive files.
  • MyBUGs view - shows the currently running Virtual BUGs and physical BUGs on your local machine and your network. For each BUG it lists the currently installed modules, applications, and available web services.
  • BUG Application Wizard - manages the OSGi framework for the applications developer

BUGnet

Currently BUGnet is used as a repository for applications. Anyone can upload and download the applications through the web or Dragonfly (the BUG SDK). This allows developers to share applications with other developers and users.

BUG Services

Web Services

The BUG exposes several services through HTTP calls in addition to OSGi interfaces using a standard XML format. This allows programs of all languages running outside of the standard framework to interact with the modules and services. To access the interface for the Virtual BUG, start the Virtual BUG in Dragonfly (the BUG SDK) and navigate your browser to http://localhost:8082/. With a real BUG the default address is http://10.10.10.10/. That URL will show the screen below, which allows access to a list of the available web services, modules, and applications on BUG.

For a list of modules, navigate to http://localhost:8082/module

<modules>
   <module index="1" name="CAMERA"/>
   <module index="3" name="MOTION"/>
</modules>

An individual module listing is available by each modules index number http://localhost:8082/module/3

<module name="MOTION">
   <property value="MOTION" type="String" mutable="false" name="moduleName"/>
   <property value="3" type="String" mutable="false" name="Slot"/>
</module>

To get the list of services, navigate to http://localhost:8082/service. This will provide a listing similar to the one below. This listing is for a BUG with a motion sensor and a camera.

<Services>
   <Service description="Returns the last time motion was detected" name="Motion">
      <Get parameters="" returns="text/xml"/>
   </Service>
   <Service description="A web service message board." name="Statusbar">
      <Get parameters="" returns="text/plain"/>
      <Put parameters="[Message]" returns="text/plain"/>
   </Service>
   <Service description="Retrieves image from camera module." name="Picture">
     <Get parameters="" returns="image/jpeg"/>
   </Service>
</Services>

Navigating to the Name of the service gives access to that service, for example http://localhost:8082/service/Picture will take a picture with the camera and return that in JPEG form to the requester.

A list of programs is available in the same form as the two above, though the listing is large. In that case the individual programs are indexed by their id number, and a GET request will return a .jar archive of the program.

You can also register your own applications as a service on equal footing with those provided by the BUG modules themselves. The interface is the PublicWSProvider, and is detailed below.

OSGi Console

OSGi provides a console for handling the bundles running in the framework. The commands available are listed below.

  • bundles [filter]: Returns a list of bundles present in current OSGi runtime environment.
  • consumers [(bundle id | bundle name)]: Returns a list of services that each bundle consumes is currently consuming.
  • exit: Stop all bundles and shutdown OSGi runtime.
  • gc  : Collects the JVM garbage.
  • headers bundleId [filter]: Returns the headers for a given bundle.
  • help [filter]  : Print table of currently available commands.
  • install bundle_URL  : Install a bundle from the given URL.
  • printenv [filter]  : Returns the system settings.
  • printlog [filter]  : Displays log messages.
  • producers [(bundle id | bundle name)]: Returns a list of services that each bundle is currently offering.
  • quit  : Stop the shell bundle.
  • restart  : Stop and restart all active bundles.
  • services [(bundle id | bundle name)]: Returns a list of services present in the runtime.
  • start (bundle_Id | bundle_name)  : Starts the specified bundle.
  • stop (bundle_Id | bundle_name)  : Stops the specified bundle.
  • uninstall (bundle_Id | bundle_name)  : Uninstalls the specified bundle.
  • update (bundle_Id | bundle_name)  : Updates the specified bundle.

Basic Anatomy of a BUG application

Activator

  • Activator.java - Every application created in Dragonfly (the BUG SDK) by the new project wizard includes an Activator class. When the OSGi framework loads, the Start method is called, and upon closing of the OSGi framework Stop is called. This is the equivalent of the public static void main(String[] args) method in most Java programs. Anything called from this method will run for the entire lifetime of the bundle.

ServiceTracker

  • ServiceTracker.java - Most applications will consume or provide services, in this case there will be a ServiceTracker class. In this case the Activator class will not have to be modified. Upon initialization the Activator class will setup the ServiceTracker, which runs for the entire lifetime of the bundle. The ServiceTracker lists what services the bundle needs to start in the initServices() method. Once those services are available, the OSGi framework calls the doStart() method in the ServiceTracker class. If one of those services is lost, or the bundle is closed, the doStop() method is called. Thus in these type of applications doStart() in your ServiceTracker class is the equavalent of public static void main(String[] args). Much like the Activator class, the ServiceTracker class is primarily boilerplate generated by the New Project wizard. Services selected in the wizard will be added to the ServiceTracker class appropriately and the start() method of the Activator class will be setup to start the ServiceTracker. The only methods you will have to edit are the doStart() and doStop() methods of the ServiceTracker.

Manifest

  • Manifest.mf - the import-package and export-package properties are set by the new project wizard, but any changes to OSGi dependencies will have to be reflected here

Creating a BUG application

Creating a New Project

  1. Install the Dragonfly SDK
  2. Decide what services the application needs
  3. If necessary, switch Perspective to the Dragonfly perspective.
  4. Select the New BUG Project icon in the tool bar, or File->New->Project->Dragonfly->BUG Application
  5. In the New BUG Application window, fill out the Name and Author fields
  6. Click Next.
  7. Click Start Virtual BUG
  8. Once the Virtual Bug has started, right click on the slots and add the module(s) your application will use
  9. Under Target BUG select Virtual BUG
  10. Check off the services your application will require
  11. Click Finish
  12. Click Run As and choose Virtual BUG

Almost all applications will consume OSGi services. This is the most direct way to gain access to the various modules, so we begin with a simple example of gaining access to the LCD. In this case select the IModuleDisplay service during the create New Project wizard. The Activator, ServiceTracker, and manifest files will all be created. The Activator and manifest need no changes. Within the ServiceTracker the method that needs to be modified is doStart().

Consuming a Service

To consume any OSGi service, you will need a ServiceTracker class. This is created by the

import org.osgi.framework.BundleContext;
import com.buglabs.application.AbstractServiceTracker;

import simplegui.app.*;

import com.buglabs.bug.module.lcd.pub.*;
 /**
 *	Service tracker for the BugApp Bundle;
 *
 */
public class MyAppServiceTracker extends AbstractServiceTracker {	

	public MyAppServiceTracker(BundleContext context) {
		super(context);
	}
	
	/**
	 * Determines if the application can start.
	 */
	public boolean canStart() {
		return super.canStart();
	}
	
	/**
	 * If canStart returns true
     * this method is called to start the application.
     * Place your fun logic here. 
	 */
	public void doStart() {
		System.out.println("MyAppServiceTracker: start");
		IModuleDisplay display = (IModuleDisplay) getService(IModuleDisplay.class);
		MyApp app = new MyApp(display);
	}

	/**
	 * Called when a service that this application depends is unregistered.
	 */
	public void doStop() {
		System.out.println("MyApp: stop");

	}

	/**
	 * Allows the user to set the service dependencies by
     * adding them to services list returned by getServices().
     * i.e.nl getServices().add(MyService.class.getName());
	 */
	public void initServices() {
		getServices().add("com.buglabs.bug.module.lcd.pub.IModuleDisplay");
	}
}

The important piece of code is:

IModuleDisplay display = (IModuleDisplay) getService(IModuleDisplay.class);
MyApp app = new MyApp(display);

This passes access to the LCD to the MyApp class, which is shown below.

import com.buglabs.bug.module.lcd.pub.*;
import java.awt.Frame;

public class MyApp  {
	
	/**
	 * The BUG's LCD module interface
	 */
	private IModuleDisplay display;
	
	
	/**
	 * This takes the display from the service tracker and passes it in
	 * @param display
	 */
	public MyApp(IModuleDisplay display) {
		super();
		this.display = display;
                Frame frame = display.getFrame();
                frame.show();
	}
}

In the above class, the application now has access to the service IModuleDisplay and can use all of its methods. This is the way in which all the APIs work, simply grab the service using the getService(class) method, and then use its methods. You do not have to worry about if the service is unavailable - the framework handles all the life cycle details. You do not need to worry about which slot the module is connected to or even what is providing the service.

Two basic examples of consuming services:


Providing a Service

Some applications will want to provide an OSGi service to multiple applications. This case is a bit more specialized, but no more difficult. Simply use the context.registerService(class, class, params) method.

 package publishexample;
 
 import java.util.Random; 
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 
 public class Activator implements BundleActivator, ExampleServiceInterface {  
 
 	private ServiceRegistration serviceRegistration;
 
 	public void start(BundleContext context) throws Exception {
 		serviceRegistration = context.registerService(ExampleServiceInterface.class.getName(), this, null);
 		
 		ServiceConsumer consumer = new ServiceConsumer(context);
 	} 
 
 	public void stop(BundleContext context) throws Exception {
 		serviceRegistration.unregister();
 	}
 
 	public int getSpecialValue() {
 		Random r = new Random();
 		return r.nextInt();
 	}
 	
 	private class ServiceConsumer {
 
 		public ServiceConsumer(BundleContext context) {
 			ServiceReference sr = context.getServiceReference(ExampleServiceInterface.class.getName());
 			ExampleServiceInterface esi = (ExampleServiceInterface) context.getService(sr);
 			
 			System.out.println("Example service value: " + esi.getSpecialValue());
 		}
 		
 	}
 }
 package publishexample;
 
 public interface ExampleServiceInterface {
 	public int getSpecialValue();
 }

Creating a New Web Service

There are multiple ways to create a web service, but the simplest way is to use the PublicWSProvider interface. This is specific to the BUG, but has the advantage of hiding many of the details for those new to web programming. Additionally it registers the application in the BUG's list of services, just like the modules' services. Below is a simple "Hello World" example that can be accessed at http://localhost:8082/service/examplews.

/**
 *	Generated by Dragonfly SDK
 *
 */
package examplews.servicetracker;

import org.osgi.framework.BundleContext;
import com.buglabs.application.AbstractServiceTracker;

import com.buglabs.services.ws.PublicWSAdmin;

import examplews.app.ExampleWSApp;
 /**
 *	Service tracker for the BugApp Bundle;
 *
 */
public class ExampleWSServiceTracker extends AbstractServiceTracker {	

	private PublicWSAdmin wsAdmin;
	
	private ExampleWSApp app;
	
	public ExampleWSServiceTracker(BundleContext context) {
		super(context);
	}
	
	/**
	 * Determines if the application can start.
	 */
	public boolean canStart() {
		return super.canStart();
	}
	
	/**
	 * If canStart returns true
     * this method is called to start the application.
     * Place your fun logic here. 
	 */
	public void doStart() {
		System.out.println("ExampleWSServiceTracker: start");
		app = new ExampleWSApp();
		wsAdmin = (PublicWSAdmin) getService(PublicWSAdmin.class);
		wsAdmin.registerService(app);
	}

	/**
	 * Called when a service that this application depends is unregistered.
	 */
	public void doStop() {
		System.out.println("ExampleWSServiceTracker: stop");
		wsAdmin.unregisterService(app);
	}

	/**
	 * Allows the user to set the service dependencies by
     * adding them to services list returned by getServices().
     * i.e.nl getServices().add(MyService.class.getName());
	 */
	public void initServices() {
		getServices().add("com.buglabs.services.ws.PublicWSAdmin");
	}
	
}

The registered PublicWSProvider will be passed com.buglabs.services.ws.IWSResponse objects when a HTTP call is made to the URL of the service. This URL is determined by the string returned by getPublicName(). The location of the service in this example will be http://localhost:8082/service/exampleWS.

The execute(int operation, String input) method runs when a HTTP call is recieved. The operation is either a GET, PUT, POST, or DELETE. The input string the body of the message recieved. For GET calls this will just be the URL of the service. For a POST call, this value be the form data, in the form of key1=value1&key2=value2 etc.


package examplews.app;

import java.util.List;

import com.buglabs.services.ws.IWSResponse;
import com.buglabs.services.ws.PublicWSDefinition;
import com.buglabs.services.ws.PublicWSProvider;

public class ExampleWSApp implements PublicWSProvider {

	public String getDescription() {
		return "Example Web Service";
	}

	public String getPublicName() {
		return "exampleWS";
	}

	public PublicWSDefinition discover(int operation) {
		if (operation == PublicWSProvider.GET) {
			return new PublicWSDefinition() {
				public String getReturnType() {
					return "text/html";
				}

				public List getParameters() {
					return null;
				}
			};
		}
		return null;
	}

	public IWSResponse execute(int operation, String input) {
		if (operation == PublicWSProvider.GET) {
			return new IWSResponse() {

				private String message = "";

				public Object getContent() {
					System.out.println("get");
					StringBuffer strbuf = new StringBuffer();
					strbuf.append("<h2>Hello World!</h2>");
					return strbuf.toString();
				}

				public int getErrorCode() {
					return 0;
				}

				public String getErrorMessage() {
					return null;
				}

				public String getMimeType() {
					return "text/html";
				}

				public boolean isError() {
					return false;
				}

			};
		} else {
			return new IWSResponse() {
				private String message = "";

				public Object getContent() {
					return new Object();
				}

				public int getErrorCode() {
					return 405;
				}

				public String getErrorMessage() {
					return "Only GET requests are available on this resource";
				}

				public String getMimeType() {
					return "text/html";
				}

				public boolean isError() {
					return true;
				}
			};
		}

	}
}

Where to go from here

This is only a brief introduction, and not meant to be a definitive guide. This Wiki contains a great deal of reference documentation, from Javadocs and schematics to step by step tutorials. Additionally, all of the applications on BUGnet include their source. Several of these applications are examples. Also, check the Developer Resources page for links to the forums, mailing list, and IRC channel.