Netbeans Platform: excluding locales from zip and installer distribution

This article explains how to prevent Netbeans RCP from packaging unwanted locales into the zip distribution and installers.

This article originally appeared on https://techtavern.wordpress.com

Current situation:

  • The default Netbeans RCP build always includes all localization jars from the harness directory: Russian, Chinese, Japanese and Brazilian Portuguese.
  • Netbeans project properties and branding properties lack configuration to select bundled languages.
  • The build templates did not foresee features to exclude locales.

Problem:

  • Annoying long build times. Proportional to the number of jars, build time increases by five if all default languages are included.
  • Larger distributable artifact size. Localization jars take about 500kB.

Solution:

  • Netbeans Ant build is reasonably organized by build templates. And Ant allows overriding targets inherited from templates.
  • We override the build-zip target. This target generates the .zip distribution artifact, which also serves a input for the installer.

The modified script:

The original build-zip target (netbeans 8.0) is found in the harness directory of your Netbeans installation. We place an adapted copy into the build.xml of your application root directory, thus overriding the behavior of the original target. The red lines were added to prevent adding localization for Russian, Chinese and Japanese.

<target name="build-zip" depends="build,build-launchers" description="Builds a ZIP distribution of the suite, launchers, and selected modules from the platform.">
    <echo message="Custom Build ZIP" />
   <mkdir dir="${dist.dir}"/>
   <!-- pathfileset does not support 'prefix' and 'filemode' parameters,
        we have to copy them to temp location -->
   <tempfile property="temp.dir.nbexec" destdir="${suite.build.dir}" deleteonexit="true" prefix="nbexec"/>
   <tempfile property="temp.dir.rest" destdir="${suite.build.dir}" deleteonexit="delete" prefix="rest"/>
   <subant genericantfile="${harness.dir}/suite.xml" target="copy-cluster" inheritrefs="true">
       <property name="dest.dir" value="${temp.dir.rest}"/>
       <property name="nbexec.dir" value="${temp.dir.nbexec}"/>
       <property name="build.dir" value="${suite.build.dir}"/>
       <resources refid="zip.platform.clusters"/>
   </subant>
   <zip destfile="${dist.dir}/${app.name}.zip">
       <zipfileset dir="${build.launcher.dir}/bin/" filemode="755" prefix="${app.name}/bin"/>
       <zipfileset dir="${build.launcher.dir}/etc/" prefix="${app.name}/etc"/>
       <zipfileset dir="${temp.dir.nbexec}" filemode="755" prefix="${app.name}"/>
       <zipfileset dir="${temp.dir.rest}" prefix="${app.name}"><exclude name="config/Modules/*.xml_hidden"/>
           <exclude name="**/*_ja.jar" />
           <exclude name="**/*_ru.jar" />
           <exclude name="**/*_zh_CN.jar" />
       </zipfileset>  

       <!-- Yes, the doubled app.name is a bit ugly, but better than the alternative; cf. #66441: -->
       <zipfileset dir="${cluster}" prefix="${app.name}/${app.name}">
           <exclude name="config/Modules/*.xml_hidden"/>
           <exclude name="**/*_ja.jar" />
           <exclude name="**/*_ru.jar" />
           <exclude name="**/*_zh_CN.jar" />
       </zipfileset>            
   </zip>
</target>

Netbeans actions toolbar with local lookup context

This article talks about Netbeans and an action toolbar that is sensitive to a local selection lookup context. Actually, global selection context sensitive actions is powerful mechanism with a subtle limitation: everything happens globally. It is no use for a local actions menu or toolbar sensitive to a limited selection context restricted to a JPanel or TopComponent. Not surprisingly, the Netbeans Java IDE itself reinvents the wheel each time this requirement arises, for example, on the ‘variables’ and ‘breakpoint’ windows.

Thus, we still look forward for our local context to reuse conveniences like action annotations that prevent boilerplate code and extensibility through the layer.xml configuration.

Originally published at https://techtavern.wordpress.com.

We suppose that the JPanel or TopComponent owns a JToolbar that will hold all actions that apply to the local selection context. We recommend turning off the ‘floatable’ property, or the user may drag the toolbar out of the JPanel or TopComponent.

The solution relies on the convention that interested actions are to be declared under a pre-defined path within the layer.xml configuration. This allows any module to later provide further actions and works similar to the global toolbar convention itself. We have arbitrarily chosen to declare the actions under “TopComponentName/Toolbar”.

Actions for the local selection context are declared as any global action, except for the @ActionReference annotation, which references the previously pre-defined path instead of an entry in the global menu or global toolbar.

@ActionID(category = "category ",
  id = "fully.qualified.name.of.LocalAction")
@ActionRegistration(
  iconBase = "path/to/icon.png",
  displayName = "#CTL_LocalAction")
@ActionReferences({
  @ActionReference(path = "TopComponentName/Toolbar", position = 30)
})
@Messages("CTL_LocalAction=Action text")
public final class LocalAction implements ActionListener {
  public LocalAction (Type context) {
	...
  }

  @Override
  public void actionPerformed(final ActionEvent ev) {
    ...
  }
}

If fact, this action is global, as actions define a business procedure on a compatible context, regardless on which view or presentation, regardless if made available globally or locally.

Here is the code that populates a local toolbar:

List<? extends Action> actions = Utilities.actionsForPath("TopComponentName/Toolbar");
for (Action action : actions) {
  if (action == null) {
	toolbar.addSeparator();
  } else {
	if (lookup != null && action instanceof ContextAwareAction) {
	  action = ((ContextAwareAction) action).createContextAwareInstance(lookup);
	}
	Component item;
	if (action instanceof Presenter.Toolbar) {
	  item = ((Presenter.Toolbar) action).getToolbarPresenter();
	  if (item == null) {
		continue;
	  }
	} else {
	  item = ActionPresenterProvider.getDefault().createToolbarPresenter(action);
	}
	toolbar.add(item);
  }
}