OSGi : Lab Example

From CDOT Wiki
Jump to: navigation, search

Voting System (Example for the OSGi Lab)

Let us suppose that you were asked to implement a voting system as an OSGi service system.

The system should be generic enough so that it could be used for any election. Therefore you will not be using specific candidate names, as they can change for different elections.

As a proof of concept, you will be given three candidates and as the election takes place you have to print the number of votes for each candidate. The code for the entire project could be found at: the code from https://guest:1673852@zenit.senecac.on.ca/svn/ecl500/Lectures/trunk/LAB_OSGi_Example/

Design and implementation

1. Define the Service

Create the bundle cs.ecl.osgi.voting_1.0.1.qualifier

1.1 Create the VotingSystem.java
package cs.ecl.osgi.voting;

public interface VotingSystem {
	int[] countVotesFor(String code);
}
1.2 Define the MANIFEST.MF for the service
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Voting System
Bundle-SymbolicName: cs.ecl.osgi.voting
Bundle-Version: 1.0.1.qualifier
Bundle-Vendor: Seneca College - Eclipse Course
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: cs.ecl.osgi.voting

Note: The bundle does not need an activator.


2. Define the Service Provider

Create the bundle cs.ecl.osgi.votingsystem.provider_1.0.0.qualifier

2.1 Create the VotingActivator.java
package cs.ecl.osgi.votingsystem.provider;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import cs.ecl.osgi.voting.VotingSystem;
import cs.ecl.osgi.votingsystem.provider.internals.SimpleVotingSystem;

public class VotingActivator implements BundleActivator {

	private static BundleContext context;

	static BundleContext getContext() {
		return context;
	}
	
	public void start(BundleContext bundleContext) throws Exception {
		VotingActivator.context = bundleContext;
		
		VotingSystem vs = new SimpleVotingSystem();
		context.registerService(VotingSystem.class.getName(), vs, null);
		System.out.println("Voting System Registered !");		
	}

	public void stop(BundleContext bundleContext) throws Exception {
		VotingActivator.context = null;
		System.out.println("Voting System Stopped !");
	}
}
2.2 Create the SimpleVotingSystem.java

This is the internal implementation of the service and should be done in a packet that will not be exported.

package cs.ecl.osgi.votingsystem.provider.internals;

import cs.ecl.osgi.voting.VotingSystem;

public class SimpleVotingSystem implements VotingSystem {

	public static int[] votes = { 0, 0, 0, 0 }; // the test for 3 candidates +
								      // the invalid vote

	public int[] countVotesFor(String code) {

		// simple and trivial implementation of a voting system
		String[] votCodes = { "default", "yesno", "yeahnah", "ync" };
		for (int i = 0; i < votes.length; i++) {
			if (code.equals(votCodes[i]))
				++votes[i];
		}
		return votes;
	}

}
2.3 Define the MANIFEST.MF for the service provider
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Voting System Provider
Bundle-SymbolicName: cs.ecl.osgi.votingsystem.provider
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: cs.ecl.osgi.votingsystem.provider.VotingActivator
Bundle-Vendor: Seneca College - Eclipse Course
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: cs.ecl.osgi.voting,
   org.osgi.framework;version="1.3.0"

3. Define the Service Consumer (the voter)

Create the bundle cs.ecl.osgi.votingsystem.client_1.0.0.qualifier. This bundle will have a GUI for letting the voter to exercise his/her public duties.

3.1 Create the ClientActivator.java
package cs.ecl.osgi.votingsystem.client;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import cs.ecl.osgi.voting.VotingSystem;

public class ClientActivator implements BundleActivator {

	private static BundleContext context;
	private VotingSystem vs;

	static BundleContext getContext() {
		return context;
	}

	public void start(BundleContext bundleContext) throws Exception {
		ClientActivator.context = bundleContext;

		ServiceReference reference = context
				.getServiceReference(VotingSystem.class.getName());
		if (reference != null) {
			vs = (VotingSystem) context.getService(reference);

			if (vs != null) {
				// invoke GUI
				VoteDialog.runDialog(vs);
				context.ungetService(reference);
			} else
				System.err.println("the Voting Sytem cannot be used !!!");
		} else
			System.err.println("the Voting Sytem cannot be found !!!");

	}

	public void stop(BundleContext bundleContext) throws Exception {
		ClientActivator.context = null;
	}
}

If the VotingSystem object is retrieved from the OSGi system, then a dialog box is created and the object is sent to GUI.

3.2 Create the VoteDialog.java
package cs.ecl.osgi.votingsystem.client;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;
import cs.ecl.osgi.voting.VotingSystem;

public class VoteDialog extends JPanel {

	JLabel label;
	JFrame frame;
	String simpleDialogDesc = "The candidates";
	static VotingSystem vs;
	static int[] votesSoFar = new int[4];

	public VoteDialog(JFrame frame) {
		super(new BorderLayout());

		this.frame = frame;
		JLabel title;

		// Create the components.
		JPanel choicePanel = createSimpleDialogBox();

		System.out.println("passed createSimpleDialogBox");

		title = new JLabel("Click the \"Vote\" button"
				+ " once you have selected a candidate.", JLabel.CENTER);

		label = new JLabel("Vote now!", JLabel.CENTER);
		label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		choicePanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 5, 20));

		// Lay out the main panel.
		add(title, BorderLayout.NORTH);
		add(label, BorderLayout.SOUTH);
		add(choicePanel, BorderLayout.CENTER);
	}

	void setLabel(String newText) {
		label.setText(newText);
	}

	private JPanel createSimpleDialogBox() {
		final int numButtons = 4;
		JRadioButton[] radioButtons = new JRadioButton[numButtons];

		final ButtonGroup group = new ButtonGroup();

		JButton voteButton = null;

		final String defaultMessageCommand = "default";
		final String yesNoCommand = "yesno";
		final String yeahNahCommand = "yeahnah";
		final String yncCommand = "ync";

		radioButtons[0] = new JRadioButton(
				"<html>Candidate 1: <font color=red>Sparky the Dog</font></html>");
		radioButtons[0].setActionCommand(defaultMessageCommand);

		radioButtons[1] = new JRadioButton(
				"<html>Candidate 2: <font color=green>Shady Sadie</font></html>");
		radioButtons[1].setActionCommand(yesNoCommand);

		radioButtons[2] = new JRadioButton(
				"<html>Candidate 3: <font color=blue>R.I.P. McDaniels</font></html>");
		radioButtons[2].setActionCommand(yeahNahCommand);

		radioButtons[3] = new JRadioButton(
				"<html>Candidate 4: <font color=maroon>Duke the Java<font size=-2></font size> Platform Mascot</font></html>");
		radioButtons[3].setActionCommand(yncCommand);

		for (int i = 0; i < numButtons; i++) {
			group.add(radioButtons[i]);
		}

		// Select the first button by default.
		radioButtons[0].setSelected(true);

		voteButton = new JButton("Vote");

		voteButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String command = group.getSelection().getActionCommand();

				System.out.println("SELECTION " + command);

				// ok dialog
				if (command == defaultMessageCommand) {
					JOptionPane.showMessageDialog(frame,
							"This candidate is a dog. Invalid vote.");
					votesSoFar = vs.countVotesFor(defaultMessageCommand);
					printVotes(votesSoFar);
					// yes/no dialog
				} else if (command == yesNoCommand) {
					int n = JOptionPane
							.showConfirmDialog(
									frame,
									"This candidate is a convicted felon. \nDo you still want to vote for her?",
									"A Follow-up Question",
									JOptionPane.YES_NO_OPTION);
					if (n == JOptionPane.YES_OPTION) {
						setLabel("OK. Keep an eye on your wallet.");
						printVotes(vs.countVotesFor(yesNoCommand));
					} else if (n == JOptionPane.NO_OPTION) {
						setLabel("Whew! Good choice.");
					} else {
						setLabel("It is your civic duty to cast your vote.");
					}

					// yes/no (with customized wording)
				} else if (command == yeahNahCommand) {
					Object[] options = { "Yes, please", "No, thanks" };
					int n = JOptionPane
							.showOptionDialog(
									frame,
									"This candidate is deceased. \nDo you still want to vote for him?",
									"A Follow-up Question",
									JOptionPane.YES_NO_OPTION,
									JOptionPane.QUESTION_MESSAGE, null,
									options, options[0]);
					if (n == JOptionPane.YES_OPTION) {
						setLabel("I hope you don't expect much from your candidate.");
						printVotes(vs.countVotesFor(yeahNahCommand));

					} else if (n == JOptionPane.NO_OPTION) {
						setLabel("Whew! Good choice.");
					} else {
						setLabel("It is your civic duty to cast your vote.");
					}

					// yes/no/cancel (with customized wording)
				} else if (command == yncCommand) {
					Object[] options = { "Yes!", "No, I'll pass",
							"Well, if I must" };
					int n = JOptionPane.showOptionDialog(frame,
							"Duke is a cartoon mascot. \nDo you  "
									+ "still want to cast your vote?",
							"A Follow-up Question",
							JOptionPane.YES_NO_CANCEL_OPTION,
							JOptionPane.QUESTION_MESSAGE, null, options,
							options[2]);
					if (n == JOptionPane.YES_OPTION) {
						setLabel("Excellent choice.");
						printVotes(vs.countVotesFor(yncCommand));

					} else if (n == JOptionPane.NO_OPTION) {
						setLabel("Whatever you say. It's your vote.");
					} else if (n == JOptionPane.CANCEL_OPTION) {
						setLabel("Well, I'm certainly not going to make you vote.");
					} else {
						setLabel("It is your civic duty to cast your vote.");
					}
				}
				return;
			}

			private void printVotes(int[] votesSoFar) {
				for (int i = 0; i < votesSoFar.length; i++)
					System.out.println(" Candidate = " + (i + 1) + " has "
							+ votesSoFar[i]);

			}
		});
		System.out.println("calling createPane");
		return createPane(simpleDialogDesc + ":", radioButtons, voteButton);
	}

	private JPanel createPane(String description, JRadioButton[] radioButtons,
			JButton showButton) {
		int numChoices = radioButtons.length;
		JPanel box = new JPanel();
		JLabel label = new JLabel(description);

		box.setLayout(new BoxLayout(box, BoxLayout.PAGE_AXIS));
		box.add(label);

		for (int i = 0; i < numChoices; i++) {
			box.add(radioButtons[i]);
		}

		JPanel pane = new JPanel(new BorderLayout());
		pane.add(box, BorderLayout.NORTH);
		pane.add(showButton, BorderLayout.SOUTH);
		System.out.println("returning pane");
		return pane;
	}

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event-dispatching thread.
	 */
	private static void createAndShowGUI(VotingSystem vs) {

		// Make sure we have nice window decorations.
		JFrame.setDefaultLookAndFeelDecorated(true);
		JDialog.setDefaultLookAndFeelDecorated(true);

		// Create and set up the window.
		JFrame frame = new JFrame("VoteDialog");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Set up the content pane.
		Container contentPane = frame.getContentPane();
		contentPane.setLayout(new GridLayout(1, 1));
		contentPane.add(new VoteDialog(frame));
		VoteDialog.vs = vs;
		// Display the window.
		frame.pack();
		frame.setVisible(true);
	}

	public static void runDialog(final VotingSystem vs) {
		// Schedule a job for the event-dispatching thread:
		// creating and showing this application's GUI.
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI(vs);
			}
		});
	}

}
3.3 Define the MANIFEST.MF for the service consumer
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Voting System Client
Bundle-SymbolicName: cs.ecl.osgi.votingsystem.client
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: cs.ecl.osgi.votingsystem.client.ClientActivator
Bundle-Vendor: Seneca College - Eclipse Course
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: cs.ecl.osgi.voting,
    org.osgi.framework;version="1.3.0"

4. Run the Voting System (OSGi Implementation)

In Eclipse define the configuration for the Client bundle and run it.

Important Note: While there are some issues (a bug in Eclipse) with multithreaded Java Swing on Mac OX and Unix, we can run the OSGi system, if you remove the „-ws ${target.ws}“ from the launch configuration. Just delete what is highlighted below and the window should start up instantly. FixOSGi-swing-Mac.png

Results

Lab-osgi-1.png

The cs.ecl.osgi.votingsystem.client bundle running should display the Voting dialog:

Lab-osgi-2.png