Friday Mar 20, 2009

Adding Remote Connectivity to Your Visual Panel

One of the benefits of using Visual Panels to develop a system management application is that the ability to manage a remote host comes for free. Visual panels uses Java Management Extensions (JMX) to provide this remote connectivity with minimal effort to the panel developer.

The initial panel we created shows the current time on the system running the UI, not necessarily the host that the UI is administering.

We now want to change our UI to show the time of the correct host, add the ability to set the time, and add some standard UI controls to facilitate navigation. Mapping out our changes:

  1. Create the server-side classes
    1. Create the MXBean
    2. Create the Cacao Module
    3. Deploy the Cacao Module
  2. Amend the client-side classes
    1. Expose the TimeMXBean from TimePanelDescriptor
    2. Use the TimeMXBean in TimeControl
    3. Make TimePanel a SettingsPanel


  1. This discussion assumes Visual Panels is installed on the server you wish to manage.
  2. The source for all examples discussed here can be found in the Visual Panels Mercurial repository (see "Get the source") under usr/src/java/vpanels/panels/examples.

Create the server-side classes

Setting up the server end of the application is straightforward: create the classes, then deploy them into the JMX container.

Create the MXBean

The MXBean (see the tutorial if you aren't familiar with them) for our time management panel encapsules the functionality we wish to expose from a remote host. From

1. public interface TimeMXBean { 2. long getTime(); 3. void setTime(long millis) throws IOException; 4. }

This interface is shared between the client and server, whereas the implementation of it,, is specific to the server:

1. public class Time implements TimeMXBean, MBeanRegistration { 2. @Override 3. public long getTime() { 4. return System.currentTimeMillis(); 5. } 6. 7. @Override 8. public void setTime(long millis) throws IOException { 9. Date date = new Date(millis); 10. DateFormat format = new SimpleDateFormat(""); 11. String dateString = format.format(date); 12. 13. try { 14. ProcessUtil.exec(0, "/usr/bin/date", dateString); 15. 16. } catch (CommandFailedException e) { 17. throw new IOException(e); 18. 19. // Not likely 20. } catch (InterruptedException e) { 21. throw new IOException("command interrupted"); 22. } 23. } 24. ...

Rather than bothering with native methods, this implementation just calls /usr/bin/date (14) to set the time.

Lastly, Time implements the MBeanRegistration interface (1) to set the ObjectName of the MBean within the JMX container:

24. ... 25. @Override 26. public ObjectName preRegister(MBeanServer server, ObjectName name) { 27. if (name == null) { 28. name = TimeUtil.OBJECT_NAME; 29. } 30. return name; 31. } 32. }

Create the Cacao Module

Visual Panels uses Cacao (the Common Agent Container, also known as CAC outside of Sun) as its JMX container. Deploying into Cacao involves delivering a subclass of com.sun.cacao.Module. From

1. public class TimeModule extends Module { 2. private ObjectName name; 3. 4. public TimeModule(DeploymentDescriptor descriptor) { 5. super(descriptor); 6. } 7. 8. @Override 9. protected void start() { 10. TimeMXBean bean = new Time(); 11. try { 12. name = getMbs().registerMBean(bean, null).getObjectName(); 13. } catch (Exception e) { 14. availabilityStatusSetAdd(AvailabilityStatusEnum.FAILED); 15. setOperationalState(OperationalStateEnum.DISABLED); 16. } 17. } 18. 19. @Override 20. protected void stop() { 21. try { 22. getMbs().unregisterMBean(name); 23. } catch (Exception e) { 24. } 25. } 26. }

This most basic of modules simply registers our TimeMXBean implementation with the MBeanServer when the module is started (12), then unregisters it when it is stopped (22).

Deploy the Cacao Module

The next step is to create a Cacao deployment descriptor for the TimeModule:

1. <?xml version='1.0' encoding='utf-8'?> 2. <!DOCTYPE module SYSTEM "urn:sun:n1:cacao:module:dtd:1_1"> 3. 4. <module name="org.opensolaris.os.vp.panels.example.time2.server.module.TimeModule" 5. version="1.0" 6. initial-administrative-state="UNLOCKED" 7. ignored-at-startup="No" 8. enable-on-demand="No"> 9. 10. <description>Time module</description> 11. 12. <module-class> 13. org.opensolaris.os.vp.panels.example.time2.server.module.TimeModule 14. </module-class> 15. 16. <public-path> 17. <path-element> 18. file:/path/to/myjar.jar 19. </path-element> 24. </public-path> 25. 26. <cacao-version-supported>2.0</cacao-version-supported> 27. <heap-requirements-kilobytes>0</heap-requirements-kilobytes> 28. </module>

This file, org.opensolaris.os.vp.panels.example.time2.server.module.TimeModule.xml, provides Cacao with the information it needs to run our module. Line 18 identifies the full path to the jar file containing the module and the MBean classes.

Once this file has been created (and the jar file copied to the location specified), Cacao can be made aware of it:

# cp org.opensolaris.os.vp.panels.example.time2.server.module.TimeModule.xml \\ /etc/cacao/instances/default/modules # /usr/sbin/cacaoadm restart

That should be it for the server side of the equation. Now all that's left is to tie the client UI to it.

Amend the client-side classes

Building on the classes we created in the last example, we can now modify our UI to use the new server code.

Expose the TimeMXBean from TimePanelDescriptor

The connection between the client and the server starts with the PanelDescriptor. Here we amend to provide this new functionality:

1. public class TimePanelDescriptor extends AbstractSwingPanelDescriptor 2. implements ConnectionListener { 3. 4. private TimeMXBean bean; 5. ... 6. public TimePanelDescriptor(String id, ClientContext context) { 7. ... 8. setBean(context.getConnectionInfo()); 9. context.addConnectionListener(this); 10. } 11. 12. @Override 13. public void connectionChanged(ConnectionEvent event) { 14. setBean(event.getConnectionInfo()); 15. } 16. 17. public TimeMXBean getTimeBean() { 18. return bean; 19. } 20. 21. private void setBean(ConnectionInfo info) { 22. try { 23. bean = JMX.newMXBeanProxy( 24. info.getConnector().getMBeanServerConnection(), 25. TimeUtil.OBJECT_NAME, TimeMXBean.class); 26. } catch (IOException e) { 27. bean = null; 28. } 29. } 30. }

The ClientContext passed to the TimePanelDescriptor constructor (6) provides access to the that connects us to the JMX container on the server. The setBean method uses that JMXConnector to instantiate a proxy (23) to the TimeMXBean we implemented and registered on the server.

If the user changes his credentials after the panel is started, we need to know about it since it may affect what he has permission to do on the server. Fortunately, the Visual Panels API makes this easy: we first implement the ConnectionListener interface (2), add this TimePanelDescriptor to the ClientContext's list of ConnectionListeners (9), then re-invoke setBean when the credentials change (14).

Use the TimeMXBean in TimeControl

Now that we have a handle to our remote TimeMXBean, we can modify to use it to initialize our UI:

1. public class TimeControl 2. extends SwingSettingsControl<TimePanelDescriptor, TimePanel> { 3. 4. private Date date; 5. ... 6. @Override 7. protected void initComponent() { 8. long time = getPanelDescriptor().getTimeBean().getTime(); 9. date = new Date(time); 10. getComponent().getSpinnerDateModel().setValue(date); 11. } 12. ...

By using the TimeMXBean exposed by our PanelDescriptor, initComponent can now get the time from the managed host (8), rather than from the host running the UI.

Similarly, we can also provide a save method to set the time on the managed host:

12. ... 13. @Override 14. protected void save() throws ActionAbortedException, ActionFailedException, 15. ActionUnauthorizedException { 16. 17. Date newDate = (Date)getComponent().getSpinnerDateModel().getValue(); 18. if (!newDate.equals(date)) { 19. long time = date.getTime(); 20. try { 21. getPanelDescriptor().getTimeBean().setTime(time); 22. } catch (IOException e) { 23. throw new ActionFailedException(e); 24. } catch (SecurityException e) { 25. throw new ActionUnauthorizedException(e); 26. } 27. } 28. } 29. ...

Any errors that occur in this process are repackaged (23, 25) as ActionExceptions so that the Visual Panels framework can handle them appropriately:

  • ActionFailedExceptions cause an error to be shown to the user.
  • ActionUnauthorizedExceptions cause the user to be prompted to update his credentials.
  • ActionAbortedExceptions indicate that the user cancelled the action willingly, so no handling is done.

Who calls save? No one, yet. But we can change that by modifying our createComponent method:

29. ... 30. @Override 31. protected TimePanel createComponent() { 32. TimePanel panel = new TimePanel(); 33. 34. addDefaultApplyAction(panel); 35. addDefaultCancelAction(panel, true); 36. addDefaultOkayAction(panel, true); 37. 38. return panel; 39. } 40. }

You may have noticed that our TimeControl now extends SwingSettingsControl (2), instead of SwingControl as in the previous example. This change allows us to display Apply (34), Cancel (35), and Okay (36) buttons, and tie them to the appropriate actions with minimal effort: Apply and Okay invoke save (14) and Cancel invokes reset (not implemented here).

By virtue of the true arguments (35, 36), Okay and Cancel will attempt to close the panel if their respective actions have completed successfully. This involves unwinding the navigation stack, which may result in more prompts to the user to handle unsaved changes. Using other methods to close the panel, namely System.exit, do not take this precaution and should be avoided. Note also that System.exit will kill the entire JVM, which may be running panels other than this one!

Make TimePanel a SettingsPanel

The final change to our example is a minor one. To be able to display the Apply, Cancel, and Okay buttons, our TimePanel must extend SettingsPanel. From

1. public class TimePanel extends SettingsPanel { 2. private SpinnerDateModel model; 3. 4. public TimePanel() { 5. JLabel label = new JLabel(Finder.getString("time.label")); 6. model = new SpinnerDateModel(); 7. JSpinner spinner = new JSpinner(model); 8. 9. JPanel panel = new JPanel(new BorderLayout(5, 0)); 10. panel.add(label, BorderLayout.WEST); 11. panel.add(spinner, BorderLayout.EAST); 12. 13. setContent(panel); 14. } 15. 16. public SpinnerDateModel getSpinnerDateModel() { 17. return model; 18. } 19. }

Other than its new superclass (1), the only change from the previous example is the call to setContent (13). See the SettingsPanel API for details.

Putting it together

The resulting UI, slightly more usable than its predecessor, is shown below:

If a non-root user attempts to commit a time change, Visual Panels catches the resulting ActionUnauthorizedException thrown from save and prompts the user to upgrade his credentials:

Wednesday Dec 07, 2005

The Sun Java Web Console may not be enabled at first boot, leaving the ZFS Administration application inaccessible. To workaround this, simply start the web server manually (as root):

# /usr/sbin/smcwebserver start

If the SUNWzfsg package (the ZFS Administration application) is installed, the ZFS Administration application should then be available from https://<host>:6789 as mentioned earlier. The Sun Java Web Console will also start automatically after subsequent reboots.

Wednesday Nov 16, 2005

In case you thought ZFS couldn't get any easier, there is now a web-based ZFS Administration application to quash any remaining traces of reluctance to embrace a new technology.

Does ZFS really need a GUI?

While much has been made about how simple ZFS is to administer, there are still times when you need to visualize, browse, and tweak your ZFS configuration.

Oh, and do it all from a browser.

And what the heck, why not have it help you learn the ZFS CLI while you're at it?

What can it do?

A picture is worth 1kW (click to enlarge):

The opening screen, with the most common tasks ready to run.
A quick summary of all your ZFS file systems. Summary pages for pools, volumes, and snapshots are also available.
An in-depth look at a ZFS file system. Common tasks for this file system, properties, snapshots, and child datasets are displayed (out of view here).

One of the themes of the GUI is that it will show you any commands that it runs to change your configuration. This will help you learn the ZFS command line syntax, and provide a starting point for your ZFS set-up shell scripts.

The GUI uses a series of "wizards" to guide you through each task. Here's an example of creating a storage pool:

Select the redundancy level.
Choose the devices.
View the configuration before committing.
View the generated commands.

Where can I get it?

The ZFS Administration application will be available in build 28 of Solaris 11 (2-3 weeks from now) and Solaris 10 update 2. OpenSolaris, stay tuned.

How do I run it?

Just point your browser at https://<host>:6789 and you're off and running.

<script type="text/javascript" language="JavaScript"> </script> <script type="text/javascript" language="JavaScript" src="index_files/s_code_remote.js"></script><script type="text/javascript" src="index_files/metrics_group1.js"></script>

This blog copyright 2009 by stephentalley

Category: Visual Panels

Tags: none

Permanent link to this entry | Comments [0]

Wednesday Mar 04, 2009

Creating a Visual Panel

Visual Panels has gained a lot of momentum recently as a platform for developing quick system management applications for the OpenSolaris desktop.

Creating a basic application, or "panel", for VP is fairly straightforward:

  1. Implement the PanelDescriptor interface.
  2. Extend the abstract Control class.
  3. Create a jar file with all code and resources.
  4. Deploy your panel on the server to be managed.

Let's look at each of these steps in more depth while creating a basic panel that shows the current date and time. Future posts will elaborate on this example to turn it into a useful administration panel.


  1. This discussion assumes Visual Panels is installed on the server you wish to manage.
  2. The source for all examples discussed here can be found in the Visual Panels Mercurial repository (see "Get the source") under usr/src/java/vpanels/panels/examples.

Implement PanelDescriptor

The PanelDescriptor interface is the entry point of the panel into Visual Panels. It describes to the framework how the panel should be integrated into the client.

Here we implement this interface in

1. public class TimePanelDescriptor extends AbstractSwingPanelDescriptor { 2. 3. private Control control; 4. 5. /\*\* 6. \* Constructs a {@code TimePanelDescriptor}. 7. \* 8. \* @param id 9. \* a unique identifier for this Panel, taken from the panel 10. \* registration 11. \* 12. \* @param context 13. \* a handle to interact with the Visual Panels client 14. \*/ 15. public TimePanelDescriptor(String id, ClientContext context) { 16. super(id, context); 17. control = new TimeControl(this); 18. }

TimePanelDescriptor extends from AbstractSwingPanelDescriptor (1), which provides default implementations for many of the PanelDescriptor methods.

Visual Panels requires a PanelDescriptor subclass to define a public constructor with the id (8) and context (12) arguments described above. The first is derived from the panel registration and is mostly used internally by the framework. The latter, a ClientContext, provides hooks into various exposed features of the Visual Panels client, like connection monitoring, logging, and help management.

As a ManagedObject, TimePanelDescriptor must implement the getName method to provide a user-friendly name for use in the client:

19. @Override 20. public String getName() { 21. return Finder.getString(""); 22. }

Finally, the getControl method points the client to the panel's top-level Control, a TimeControl created on line 17:

23. @Override 24. public Control getControl() { 25. return control; 26. } 27. }

Extend Control

The Control class governs the UI and navigation flow of the panel. It is the "C" component of "MVC".

In this example, we create

1. public class TimeControl extends SwingControl<TimePanelDescriptor, TimePanel> { 2. 3. public TimeControl(TimePanelDescriptor descriptor) { 4. super(descriptor.getId(), descriptor.getName(), descriptor); 5. } 6. 7. @Override 8. protected TimePanel createComponent() { 9. return new TimePanel(); 10. } 11. 12. @Override 13. protected void initComponent() { 14. Date date = new Date(); 15. getComponent().getSpinnerDateModel().setValue(date); 16. }

TimeControl extends SwingControl (1), which provides a default, Swing-based implemention of the Control class.

The SwingControl class controls the creation and display of a single java.awt.Component. In this example that Component's type is defined as a TimePanel (1) (see, then created within the createComponent method (8). This method is called by the superclass the first time the TimeControl is started.

Finally, the TimePanel is initialized on line 15. The initComponent method is called by the superclass each time the TimeControl is started. In this simple implementation, we simply update the model with the current date.

Create a jar file

Once our classes have been created, we need to add them to a jar file, along with any other classes we use, to a jar file:

% jar cf vpanels-panels-examples.jar \\ org/opensolaris/os/vp/panels/example/time1

Don't worry about including other jar dependencies in this jar's manifest -- they'll be accounted for in the next step.

Deploy your panel

The first step in deploying your panel is to copy the jar file created in the last step to some directory on the server:

# cp vpanels-panels-examples.jar /usr/share/vpanels/panel

Next, create a deployment descriptor that describes the panel and identifies the jar to use:

1. <?xml version="1.0"?> 2. <!DOCTYPE panel SYSTEM "/usr/share/lib/xml/dtd/vpanel.dtd.1"> 3. <panel name='example-time1'> 4. <description xml:lang='C'>Application to set the time and date.</description> 5. <custom type='swing'> 6. <mainclass>org.opensolaris.os.vp.panels.example.time1.client.swing.TimePanelDescriptor</mainclass> 7. <approot>/usr/share/vpanels</approot> 8. <file>panel/vpanels-panels-examples.jar</file> 9. </custom> 10. </panel>

Line 3 specifies a name ("example-time1") that will be used to launch your panel.

Line 4 specifies a description that may be used within the UI to provide further detail about your panel.

Line 6 specifies the full class name of your PanelDescriptor subclass.

Line 8 identifies where you copied your jar file on the server. That location is relative to the approot directory on line 7.

The final step in deployment is to copy this descriptor to the /usr/share/vpanels/panels directory on the sever:

# cp example-time1.xml /usr/share/vpanels/panels

Run your panel

Your panel is ready to run at this point:

% /usr/bin/vp example-time1

Or, if you are running on a different host than the server on which you deployed your panel:

% /usr/bin/vp -h server example-time1

The resulting, if limited, UI is shown below:

Undeploy your panel

To undeploy your panel, simply remove the jar file and deployment descriptor from the file system:

# rm /usr/share/vpanels/panels/example-time1.xml \\ /usr/share/vpanels/panel/vpanels-panels-examples.jar



Recent Posts
  • Visual Panels
  • ZFS
« April 2014