mConsole provides a rich set of APIs that allow you to customize and extend its capabilities through custom plug-ins. This document describes the internal architecture of mConsole, and the ways to create different types of plug-ins.
Contents:
mConsole consists of two basic layers: management container and plug-ins deployed over the container.
The mConsole container is actually an OSGi framework. Specifically, this is an mBS with core OSGi-compliant bundles providing functionality. You can manage the container as you would do with any mBS. You could add/remove bundles, configure properties, issue console commands, etc.
So, logically, a plug-in for mConsole is actually a normal OSGi-compliant bundle running in the container framework. Plug-ins can share resources by registering and getting OSGi services, and by exporting and importing Java packages. The mConsole container uses the OSGi Framework API for essential plug-in management. The additional API libraries for creating GUI and functional plug-ins are exported in the container framework, so custom plug-ins can use them by importing them in their manifests. The most important components of mConsole's visual interface and functionality are exported as OSGi services so they can be used by custom plug-ins.
The overall structure of mConsole is depicted in Figure 1. It consists of the container framework, the general-purpose plug-ins, and the specific administration plug-ins (mBS-administration plug-ins, mConsole-administration plug-ins, mPRM-administration plug-ins, and your custom ones). Each layer provides services via well-defined API to the components of the above layer.

Figure 1: Overall architecture of mConsole
Let's go through the components of this structure.
As already mentioned, the base API used by mConsole plug-ins is the OSGi Framework
API (the org.osgi.framework package). It enables plug-ins to share
packages and services, and to use the framework's resources.
The GUI components of mConsole are constructed on the basis of the PGUI API
library. Menus, toolbars, buttons, consoles, etc. are created using classes
defines in PGUI, such as com.prosyst.pgui.PMenu, com.prosyst.pgui.PMenuItem,
com.prosyst.pgui.PToolbar, etc. PGUI is based on the standard AWT
library, extending its capabilities in a way similar to the Swing library. More
information about PGUI is available in the PGUI Tutorial (downloadable from
http://dz.prosyst.com/pdoc/).
The visual components and additional functions of mConsole use the mConsole
API (the com.prosyst.mc and com.prosyst.mc.login packages)
enabling custom plug-ins to plug custom GUI components and administrate different
types of servers. The PGUI components, which are the actual building blocks
of mConsole, are wrapped by the mConsole API. So, if you need to add GUI components
to mConsole, you have to create them through the mConsole API.
The administration of the services available on the managed OSGi framework
is possible through the Remote Services API (all com.prosyst.mc.admin.*
packages), exported by the mBS-specific plug-ins. This API provides remote implementation
of the most essential OSGi-defined and ProSyst-defined services. The connection
to the framework and the remote administration is acquired over the ProSyst
Message Protocol (PMP). Detailed description and specification of PMP is available
in the Bundles -> RPC Category
chapter of this documentation.
There is a set of login services running in the mConsole container. Each login
service (com.prosyst.mc.login.LoginService) enables mConsole to
connect to a specified host and log in the specified system, using its own set
of connection and login properties. All are registered under the same service
interface but have different registration properties which determine the type
of server it will connect (mBS, MC, mPRM, Tracer or a custom type).
Each login service performs two very important functions:
There are three default login services:
You can also create custom types of login services, that will enable you to use mConsole for login and administration of custom types of servers.
The Login Box of mConsole detects the login services registered in the framework, and uses the connection properties defined for each login service to visualize their settings.
The Visual Container (com.prosyst.mc.VisualContainer) service
provides access to the basic components of mConsole, such as status bar, error
messages, info messages, progress bar, etc. (see Components
of the mConsole Window). You can use it to access these basic elements,
as well as add visual components (tabs or Settings dialogs) directly
to the container.
Each GUI plug-in and property editor containing GUI components, such as menus,
toolbars, tabs and subtabs, is represented by a GUI factory (com.prosyst.mc.GUIFactory)
service. Each GUI factory is registered with a GUIFactory.GUI_NAME
property. It indicates its display name. Optionally, a GUI factory can have
a custom display icon, if it is registered with the GUIFactory.GUI_ICON
property.
The Visual Container listens for the registration of GUIFactory
services. It takes care of displaying the ones that are indicated by the corresponding
login service.
Additional GUI factory (com.prosyst.mc.mbs.bundles.AdditionalGUIFactory)
services are specific to the MBS server type. If a plug-in needs to add
additional GUI components specific to MBS connection type, such as subtabs,
bundle tree, etc., it must export an additional GUI factory service. The main
idea of additional GUI factories is the same as the idea of GUI factory services.
Each additional GUI factory is registered with an AdditionalGUIFactory.GUI_NAME
property. It indicates its display name. Optionally, an additional GUI factory
can have a custom display icon, if it is registered with the AdditionalGUIFactory.GUI_ICON
property.
mConsole GUI components are PGUI elements, but they are not created directly using the PGUI API. The PGUI elements are wrapped in the mConsole API.
As already described, GUIInterface is the parent interface for
any GUI component added to mConsole. There are three child interfaces that extend
GUIInterface and provide utility methods for the type of component
they represent:
com.prosyst.mc.mbs.bundles.BundlesGUIInterface – Represents
bundle-related components.com.prosyst.mc.mbs.bundles.AdditionalGUIInterface – Represents
any additional bundle component. It provides utility methods which will be
used by the visual container to place the component and its toolbar, menu
item and setting component (if such exists). com.prosyst.mc.mbs.editor.EditorGUIInterface – Represents
the basic interface for a bundle’s property editor. Property editors are specific
components that allow the user to edit the properties contained in a bundle
available on the connected framework.In addition, the com.prosyst.mc.SettingsGUIInterface represents
components added to the Settings dialog.
Figure 2: Types of GUI components of mConsole
All GUI components must be provided through a GUI Factory service so as to be detected by the Visual Container service.
A basic GUI component of mConsole can be:
com.prosyst.pgui.PMenu object.com.prosyst.pgui.PToolBar object.com.prosyst.pgui.PComponent object.Usually, the default GUI plug-ins distributed with mConsole add a uniform set of GUI components: a menu, a toolbar and a tab.
If you want to add other GUI components, such as bundle-related subtabs, or access the bundle tree and existing tabs, refer to the Using the mBS GUI Components section.
To create GUI component(s), take the following steps:
com.prosyst.mc.GUIInterface interface. It will
provide the basic GUI components added to the mConsole container.GUIFactory interface and register it as a service.
Its getInstance method will return new instances each time or
one cached instance of the GUIInterface implementation, created
in step 1.In this section, we shall demonstrate the creation of custom GUI components, using the source code of the GUI Plugin Demo, available at demo/framework/mcplugin.
GUIInterface represents the base mConsole GUI component. It provides
utility methods which will be used by the visual container to place the component
and its toolbar, menu item and setting component (if they exist).
To create the plug-in's main component, you need to implement the getComponent()
method of GUIInterface. The main component of a plug-in will be
placed in a new tab added to mConsole's main panel (see mConsole
General Description: Components of the mConsole Window). The created main
component must be returned by the implementation of this method in the form
of a com.prosyst.pgui.PComponent object.
Listing 1.1.1 illustrates creating a custom plug-in component. The sample implementation
of the getComponent() method constructs a panel from a com.prosyst.pgui.PPanel
object.
public PComponent getComponent() {
if (mainPanel == null) {
mainPanel = new PPanel(new GridBagLayout());
mainPanel.setBorder(new Borders.BevelBorder(Borders.BevelBorder.RAISED));
PLabel label = new PLabel("This is the Demo Plugin Tab");
label.setFont(new Font("SansSerif", Font.BOLD, 38));
mainPanel.add(label,
new GridBagConstraints2(0, 0, 1, 1, 1.0, 0.0,
GridBagConstraints2.CENTER, GridBagConstraints2.NONE,
new Insets(5, 5, 5, 5), 0, 0)
);
mainPanel.add(new PLabel(new ImageIcon(DemoToolBar.class.getResource("demo1.gif"))),
new GridBagConstraints2(0, 1, 1, 1, 1.0, 1.0,
GridBagConstraints2.CENTER,
GridBagConstraints2.NONE, new Insets(0, 5, 5, 5), 0, 0));
}
return mainPanel;
}
|
To create a toolbar that will be added to the toolbar area of mConsole, use
the getToolBar() method of GUIInterface. The created
toolbar must be a com.prosyst.pgui.PToolBar object.
Listing 1.1.2 shows creating the plug-in's toolbar.
public PToolBar getToolBar() {
if (demoToolBar == null) {
demoToolBar = new DemoToolBar(this);
}
return demoToolBar;
}
|
To create a menu for the plug-in, use the getMenuRoot() method
of GUIInterface. The created menu must be a com.prosyst.pgui.PMenu
object.
Listing 1.1.3 shows creating the plug-in's menu.
public PMenu getMenuRoot() {
if (menu == null) {
menu = new PMenu("Demo Menu");
item = new PMenuItem("Hello");
item.setIcon(DEMO_GIF);
item.setActionCommand(GREETING_CMD);
item.addActionListener(this);
item.setEnabled(false);
secondItem = new PMenuItem("Progress Demo");
secondItem.setIcon(DEMO2_GIF);
secondItem.setActionCommand(THREAD_CMD);
secondItem.addActionListener(this);
secondItem.setEnabled(false);
menu.add(item);
menu.addSeparator();
menu.add(secondItem);
System.out.println("Demo plug-in: Demo Menu created");
}
return menu;
}
|
To create a Settings dialog, use the getSettingInterface() method
of GUIInterface. The created dialog is a com.prosyst.mc.SettingsGUIInterface
object.
Using the Visual Container service allows you to access all mConsole components such as message dialogs, status bar, progress bar, print dialog, etc. The Visual Container service is exported by default in the mConsole container.
To use the Visual Container, you need to get it as a service from the framework. Listing 1.1.4 shows getting the Visual Container service.
visContRef = bc.getServiceReference(VisualContainer.class.getName());
visContainer = (VisualContainer)bc.getService(visContRef);
|
Listing 1.2.2 shows using the progress bar and message dialogs of mConsole through the Visual Container service.
visContainer.clearProgressBar();
visContainer.setProgressMaximum(100);
for (int i = 1; i <= 100; i++) {
visContainer.updateProgressValue("Current percents: " + i + "%");
synchronized (this) {
try {
this.wait(50);
} catch (Exception exc) {}
}
}
demoGUI.notifyAfterFinished();
visContainer.clearProgressBar();
visContainer.showMessageDialog(VisualContainer.INFO_MESSAGE,
"Hello there!\n The progress demo is finished.",
"Just a greeting", null);
|
The GUIFactory interface has a single method, the getInstance(String
serverType), which must create and return a new instance of the GUIInterface
interface. As already mentioned, this can be a new instance of the GUIInterface
each time this method is called, or one cached instance for all method calls.
The parameter passed to this method by mConsole is the connected server type.
You can use it to define whether the GUIInterface will be loaded
for the current server type. It can be:
LoginService.MBS_CONNECTION LoginService.MC_CONNECTIONLoginService.MPRM_CONNECTION Listing 1.2.1 illustrates implementing the getInstance method.
We want this sample implementation to add GUI components to mConsole only in
MBS connection mode. For that reason, it checks the value of the serverType
parameter. If it is not null and it is equal to the constant LoginService.MBS_CONNECTION,
then we make a new instance of the GUIInterface implementation
and return it:
public synchronized GUIInterface getInstance(String serverType) {
if(serverType !=null && serverType.equals(LoginService.MBS_CONNECTION)) {
gui = new DemoGUI(bc); //our GUIInterface implementation
return gui;
} else {
return null;
}
}
|
Your bundle must register the com.prosyst.mc.GUIFactory implementation
as a service:
// register this as a service Hashtable props = new Hashtable(); props.put(GUI_NAME, "Demo Console Tab"); guiFactoryRegistration = bc.registerService(GUIFactory.class.getName(), this, props); |
The name of the tab must be added as a property of the registered service.
The name of the property is defined as a constant in the com.prosyst.mc.GUIFactory
interface – GUI_NAME.
Note: Optionally, the GUIFactory
service can be registered with an additional property – the GUIFactory.GUI_ICON.
It will specify the icon with which the GUI component will appear. If you don’t
specify an icon, the default ProSyst logo will be used as an icon.
For connections to MBS server types (OSGi frameworks), mConsole provides remote implementations of the core OSGi-defined services. You can use them instead of trying to get remote reference to the services running on the connected framework over PMP. These are:
com.prosyst.admin.services.cm.RemoteConfigAdmin
service
com.prosyst.admin.services.http.RemoteHttpHelper
service
com.prosyst.admin.services.log.RemoteLogService
service
com.prosyst.admin.services.permissions.RemotePermissionAdmin
service
com.prosyst.admin.services.useradm.RemoteUserAdmin
service.
In addition, some of the essential ProSyst services have their remote implementations available in the MC API. These are:
com.prosyst.admin.services.power.RemotePower
service
com.prosyst.admin.services.powerman.RemotePowerManager
service
com.prosyst.admin.services.resman.RemoteResourceManager
service
com.prosyst.admin.services.packages.RemotePackageAdmin
service
com.prosyst.admin.services.snmp.RemoteSNMPAdmin
service.
All these services can be invoked locally on the mConsole framework
by using the getServiceReference and getService standard
methods of org.osgi.framework.BundleContext. They will receive
remote reference to the corresponding services running on the connected
OSGi framework and will be able to manipulate them. In this way you do not
have to bother with calling the necessary services yourself.
The following code example illustrates getting the remote User Admin Service and taking a couple of actions through it (namely, adding a new user to the “Administration” group and changing the password of the “admin” user). The remote User Admin will take care of making the User Admin running on the connected OSGi framework perform these actions, in turn.
ServiceReference ref = bc.getServiceReference(RemoteUserAdmin.class.getName()); |
In addition, the mConsole Remote Services API provides utility packages providing
means for obtaining miscellaneous information about the connected framework
through the com.prosyst.admin.framework package.
Note: Optionally, you can use directly the PMP API for obtaining remote reference to the services running on the OSGi framework. This is done in the standard way described in the PMP Bundle documentation.
A plug-in can access MBS connection specific components by implementing and
registering as a service the com.prosyst.mc.mbs.bundles.AdditionalGUIFactory
interface. Its getInstance method will return new instances each
time or one cached instance of the AdditionalGUIInterface implementation.
As you may have notices, the pattern for using AdditionalGUIFactory
and AdditionalGUIInterface is similar to the pattern for using
GUIFactory and GUIInterface.
By creating a custom login service, you can enable mConsole to log in a custom type of servers, and administrate them in a custom way. In summary, you can:
To create a custom login service, you must implement and register as a service
the com.prosyst.mc.login.LoginService interface.
In this section, we shall demonstrate the step-by-step creation of a custom login service with name "TEST". It will provide TCP/IP connection to a custom server type. The login properties to be defined by the user in the Login Box are: server host and server port.
For simplicity, our custom server type will be a simple server application
listening for client conections on TCP port 3333. You can create one (you just
need a standard java.io.ServerSocket listening) or pack in a bundle
the code shown in Listing 1.1 of the Connector
Service Bundle document.
Step 1. The first aspect in implementing a login service is defining its login properties. They will be visualised by mConsole's Login Box, and the user will be able to set appropriate values to them.
The login properties of the service can contain any information you need for your custom connections. For example, this could be: host, port, user account, timeout, etc.
Defining the login settings takes two tasks:
ObjectClassDefinition created from the configuration
resource by your custom implementation of the getLoginProperties()
method of LoginService. Note: The Server Name and Server Type login properties will automatically be added to the settings displayed in the Login Box. You don't have to add them to the login service's configuration.
1.1. Providing a configuration resource. Here, we shall not provide detailed explanations of the principles for using the Configuration Admin. We only present a sample code and configuration XML that enables our "TEST" login service to provide the custom server's host and port as user-configurable login properties.
Listing 3.1.1 shows a sample org.osgi.service.cm.ManagedService
implementation that will provide the configuration resource of our TEST login
service. It registers a Managed Service with PID: test.login.service.pid.
It contains a couple of configuration properties:
host_ID" - The server hostport_ID" - The server port.
import org.osgi.framework.*;
import java.util.Hashtable;
import java.util.Dictionary;
import org.osgi.service.cm.ManagedService;
/*
* The role of this class is to provide the host and port
* login properties as configuration resources in the
* OSGi Configuration Admin.
*/
public class TestLoginManagedService implements ManagedService {
private ServiceRegistration reg;
private Hashtable props;
public TestLoginManagedService(BundleContext bc) throws BundleException {
props = new Hashtable();
//The PID of the login service's configuration
props.put(Constants.SERVICE_PID,"test.login.service.pid");
//The properties contained in the configuration
props.put("host_ID","HOST");
props.put("port_ID","PORT");
bc.registerService("org.osgi.service.cm.ManagedService", this, props);
}
public void destroyConfiguration() {
reg.unregister();
}
public void updated(Dictionary properties) {
if(properties==null) {
reg.setProperties(props);
} else {
reg.setProperties(properties);
}
}
}
|
And Listing 3.1.2 shows the configuration XML that describes the login service's configuration PID and the host and port properties it contains.
<?xml version="1.0" encoding="UTF-8"?> |
1.2. Returning the ObjectClassDefinition through the getLoginProperties()
method implementation. Implement the getLoginProperties() method
so as to obtain the ObjectClassDefinition created by the MetaTypeProvider
of your login service configuration. This method will be called internally by
mConsole to get the appropriate login properties and visualize them in the Login
Box when this type of server is selected from the Server Type combo box.
public ObjectClassDefinition getLoginProperties() {
try {
//Calling the MetaDataManager to provide the necessary ObjectClassDefinition
ServiceReference ref = bc.getServiceReference(MetaDataManager.class.getName());
|
Step 2. Establishing/closing connections to your custom server
type. The connecting/disconnecting mechanism of the custom login service
is the next essential aspect that must be implemented. The connection logic
of login services is defined through the implementation of the connect(Dictionary
properties) method of the LoginService interface. This method
is called internally by mConsole when the user selects a server of the custom
type from the Login Box, and presses the Connect button. It establishes
physical connection to the specified server. The properties parameter
contains the configuration properties defined by our login configuration resource,
but the ObjectClassDefinition is transformed into a java.util.Dictionary
object, with contents in a <property> - <value> form. The
<property> part will reflect the ID of the configuration property,
not the property name.
The connect method returns a boolean indicating if
the connection is successful or not.
In the case with our TEST login service, we need to get the values of the two
properties we defined in the service configuration: the "host_ID",
representing the server host, and the "port_ID", representing
the port. On the basis of the host and port information, we shall create a TCP/IP
connection to the user-specified server. We shall pass a socket URI to the Connector
Service, and it will create a TCP connection to the specified server.
public boolean connect(Dictionary dict) throws LoginException,
IllegalArgumentException {
//Getting the values of the host and port properties
String host = (String) dict.get("host_ID");
String port = (String) dict.get("port_ID");
//Getting the Connector Service to create the TCP connection
connRef = bc.getServiceReference(ConnectorService.class.getName());
if(connRef != null) {
connector = (ConnectorService) bc.getService(connRef);
try {
//the socket stream connection to the specified host and port
connection = (StreamConnection)connector.open("socket://" +
host + ":" +
port);
DataOutputStream outputStream = connection.openDataOutputStream();
. . .//do something with the obtained connection
return true; //we return true to show the connection was successful
} catch(java.io.IOException ioe) {
ioe.printStackTrace();
return false; //in case of exception, we return false
}
}
else {
//If the Connector Service is unavailable, we cannot establish connection
return false;
}
}
|
The disconnect() method closes the established connection. Make
sure your implementation of this method closes the connection properly.
The closing of the connection for our TEST login service is an easy task. It is illustrated in Listing 3.3.2.
public void disconnect(Dictionary dictionary) throws LoginException {
|
When a problem occurs during connection and login, a LoginException
is thrown.
2.1. Providing connection events to interested listeners and
mConsole itself. If you want to notify mConsole plug-ins about changes in
connections' status, you must distribute events in the implementation of the
connect method. You can send events when the connection is established
or broken, and while the connection establishment is in progress.
Connection events are represented by com.prosyst.mc.login.ConnectionEvent
objects. A connection event can be:
ConnectionEvent.CONNECTED - Shows a successful connection has
been establishedConnectionEvent.CONNECTING - Shows mConsole is currently trying
to establish connection to the specified server. This event type can be issued
periodically during the login process to update the information about the
percentage of successful connection establishment.ConnectionEvent.DISCONNECTED - Shows the previously established
connection has been broken.The most interesting connection event type of the three is the ConnectionEvent.CONNECTING.
Issuing CONNECTING events periodically during the login process,
and updating the information about the percentage of successful establishment,
allows mConsole to display messages about the current operation it performs
and percentage elapsed in the login progress bar.
Listing 3.4 shows re-writing the connect method by creating and
sending connection events when necessary.
public boolean connect(Dictionary dict) throws LoginException, IllegalArgumentException {
. . .
//When the Connector Service was successfully obtained
ConnectionEvent conn10percent = new ConnectionEvent(ConnectionEvent.CONNECTING,
serverType,
"Getting the Connector Service...",
10);
sendEvent(conn10percent);
. . .//After the connection has been opened successfully
ConnectionEvent conn40percent = new ConnectionEvent(ConnectionEvent.CONNECTING,
serverType,
"Opening the connection...",
40);
sendEvent(conn40percent);
. . .//After the greeting has been sent to the server
ConnectionEvent conn70percent = new ConnectionEvent(ConnectionEvent.CONNECTING,
serverType,
"Sending greeting to the server...",
40);
sendEvent(conn70percent);
. . .//When all phases of the connection establishment have passed
ConnectionEvent connected = new ConnectionEvent(ConnectionEvent.CONNECTED,
serverType,
"Connection established successfully...",
100);
sendEvent(connected);
. . .
}
|
Step 3. Providing the GUI components for the connection to
your custom server type. For each established connection, mConsole loads
only the GUI components specified by the login service responsible for
this type of connection. Defining the GUI components for a specific server type
is done by implementing the getGUINames() method of the LoginService
interface. This method returns as String[] the names of the GUI interfaces to
be loaded. It is called internally by the Visual Container service on each connection
to a server of this type.
The mechanism of providing the GUI interface names is up to the needs of your
custom implementation. For example, you may provide the names statically, by
hard-coding them in the body of the getGUINames() method. Or, you
can provide dynamic retrieval of the names by reading a configuration resource
from the OSGi Configuration Admin (similarly to the retrieval of login properties
demonstrated in Step 1). The latter method would be useful if you need flexible
mechanism of GUI loading.
Note: When you add a GUI interface
name to the array returned by getGUINames(), make sure there is
an implementation of GUI interface with the same name in the mConsole framework.
Listing 3.5 demonstrates very simple implementation of the getGUINames()
method. It returns the name of the GUI interface we created in part Creating
Basic GUI Components.
public String[] getGUINames() {
|
After implementing a login service, register the LoginService
interface implementation as a service in the framework. This is necessary for
mConsole to recognize the new server type and take care of visualizing its proper
login settings, and call its methods when necessary.
The login service must be registered with at least the LoginService.CONNECTION_TYPE
property. Its value must have the display name of your custom login type (the
name that will appear in the Server Types combo box).
Hashtable props = new Hashtable(); |
Figure 3.1: The custom login service appears in the Server
Type drop-down list of the Login Box
Figure 3.2: TEST server settings in the Login Box
Through connection listeners you can register for receiving connection events.
The addConnectionListener(ConnectionListener) and removeConnectionListener(ConnectionListener)
methods register/unregister a connection listener respectively.
When you register your custom login service, mConsole automatically registers a service listener for it. Once the login service is registered, it is visualized in the login box. If the service becomes unregistered, the login box stops displaying it.
If you try to establish connection with a server with type your custom login service, then mConsole will also register a connection listener to the login service.
If you want to add a custom connection listener to a login service, you need to:
com.prosyst.mc.login.ConnectionListener interfaceaddConnectionListener method.
import com.prosyst.mc.login.ConnectionEvent;
import com.prosyst.mc.login.ConnectionListener;
public class TestConnectionListener implements ConnectionListener {
public void eventOccurred(ConnectionEvent ce) {
System.out.println("[Test connection listener] A new event of type: " +
ce.getEventType() +
" is received.\n Details:\n " +
"Server type" +
ce.getServerType() + "\n" +
"Message: " + ce.getMessage()
);
}
}
|
As there are a number of login services in the mConsole container, you need
to be sure you have obtained the right service instance. This is done by applying
an LDAP search filter LoginService.CONNECTION_TYPE equal to the
value of the service registration property with the same name (see Registering
the Login Service).
import org.osgi.framework.*;
import com.prosyst.mc.login.LoginService;
import com.prosyst.mc.login.ConnectionListener;
. . .
private BundleContext bc;
private ServiceReference[] sRef;
private LoginService testLoginService;
/*The LDAP filter for retrieving the right login service instance*/
private String filter = "(" + LoginService.CONNECTION_TYPE + "TEST)";
private TestConnectionListener testConnListener;
. . .
/*Getting the login services corresponding to the filter*/
sRef = bc.getServiceReferences(LoginService.class.getName(), filter);
if(sRef !=null) {
for (int i = 0; i < sRef.length; i++) {
testLoginService = (LoginService) bc.getService(sRef[i]);
//Adding the connection listener
testConnListener = new TestConnectionListener();
testLoginService.addConnectionListener(testConnListener);
}
}
|
A property editor allows editing configuration properties of bundles available on the connected OSGi framework. mConsole allows dynamic loading of property editors for bundles. There are two types of editors: general and custom. The general property editor is automatically created by mConsole and you do not need to write it yourself. However, if you want your bundle to have a custom editor, you do need to write it.
Both types of editors are represented by the com.prosyst.mc.GUIFactory
service interface. For general property editors, the service is automatically
registered by mConsole with a property: EditorGUIInterface.GENERAL_TYPE.
Custom editors must be created by the developer and must be registered with
the EditorGUIInterface.CUSTOM_TYPE property.
Each time a configuration node from the tree is clicked, mConsole checks if
there is a GUI Factory Service registered with the EditorGUIInterface.CUSTOM_TYPE
or EditorGUIInterface.GENERAL_TYPE property. FIRST mConsole checks
for CUSTOM editor services, and if there aren't any, checks for an available
GENERAL editor service. By default, the general editor service is exported by
the osgieditors.jar bundle. If the bundle is has been stopped or uninstalled,
the general property editor will be unavailable and only ProSyst's icon will
be displayed on selecting the configuration node from the tree.
A bundle having a property editor must correspond to the following conditions:
If the bundle contains a factory configuration, Its manifest must contain
the FactoryConfig attribute. Its value can be one of the following:
xml=<xml_resource_location>; pid=<service_fpid>; name=<display_name>;
or simply
name=<display_name>;
EditorGUIInterface.CUSTOM_TYPE.The source code of the bundle itself does not imply the availability or absence of a property editor of any type. Usually, bundles with property editors use the OSGi Configuration Admin Service for storing bundle configurations, but this is NOT required. For example, the ProSyst User Admin Bundle owns a custom property editor for managing users and groups, but the information is not stored in the OSGi Configuration Admin Service.
The following example is taken from the sources of the Property Editor Demo (demo/framework/mc). The JAR file of this demo bundle is demo/bundles/editordemo.jar. It stores a simple set of properties with the OSGi Configuration Admin Service.
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.cm.ConfigurationException;
/**
* This class is responsible for creating
* and updating the configuration of phone book.
* The configuration will be viewed on Managment Console custom property editor.
*/
public class EditorDemoActivator implements BundleActivator, ManagedService {
/**
* Service PID for this managed service.
*/
public static final String pid = "demo.mc.pid";
// This field contains phone book content.It is a sequence of name, telephone pairs.
private String[] addresses;
// The bundle context object of this bundle.
private BundleContext bc;
private ServiceRegistration managedServiceReg;
// Contains properties for this ManagedService.
private Hashtable props;
/**
* Starts the bundle. Registers the ManagedService service.
*
* @param bc the BundleContext of this bundle
* @exception BundleException if an error occurs
*/
public void start(BundleContext bc) throws BundleException {
try {
this.bc = bc;
props = new Hashtable(1, 1);
props.put("service.pid", pid);
managedServiceReg = bc.registerService(ManagedService.class.getName(), this, props);
} catch (Exception e) {
throw new BundleException(e.getMessage(), e);
}
}
/**
* Stops the bundle. Unregisters the ManagedService service.
*
* @param bc the BundleContext of this bundle
* @exception BundleException if an error occurs
*/
public void stop(BundleContext bc) throws BundleException {
try {
managedServiceReg.unregister();
managedServiceReg = null;
} catch (Exception e) {
throw new BundleException(e.getMessage(), e);
}
}
/**
* Updates phone book configuration.
* @param properties configuration properties, or null
* @throws ConfigurationException when the update fails
*/
public void updated(Dictionary properties) throws ConfigurationException {
if (properties != null) {
addresses = (String[])properties.get("phonebook");
}
}
}
|
The manifest of this bundle is:
Manifest-Version: 1.0 Bundle-Vendor: ProSyst Bundle-Version: 1.0 Bundle-Category: demo Bundle-Activator: demo.mc.bundle.EditorDemoActivator Bundle-Name: Editor Demo Bundle Bundle-Description: Import-Package: org.osgi.service.cm; specification-version="1.0" Config: xml=/demo/mc/bundle/config.xml; pid=demo.mc.pid; name=Editor Demo |
We'll create a custom property editor for the above bundle. It is packed in the demo/bundles/preditor.jar file. It will be installed and started on the mConsole framework.
...//imports
public class DemoActivator implements BundleActivator, ServiceListener, GUIFactory {
// Holds the display name of the editor.
public static final String EDITOR_NAME = "Editor Demo";
....
public DemoActivator() {
//holds the obtained property editors
guiHash = new Hashtable(2);
//caches the property editors which are not currently in use
guiVector = new Vector(2, 1);
props = new Hashtable(3);//the service properties
props.put(GUIFactory.GUI_NAME, EDITOR_NAME);
props.put(EditorGUIInterface.EDITOR_TYPE, EditorGUIInterface.CUSTOM_TYPE);
}
......
public void start(BundleContext bc) throws BundleException {
this.bc = bc;
try {
bc.addServiceListener(this, "((objectClass=" + VisualContainer.class.getName() +
")(objectClass=" + RemoteConfigAdmin.class.getName() + "))");
} catch (InvalidSyntaxException exc) {
// Nothing to do, The syntax is right.
}
containerRef = bc.getServiceReference(VisualContainer.class.getName());
if (containerRef != null) {
container = (VisualContainer) bc.getService(containerRef);
}
//registers the GUI Factory service
guiRegistration = bc.registerService(GUIFactory.class.getName(), this, props);
}
.....
/**
|
The full source code of the property editor is available in the demo/framework/mc/demo/mc/editor folder.
If we do not activate the custom property editor (preditor.jar) on the mConsole, the general property editor will be loaded, which looks like this:
Figure 4.1: The general property editor for the demo bundle.
It appears if we haven't started its custom property editor.
If we activate the custom property editor, however, it will look like this:
Figure 4.2: The custom property editor for the demo
If you want the GUI components of your plug-in to be loaded by the Visual Container service when mConsole is connected to an MBS server, you need to do the following prerequisites:
GUI_NAME
registration property) to the list of GUI interfaces. The “GUI Interface names array” property of the mBS Login Service determines
the GUIInterface components that will be displayed when mConsole
is connected to an OSGi framework over “MBS” connection type. Hence, you need
to add the name (corresponding to the GUI_NAME registration property
of the plug-in) of the GUI component to the array of GUI names available in
the mBS Login Service.
This is done with the following steps:
Figure 5: Adding the name of the GUI component
to the “GUI Interface names array” property
If everything goes well, the next time you log in an MBS server you should see the demo's menu, toolbar and main component (tab) appear in mConsole (like in Figure 6.1).
Figure 6.1: GUI components added by the demo plug-in
When you select the Demo Menu -> Hello command or press the
button, the following greeting message appears:
Figure 6.2: The message
sent by the demo plug-in when the Demo Menu -> Hello command is invoked
If you select the Demo Menu -> Progress Demo command or press the
button, you will see a progressive operation simulated by the plug-in:
Figure 6.3: Progress bar of the demo
When the progressing operation finishes successfully, you should see the following message:
Figure 6.4: The message dialog that you see after the progress
demo finishes