OSGi to Maven Extension Migration

edit Introduced: 7.0

OSGi to Maven Extension Migration

This recipe guides maintainers (and AI-assisted sessions) through converting a legacy OSGi Lucee extension to the Maven-based extension model in Lucee 7.

For build file snippets, version matrices, CI configuration, and pitfall details, see the technical specification at /docs/technical-specs/maven-extension-migration.yaml.

The worked example is the Image extension (3.0.x OSGi → 3.1.x Maven). For concepts, see Maven Based Extensions.

When to Migrate

Migrate when the extension targets Lucee 7+, you want standard Maven dependency management, and you are ready to drop OSGi bundle wiring.

Keep an OSGi line when you must support Lucee 6.2 for BIF/tag extensions, or need a stable bugfix branch for older installs.

A common strategy: bump the major/minor version line for the Maven branch (e.g. Image 3.0.x for 6.2, 3.1.x for 7+).

Minimum Lucee Core Version

The lucee-core-version in MANIFEST.MF must reflect what your extension actually uses — not a blanket 7.1 for every migration.

Extension type Typical minimum Example
Manifest handlers only (resource, cache, JDBC) 7.0.0.68+ S3 declares 7.0.0.211-BETA
BIF functions (FLD with maven=) 7.0.2.86+ Most function-only extensions
Custom tags (TLD with maven=) 7.1.0.2+ Rare — Image defines <cfimage>

Image requires 7.1.0.2+ not because Maven extensions in general need 7.1, but because it ships a TLD with custom tags. S3-style extensions (manifest-only, no FLD/TLD) follow the 7.0 baseline.

What Changes

At a high level:

  1. Build — Maven compiles the JAR; Ant assembles the .lex (see Maven Based Extensions)
  2. Manifeststart-bundles: false; keep the extension UUID unchanged
  3. FLD/TLD — replace bundle-name/bundle-version with maven="{maven}"; build.xml substitutes coordinates at package time
  4. Dependencies — move from libs/ and manual copies into source/java/pom.xml
  5. Artifacts — publish a full .lex (with embedded maven/ repo) and optionally a lite extension (.lite.lex, Maven classifier lite)

Migration Checklist

1. Build system

  • [ ] Add root pom.xml and source/java/pom.xml
  • [ ] Wire build.xml via maven-antrun-plugin
  • [ ] Ensure maven-build depends on init (wipes target/ each build)
  • [ ] Copy dependencies with Maven repository layout; run copy-parent-poms.sh
  • [ ] Package full .lex and lite extension .lite.lex

2. Extension metadata

  • [ ] Set start-bundles: false
  • [ ] Set lucee-core-version per table above
  • [ ] Keep extension id (UUID) unchanged

3. FLD / TLD (if applicable)

  • [ ] Switch to maven="{maven}" on all <class> and <tag-class> entries
  • [ ] Add javax-tag-class if dual javax/jakarta tag support is needed

4. Dependencies

  • [ ] Move JARs into pom.xml; provided scope for org.lucee:lucee
  • [ ] Remove system scope, manual build.xml JAR copies, and legacy org.lucee repackages

5. Java source

  • [ ] Remove OSGi bundle manifest from extension JAR
  • [ ] Replace OSGi-specific version lookups

6. CI and testing

  • [ ] mvn clean install -Dgoal=install
  • [ ] Test with lucee/script-runner, extensionDir: target/
  • [ ] Upload target/*.lex as artifacts

Details for each step: maven-extension-migration.yaml.

Full vs Lite Extension

Most Maven extensions publish two install packages:

Package File Contents
Full {name}-extension-{version}.lex Metadata + embedded maven/** JARs
Lite extension {name}-extension-{version}.lite.lex Metadata only (no maven/**)

The lite extension is for Lucee Light/Zero and Docker setups where dependencies resolve from Maven Central at install time. The full .lex is for offline/self-contained installs and is what CI typically tests against.

Maven coordinates for the lite extension use classifier lite:

org.lucee:image-extension:3.1.0.9-BETA:lex        # full
org.lucee:image-extension:3.1.0.9-BETA:lite:lex   # lite extension

See Extension Installation for install methods.

Testing

mvn -B -e -f pom.xml clean install -Dgoal=install
ls -lh target/*.lex

Run extension tests via script-runner with extensionDir pointing at target/. Label test components (e.g. labels="image") and pass testAdditional for extension-local tests.

If tests use _internalRequest for .cfm fixtures, paths must use contractPath() — not raw filesystem paths. See the tech spec test_patterns.internal_request section.

Common Issues

Problem Quick fix
Old JARs still in .lex after dependency removal maven-build must depend on init; delete target/ locally
Lite extension fails at runtime offline Use full .lex, or pre-populate {lucee-server}/../mvn/
Tags fail on 7.0.x Extension defines TLD tags — requires 7.1.0.2+
MissingIncludeException in CI tests Use contractPath() web paths in _internalRequest

Full pitfall reference: tech spec pitfalls section.

Reference Repositories

Extension Use as template for
extension-s3 Manifest-only handler, lite extension, 7.0 baseline
extension-mail Simple Maven migration with TLD
extension-image FLD + TLD (custom tags), dependency cleanup, 7.1 minimum
extension-dynamodb Cache handler, no FLD/TLD
extension-ftp Resource provider

AI Session Quick Start

  1. Read Maven Based Extensions and maven-extension-migration.yaml
  2. Pick the closest reference repo (S3 for manifest-only; Image for FLD+TLD)
  3. Determine lucee-core-version: 7.0 default; 7.1.0.2+ only if the extension defines TLD custom tags
  4. Follow the checklist above; verify target/*.lex after build
  5. Add a lite extension if transitive dependencies are large
  6. Keep the OSGi branch until 6.2 support is officially dropped

See also