Lucee is a high-performance, open-source CFML server written in Java. Supporting JDBC, Caching, Parallelism, HTTP, ORM, REST, S3 and dynamic Java integration, Lucee is great for developers aiming to build fast, scalable applications, delivering faster startup, rapid compilation and better runtime performance.
Lucee 6 comes with a lot of new features and functionality that improve your interaction with Lucee both directly, through new features, and indirectly, by enhancing the existing ones. The focus, as always, is not simply to add functionality that you can achieve yourself with CFML code, but to enhance the language itself.
Stay tuned as we explore the exciting world of Lucee 6. Get ready to elevate your CFML game with the latest and greatest.
Lucee now offers an array of enhanced functionalities for a more seamless integration between Lucee and Java applications and code.
In Lucee 6 you have the flexibility to incorporate Java code directly within your CFML code opening up new possibilities for seamless integration.
int function echoInt(int i) type="java" {
if(i==1) throw new Exception("Oopsie-daisy!!!");
return i*2;
}
Simply add the attribute [type="java"]
and you can effortlessly embed Java code within your CFML template.
to_string = function (String str1, String str2) type="java" {
return new java.lang.StringBuilder(str1).append(str2).toString();
}
Please note that this feature isn’t supported for Lambda Functions as the Lambda Syntax conflicts with the attribute “type=java”.
component {
int function echoInt(int i) type="java" {
if(i==1)throw new Exception("Oopsie-daisy!!!");
return i*2;
}
to_string = function (String str1, String str2) type="java" {
return new java.lang.StringBuilder(str1).append(str2).toString();
}
}
With Lucee 6, you can seamlessly blend Java and CFML to unlock new dimensions of versatility.
In Lucee 6, if the interface of a function matches one of the Java Functional Interfaces found in the Java Standard Library, Lucee automatically implements that interface. Consequently, the function can be seamlessly passed to Java as a Java Function.
For instance, let’s revisit our previous example, which implements the “IntUnaryOperator” interface. You can verify this implementation in the function’s metadata or its dump.
But why does Lucee implement these interfaces? Lucee goes the extra mile by matching the function’s signature, including its arguments and return type, to any of the existing Java Functional Interfaces. And guess what? It all happens automatically!
This feature empowers you to use these functions seamlessly in your Java code, bridging the gap between CFML and Java effortlessly.
Lucee 6 takes CFML to a whole new level by allowing you to seamlessly integrate CFML components and functions with specific Java classes that implement certain interfaces or Java Functions.
When you pass a CFML component to a Java method, and that method expects a specific class, Lucee performs automatic conversion or wrapping of the component into that class. You don’t need to follow any specific steps; Lucee handles it all behind the scenes.
All you have to do is create a component that implements all the methods of a Java interface as functions. For instance, consider a component that implements the CharSequence interface:
// component that implements all methods from interface CharSequence
component {
function init(String str) {
variables.str=reverse(arguments.str);
}
function length() {
SystemOutput("MyString.length:"&str.length(),1,1);
return str.length();
}
function charAt( index) {
SystemOutput("MyString.charAt("&index&"):"&str.charAt( index),1,1);
return str.charAt( index);
}
function subSequence(start, end) {
SystemOutput("MyString.subSequence("&start&", "&end&"):"&str.subSequence(start, end),1,1);
return str.subSequence(start, end);
}
function toString() {
SystemOutput("MyString.toString():"&str.toString(),1,1);
return str.toString();
}
}
// this class has a method that takes as an argument a CharSequence, that way we can force Lucee to convert/wrap our component to that interface.
HashUtil = createObject("java","lucee.commons.digest.HashUtil");
// this component implements all necessary functions for the CharSequence interface
cfc=new MyString("Susi Sorglos");
// calling the method HashUtil.create64BitHashAsString(CharSequence cs) with our component as argument
hash=HashUtil.create64BitHashAsString(cfc);
dump(hash);
component implementsJava="java.util.List" {
function onMissingMethod(name,args) {
if(name=="size") return 10;
throw "method #name# is not supported!";
}
}
In this case, we explicitly implement the “java.util.List” interface, allowing Lucee to handle it as an array. With this approach, you don’t need to define all the methods, as Lucee doesn’t enforce a strict function match due to the “implementsJava” attribute.
When you pass a CFML Function (UDF/Closure/Lambda) to Java like this
and the Java interface expects a Java Function (Lambda), Lucee automatically converts or wraps that function into a Java function of the corresponding type.
This seamless integration allows you to harness the full power of your CFML functions in your Java code.
In the following example, we demonstrate how Lucee implicitly implements the “IntUnaryOperator” interface. You can then pass it to Java and use it as such, making the integration between CFML and Java even smoother.
int function echoInt(int i) type="java" {
if(i==1)throw new Exception("Oopsie-daisy!!!");
return i*2;
}
In Lucee 6, we’ve expanded your options by introducing sub-components, a powerful addition to your CFML toolkit. Unlike traditional components defined in separate .cfc files, sub-components reside within the same template as the main component, offering you increased flexibility and improved code organization.
Sub-components bring several benefits to the table:
These additional benefits highlight the versatility and advantages of using sub-components in your CFML applications.
component {
function mainTest() {
return "main";
}
}
component name="Sub" {
function subTest() {
return "sub";
}
}
// Usage example
cfc = new MyCFC();
echo("main -> " & cfc.mainTest());
echo("<br>");
cfc = new MyCFC$Sub();
echo("sub -> " & cfc.subTest());
echo("<br>");
Sub components expand your horizons and provide new avenues for structuring your CFML code effectively.
In Lucee 6, we not only introduce sub components but also unleash the power of inline components. These components are defined directly within your code, eliminating the need for extra files or templates.
Inline components bring several advantages to your CFML code:
These additional benefits highlight how inline components can streamline your CFML code, making it more concise, efficient, and maintainable while enhancing code organization and flexibility.
inline=new component {
function subTest() {
return "inline<br>";
}
};
dump("inline->"&inline.subTest());
dump(inline);
In the example above, we create an inline component that defines a subTest function. This inline component is perfect for situations where you need a component temporarily, enhancing code efficiency and maintainability.
Inline components offer a powerful way to enhance the structure and readability of your CFML code while efficiently managing local components.
The humble query tag was among the pioneers in CFML, and it remains a cornerstone of the language’s functionality. In Lucee 6, we’ve taken a deep dive into enhancing this critical component, unlocking its full potential to empower your data manipulation and retrieval tasks. Let’s explore the remarkable advancements that make querying in CFML an even more formidable superpower.
In Lucee 6, we introduce the concept of query listeners, a powerful tool that enables you to monitor and manipulate every query execution within your CFML application. By defining query listeners in your Application.cfc, you gain fine-grained control over the query lifecycle, from before execution to after completion.
You can define query listeners using the following syntax in your Application.cfc:
this.query.listener= {
before: function (caller,args) {
dump(label:"before",var:arguments);
},
after: function (caller,args,result,meta) {
dump(label:"after",var:arguments);
}
}
With this configuration, you have access to two listener functions:
Alternatively, you can encapsulate query listener functionality within a dedicated component. Here’s how you can do it:
this.query.listener = new QueryListener();
By using a dedicated component, you can organize and encapsulate your query listener logic, making it easier to manage and maintain, especially in larger applications like this:
component {
function before(caller,args) {
args.sql="SELECT TABLE_NAME as abc FROM INFORMATION_SCHEMA.TABLES"; args.maxrows=2;
return arguments;
}
function after(caller,args,result,meta) {
var row=queryAddRow(result);
result.setCell("abc","123",row);
return arguments;
}
function error(args,caller,meta,exception) {
//handle exception
}
}
Query listeners offer several benefits:
Query listeners provide a valuable toolset for optimizing query execution, troubleshooting issues, and customizing query behavior to meet your application’s demands.
In Lucee 6, we introduce the powerful “async” attribute for queries, allowing you to boost the responsiveness of your applications. With “async,” you can instruct Lucee not to wait for the query’s execution to complete. This asynchronous approach is particularly useful when you want to monitor exceptions or retrieve the query result without causing delays in your application’s flow.
To leverage the “async” attribute, simply add it to your query tag, like this:
query
datasource="mysql"
async=true
listener={
error:function (args,caller,meta,exception){
systemOutput(exception,true,true);
}
} {
```update user set lastAccess=now()```
}
In this example, we’ve set the “async” attribute to “true” for the query, indicating that Lucee should execute the query asynchronously.
Using a Local Listener:
If you want to capture exceptions or retrieve the query result when using “async,” you can define a local listener within the query tag. The local listener is responsible for handling any exceptions that may occur during the asynchronous query execution.
Why Use “async” Queries?
Async queries offer several advantages:
Async queries provide a valuable tool for enhancing the responsiveness of your applications while efficiently handling database operations and exceptions. By incorporating “async” into your query strategy, you can strike a balance between speed and robust error handling.
In Lucee 6, we introduce the powerful “queryLazy” function, a game-changer when dealing with queries that return enormous result sets. Traditional queries might consume excessive memory when handling millions of records, but with “queryLazy,” you can efficiently read queries in manageable blocks, ensuring that memory constraints are never a concern.
The “queryLazy” function allows you to process queries in blocks, making it ideal for scenarios where you expect a large result set. Consider this example:
queryLazy(
sql:"select * from user",
listener: function (rows){
dump(rows);
},
options:{
datasource:datasource,
blockfactor:1000
}
);
In this example, we retrieve records from the “user” table in blocks of a maximum of 1000 records at a time. The “listener” function is invoked for each block of records, enabling you to process them efficiently. By doing so, you prevent memory exhaustion, even when dealing with massive data sets.
“queryLazy” offers several advantages:
“queryLazy” is your go-to solution for efficiently handling large result sets, ensuring optimal memory management, and maintaining the responsiveness of your CFML applications.
In Lucee 6, we introduce the capability to set an index for a query result, providing you with a powerful tool for efficient data retrieval. By defining an index for your query, you can access specific rows, row data, or individual cells with ease, streamlining your data manipulation tasks.
You can set an index for a query result using the “indexName” attribute in your
<cfquery name="qry" datasource="mysql" indexName="id">
select 1 as id, 'Susi' as name
union all
select 2 as id, 'Peter' as name
</cfquery>
In this code, we’ve defined an index named “id” for the query result.
Once you’ve set an index for a query, you can leverage it for various purposes:
<cfdump var="#QueryRowByIndex(qry,2)#">
<cfdump var="#QueryRowDataByIndex(qry,2)#">
<cfdump var="#QueryGetCellByIndex(qry,"name",2)#">
Query indexes offer several benefits:
Query indexes empower you to streamline your data manipulation tasks, making it easier to access and work with specific data points within your query results. Whether you need to retrieve entire rows, row data, or individual cell values, indexes provide an efficient and convenient solution.
In Lucee 6, the cfquery tag and the functions QueryExecute and QueryLazy introduce the new attribute/option, “returntype,” which allows you to define the format of the result returned by the query. This flexibility enables you to tailor the query result to suit your specific needs.
Using the “returntype” Attribute:
<cfquery name="res" datasource="h2" returntype="array">
select id, label from tasks
</cfquery>
In this example, the “returntype” is set to “array.” As a result, the variable “res” does not hold a query object but an array of structs, as shown below:
[
{"id": 10, "label": "First task"},
{"id": 20, "label": "Second task"}
]
If you set the “returntype” to “struct,” you can also define which column serves as the key for the struct using the “columnKey” attribute, like so:
<cfquery name="res" datasource="h2" returntype="struct" columnKey="id">
select id, label from tasks
</cfquery>
In this case, the ordered struct returned will look like this:
{
"10": {"id": 10, "label": "First task"},
"20": {"id": 20, "label": "Second task"}
}
By utilizing the “returntype” attribute, you gain control over the format of your query results, allowing you to structure data in a way that best suits your application’s requirements. Whether you prefer arrays of structs or ordered structs with custom keys, Lucee 6 provides the flexibility to customize query result formats as needed.
In Lucee 6, we’ve gone the extra mile to supercharge your email handling capabilities, just as we did with queries. Our enhancements in this area empower you to streamline your email communication, ensuring that sending and receiving messages is a breeze.
Similar to our support for query listeners, Lucee 6 introduces the capability to use listeners for handling email. These listeners allow you to closely monitor and manipulate email-related tasks, whether you’re sending or receiving messages. You can define mail listeners within your Application.cfc or directly within the mail tag itself, offering flexibility and control over your email interactions.
Mail listeners are defined using the “this.mail.listener” structure within your Application.cfc or directly within the mail tag. Here’s an example:
this.mail.listener = {
before = function (caller,nextExecution,created,detail,closed,advanced,tries,id,type,remainingtries) {
detail.from&=".de";
return arguments.detail;
},
after = function (caller,created,detail,closed,lastExecution,advanced,tries,id,type,passed,remainingtries) {
systemOutput(arguments.keyList(),1,1);
}
};
In this example, we’ve defined both “before” and “after” listener functions. The “before” function allows you to modify email details before execution, while the “after” function provides insights into the email’s processing with access to various data points.
Mail listeners offer several advantages:
Mail listeners empower you to take charge of your email processing, ensuring that it aligns perfectly with your application’s needs. Whether you need to customize email content, monitor email interactions, or perform data manipulations, mail listeners offer a versatile solution for optimizing your email handling tasks.
Like with query the mail tag also supports the “async” attribute, this is not new before this was simply handled by the attribute “spoolenable”.
But like with query you can now combine this as well with a local listener.
In Lucee 6, the cftimeout tag empowers you to take control over the execution time of specific code blocks. It allows you to set a timeout for a code segment and define how to handle both timeout and error scenarios.
<cftimeout timespan="#createTimespan(0, 0, 0, 0,100)#" forcestop=true ontimeout="#function(timespan) {
dump(timespan);
}#">
<cfset sleep(1000)>
</cftimeout>
In this basic example, a timeout of 0.1 seconds (100 milliseconds) is defined using the timespan attribute. The code inside the “ontimeout” function is executed when the timeout is reached. In this case, it will display the timespan.
The cftimeout tag provides further possibilities for handling code execution. You can enhance your control over execution time by utilizing additional attributes like forcestop and defining both “ontimeout” and “onerror” functions.
<cftimeout timespan="#createTimespan(0, 0, 0, 0, 100)#" forcestop="true"
ontimeout="#function(timespan) {
dump(timespan);
}#">
<cfset sleep(1000)>
</cftimeout>
In this enhanced example, the forcestop attribute is set to “true,” which means that Lucee will forcefully stop the execution of the code block when the timeout is reached. The “ontimeout” function will be called, displaying the timespan. This additional level of control allows you to decide whether to stop or continue code execution upon reaching the timeout.
The cftimeout tag provides flexibility in managing code execution, allowing you to tailor your application’s behavior to meet your specific requirements. Whether it’s handling timeouts or errors, “CFTimeout” gives you the tools to control how your application responds to different scenarios.
In Lucee 6, we’ve introduced a more streamlined approach to configuring and managing your Lucee instances.
The Lucee Administrator is a crucial part of managing your Lucee installations. It provides a user-friendly web-based interface to configure and monitor various settings, ensuring your Lucee server runs smoothly. With Lucee 6, we’ve enhanced the Administrator experience to offer more control and flexibility.
Lucee 6 introduces two distinct modes of operation for the Administrator, allowing you to tailor your configuration to your specific needs:
Additionally, with Lucee 6, you have the flexibility to switch between “Single Mode” and “Multi Mode” directly from within the Lucee Administrator. This means you can adapt your configuration as your needs evolve, ensuring that Lucee always aligns with your changing requirements.
With this seamless transition option, Lucee 6 empowers you to maintain full control over your server’s configuration, regardless of whether you start in “Single Mode” or “Multi Mode.” We’re committed to making your Lucee experience as adaptable and efficient as possible.
In Lucee 6, we’ve introduced a more streamlined approach to managing your Lucee instances. While Lucee has traditionally supported both the Lucee Web Administrator and the Lucee Server Administrator, we understand that not all situations require this level of complexity.
In many cases, especially when running a single web context in Lucee, managing both administrators can become an unnecessary overhead. With Lucee 6, we’ve listened to your feedback and introduced a new way to operate: “Single Mode.”
In “Single Mode,” you’ll find a simplified Lucee experience with just one Administrator to manage. This mode is ideal when you have a single web context and don’t need the added complexity of multiple administrators.
However, we understand that some scenarios demand more fine-grained control. If you’re running a multi-web context environment and want to limit access from one web context to another, “Multi Mode” is still available. This mode retains the traditional “Server” and “Web” Administrators for comprehensive control.
With Lucee 6, we’ve put the power in your hands, allowing you to choose the administration mode that best suits your needs, whether it’s the simplicity of “Single Mode” or the versatility of “Multi Mode.” We believe in providing you with the flexibility to streamline your workflow and make your Lucee experience even more efficient.
So far Lucee did support 2 kinds of Administrators, the Lucee Web Administrator and the Lucee Server Administrator. This is a big benefit when you have a multi web context environment and you wanna limit the access from one web context to the other.
But like with every feature, when you don’t need it, for example when you run just one web context in Lucee, this is an unnecessary overhead and a hassle.
Lucee 6 allows you to run Lucee in “Single Mode”, what means you only have one Administrator or in “Multi Mode” to have the old behavior with “Server” and “Web” Administrator.
When you install a new Lucee 6 version, it will be in single mode by default, when you update an existing Lucee 5 version to Lucee 6, it will be in multi mode. So we are backward compatible for updates only.
But you can easily switch between multi and single in the Lucee Administrator on the overview page.
So far Lucee did use XML to store it’s configuration, with Lucee 6 we move away from that and use cfconfig (json) instead, a configuration standard introduces by Ortus Solutions.
With the move to that new format, we did not only add support for all possible settings you could do in the XML, we added new functionality.
One significant improvement in Lucee 6 is the handling of extensions. Previously, extensions in the XML configuration merely reflected what was already installed on the server. However, with the new version, you can now define extensions, and Lucee will take care of the installation process if they are not already present. Extensions can be configured in three distinct ways, providing you with greater flexibility:
{
"source":"/Users/susi/Downloads/com.teradata.jdbc-16.20.0.12.lex",
"name":"Teradata"
}
In this example, the extension is defined with a source attribute pointing to a local .lex file, this can be any supported virtual file system like http, S3, ftp, … .
{
"source":"https://ext.lucee.org/org.postgresql.jdbc-42.2.20.lex",
"name":"PostgreSQL"
}
Alternatively, you can specify the source as an URL endpoint, making it more accessible for remote installations. Lucee supports various virtual filesystems (such as S3, FTP, HTTPS, HTTP, GitHub, and more) as endpoints for these extensions. However, please note that you can only use an endpoint if the corresponding virtual filesystem is installed. For instance, you can’t point to an S3 endpoint if the S3 extension is not installed.
{
"id":"0F5DEC68-DB34-42BB-A1C1B609175D7C57"
,"version":"7.1.3"
,"name":"Exasol"
}
The third method allows you to define extensions by specifying their unique identifier (id). If the extension is not available locally, Lucee will automatically download it from the Lucee extension provider. This feature simplifies the process and ensures that your extensions are always up to date and readily accessible.
Extensions are installed before the rest of the configuration is loaded. This enables you to, for example, install the S3 extension and immediately utilize it for a mapping that points to your S3 storage, simplifying the setup process and ensuring smooth integration of essential features.
With these enhancements in Lucee 6, we aim to make your configuration experience more versatile, efficient, and user-friendly, ultimately providing you with a modern and powerful toolset to tailor Lucee to your exact requirements.
With Lucee 6, we’ve added the configImport function. This practical tool simplifies the process of updating your current configuration. Here’s a quick example to show you how it works:
configImport({
"fileSystem": {
"functionAdditionalDirectory": "{web-root-directory}/addfunction/"
}
},
"web",
"mypassword"
);
Use configImport to streamline your configuration updates in Lucee 6.
Next to the bigger changes, Lucee comes with a lot of “smaller” changes, but these can have big benefits for your daily life. Let’s delve into some of these enhancements:
Lucee 6 introduces the convenience of using .cfs files. These files are designed to streamline your scripting experience by allowing script code to be written directly, without the need for enclosing it in <cfscript>
tags.
<cfscript>
tags, making your code cleaner and more straightforward.This new feature is all about making your development process more efficient and less cluttered. Give .cfs files a try and experience a smoother scripting workflow in Lucee 6.
In Lucee 6, we’ve made a significant change in how numbers are handled internally. Moving away from the previous standard of using “double”, Lucee 6 now employs “BigDecimal” for number processing.
This upgrade to BigDecimal in Lucee 6 marks a leap forward in offering more precise and efficient number handling for your applications.
In Lucee 6, there’s a significant change in how datasource connections are managed. Moving away from the custom-made connection pool that was previously used, Lucee now integrates Apache Commons Pool2 for handling datasource connections.
By adopting Apache Commons Pool2, Lucee 6 sets the stage for more efficient and effective connection pooling, aligning with modern standards and practices.
Lucee 6 brings a host of new and enhanced functions and tags, broadening the capabilities and efficiency of your coding experience. Here’s a snapshot of what’s new:
<cfsetting>
tag gets additional attributes for more control.This is just a glimpse of the extensive list of new functionalities in Lucee 6, designed to make your development process more efficient and versatile.
In Lucee 6, we’ve taken significant steps to optimize and streamline the core by moving more core functionalities to extensions. Key features like Axis, ESAPI, and Image processing have been transitioned into extension modules.
This strategic reorganization of core functionalities into extensions marks a significant step towards a more modular, efficient, and performance-oriented Lucee 6.
In Lucee 6, we’ve made a strategic decision regarding the handling of XML data. We’ve removed the external XML transformers and parsers that were previously part of Lucee and have shifted to utilizing the capabilities inherent in the Java Virtual Machine (JVM).
This update in Lucee 6 reflects our ongoing commitment to optimizing and simplifying the platform, ensuring that it remains as efficient and developer-friendly as possible.
Lucee 6 introduces the capability to define a global proxy. This feature allows you to specify proxy settings either through the Lucee Administrator or directly within the Application.cfc. The global proxy settings ensure that all HTTP requests from Lucee go through the specified proxy server.
this.proxy = {
server: "myproxy.com",
port: 1234,
username: "susi",
password: "sorglos",
excludes: ["lucee.org"],
includes: ["whatever.com"]
};
This feature is particularly useful for scenarios where internet access is restricted or monitored through a proxy, ensuring seamless integration of Lucee applications in such environments.
Next to the Lucee core we also have a lot of changes made to Lucee extensions.
Lucee 6 brings a significant enhancement with its new event-driven Task engine extension. This extension is designed to execute tasks in an innovative yet familiar way, offering flexibility and efficiency in task management.
The introduction of the event-driven Task engine in Lucee 6 represents a step forward in simplifying and enhancing the task execution process, aligning with modern development practices.
Lucee 6 expands the functionality of its Image extension to support a wider range of image formats and includes built-in support for JDeli, a commercial library known for its advanced imaging capabilities.
The enhanced Image extension in Lucee 6, with its wider format support and JDeli integration, significantly improves image processing capabilities, making it easier to work with a variety of image formats in your applications.
The Redis Cache Extension in Lucee 6 has undergone significant improvements. These enhancements not only involve a shift to a new library but also the introduction of a feature for direct, native access.
These improvements in the Redis Cache Extension for Lucee 6 are designed to enhance the overall performance and functionality of Redis caching, making it more adaptable to the diverse needs of web applications.
Enhanced Direct Access and Expanded Support in Lucee 6
The S3 Extension in Lucee 6 has received substantial updates, introducing native access functions and broadening support for various S3 providers.
These enhancements in the S3 Extension for Lucee 6 are designed to improve both the versatility and performance of S3 interactions, offering a more robust and flexible approach to managing S3 resources.