How to update NetBeans Platform Application silently?

In a mail thread [openide-dev] Automatic Autoupdate I've been asked for a guidance how to use Autoupdate Services for installing updates of your NetBeans Platform Application with as few as possible user's interactions.

The blueprint should look like:

  1. check the server for any updates
  2. download any updates 
  3. install all updates (without prompting the user) 
  4. notify the user to restart the application, if necessary.

Check the server for any updates

First, you need to register your server which can contain updates of your application. In NetBeans world we say it's Update Center, usually a Update Center Descriptor file at some URL. Let's suppose you have a intended Update Center with prospective updates.

You need to register this one in your application. Either create a particular module in your platform application which will care for invoking Autoupdate Services or use some other suitable for this. Easy use Update Center template in New|File wizard in this module. In the wizard just specify URL to your Update Center.

Tip: Use GZip to compressing this Update Center Descriptor to lower download size and speed up downloading of updates. Only append ".gz" extention into the URL to make NetBeans Platform to use a GZip reader.

Download any updates

Make sure your  has Autoupdate Services in its libraries. Then use Module Installer and create there a point invoking check  Update Center for available updates, don't forget to wait a while before invoke checking to don't affect startup time or your application.

Like this:

public class Installer extends ModuleInstall {

@Override
public void restored () {
// schedule refresh providers
// install update checker when UI is ready (main window shown)
WindowManager.getDefault().invokeWhenUIReady(new Runnable () {
public void run () {
RequestProcessor.getDefault ().post (doCheck, 10000);
}
});
}
private static Runnable doCheck = new Runnable () {
public void run() {
if (SwingUtilities.isEventDispatchThread ()) {
RequestProcessor.getDefault ().post (doCheck);
return ;
}
if (timeToCheck ()) {
doRealCheck();
}
}
};
}

Now, do a real check for available updates. You need instance of org.netbeans.api.autoupdate.UpdateManager which is an entry into content on UpdateCenters. Ask this manager for org.netbeans.api.autoupdate.UpdateUnits which represents one plugin and knows whether the plugin in already install in application or now and all available versions of registered UpdateCenters.

public Collection<UpdateElement> doRealCheck () {
Collection<UpdateElement> elements4update = new HashSet<UpdateElement> ();
List<UpdateUnit> updateUnits = UpdateManager.getDefault ().getUpdateUnits ();
for (UpdateUnit unit : updateUnits) {
if (unit.getInstalled () != null) { // means the plugin already installed
if (! unit.getAvailableUpdates ().isEmpty ()) { // has updates
elements4update.add (unit.getAvailableUpdates ().get (0)); // add plugin with highest version
}
}
}
return elements4update;
}

Okay, we have collection of plugins which has update. We need now a container for installing updates into application. Add these updates into container, append also all required plugin's in that case an update of any plugin needs.

Like this:

public OperationContainer<InstallSupport> getContainerForUpdate (Collection<UpdateElement> elements4update) {
OperationContainer<InstallSupport> container = OperationContainer.createForUpdate ();
for (UpdateElement element : elements4update) {
if (container.canBeAdded (element.getUpdateUnit (), element)) {
OperationInfo<InstallSupport> operationInfo = container.add (element);
if (operationInfo == null) {
continue;
}
container.add (operationInfo.getRequiredElements ());
}
}
return container;
}

Tip: An instance of OperationInfo has a method getBrokenDependencies(). Only plugins with no broken dependencies can be installed. Otherwise, plugins with such broken dependencies won't be loaded by NetBeans Module System.

Make sure all licenses of updates are approved and also container contains no invalid plugins. Without it, don't continue install updates.

public boolean allLicensesApproved (OperationContainer<InstallSupport> container) {
    if (! container.listInvalid ().isEmpty ()) {
        return false;
    }
    for (OperationInfo<InstallSupport> info : container.listAll ()) {
        String license = info.getUpdateElement ().getLicence ();
        if (! isLicenseApproved (license)) {
            return false;
        }
    }
    return true;
}

public boolean isLicenseApproved (String license) {
    // place your code there
    return false;
}
Great! Finally we can download all updates. That's easy:
public Validator doDownload (OperationContainer<InstallSupport> container) throws OperationException {
    InstallSupport install = container.getSupport ();
    ProgressHandle downloadHandle = ProgressHandleFactory.createHandle ("dummy-download-handle");
    return install.doDownload (downloadHandle, true);
}

Install all updates (without prompting the user)

public Restarter doInstall (InstallSupport support, Validator validator) throws OperationException {
ProgressHandle validateHandle = ProgressHandleFactory.createHandle ("dummy-validate-handle");
Installer installer = support.doValidate (validator, validateHandle); // validates all plugins are correctly downloaded
ProgressHandle installHandle = ProgressHandleFactory.createHandle ("dummy-install-handle");
return support.doInstall (installer, installHandle);
}

Notify the user to restart the application, if necessary.

The doInstall method returns an instance of Restarter if and only if restart of application is needed to finish the operation. Because installing of any update needs restart implicitly, you need to perform restarting. For that restart use simply Restarter as a parameter in InstallSupport which offers two way how to restart application: restart now or restart later, i.e. before next start of the application.

If you don't want to let your users know about updating process at all, just simply choose the second option:

public void doRestartLater (InstallSupport support, Restarter restarter) {
    support.doRestartLater (restarter);
}

In the case you need to notify the users about Required restart application to complete update of application you need to choose either pop-up a message box, or notify users in the status line, in both of them you have to perform this code:

public void doRestartNow (InstallSupport support, Restarter restarter) throws OperationException {
ProgressHandle installHandle = ProgressHandleFactory.createHandle ("dummy-install-handle");⁞
support.doRestart (restarter, installHandle);
}

The status line option, in detail:

Use the service StatusLineElementProvider and create enclosing class StatusLineNotification invokes restart of the application on user's click on appropriate icon in the status line.

@org.openide.util.lookup.ServiceProvider(service=org.openide.awt.StatusLineElementProvider.class, position=610)
public class StatusLineNotification implements org.openide.awt.StatusLineElementProvider {
private static InstallSupport supp;
private static Restarter r;

public Component getStatusLineElement () {
ImageIcon img = new ImageIcon (ImageUtilities.loadImage ("org/netbeans/modules/mymodule/restart.png", false));
JPanel p = new StatusLinePanel (img);
p.setVisible (false);
return p;
}

public static void setRestartData (InstallSupport support, Restarter restarter) {
supp = support;
r = restarter;
}

public static class StatusLinePanel extends JPanel {
private ImageIcon icon = null;
public StatusLinePanel (ImageIcon img) {
icon = img;
}

@Override
public void addNotify () {
this.addMouseListener (new MouseListener () {
public void mouseClicked (MouseEvent e) {
if (isVisible ()) {
try {
ProgressHandle installHandle = ProgressHandleFactory.createHandle ("dummy-install-handle");
supp.doRestart (r, installHandle);
} catch (OperationException ex) {
Exceptions.printStackTrace (ex);
}
}
}
public void mousePressed (MouseEvent e) {}
public void mouseReleased (MouseEvent e) {}
public void mouseEntered (MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
});
}

@Override
public void paint (java.awt.Graphics g) {
if (isVisible ()) {
icon.paintIcon (this, g, 0, 0);
}
}
}
}

Enjoy coding this and if you have any problem let me know here or file new issue in NetBeans Issuezilla. Thanks

Comments:

Great article. Many companies need silent updates on the NetBeans platform. Thanks for your investigation!

josh.

Posted by Aljoscha Rittner on December 23, 2008 at 05:57 AM CET #

Josh, I'm glad to hear it could be helpful to you. Regards.
-jiri

Posted by Jiri Rechtacek on December 27, 2008 at 06:55 AM CET #

org.netbeans.modules.autoupdate.services is not public,how can i import this package?Thank you!

Posted by jim on February 05, 2009 at 12:26 AM CET #

sorry about last comment,I have found what's the problem.forget it,thand you!

Posted by jim on February 05, 2009 at 02:13 AM CET #

Looks like the code only works when the Netbeans Auto Update UI libraries are included in the Module Suite.

Otherwise, I observed two messages are printed out to my RCP app message log:

1. "Property PROP_IDE_IDENTITY hasn't been initialized yet"

2. [org.netbeans.modules.autoupdate.updateprovider.AutoupdateCatalog]: No content in cache for com_myapp_desktopui_update_center provider. Returns EMPTY_MAP

When the Auto Update UI library is added, it works alright.

Can I get this code to work with just the Auto Update Services libraries, leaving out the Auto Update UI libraries?

Posted by Osho on February 27, 2009 at 02:15 PM CET #

Can u help me about StatusLineNotification class,
Is any setting is required for this class in netbeans.

Posted by Preet on March 03, 2009 at 05:42 AM CET #

Hi Osho,

I am sure the code should work without Autoupdate UI. There is no code in Autoupdate Services which depends on UI part.

> Otherwise, I observed two messages are printed out to my RCP app message log:
>
> 1. "Property PROP_IDE_IDENTITY hasn't been initialized yet"

Well. This property sets Autoupdate UI, but it never mind. You don't need such property for your application.

> 2. [org.netbeans.modules.autoupdate.updateprovider.AutoupdateCatalog]: No content in cache for com_myapp_desktopui_update_center provider. Returns EMPTY_MAP

It might be caused if there is broken connection from your application to your Update Center (check Network Proxy Settings in Tools|Options) or your queries UpdateUnits earlier than the UC is downloaded and parsed. That may be the reason why it worked for your with Autoupdate UI library. The AU UI does initial scan of all UC. So, you should do such scan too, it means call UpdateUnitProviderFactory.getDefault ().refreshProviders (null, true); It will initialize your UC.

Hope it helps,
-jiri

Posted by Jiri Rechtacek on March 03, 2009 at 09:22 AM CET #

Hi Preet,

check out the article at Emilian Bold's blog about StatusLine notification: http://emilian-bold.blogspot.com/2006/08/netbeans-platform-statuslineelementpro.html which could be helpful.

Regards,
-jiri

Posted by Jiri Rechtacek on March 03, 2009 at 09:30 AM CET #

Calling UpdateUnitProviderFactory.getDefault ().refreshProviders (null, true) solved the problem.

Thanks jiri.

Posted by Osho on March 09, 2009 at 04:54 AM CET #

Jiri,

Let me take you back to the question asked by Preet about having a clickable status bar icon.

Unfortunately, Emilian's blog only detail how to change icon position on the status bar by setting an attribute in the META-INF/services entry.

I have been experimenting and I can manage to get icons on the status bar by implementing org.openide.awt.StatusLineElementProvider and registering the implementing class (META-INF or layer.xml) based on the code you posted. But these icons are always visible. If I make the icons (JLabel + ImageIcon) initially invisble by calling setVisible(false) on the Component before returning from getStatusLineElement() (within the implementation class), then the icons are always invisible and I cant get them to show on the status bar at all.

What I want is for the icons to become visible if and only if there was an update and restart is required.

Can you kindly explain how I can show/hide icons (not text!!) on the status bar please from a thread spun of from within the ModuleInstall subclass.

Many thanks for your help

Posted by guest on March 10, 2009 at 02:56 AM CET #

The last comment was from me . . . sorry for leaving out my name

Posted by Osho on March 10, 2009 at 11:33 AM CET #

Hi Osho,
I use this way for this case:
==================
public class MyIcon implements StatusLineElementProvider {
public static JPanel statusPanel = new JPanel ();
public Component getStatusLineElement () {
return statusPanel;
}
}
==================
I don't make this panel invisible but empty. When I need to place a icon into status line, I add the icon into that panel. It will make the icon visible there.

Regards,
-jiri

Posted by Jiri Rechtacek on March 12, 2009 at 03:30 AM CET #

Thank you! Your article was very useful to me!
Maybe is a good idea tho add it to dev faq. I found it browsing dev list.

Posted by Mario Burdman on October 01, 2009 at 11:36 AM CEST #

Hi Mario, it's a good idea. I'll add cross-reference from NetBeans Developer's FAQ and this article. Thanks

Posted by Jiri Rechtacek on October 05, 2009 at 01:50 PM CEST #

Hi Jiri!

At my blog I describe (in german) a complete silent installation (or update) for downloaded NBM files (w/o an existing update center).

br, josh.

link to the topic: http://www.sepix.de/blogs/blogrittner/blog/archive/2010/january/12/beandev_silent_updateinstall_mit_nbm_dateien/index.html

Posted by Aljoscha Rittner on January 12, 2010 at 04:02 AM CET #

Aljoscha Rittner ,

Do you have an english version of this.

Would be very useful.

Thanks,

Posted by dale on February 04, 2010 at 11:15 AM CET #

I'll create an English version. Please be patient.

br, josh.

Posted by Aljoscha Rittner on February 18, 2010 at 05:41 AM CET #

What do you replace for mymodule in:
org/netbeans/modules/mymodule/restart.png

I tried project name and package (ie. com/project) as defined in the module without luck. Obviously the icon is in the project's root.

To get to your module. Also is there a way to reuse the one that Netbeans uses itself?

Posted by Javier Ortiz on April 07, 2010 at 10:53 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Rechtacek's

Search

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