Extensions in Lucee 5

Lucee 5 - Extensions

Creating an extension in Lucee is very straightforward as they are convention based. An extension is basically a ZIP file with the file extension .lex (Lucee EXtension) with the correct file structure.

The following is a simple example of a Lucee Extension and we will extend it step-by-step.

Base structure (required)

At the heart of every extension is the manifest file. This file, “Manifest.mf”, is located in the “/META-INF/” folder inside your lex file.

The manifest file is very powerful, but for the moment we’ll start with the base settings you need to have in place. Simply add the following settings to your manifest file.

Manifest-Version

Manifest-Version: 1.0

The manifest version number. This should be set to the value 1.0 as above.

ID

id: <uuid>

A unique identifier for your extension. The value for the ID has to be a UUID, which you can create with the help of the Lucee function “createuuid”. This identifier will work across multiple extension providers, so if your extension is available on more than one extension provider, Lucee will recognize it and always obtain the newest version.

Version

version: 1.2.3.4

The version number of your extension. Please follow the following pattern for your version number.

<major-number>.<minor-number>.<release-number>.<patches-number-or-string>

Name

name: Beer

This is the name of the extension. You can also add double quotes to your name values to ensure it is interpreted as a string.

These four items are everything we need to have a working extension, so are example MANIFEST.MF file looks like this:

Manifest-Version: 1.0
id: "FAD1E8CB-4F45-4184-86359145767C29DE"
version: "1.2.3.4"
name: Beer

Now we create a ZIP file with the file META-INF/MANIFEST.MF in it and then change the file extension of the resulting ZIP to “.lex”.

To test your new extension simply copy it to /lucee-server/deploy/ a of a running Lucee 5 installation. Wait a little bit, it can take Lucee up to a minute to recognize and install the extension. After the extension disappears from the “deploy” directory it is installed. If you now go to the Lucee Server Admin and to the “Extension/Application” section, you should see it there as installed.

Base structure (optional)

Now let us make our extension richer by adding some optional settings to the MANIFEST.MF file.

Description

description: "This extension installs a tag, that when used shows you where to have a Beer nearby."

The description for your extension.

Category

category: Fun

Category of the extension, this can be a list of multiple categories (comma separated).

Built date

Built-Date: 2016-02-05 11:39:44

Built date of the extension.

Lucee core version

lucee-core-version: "5.0.0.157"

Minimal Lucee core version this extension needs to work properly.

Lucee loader version

lucee-loader-version: "5.8"

The minimum Lucee loader version this extension needs to run properly.

Release type

release-type: web

If this extension should be made available in the server context only, then set to “server”. If this extension only should be available in the web context, then set to “web”. If it should be available in web and server context then set to “all”. The “all” value is the default.

We now have a MANIFEST.MF file that covers all the base settings, the extension still does not do anything, but it is more informative now. This is what the manifest file now has in it:

Manifest-Version: 1.0
id: "FAD1E8CB-4F45-4184-86359145767C29DE"
version: "1.2.3.4"
name: Beer
description: "This extension installs a tag, that when used shows you where to have a Beer nearby."
category: Fun
Built-Date: 2016-02-05 11:39:44
lucee-core-version: "5.0.0.157"
lucee-loader-version: "5.8"
release-type: web

Now we create the .lex file with the file META-INF/MANIFEST.MF inside it again and we are done.

Logo

/META-INF/logo.png

Adding a logo/icon for your extension is very simple. Copy a PNG image with the name “logo.png” to the folder “/META-INF/”.

Installing artifacts

Now that we have covered how the base settings for an extension work, we can make it do something useful.

To begin with we start by installing some CFML based tags.

Adding CFML based tags

/tags

To add CFML based tags create a directory with the name “/tags” and copy the CFML based tag(s) into this directory. After creating your tag(s), your .lex file (zip) will have the following structure.

/META-INF/MANIFEST.MF
/tags/Beer.cfc

Documentation of the tag(s) is via the doc comments and/or hint attributes for the tag. You can add as many tags to the tags directory as you need.

After copying this new extension file to the “deploy” directory (see above) you will see in the Lucee admin that the extension is installed and the new tag(s) will now be available globally.

Adding CFML based functions

/functions

To add CFML based functions create a directory with name “/functions” and copy a CFML template containing the function to that directory. After creating your function(s), your .lex file (zip) will have the following structure.

/META-INF/MANIFEST.MF
/tags/Beer.cfc
/functions/beer.cfm

Documentation of the function(s) is via the doc comments and/or hint attributes for the function. You can add as many functions to the functions directory as you need.

After copying this new extension file to the “deploy” directory (see above) you will see in the Lucee admin that the extension is installed and the new function(s) will now be available globally.

Install context files

/context

The “context” files are files that are copied to the Lucee context directory. This directory is available via the mapping “/lucee” in the case of a web context extension (see “release-type” above) or “/lucee-server” in the case of a server context extension.

The “context” file can be used for various things:

  • make a web service available
  • make a component available
  • extend the Lucee admin with a plugin (by using the path /context/admin/plugin/MyPlugin)
  • extend the Lucee admin with a JDBC Driver (by using the path /context/admin/dbdriver/MyDB.cfc)
  • extend the Lucee admin with a cache (by using the path /context/admin/cdriver/MyCache.cfc)
  • extend the Lucee admin with a debug template (by using the path /context/admin/debug/MyDebugTemplate.cfc)

The content of the extensions “context” folder could look something like this:

/META-INF/MANIFEST.MF
/context/admin/plugin/NotePlus/Action.cfc
/context/admin/plugin/NotePlus/language.xml
/context/admin/plugin/NotePlus/overview.cfm

Web Contexts Files

/webcontexts

These are similar to the “context files”, the difference being that “context files” only define context files for the current context (server or web). The web contexts files however are used in a server context extension to install files to ALL existing and upcoming web contexts. This means that when you add a new web context after this extension is installed, the files defined in this folder will be available to this new web context.

The content of the extensions “webcontext” files could look something like this:

/META-INF/MANIFEST.MF
/webcontexts/admin/debug/MyDebugTemplate.cfc

Applications

/application

If this extension is installed in a web context the content of this folder is copied to the web root directory of that context.

The content of the extensions “application” folder could look something like this:

/META-INF/MANIFEST.MF
/applications/Application.cfc
/applications/index.cfm

Plugins

/plugins

Installs content from the folder /plugin to the plugin directory of the server or web context.

This is a shortcut for “/context/admin/plugin” (see above).

The content of the extensions “plugin” folder could look something like this:

/META-INF/MANIFEST.MF
/plugins/NotePlus/Action.cfc
/plugins/NotePlus/language.xml
/plugins/NotePlus/overview.cfm

You can read more about creating plugins in our guide for Admin Plugins

Archives

/archives

The folder /archives is used to install regular, component or custom tag archives. Lucee archives can be generated in the details view of regular, component or custom tag mappings in the administrator.

The content of the extensions “archives” folder could look something like this:

/META-INF/MANIFEST.MF
/archives/myapp.lar
/archives/mycomponents.lar
/archives/mycustomtag.lar

JARs

/jars
start-bundles:true

Many Lucee extensions use Java libraries (jars) to work.

Lucee 5 support “classic” Jars and OSGI bundles (a subset of jars). Lucee will detect if the given input is a “classic” jar or an OSGi bundle and handle the file accordingly.

OSGi Bundles

OSGi is the framework Lucee 5 is based on, that allows for the loading and unload of JAR files on the fly at any time without a restart necessary. In addition you can have different versions of the same JAR in your environment without causing a conflict. The term “Installing an OSGi bundle” is not accurate in this context, adding a bundle to the JARs directory makes the bundle available in the environment but does not “install” it.

To make Lucee load (install) the JARs into the environment you need to define the setting “start-bundles” in the Manifest file, but in many cases this is not necessary, for example when your JAR contains a tag that you are defining in a tld, you simply define the bundle name and version with the class definition and Lucee will load the JAR automatically when required. This has the benefit that bundles are only loaded into the memory when used.

Please note: It is not actually necessary to bundle the OSGi bundle with the extension, if Lucee cannot find the OSGi bundle locally, Lucee will attempt to download it from the update provider.

“Classic” JAR

As “Classic” JAR we name all JARs that are not OSGi bundle, we highly suggest not to use them, but of course it is possible with all downsides they have.

For example under MS Windows it is not possible to update an already loaded JAR, because Windows has a lock on that JAR. Every update also need a restart of the complete Lucee server.

The content of the extensions “jars” folder could look something like this:

/META-INF/MANIFEST.MF
/jars/mybundle.jar

Components

/components

You can make components globally available by adding them to the folder /components.

The content of the extensions “components” folder could look something like this:

/META-INF/MANIFEST.MF
/components/org/lucee/my/com/MyComp.cfc

Installing

After we have looked at how to install artifacts and configure base settings, we will explain how to install specific functionality to extend Lucee.

Resources (Virtual file system)

resource:{...}

Lucee has an interface to install a virtual file system that can be used in every aspect of the language.

Lucee support a Java and a CFML interface for virtual file systems, so a virtual filesystem can be written in either Java or CFML.

However the installation of a Java based virtual file system is different to the installation of a CFML based virtual file system, therefore we will cover these 2 things separately.

Java based Virtual File System

For a Java virtual file system we need to register the class that implements the interface “ResourceProvider” for this we add the following setting to the MANIFEST.MF File:

resource:"[{'class':'my.very.special.fs.WhateverProvider','bundleName':'my.whatever','bundleVersion':'1.2.3.4','scheme':'we','lock-timeout':'10000'}]"

We define this setting as a JSON String. We define the class that implements the ResourceProvider and the bundle name and version. In addition we can also define other settings such as “lock-timeout”, in the case above. All of these configuration settings are simply forwarded to the “init” method of the class defined.

Copy the OSGi bundle (JAR) containing your ResourceProvider to the “/jars” folder in your extension. It is not necessary to load the JAR with the help of the “start-bundles” setting, Lucee will find the JAR with the help of the bundle definition and only load it as required.

CFML based Virtual File System

For CFML based virtual File systems we need to register the Resource Provider as well, but this time as a CFML component:

Resource:"[
{'component':'my.very.special.fs.WhateverProvider','scheme':'we','lock-timeout':'10000'}
]"

Now we have to make that component available for Lucee, there are 2 ways to do this. You can make the component available as part of a component archive (see “Archives” above) or you simply add the component to the “/components” folder (see “Components” above).

Example implementation:

https://github.com/lucee/extension-s3

Mapping

mapping:[]

You can define mappings that point to files, archives or virtual file systems you are installing. You can define as many mappings as you need in the following way:

mapping: "[
{
'virtual':'/org/lucee',
'physical':'http://lucee.org',
'inspect':'never'
},
{	
'primary':'archive',
'virtual':'/myApp',
'toplevel':true,
'archive':'/../myapp.lar'
}]"

JDBC

Installing a JDBC Driver.

jdbc: "[{
'label':'${label}',
'class':'${className}',
'bundleName':'${symbolicName}',
'bundleVersion':'${symbolicVersion}'
}]"

Copy the OSGi bundle (JAR) containing your JDBC to the “/jars” folder in your extension. It is not necessary to load the JAR with the help of the “start-bundles” setting, Lucee will find the JAR with the help of the bundle definition and only load it as required.

Example implementation:

https://github.com/lucee/JDBC-extension-factory

Search

Installing a Search Engine.

To install a search engine you need to register the class that implements the interface “lucee.runtime.search.SearchEngine” for this we add the following setting to the MANIFEST.MF File:

search: "[{
'class':'${class}',
'bundleName':'${bundlename}',
'bundleVersion':'${bundleversion}${build.number}'
}]"

Copy the OSGi bundle (JAR) containing your Search Engine implemenation to the “/jars” folder in your extension. It is not necessary to load the JAR with the help of the “start-bundles” setting, Lucee will find the JAR with the help of the bundle definition and only load it as required.

Example implementation:

https://github.com/lucee/extension-lucene

ORM

To install an ORM Engine you need to register the class that implements the interface “lucee.runtime.orm.ORMEngine” for this we add the following setting to the MANIFEST.MF File:

orm: "[{
'class':'${class}',
'name':'${bundlename}',
'version':'${bundleversion}${build.number}'
}]"

Copy the OSGi bundle (JAR) containing your ORM Engine implemenation to the “/jars” folder in your extension. It is not necessary to load the JAR with the help of the “start-bundles” setting, Lucee will find the JAR with the help of the bundle definition and only load it as required.

Example implementation:

https://github.com/lucee/extension-hibernate

Java based Tag/Function (TLD/FLD)

/jars
/tlds
/flds

Installing one or multiple Java based Tags or Functions. Installing a tag or a function is the same process, because of that we handle this 2 topics together. A Java based tag/Function needs 2 things:

  1. A OSGi Bundle (JAR) containing the class that implements the interface “javax.servlet.jsp.tagext.Tag” for tags or “lucee.runtime.ext.function.BIF” for a function.
  2. A tld/fld file that contains the description of one or multiple the tags/functions

So copy the OSGi bundles to the folder “/jars” and the tld/fld file to the folder “/tlds” or “/flds”.

Side note: for TLD files better use the extension “.tldx”, otherwise it could break Tomcat when it attempts to read the .tld file. Lucee can handle the extension “.tld” and “.tldx”, tomcat will ignore it.

Example implementation:

https://github.com/lucee/extension-pdf

Monitor

Install a Monitor (more detailed documentation will follow).

monitor: "[{
  'name':'${actionMonitorName}',
  'type':'${actionMonitorType}',
  'class':'${actionMonitorClass}',
  'bundleName':'${bundlename}',
  'bundleVersion':'${bundleversion}${build.number}'
},
{
  'name':'${intervalMonitorName}',
  'type':'${intervalMonitorType}',
  'class':'${intervalMonitorClass}',
  'bundleName':'${bundlename}',
  'bundleVersion':'${bundleversion}${build.number}'
},
{
  'name':'${requestMonitorName}',
  'type':'${requestMonitorType}',
  'class':'${requestMonitorClass}',
  'bundleName':'${bundlename}',
  'bundleVersion':'${bundleversion}${build.number}'
}]"

Cache Handler

This has nothing to do with installing a “Cache” like “EHCache”, this extends the functionality of all “cachedwithin” attributes in Lucee.

The Lucee core support 2 Cache Handler types out of the box:

  1. Time span cache (cachewithin=createTimeSpan(0,0,0,10))
  2. Request span cache (cachewithin="request")

So with this option you can extend this functinality,

To install a Cache Handler you need to register the class that implements the interface “lucee.runtime.cache.tag.CacheHandler” for this we add the following setting to the MANIFEST.MF File:

cache-handler: "[{
  'id':'smart',
  'class':'${class}',
  'name':'${bundlename}',
  'version':'${bundleversion}'
}]"

Copy the OSGi bundle (JAR) containing your Cache Handler implemenation to the “/jars” folder in your extension. It is not necessary to load the JAR with the help of the “start-bundles” setting, Lucee will find the JAR with the help of the bundle definition and only load it as required.l

AMF (Flex)

To install an AMF Engine you need to register the class that implements the interface “lucee.runtime.net.amf.AMFEngine” for this we add the following setting to the MANIFEST.MF file:

amf: "{
  'class':'${class}',
  'bundleName':'${bundlename}',
  'bundleVersion':'${bundleversion}',
  'caster':'modern',
  'configuration':'manual'
}"

Example implementation:

https://github.com/lucee/extension-flex

Event Handler

(TODO)

Event Gateway

(TODO)