Skip to content

Builder Packager

opencode-agent[bot] edited this page May 10, 2026 · 1 revision

Builder Packager

Ant build system tools for packaging third-party JARs/resources as JNode plugins with generated descriptors and shell scripts.

Overview

The Builder Packager is a set of Ant tasks that transforms third-party JAR files and application directories into JNode plugins suitable for inclusion in the boot image or CD-ROM distribution. It handles plugin descriptor generation, shell script creation, and plugin list integration.

Key Components

Class Purpose
PluginBuilder Main task that builds plugins from user JARs/directories
PackagerTask Abstract base with common property handling
ScriptBuilder Generates JNode scripts from Unix/MS-DOS scripts or from scratch
MainFinder Searches for main classes in JAR files
PluginListInsertor Adds user plugins to plugin lists

How It Works

Plugin Building Flow

userApplicationsDir
    ├── plugin.jar          → processed → PluginBuilder
    ├── myapp/            → directory → ScriptBuilder
    └── plugins.properties  → written by finish()
  1. Discovery: PluginBuilder scans userApplicationsDir for .jar files and directories
  2. Descriptor Generation: For each JAR without an .xml descriptor, builds one automatically via buildDescriptor()
  3. Main Class Detection: Uses MainFinder to locate main(String[]) methods
  4. Script Generation: ScriptBuilder converts Unix/MS-DOS scripts to JNode .jns format
  5. Property File: Writes plugins.properties with comma-separated plugin IDs

PluginBuilder.java (lines 84-103)

public void execute(ThreadPoolExecutor executor, final Map<String, File> descriptors) {
    if (isEnabled()) {
        if (path == null) {
            throw new BuildException("pathRefId is mandatory");
        }
        
        File[] userJars = userApplicationsDir.listFiles(new FileFilter() {
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".jar") || pathname.isDirectory();
            }
        });
        
        for (File userJar : userJars) {
            processUserJar(executor, descriptors, userJar, userPluginIds);
        }
    }
}

Descriptor Generation

The generated XML descriptor (lines 192-250):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plugin SYSTEM "jnode.dtd">

<plugin id="myapp"
  name="myapp"
  version=""
  class="org.jnode.plugin.AutoUnzipPlugin"
  auto-start="true"
  license-name="unspecified">
  <runtime>
    <library name="myapp.jar">
      <export name="*"/>
    </library>
  </runtime>
  <extension point="org.jnode.shell.aliases">
    <alias name="MyMain" class="com.example.MyMain"/>
  </extension>
  <extension point="org.jnode.security.permissions">
    <permission class="java.security.AllPermission" />
  </extension>
</plugin>

ScriptBuilder Flow

ScriptBuilder.build()尝试三种脚本生成策略,按优先级:

  1. Unix scripts (.sh): Parse with # comment, : path separator
  2. MS-DOS batches (.bat): Parse with REM comment, ; path separator
  3. From scratch: Use MainFinder to locate main classes, generate scripts

生成的JNode脚本 (.jns):

# File automatically generated by JNode Packager

# enable exception tracing
propset jnode.debug true

# set system properties

# set classpath
classpath --add file:///jnode/myapp/myapp.jar

# TODO : add permissions

# launch the application
java com.example.MyMain arg1 arg2

MainFinder.java (lines 58-127)

两种main类��索策略:

  1. Manifest 优先: 检查 META-INF/MANIFEST.MF 中的 Main-Class 属性
  2. 字节码扫描: 使用 ASM 库遍历 .class 文件,查找 public static void main(String[])
public static List<String> searchMain(File userJar) throws FileNotFoundException, IOException {
    List<String> mainList = new ArrayList<String>();
    JarFile jarFile = new JarFile(userJar);
    
    // Try manifest first
    if (jarFile.getManifest() != null) {
        Object value = jarFile.getManifest().getMainAttributes().get(Attributes.Name.MAIN_CLASS);
        if (value != null) {
            mainList.add(String.valueOf(value));
            return mainList;
        }
    }
    
    // Scan for main methods in bytecode
    for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
        JarEntry entry = e.nextElement();
        if (entry.getName().endsWith(".class")) {
            // Use ASM to detect main(String[]) method
        }
    }
    return mainList;
}

Gotchas

  • Thread Safety: userPluginIds is a StringBuilder accessed from multiple threads via ThreadPoolExecutor
  • Descriptor Overwrite: Existing .xml descriptors are NOT regenerated; set force.overwrite=true to update
  • Permission Security: Generated descriptors grant AllPermission — should be restricted in production
  • Script Path: Hard-coded /jnode/ path in ScriptBuilder.build() (line 104) should use java.home
  • Manifest Priority: MainFinder only scans bytecode if no manifest Main-Class is found
  • Properties File: Must contain user.plugin.ids property; format is plugin1,plugin2,plugin3...

Related Pages

Clone this wiki locally