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

Notes:

  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 TimeMXBean.java:

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, Time.java, 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("MMddHHmmyyyy.ss"); 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 TimeModule.java:

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 TimePanelDescriptor.java 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 javax.management.remote.JMXConnector 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 TimeControl.java 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 TimePanel.java:

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.
Success!

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

« Creating a Visual... | Main
Comments:

Post a Comment:
About

stephentalley

Search

Recent Posts
Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks
Feeds