From b8db6f2adfdfae811668ff98c86fee173e0c35a6 Mon Sep 17 00:00:00 2001 From: michaeloffner Date: Thu, 6 Jun 2024 10:34:31 +0200 Subject: [PATCH] add recipes --- .github/scripts/generate-index-recipes.js | 34 ++ .github/workflows/index-recipes.yml | 39 ++ docs/recipes/Externalizing_Strings.md | 54 ++ docs/recipes/QOQ_Sucks.md | 133 +++++ docs/recipes/application-context-basic.md | 262 +++++++++ .../application-context-set-mapping.md | 262 +++++++++ docs/recipes/application-context-update.md | 46 ++ docs/recipes/basic-date.md | 37 ++ docs/recipes/cache-list.md | 48 ++ docs/recipes/cached-within-request.md | 44 ++ docs/recipes/caches-in-application-cfc.md | 82 +++ docs/recipes/cfml-to-java.md | 79 +++ docs/recipes/check-for-changes.md | 54 ++ docs/recipes/checksum.md | 146 +++++ .../configuration-administrator-cfc.md | 34 ++ docs/recipes/datasource-define-datasource.md | 88 +++ docs/recipes/deploy-archives.md | 89 +++ docs/recipes/encryption_decryption.md | 78 +++ docs/recipes/event-gateway-create.md | 144 +++++ docs/recipes/event-gateways-overview.md | 124 ++++ docs/recipes/event-gateways.md | 80 +++ docs/recipes/exception-cause.md | 59 ++ docs/recipes/exception-output.md | 61 ++ docs/recipes/file-extensions.md | 67 +++ .../filesystem-mapping-define-mapping.md | 105 ++++ docs/recipes/filesystem-mapping.md | 29 + docs/recipes/flying_saucer.md | 125 ++++ docs/recipes/function-listeners.md | 237 ++++++++ docs/recipes/gateways-overview.md | 124 ++++ docs/recipes/global-proxy.md | 57 ++ docs/recipes/hidden_gems.md | 123 ++++ docs/recipes/index.json | 380 +++++++++++++ docs/recipes/inline-components.md | 31 + docs/recipes/java-in-functions.md | 55 ++ docs/recipes/labels.md | 60 ++ docs/recipes/lazy_queries.md | 99 ++++ docs/recipes/loop_through_files.md | 71 +++ docs/recipes/mail-listener.md | 54 ++ docs/recipes/mail-send.md | 48 ++ docs/recipes/mathematical-precision.md | 30 + docs/recipes/monitoring.md | 131 +++++ docs/recipes/null_support.md | 84 +++ docs/recipes/precompiled-code.md | 45 ++ docs/recipes/query-async.md | 48 ++ docs/recipes/query-handling.md | 153 +++++ docs/recipes/query-indexes.md | 37 ++ docs/recipes/query-listener.md | 61 ++ docs/recipes/query-of-queries.md | 126 ++++ docs/recipes/query_return_type.md | 105 ++++ docs/recipes/request-timeout.md | 81 +++ docs/recipes/retry.md | 76 +++ docs/recipes/s3.md | 72 +++ docs/recipes/sax.md | 147 +++++ docs/recipes/script-templates.md | 19 + docs/recipes/settings.md | 537 ++++++++++++++++++ docs/recipes/startup-listeners-code.md | 105 ++++ docs/recipes/static_scope.md | 214 +++++++ docs/recipes/sub-components.md | 45 ++ docs/recipes/supercharge-your-website.md | 44 ++ docs/recipes/systemoutput_function.md | 80 +++ docs/recipes/thread_task.md | 121 ++++ docs/recipes/thread_usage.md | 192 +++++++ docs/recipes/timeout.md | 49 ++ docs/recipes/types_lucee.md | 109 ++++ docs/recipes/virtual-file-system.md | 171 ++++++ docs/recipes/xml_fast-easy.md | 429 ++++++++++++++ 66 files changed, 7053 insertions(+) create mode 100644 .github/scripts/generate-index-recipes.js create mode 100644 .github/workflows/index-recipes.yml create mode 100644 docs/recipes/Externalizing_Strings.md create mode 100644 docs/recipes/QOQ_Sucks.md create mode 100644 docs/recipes/application-context-basic.md create mode 100644 docs/recipes/application-context-set-mapping.md create mode 100644 docs/recipes/application-context-update.md create mode 100644 docs/recipes/basic-date.md create mode 100644 docs/recipes/cache-list.md create mode 100644 docs/recipes/cached-within-request.md create mode 100644 docs/recipes/caches-in-application-cfc.md create mode 100644 docs/recipes/cfml-to-java.md create mode 100644 docs/recipes/check-for-changes.md create mode 100644 docs/recipes/checksum.md create mode 100644 docs/recipes/configuration-administrator-cfc.md create mode 100644 docs/recipes/datasource-define-datasource.md create mode 100644 docs/recipes/deploy-archives.md create mode 100644 docs/recipes/encryption_decryption.md create mode 100644 docs/recipes/event-gateway-create.md create mode 100644 docs/recipes/event-gateways-overview.md create mode 100644 docs/recipes/event-gateways.md create mode 100644 docs/recipes/exception-cause.md create mode 100644 docs/recipes/exception-output.md create mode 100644 docs/recipes/file-extensions.md create mode 100644 docs/recipes/filesystem-mapping-define-mapping.md create mode 100644 docs/recipes/filesystem-mapping.md create mode 100644 docs/recipes/flying_saucer.md create mode 100644 docs/recipes/function-listeners.md create mode 100644 docs/recipes/gateways-overview.md create mode 100644 docs/recipes/global-proxy.md create mode 100644 docs/recipes/hidden_gems.md create mode 100644 docs/recipes/index.json create mode 100644 docs/recipes/inline-components.md create mode 100644 docs/recipes/java-in-functions.md create mode 100644 docs/recipes/labels.md create mode 100644 docs/recipes/lazy_queries.md create mode 100644 docs/recipes/loop_through_files.md create mode 100644 docs/recipes/mail-listener.md create mode 100644 docs/recipes/mail-send.md create mode 100644 docs/recipes/mathematical-precision.md create mode 100644 docs/recipes/monitoring.md create mode 100644 docs/recipes/null_support.md create mode 100644 docs/recipes/precompiled-code.md create mode 100644 docs/recipes/query-async.md create mode 100644 docs/recipes/query-handling.md create mode 100644 docs/recipes/query-indexes.md create mode 100644 docs/recipes/query-listener.md create mode 100644 docs/recipes/query-of-queries.md create mode 100644 docs/recipes/query_return_type.md create mode 100644 docs/recipes/request-timeout.md create mode 100644 docs/recipes/retry.md create mode 100644 docs/recipes/s3.md create mode 100644 docs/recipes/sax.md create mode 100644 docs/recipes/script-templates.md create mode 100644 docs/recipes/settings.md create mode 100644 docs/recipes/startup-listeners-code.md create mode 100644 docs/recipes/static_scope.md create mode 100644 docs/recipes/sub-components.md create mode 100644 docs/recipes/supercharge-your-website.md create mode 100644 docs/recipes/systemoutput_function.md create mode 100644 docs/recipes/thread_task.md create mode 100644 docs/recipes/thread_usage.md create mode 100644 docs/recipes/timeout.md create mode 100644 docs/recipes/types_lucee.md create mode 100644 docs/recipes/virtual-file-system.md create mode 100644 docs/recipes/xml_fast-easy.md diff --git a/.github/scripts/generate-index-recipes.js b/.github/scripts/generate-index-recipes.js new file mode 100644 index 000000000..eb937a369 --- /dev/null +++ b/.github/scripts/generate-index-recipes.js @@ -0,0 +1,34 @@ +const fs = require('fs-extra'); +const path = require('path'); +const crypto = require('crypto'); + +async function generateIndex() { + const recipesDir = path.join(__dirname, '../../docs/recipes'); + const outputPath = path.join(recipesDir, 'index.json'); + const files = await fs.readdir(recipesDir); + const index = []; + + for (const file of files) { + if (file.endsWith('.md')) { + const filePath = path.join(recipesDir, file); + const content = await fs.readFile(filePath, 'utf-8'); + const titleMatch = content.match(/^#\s+(.+)$/m); + const title = titleMatch ? titleMatch[1] : 'Untitled'; + const hash = crypto.createHash('md5').update(content).digest('hex'); + index.push({ + file: file, + title: title, + path: `/docs/recipes/${file}`, + hash: hash, + }); + } + } + + await fs.writeJson(outputPath, index, { spaces: 2 }); + console.log(`Index written to ${outputPath}`); +} + +generateIndex().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/.github/workflows/index-recipes.yml b/.github/workflows/index-recipes.yml new file mode 100644 index 000000000..ad2aa2183 --- /dev/null +++ b/.github/workflows/index-recipes.yml @@ -0,0 +1,39 @@ +name: Generate Recipes Index + +on: + push: + branches: + - '**' # This will trigger on push to any branch + paths: + - 'docs/recipes/**' + workflow_dispatch: + +jobs: + generate-index: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: Install dependencies + run: | + npm install fs-extra + + - name: Generate index + run: | + node .github/scripts/generate-index-recipes.js + + - name: Commit and push changes + run: | + BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/}) + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add docs/recipes/index.json + git commit -m "Update recipes index" + git push origin $BRANCH_NAME diff --git a/docs/recipes/Externalizing_Strings.md b/docs/recipes/Externalizing_Strings.md new file mode 100644 index 000000000..f5d3d14cd --- /dev/null +++ b/docs/recipes/Externalizing_Strings.md @@ -0,0 +1,54 @@ + +## Externalize strings ## + +Externalize strings from generated class files to separate files. This method is used to reduce the memory of the static contents for templates. We explain this method with a simple example below: + +**Example:** + +//index.cfm + +```lucee + + + +

....

+ ....... + ....... + It was popular in the #year# + ....... + .... + + +``` + +1. Here the Index.cfm file contains a lot of strings (static contents), but there is no functionality. The file just gives a cfoutput with year. The variable string 'year' is already declared by using in top of the Index.cfm page. + +2. Execute the CFM page in a browser. A class file is created in the `webapps/ROOT/WEB-INF/lucee/cfclasses/` directory while the CFM file is executed. The run time compiler compiles that file to load the Java bytecode and execute it. + +3. Right click the class file. Then see `Get info`. For example, in my class file there is 8Kb size on the disk. In Lucee, the CFM file with its strings was also loaded. So a lot of memory could be occupied just by string loading the bytecode. To avoid this problem, the Lucee admin has the following solution: + + - Lucee admin --> Language/compiler --> Externalize strings + - This `Externalize strings` setting has four options. Select any one option to test. We selected the fourth option (externalize strings larger than 10 characters). + - Again run the CFM page in a browser. The class file is created with lower memory size than the original 8Kb on disk. + - In addition, it created a text file too. The text file contains the strings from the CFM page. The cfoutput with year is simply not there. The byte code will crop the piece of cfoutput content from the CFM file. + +So, the string 'year' is no longer in memory. When the bytecode is called, it loads the string into memory. The memory is not occupied forever and this reduces the footprint of our application. + +### Footnotes ### + +Here you can see the above details in video + +[Externalize strings](https://youtu.be/AUcsHkVFXHE) \ No newline at end of file diff --git a/docs/recipes/QOQ_Sucks.md b/docs/recipes/QOQ_Sucks.md new file mode 100644 index 000000000..39bd46878 --- /dev/null +++ b/docs/recipes/QOQ_Sucks.md @@ -0,0 +1,133 @@ + +## The good, the bad and the ugly ## + +This document explains why Query of Queries (QoQ) may or may not be the best approach for your use case. + +- **PRO**: It's nice to work with in-memory datasets/queries using SQL. +- **CON**: It can be very slow, depending on the use case. + +Update: The performance of QoQ has been dramatically improved for single tables since 5.3.8! + +[Improving Lucee's Query of Query Support](http://wwvv.codersrevolution.com/blog/improving-lucees-query-of-query-support) + +There has also been a lot of work done to improve the "correctness" of the native SQL engine's behavior. + +[QoQ tickets](https://luceeserver.atlassian.net/issues/?jql=text%20~%20%22qoq%22%20ORDER%20BY%20updated) + +Currently, Lucee QoQ only supports `SELECT` statements; `UPDATE` and `INSERT` aren't yet supported. + +Lucee has two QoQ engines, a fast native engine which only works on a single table. + +Any SQL using multiple tables, i.e., with a JOIN, will fall back to the HSQLDB engine. + +The HSQLDB engine requires loading all the queries into temporary tables and is currently Java synchronized, all of which can affect performance. + +If the native QoQ engine fails on a single table query, by default, Lucee will attempt to fall back on the HSQLDB engine. + +See `LUCEE_QOQ_HSQLDB_DISABLE` and `LUCEE_QOQ_HSQLDB_DEBUG` under [[running-lucee-system-properties]]. + +### Example: ### + +```lucee+trycf + + q = QueryNew("name, description"); + loop times=3 { + getFunctionList().each(function(f){ + var fd = getFunctionData(arguments.f); + var r = QueryAddRow(q); + QuerySetCell(q,"name", fd.name, r); + QuerySetCell(q,"description", fd.description, r); + }); + } + dump(server.lucee.version); + dump(var=q.recordcount, + label="demo data set size"); + s = "the"; + + + + + q3 = q.filter(function(row){ + return (row.description contains s); + }); + + + +``` + +In this example, we have a QOQ with the persons table. + +```luceescript +// index.cfm + +directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +```luceescript +// example.cfm + +max=1000; +persons=query( + "lastname":["Lebowski","Lebowski","Lebowski","Sobchak"], + "firstname":["Jeffrey","Bunny","Maude","Walter"] + ); + +// Query of Query +start=getTickCount("micro"); +loop times=max { + query dbtype="query" name="qoq" {echo(" + select * from persons + where lastname='Lebowski' + and firstname='Bunny' + order by lastname + ");} +} +dump("Query of Query Execution Time:"&(getTickCount("micro")-start)); + +// Query Filter/Sort +start=getTickCount("micro"); +loop times=max { + qf=queryFilter(persons,function (row,cr,qry) {return row.firstname=='Bunny' && row.lastname=='Lebowski';}); + qs=querySort(qf,"lastname"); +} +dump("Query Filter/Sort Execution Time:"&(getTickCount("micro")-start)); +``` + +In this example, we have two different methods of queries. + +1) First one is QoQ. Here, `QoQ` from the `persons` table is executed a thousand times due to the looping required by QoQ. + +2) The second one is calling the function query filter. Query filter filters out the same row the same way the QoQ does. + +3) Execute it in the browser and we get two results (Query of Query execution time and Query filter/sort execution time). Query filter executes at least twice as fast as query of query. Because QoQ loops over and over again, it is slower. If you can avoid QoQ and use the Query filter/sort, your code will execute much faster. + +### Footnotes ### + +You can see these details in the video here: + +[Query of Query Sucks](https://www.youtube.com/watch?v=bUBXzo1WbSM) \ No newline at end of file diff --git a/docs/recipes/application-context-basic.md b/docs/recipes/application-context-basic.md new file mode 100644 index 000000000..0b1f44333 --- /dev/null +++ b/docs/recipes/application-context-basic.md @@ -0,0 +1,262 @@ + +Lucee provides several event handling functions within `Application.cfc` that can be used to manage different stages and types of requests. Here is an overview of these functions and their usage. + +### OnApplicationStart ### + +This method is triggered when the application starts. + +```cfs +component { + void function onApplicationStart() { + echo('Application started'); + } +} +``` + +### OnSessionStart ### + +This method is triggered when a session starts. + +```cfs +component { + void function onSessionStart() { + echo('Session started'); + } +} +``` + +### OnRequestStart ### + +This method is triggered at the start of each request. + +```cfs +component { + void function onRequestStart(string targetPage) { + echo('Request started: ' & targetPage); + } +} +``` + +### OnRequest ### + +This method handles the actual request. In Lucee, this function is called even if the target page does not exist physically or is never used. + +```cfs +component { + void function onRequest(string targetPage) { + echo('Hello World'); + } +} +``` + +### OnCFCRequest ### + +Similar to "onRequest", but this function is used to handle remote component calls (HTTP Webservices). + +```cfs +component { + void function onCFCRequest(string cfcName, string methodName, struct args) { + echo('Hello World'); + } +} +``` + +### OnError ### + +This method is triggered when an uncaught exception occurs in this application context. + +```cfs +component { + void function onError(struct exception, string eventName) { + dump(var:exception, label:eventName); + } +} +``` + +As arguments you receive the exception (cfcatch block) and the eventName. + +### OnAbort ### + +This method is triggered when a request is ended with help of the tag ``. + +```cfs +component { + void function onAbort(string targetPage) { + dump('request ' & targetPage & ' ended with an abort!'); + } +} +``` + +### OnDebug ### + +This method is triggered when debugging is enabled for this request. + +```cfs +component { + void function onDebug(struct debuggingData) { + dump(var:debuggingData, label:'debug information'); + } +} +``` + +### OnMissingTemplate ### + +This method is triggered when a requested page was not found and **no function "onRequest" is defined**. + +```cfs +component { + void function onMissingTemplate(string targetPage) { + echo('missing:' & targetPage); + } +} +``` + +## Application.cfc Default Template ## + +Below you can find an Application.cfc template that may serve as a starting point for your own applications settings with Lucee CFML engine. + +When creating an Application.cfc for the first time, you can configure all the settings within the Lucee Server or Web Administrator and use its "Export" tool (Lucee Administrator => Settings => Export) to move (by copy and paste) the settings into your Application.cfc file. + +```cfs +component { + + /** + * @hint onApplicationStart() is triggered when the application starts. + */ + public boolean function onApplicationStart(){ + + return true; + + } + + + /** + * @hint onSessionStart() is triggered when a session starts. + */ + public boolean function onSessionStart(){ + + return true; + + } + + + /** + * @hint onRequestStart() is triggered at the start of each request. + */ + public boolean function onRequestStart(string targetPage){ + + return true; + + } + + + /** + * @hint onRequest() is triggered during a request right after onRequestStart() ends and before onRequestEnd() starts. Unlike other CFML engines, Lucee executes this function without looking for the "targetPage" defined, while other CFML engines will complain if the targetPage doesn’t physically exist (even if not used in the onRequest() function). + */ + public void function onRequest(string targetPage){ + + include arguments.targetPage; + return; + + } + + + /** + * @hint onRequestEnd() is triggered at the end of a request, right after onRequest() finishes. + */ + public void function onRequestEnd(){ + + return; + + } + + + /** + * @hint onCFCRequest() is triggered during a request for a .cfc component, typically used to handle remote component calls (e.g. HTTP Webservices). + */ + public void function onCFCRequest(string cfcName, string methodName, struct args){ + + return; + + } + + + /** + * @hint onError() is triggered when an uncaught exception occurs in this application context. + */ + public void function onError(struct exception, string eventName){ + + return; + + } + + + /** + * @hint OnAbort() is triggered when a request is ended with help of the "abort" tag. + */ + public void function onAbort(string targetPage){ + + return; + + } + + + /** + * @hint onDebug() is triggered when debugging is enabled for this request. + */ + public void function onDebug(struct debuggingData){ + + return; + + } + + + /** + * @hint onMissingTemplate() is triggered when the requested page wasn’t found and no "onRequest()" function is defined. + */ + public void function onMissingTemplate(string targetPage){ + + return; + + } + +} +``` \ No newline at end of file diff --git a/docs/recipes/application-context-set-mapping.md b/docs/recipes/application-context-set-mapping.md new file mode 100644 index 000000000..0b1f44333 --- /dev/null +++ b/docs/recipes/application-context-set-mapping.md @@ -0,0 +1,262 @@ + +Lucee provides several event handling functions within `Application.cfc` that can be used to manage different stages and types of requests. Here is an overview of these functions and their usage. + +### OnApplicationStart ### + +This method is triggered when the application starts. + +```cfs +component { + void function onApplicationStart() { + echo('Application started'); + } +} +``` + +### OnSessionStart ### + +This method is triggered when a session starts. + +```cfs +component { + void function onSessionStart() { + echo('Session started'); + } +} +``` + +### OnRequestStart ### + +This method is triggered at the start of each request. + +```cfs +component { + void function onRequestStart(string targetPage) { + echo('Request started: ' & targetPage); + } +} +``` + +### OnRequest ### + +This method handles the actual request. In Lucee, this function is called even if the target page does not exist physically or is never used. + +```cfs +component { + void function onRequest(string targetPage) { + echo('Hello World'); + } +} +``` + +### OnCFCRequest ### + +Similar to "onRequest", but this function is used to handle remote component calls (HTTP Webservices). + +```cfs +component { + void function onCFCRequest(string cfcName, string methodName, struct args) { + echo('Hello World'); + } +} +``` + +### OnError ### + +This method is triggered when an uncaught exception occurs in this application context. + +```cfs +component { + void function onError(struct exception, string eventName) { + dump(var:exception, label:eventName); + } +} +``` + +As arguments you receive the exception (cfcatch block) and the eventName. + +### OnAbort ### + +This method is triggered when a request is ended with help of the tag ``. + +```cfs +component { + void function onAbort(string targetPage) { + dump('request ' & targetPage & ' ended with an abort!'); + } +} +``` + +### OnDebug ### + +This method is triggered when debugging is enabled for this request. + +```cfs +component { + void function onDebug(struct debuggingData) { + dump(var:debuggingData, label:'debug information'); + } +} +``` + +### OnMissingTemplate ### + +This method is triggered when a requested page was not found and **no function "onRequest" is defined**. + +```cfs +component { + void function onMissingTemplate(string targetPage) { + echo('missing:' & targetPage); + } +} +``` + +## Application.cfc Default Template ## + +Below you can find an Application.cfc template that may serve as a starting point for your own applications settings with Lucee CFML engine. + +When creating an Application.cfc for the first time, you can configure all the settings within the Lucee Server or Web Administrator and use its "Export" tool (Lucee Administrator => Settings => Export) to move (by copy and paste) the settings into your Application.cfc file. + +```cfs +component { + + /** + * @hint onApplicationStart() is triggered when the application starts. + */ + public boolean function onApplicationStart(){ + + return true; + + } + + + /** + * @hint onSessionStart() is triggered when a session starts. + */ + public boolean function onSessionStart(){ + + return true; + + } + + + /** + * @hint onRequestStart() is triggered at the start of each request. + */ + public boolean function onRequestStart(string targetPage){ + + return true; + + } + + + /** + * @hint onRequest() is triggered during a request right after onRequestStart() ends and before onRequestEnd() starts. Unlike other CFML engines, Lucee executes this function without looking for the "targetPage" defined, while other CFML engines will complain if the targetPage doesn’t physically exist (even if not used in the onRequest() function). + */ + public void function onRequest(string targetPage){ + + include arguments.targetPage; + return; + + } + + + /** + * @hint onRequestEnd() is triggered at the end of a request, right after onRequest() finishes. + */ + public void function onRequestEnd(){ + + return; + + } + + + /** + * @hint onCFCRequest() is triggered during a request for a .cfc component, typically used to handle remote component calls (e.g. HTTP Webservices). + */ + public void function onCFCRequest(string cfcName, string methodName, struct args){ + + return; + + } + + + /** + * @hint onError() is triggered when an uncaught exception occurs in this application context. + */ + public void function onError(struct exception, string eventName){ + + return; + + } + + + /** + * @hint OnAbort() is triggered when a request is ended with help of the "abort" tag. + */ + public void function onAbort(string targetPage){ + + return; + + } + + + /** + * @hint onDebug() is triggered when debugging is enabled for this request. + */ + public void function onDebug(struct debuggingData){ + + return; + + } + + + /** + * @hint onMissingTemplate() is triggered when the requested page wasn’t found and no "onRequest()" function is defined. + */ + public void function onMissingTemplate(string targetPage){ + + return; + + } + +} +``` \ No newline at end of file diff --git a/docs/recipes/application-context-update.md b/docs/recipes/application-context-update.md new file mode 100644 index 000000000..81640786e --- /dev/null +++ b/docs/recipes/application-context-update.md @@ -0,0 +1,46 @@ + +# Update Application Context + +Lucee allows you to update the existing application context defined for example in [cookbook-application-context-basic]. + +For example, add a per-application mapping: + +```lucee + + +``` + +This example doesn't extend the existing application mappings with this new one, it replaces them. So when you plan to add a mapping, it's best to first read the existing mappings with help of the function [function-getApplicationSettings] and update these mappings as follows: + +```lucee + + + + + + + + +``` + +Of course, it's not only mappings you can update. [tag-application] lets you update all the settings you can do in the Application.cfc! \ No newline at end of file diff --git a/docs/recipes/basic-date.md b/docs/recipes/basic-date.md new file mode 100644 index 000000000..45a6ec39e --- /dev/null +++ b/docs/recipes/basic-date.md @@ -0,0 +1,37 @@ + +# Output the current date + +The following examples show you how to output the current date. + +```run + +

The time is #lsdateTimeFormat(now())#

+
+``` + +The tag `` defines for the compiler that everything within a `##` is a code expression and needs to be parsed accordingly. the function `now()` is a function call that returns a date object containing the current time. [function-lsDateTimeFormat] then converts that date to a string using locale-specific formatting rules (default en_US). + +You can configure a different locale globally in the Lucee admin under "Settings/Regional". + +You can then configure a different locale for the current request in the Application.cfc file (for example: `this.locale="de_CH"`) or with the help of the function [function-setLocale] or as an argument of the function call itself as follows: + +```run + +

The time is #lsDateTimeFormat(date:now(),locale:'de_CH')#

+
+``` \ No newline at end of file diff --git a/docs/recipes/cache-list.md b/docs/recipes/cache-list.md new file mode 100644 index 000000000..b7d1b6d65 --- /dev/null +++ b/docs/recipes/cache-list.md @@ -0,0 +1,48 @@ + +## List existing Cache Connections + +There is now a built-in function in Lucee to list existing cache connections, but you can also easily do this using the following: + +```cfs +/** +* returns all available cache names as an array +*/ +array function cacheNames() { + return getPageContext().getConfig().getCacheConnections().keySet().toArray(); +} +``` + +This function returns an array containing all cache connections available. + +```cfs +/** +* checks if a cache with the given name is defined +* @cacheName name of the cache to look for +*/ +boolean function hasCache(required string cacheName) { + var it = getPageContext().getConfig().getCacheConnections().keySet().iterator(); + loop collection="#it#" item="local.name" { + if (cacheName.trim() == name) return true; + } + return false; +} +``` + +This is a variation of this function that checks if a cache with the given name exists. \ No newline at end of file diff --git a/docs/recipes/cached-within-request.md b/docs/recipes/cached-within-request.md new file mode 100644 index 000000000..9d1e2b6ff --- /dev/null +++ b/docs/recipes/cached-within-request.md @@ -0,0 +1,44 @@ + +## Cache a Query for the current request + +Perhaps you're familiar with the "cachedwithin" attribute of the tag [tag-query], which is normally used as follows: + +```coldfusion + + select * from whatever where whatsoever='#whatsoever#' + +``` + +This caches the query result for ALL users for one second. This is sometimes used to cache a query for the current request because usually most requests are completed in less than a second. + +The problem is that this cache applies to all requests and that's more complicated for Lucee, meaning unnecessary overhead on the system. + +Request query caching is a simple solution to this problem. Replace the timespan defined in the "cachedWithin" attribute with the value "request": + +```coldfusion + + select * from whatever where whatsoever='#whatsoever#' + +``` + +Then the query is cached for only the current request, independent of how long the request takes! \ No newline at end of file diff --git a/docs/recipes/caches-in-application-cfc.md b/docs/recipes/caches-in-application-cfc.md new file mode 100644 index 000000000..52fb08acf --- /dev/null +++ b/docs/recipes/caches-in-application-cfc.md @@ -0,0 +1,82 @@ + +## Adding Caches via Application.cfc + +It is possible to add cache connections in Lucee 5.1+ on a per-application basis by adding configuration to your `Application.cfc`. You can also select the default object cache, query cache, function cache, etc. Note if these caches use an extension that provides the cache driver, the extension must be installed already. + +To declare cache connections, create a struct called `this.cache.connections` in the pseudo constructor of your `Application.cfc`. Each key in the struct will be the name of the cache connection to create, and the value of the item will be another struct defining the properties of that cache connection. + +```lucee +this.cache.connections["myCache"] = { + class: 'org.lucee.extension.cache.eh.EHCache', + bundleName: 'ehcache.extension', + bundleVersion: '2.10.0.25', + storage: false, + custom: { + "bootstrapAsynchronously":"true", + "replicatePuts":"true", + etc... + }, + default: 'object' +}; +``` + +Note, there is a shortcut for `this.cache.connections["myCache"] = {}` and that is `this.cache["myCache"] = {}`. We support both since the latter is closer to how datasources are defined. + +### Generating Cache Connection code + +The easiest way to generate the code block above is to follow these steps: + +1. Start up a Lucee server +2. Create the cache you want via the web admin +3. Edit the cache and scroll to the bottom +4. Copy the code snippet that appears directly into your `Application.cfc` + +### Cache metadata + +Let's take a look at some of the keys used to define a cache connection. + +* **class** - This is the Java class of the driver for the cache engine. +* **bundleName** - Optional. The name of the OSGI bundle to load the `class` from. +* **bundleVersion** - Optional. The version of the OSGI bundle to load the `class` from. +* **storage** - A boolean that flags whether this cache can be used for client or session storage. +* **custom** - A struct of key/value pairs for configuring the cache. This struct is entirely dependent on the cache driver in use, so refer to the docs for that cache driver to see the possible values. Note, some of these custom values might be required for some cache drivers to work. +* **default** - Optional. If you want this cache to be used as a default cache, then give this one of these values: `function`, `object`, `template`, `query`, `resource`, `include`, `http`, `file`, `webservice`. + +### Default Caches + +When declaring a cache, you can make it the default cache for creation operations, but it is also possible to configure the default caches for each operation all at once in your `Application.cfc` like so: +```lucee +this.cache.object = "myCache"; +this.cache.template = "AnotherCache"; +this.cache.query = "yetAnother"; +this.cache.resource = ""; +this.cache.function = ""; +this.cache.include = ""; +this.cache.http = ""; +this.cache.file = ""; +this.cache.webservice = ""; +``` + +A single cache can only be the default storage location for a single operation at a time. For example, a cache named "myCache" cannot both be the default cache for objects as well as queries. \ No newline at end of file diff --git a/docs/recipes/cfml-to-java.md b/docs/recipes/cfml-to-java.md new file mode 100644 index 000000000..56ca2d2fb --- /dev/null +++ b/docs/recipes/cfml-to-java.md @@ -0,0 +1,79 @@ + +# Convert a CFML Function/Component to use in Java + +Lucee allows you to convert user-defined functions or components so you can use them in Java. + +## Component to Java Class + +You simply add all functions defined for a Java interface to a component like this: + +```lucee +// 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(); + } + // ... more functions here +} +``` + +### Pass to Java + +Then you can pass that component to a Java method needing a specific interface/class. + +```lucee +// This class has a method that takes as an argument a CharSequence. +// This 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 +cfc = new MyString("Susi Sorglos"); + +// Calling the method HashUtil.create64BitHashAsString(CharSequence cs) with our component as an argument +hash = HashUtil.create64BitHashAsString(cfc); +dump(hash); +``` + +### Explicit Definition and "onMissingMethod" + +Of course, you can also define the interface you want to implement explicitly and you can use “onMissingMethod” so you do not have to implement every single function separately. + +```lucee +component implementsJava="java.util.List" { + function onMissingMethod(name, args) { + if (name == "size") return 10; + throw "method #name# is not supported!"; + } +} +``` + +## User-Defined Function to Java (as Lambda) + +Functions get converted to a Lambda interface when the interface matches automatically. You can do the same with regular functions, but here the conversion happens when passing to Java. + +```lucee +numeric function echoInt(numeric i) { + if (i == 1) throw "Test output!!!"; + return i * 2; +} +``` \ No newline at end of file diff --git a/docs/recipes/check-for-changes.md b/docs/recipes/check-for-changes.md new file mode 100644 index 000000000..5fe3cb857 --- /dev/null +++ b/docs/recipes/check-for-changes.md @@ -0,0 +1,54 @@ + +# Check for changes in your configuration file automatically + +Lucee can automatically check for changes in your configuration files from the complete server or a single web context. + +This is useful if you are doing scripted deploys and/or synchronization from, for example, a master instance to many slave instances of Lucee. + +## Check for Changes in ALL the contexts + +To enable this for a whole Lucee server, find the Lucee server XML file in: + + /lib/ext/lucee-server/context/lucee-server.xml + +At the top of this file, you should see something along the lines of: + + + +Now it's simple to add the following: + + + +Now that you have made the change, you can either restart Lucee server from the administrator at: + + http://localhost:8888/lucee/admin/server.cfm?action=services.restart + +Or actually make any change in the Server Admin for the configuration to be picked up. This should now allow it to pick up any changes you have written to the lucee-server.xml file. + +## Check for changes in an individual context + +If you only want an individual context to check for changes, you can do the same configuration but you would have to go to: + + /WEB-INF/lucee/lucee-web.xml.cfm + +And add the same changes from above: + + + +Lucee will now check for any changes in the Lucee configuration files every minute, and if there is a change, reload it and enable those changes. + +A very handy little feature for those automated deployments! \ No newline at end of file diff --git a/docs/recipes/checksum.md b/docs/recipes/checksum.md new file mode 100644 index 000000000..5a47cb384 --- /dev/null +++ b/docs/recipes/checksum.md @@ -0,0 +1,146 @@ + +This document explains how to use a checksum in Lucee. + +Many servers provide a checksum for the files they provide for download. We use the checksum to validate a download file in order to avoid a corrupted file. + +If you download a file in your application, you can automatically check if the download is valid or not if the necessary info was provided in the response header. + +### Example 1 : ### + +```luceescript + +_url="http://central.maven.org/maven2/org/lucee/esapi/2.1.0.1/esapi-2.1.0.1.jar"; +http url=_url result="res"; +if(res.status_code!=200) throw "wtf"; +dump(res.responseheader); + +// store the file +localFile="esapi-2.1.0.1.jar"; +fileWrite(localFile,res.fileContent); + +// get a hash +dump(fileInfo(localFile)); +dump(hash(fileReadBinary(localFile),"md5")); +dump(hash(fileReadBinary(localFile),"SHA1")); + + +// validate file +if(!isEmpty(res.responseheader["X-Checksum-MD5"]?:"")) { +fi=fileInfo("esapi-2.1.0.1.jar"); +if(res.responseheader["X-Checksum-MD5"]==fi.checksum) { +dump("we have a match!"); +} +else { +fileDelete("esapi-2.1.0.1.jar"); +dump("something went wrong! give it another try?"); +} +} + +``` + +* Download the jar file by using cfhttp. +* Dump the file response header. You can see the "X-Checksum-MD5" "X-Checksum-SHA1" keys from the file itself. +* Save the file, and dump(fileInfo(localFile.checksum)). Check to see if the dump matches the value of the downloaded file response["X-Checksum-MD5"] header. + +Checksum values are hashed from the binaryfile itself. + +```luceescript +dump(hash(fileReadBinary(localFile),"md5")); +dump(hash(fileReadBinary(localFile),"SHA1")); +``` + +You can validate the checksum as shown below: + +```luceescript +// validate file +if(!isEmpty(res.responseheader["X-Checksum-MD5"]?:"")) { +fi=fileInfo("esapi-2.1.0.1.jar"); +if(res.responseheader["X-Checksum-MD5"]==fi.checksum) { +dump("we have a match!"); +} +else { +fileDelete("esapi-2.1.0.1.jar"); +dump("something went wrong! give it another try?"); +} +} +``` + +If the checksum is provided, we can check it. However, the checksum may not always be provided. The following example shows how to provide a checksum for all downloads. + +### Example 2 ### + +//download.cfm + +```luceescript + +fi=fileInfo("esapi-2.1.0.1.jar"); +header name="Content-MD5" value=fi.checksum; +content file="esapi-2.1.0.1.jar" type="application/x-zip-compressed"; + +``` + +This code allows a downloading application to check if the download was successful or not. Adding the file with header content "Content-MD5" is not required. + +Download the file using the below example code: + +```luceescript + +// possible MD5 headers +HEADER_NAMES.SHA1=["Content-SHA1","X-Checksum-SHA1"]; +HEADER_NAMES.MD5=["Content-MD5","X-Checksum-MD5"]; // ETag +_url=getDirectoryFromPath(cgi.request_url)&"/_download.cfm"; + +http url=_url result="res"; +if(res.status_code!=200) throw "wtf"; + +// store the file +fileWrite("clone.jar",res.fileContent); + +// see if we have one of the MD5 headers +checksum={type:"",name:""}; +loop label="outer" struct=HEADER_NAMES index="type" item="names" { +loop array=names item="name" { +if(structKeyExists(res.responseheader,name)) { +checksum.type=type; +checksum.name=name; +checksum.value=res.responseheader[name]; +break outer; +} +} +} +dump(checksum); + +// validate file +if(!isEmpty(checksum.name)) { +cs=hash(fileReadBinary("clone.jar"),checksum.type); +//dump(checksum); +if(checksum.value==cs) { +dump("we have a match!"); +} +else { +fileDelete("clone.jar"); +dump("something went wrong! give it another try?"); +} +} + +``` + +The above code checks and validates the downloaded file. + +### Footnotes ### + +You can see the details in this video: +[Checksum](https://www.youtube.com/watch?v=Kb_zSsRDEOg) \ No newline at end of file diff --git a/docs/recipes/configuration-administrator-cfc.md b/docs/recipes/configuration-administrator-cfc.md new file mode 100644 index 000000000..079f6264d --- /dev/null +++ b/docs/recipes/configuration-administrator-cfc.md @@ -0,0 +1,34 @@ + +## Configure Lucee within your application + +Lucee provides a web frontend to configure the server and each web context, but you can also do this configuration from within your application. +(For per request settings, please check out the "Application.cfc" section in the [Cookbook](/guides/cookbooks.html)). + +### Administrator.cfc + +Lucee provides the component "Administrator.cfc" in the package "org.lucee.cfml", a package auto imported in any template, so you can simply use that component as follows: + +```cfs +admin = new Administrator("web", "myPassword"); // first argument is the admin type you want to load (web|server), second is the password for the Administrator +dump(admin); // show me the doc for the component +admin.updateCharset(resourceCharset: "UTF-8"); // set the resource charset +``` + +### cfadmin Tag + +The component "Administrator" is far from being feature complete, so if you miss a functionality, best consult the unofficial tag "cfadmin" (undocumented) and check out how this tag is used inside the [Lucee Administrator](https://github.com/lucee/Lucee/blob/5.2/core/src/main/java/resource/component/org/lucee/cfml/Administrator.cfc). +Of course, it would be great if you could contribute your addition to the "Administrator" component. \ No newline at end of file diff --git a/docs/recipes/datasource-define-datasource.md b/docs/recipes/datasource-define-datasource.md new file mode 100644 index 000000000..2a63321be --- /dev/null +++ b/docs/recipes/datasource-define-datasource.md @@ -0,0 +1,88 @@ + +# How to define a Datasource + +To execute queries, you need a datasource definition, which points to a specific local or remote datasource. There are different ways to do so. + +## Create a Datasource in the Administrator + +The most common way to define a datasource is in the Lucee Server or Web Administrator. The only difference between the Web and Server Administrator is that datasources defined in the Server Administrator are visible to all web contexts, while datasources defined in the Web Administrator are only visible to the current web context. + +In your Administrator, go to the Page "Services/Datasource", in the section "create new Datasource" choose a name for your datasource and the type of your Datasource, for example "MySQL". + +![create datasource](https://bitbucket.org/repo/rX87Rq/images/3802808059-createds.png) + +On the following page, you can define settings to connect to your datasource. The look and feel of this page depend on the datasource type used. After saving this page, you get back to the overview page and you will get feedback if Lucee was able to connect to your datasource or not. + +## Create a Datasource in the Application.cfc + +You cannot only define a datasource in the Lucee Administrator, you can also do this in the [cookbook-application-context-basic]. The easiest way to do so is to create a datasource in the Administrator (see above) and then go to the detail view of this datasource by clicking the "edit button". + +![select datasource](https://bitbucket.org/repo/rX87Rq/images/4142224660-select-datasource.png) + +At the bottom of the detail page, you find a box that will look like this: + +![datasource application definition](https://bitbucket.org/repo/rX87Rq/images/1656402808-datasource-app-def.png) + +You can simply copy the code inside the box to your [cookbook-application-context-basic] body, and Lucee will pick up this definition. After that, you can delete the datasource from the Administrator. + +```cfs +this.datasources["myds"] = { + class: 'org.gjt.mm.mysql.Driver', + connectionString: 'jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true', + username: 'root', + password: 'encrypted:5120611ea34c6123fd85120a0c27ab23fd81ea34cb854' +}; +``` + +Alternatively, you can also use this pattern: + +```cfs +this.datasources["myds"] = { + type: 'mysql', + host: 'localhost', + database: 'test', + port: 3306, + username: 'root', + password: 'encrypted:5120611ea34c6123fd85120a0c27ab23fd81ea34cb854', + connectionLimit: -1, + connectionTimeout: 1, + blob: false, + clob: false, + storage: false, + timezone: 'CET', + custom: {useUnicode: true, characterEncoding: 'UTF-8'} +}; +``` + +### Default Datasource + +With the [cookbook-application-context-basic], you can also define a default datasource that is used if no "datasource" attribute is defined with the tag cfquery, cfstoredproc, cfinsert, cfupdate, etc. Simply do the following: + +```cfs +this.defaultdatasource = "myds"; +``` + +In that case, the datasource "myds" is used if there is no datasource defined. Instead of defining a datasource name, you can also define the datasource directly as follows: + +```cfs +this.datasource = { + class: 'org.gjt.mm.mysql.Driver', + connectionString: 'jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true', + username: 'root', + password: 'encrypted:5120611ea34c6123fd85120a0c27ab23fd81ea34cb854' +}; +``` \ No newline at end of file diff --git a/docs/recipes/deploy-archives.md b/docs/recipes/deploy-archives.md new file mode 100644 index 000000000..788027d49 --- /dev/null +++ b/docs/recipes/deploy-archives.md @@ -0,0 +1,89 @@ + +## Deploy Archive + +This document explains how to deploy an Application on a live server without using a single CFML file. + +### Using CFC file + +```lucee +//placed under outside root/component/org/lucee/examples/deploy/Test.cfc + +component test { + function salve() { + return "Hi There" + } +} + +``` + +You will need to add a mapping for the above CFC, because it's not inside the Root folder. + +Create component mapping in **Archives & Resources -> Component**. + +Create a mapping for test.cfc as shown below: + +``` +name: mycfc +resource: **Full folder path**/component/ +``` + +After creating the mapping, you need to create an archive file for the CFC. + +* Go to the detail view of mycfc mapping page, +* Click the button **assign archive to mapping**. + +An archive (lar file) is created automatically and saved in `WEB-INF/lucee/context/archives`. + +Now you can see the archive path on the mycfc mapping. + +### Using CFM file + +Create a mapping for the below CFM file: + +```lucee +//placed under /ROOT/test/deploy/index.cfm + +test = new org.lucee.examples.deploy.Test(); +dump(test.salve()); + +``` + +``` +name: /deploy +resource: ROOT/test/deploy/index.cfm +``` + +After creating the mapping in the Administrator, you can create an archive file by clicking **assign archive to mapping**. + +Now you can see both lar files in the `WEB-INF/lucee/context/archives` folder: + +* One is `lucee/context/archives/xxx-deploy.lar` file, +* Another one is `lucee/context/archives/xxx-mycfc.lar` + +Now you can place the archive files on your target server. + +Copy the archive files (deploy.lar, mycfc.lar) and place them in the target server's `/WEB-INF/lucee/deploy` folder. Wait for a minute, and it will successfully deploy your archives into the server. + +You can now view mappings in the admin. + +### Footnotes + +Here you can see the above details in a video: + +[Lucee Deploy Archive file](https://www.youtube.com/watch?time_continue=473&v=E9Z0KvspBAY) \ No newline at end of file diff --git a/docs/recipes/encryption_decryption.md b/docs/recipes/encryption_decryption.md new file mode 100644 index 000000000..76dc7c6e7 --- /dev/null +++ b/docs/recipes/encryption_decryption.md @@ -0,0 +1,78 @@ + +## Encryption/Decryption ## + +This document explains about Encryption/Decryption with public and private keys with simple examples. + +Encryption/Decryption is a new functionality in Lucee 5.3. We have a new way to encrypt/decrypt string values. First we start with keys. In this case, there are two keys: + +* Private key to encrypt +* Public key to decrypt + +### Example 1: ### + +```luceescript +//index.cfm +directory action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +```luceescript +key=generateRSAKeys(); +dump(key) +``` + +This function generates RSA keys. Execute the example code above in the browser and a struct is returned containing the two keys: a private key and a public key. So, we can create these keys, and store them somewhere for later use. + +### Example 2: ### + +```luceescript +key=generateRSAKeys(); +raw="Hi, Hello !!!"; +enc=encrypt(raw,key.private,"rsa"); +dump(enc); +``` + +We now create RSA keys using the [[function-generatersakeys]] function, and then use the key to encrypt using the [[function-encrypt]] function. The encrypt() function has some arguments. It has `key.private` which defines the key as the private key, and `rsa` indicates use of the RSA encryption algorithm. Then run the dump in the browser and we see the encrypted string for your input string. + +### Example 3: ### + +```luceescript +key=generateRSAKeys(); +raw="Hi, Hello !!!"; +enc=encrypt(raw,key.private,"rsa"); +dec=decrypt(enc,key.public,"rsa"); +dump(dec); +``` + +This is a full detailed example of encrypt/decrypt functions. We create a key and we encrypt with the private key. Then we [[function-decrypt]] with the public key. Then run the dump in the browser and we see the original string returned as expected. + +### Footnotes ### + +Here you can see these details in the video also: + +[Encryption/Decryption with public and private keys](https://www.youtube.com/watch?v=2fgfq-3nWfk) \ No newline at end of file diff --git a/docs/recipes/event-gateway-create.md b/docs/recipes/event-gateway-create.md new file mode 100644 index 000000000..271f2d0bc --- /dev/null +++ b/docs/recipes/event-gateway-create.md @@ -0,0 +1,144 @@ + +### Preface + +Here you will find a short introduction into writing your own Event Gateway type. + +Since you can write these in pure CFML (and Java when you want it), it is really simple to do. + +There are 2 to 3 files you need to create: + +* the Gateway CFC +* the Gateway Driver CFC +* A listener CFC + +### The Gateway CFC + +This is the file which contains the action you want your gateway to do. + +Also, it is the file which is instantiated by Lucee when the gateway starts. + +You can take the following files as an example: + +* {Lucee-install}/lib/lucee-server/context/gateway/lucee/extension/gateway/DirectoryWatcher.cfc +* {Lucee-install}/lib/lucee-server/context/gateway/lucee/extension/gateway/MailWatcher.cfc + +The example code shown underneath is a modified version of the DirectoryWatcher.cfc, which, at time of writing, is in line for reviewing at the Lucee team. + +By default, you need to have the following functions: + +* An init function, which receives the necessary config data. +* A start function, which continues to run while variables.state="running". +* A stop and restart function. +* A getState function, which returns the current state of the gateway instance (running, stopping, stopped). +* A sendMessage function, which will be called when the CFML sendGatewayMessage function is used. + +The following is all the code you need: + +```lucee + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT * + FROM FILE + WHERE directory = "#getDirectoryFromPath(variables.config.filepath)#" + AND name = "#getFileFromPath(variables.config.filepath)#" + + + + + + + + +``` + +We will save the file as {Lucee-install}/lib/lucee-server/context/gateway/filesizechecker/FileSizeWatcher.cfc. + +The variables.listener and variables.config variables did not just come falling from the sky; instead, it was saved to the variables scope in the init() function. + +Lastly, we need to create the Gateway driver. We can use the Gateway driver code shown before, and then save it as {Lucee-install}/lib/lucee-server/context/admin/gdriver/FileSizeWatcher.cfc. + +Now we are almost good to go! We do need to restart Lucee to have it pick up the new Gateway driver. So just go to the server admin, click on the menu-item "Restart", and then hit the "Restart Lucee" button. + +We can add an instance of our new Gateway type now! You can do it by using cfadmin like this: + +```lucee + +``` + +Interval: time in milliseconds to wait between each check. +Minimalsize: the minimum filesize in bytes. + +After executing the cfadmin code, or going through the admin screens, you should now have an instance of your own Event Gateway type running! + +When creating a Socket gateway or an Instant messaging gateway, you will need to do a bit more coding, but hopefully this instruction helped you out! \ No newline at end of file diff --git a/docs/recipes/event-gateways-overview.md b/docs/recipes/event-gateways-overview.md new file mode 100644 index 000000000..96ce18460 --- /dev/null +++ b/docs/recipes/event-gateways-overview.md @@ -0,0 +1,124 @@ + +## How does an Event Gateway work? + +An event gateway is a background process that continuously runs. + +While running, it is doing the following: `` for a specific time (the "interval"), then doing what it is designed for (checking changes in a directory, polling a mailserver, etc.), and after that, it goes to `` again. + +This looping and sleeping does not count for all types of event gateways. For example, a socket gateway just instantiates a Java socket server. + +This "doing what it is designed for" will be explained in more detail underneath. + +## Which gateways are available? + +Lucee comes with 2 gateways: a Directory watcher and a Mail watcher. + +### Directory watcher + +This event gateway checks a given directory for file changes. These changes (events) can be: + +* new files +* changed files +* removed files + +When this gateway starts, it first takes a snapshot of the current state of the directory. This is the starting point from where changes are calculated. + +Please note that the files in this first snapshot are NOT seen as changes! + +So if you already have some files in the directory you want to watch, these files are not seen as "new file" when the gateway starts. Also, when Lucee (or your whole server) restarts, any changes which happened within this time are not seen and will not be picked up when the Directory watcher starts up again. + +### Filters + +You can apply filters for what you exactly want to watch changes for: + +* **Watch subdirectories**: same as the "Recurse" option in `` and `directoryList()` +* **Extensions**: an optional list of comma-delimited file extensions. The default is "*", which obviously means "all files". + +Note: the Extensions setting might be changed in the near future, due to an enhancement request. + +### Mail watcher + +This gateway checks a given POP mailbox for new mail. Since it only checks for new mail, it is rather limited in what it can do, but this is what makes it fast. The Mail watcher will read the inbox, and then check all the emails found. + +### Logs + +Make sure you regularly check the logs, because when anything goes wrong, Lucee will report this in its logs. + +Lucee logs can be found here: + +* `{Lucee-server}/context/logs/` +* `{Lucee-web}/lucee/logs/` + +You can also view the logs in your web/server admin by installing the Log Analyzer plugin. + +Also, make sure that you wrap your Listener function code inside try-catch blocks and do something within the catch block. For example: + +```lucee + + + + + + + + + + +``` + +If you do not add these try-catch blocks, and anything goes wrong, it will be much harder to find out if anything went wrong! + +For example, the above code would crash if a file with the same name already exists in the directory "C:/backupfiles/". + +It might be even wiser to just email the complete error dump, so you will be semi-instantly notified of any errors. + +### Using cfadmin with Event gateways + +Instead of using the server/web admin, you can also use Lucee's `` tag. + +Add or update a gateway instance: + +```lucee + +``` + +### Remove a gateway instance + +```lucee + +``` \ No newline at end of file diff --git a/docs/recipes/event-gateways.md b/docs/recipes/event-gateways.md new file mode 100644 index 000000000..df937c70c --- /dev/null +++ b/docs/recipes/event-gateways.md @@ -0,0 +1,80 @@ + +## Lucee Event Gateways + +First of all, it is necessary to explain how Event Gateways (EG) are working in the first place. EG's are another way to communicate with your Lucee server and are kind of a service running on Lucee, reacting to certain events. These kinds of events could be something along the lines of: + +* SMS sent to a certain receiver +* File change happening in a directory +* Mail received on a mail server +* Slack notification received + +What then can be done with these events is to trigger some actions that react to these events. For instance, if an SMS is sent to the server asking for the current heap memory space, the server could respond with an SMS returning the details. So you basically have an event producer and an event consumer. + +Event Gateways have for a long time lived a quiet life in CFML for several reasons. The main reasons were the lack of diversity and implementations, which were due to the fact that EG's had to be written in Java and not every CFML developer is very familiar with Java. Given this downside, it is understandable that there are such few available event gateways available. + +## Lucee's approach + +In Lucee, EG's can be written in CFML, and this is what this description is all about, which now makes it way more attractive to write the decisive parts with your favorite language. Some parts sometimes still need perhaps a Java library, but coding around that normally is not really a problem. Just use the according JAR solution available for the specific event (like SMS or others). + +### What are the involved components in Lucee? + +There are 2 components that are important for writing an event gateway: + +* Gateway driver +* Event Gateway + +The gateway driver is a CFC that is always instantiated and running. It is responsible for managing the lifecycle of the event gateway. The event gateway is the actual implementation of the event handling logic. + +### Testing the Event Gateway + +I have created a template called testGateway.cfm and use the following code to test the result. + +```lucee + + +``` + + + +Now the sanity checks kick in and prevent faulty data from being sent to the Gateway. So once we change the code to this: + +```lucee + + +``` + +We receive the expected blank page. In the background, the message has been passed to the Gateway through the sendGateway() method and the data will be written by the start() endless loop into the logfile with the help of the method _log(). + +How you actually write your EG is totally up to you. But now, do it in CFML! + +## Further examples for Event Gateway implementations + +Above we have introduced the possibility to asynchronously log some data to a log file. There are additional other Event Gateways you can think of or use: + +* ICQ watcher +* Slack Channel inspector +* Listen to a socket +* On incoming email + +The possibilities are huge and we expect several new event gateways to emerge in the next few months. Have fun with Lucee. \ No newline at end of file diff --git a/docs/recipes/exception-cause.md b/docs/recipes/exception-cause.md new file mode 100644 index 000000000..9067bec0e --- /dev/null +++ b/docs/recipes/exception-cause.md @@ -0,0 +1,59 @@ + +# Exception - Cause + +Lucee 6.1 improves its support for exception causes, providing better debugging and error handling capabilities. + +## Tag Attribute cause + +The `` tag now includes a new attribute, `cause`, which allows you to add a cause to a newly created exception. + +```run + +try { + try { + throw "Upsi dupsi!"; + } + catch(e) { + cfthrow (message="Upsi daisy!", cause=e); + } +} +catch(ex) { + dump(ex.message); + dump(ex.cause.message); +} + +``` + +Thanks to this enhancement, you get not only the tag context and Java stack trace from the top-level exception, but also the same information for the "cause" exception. + + +## Parent Thread Context + +When you throw an exception from a child thread, for example, a `cfhttp` call executed in parallel or an exception inside the `cfthread` tag, you can now see the stack trace from where that thread was started. Previously, you only saw the stack trace within the child thread. With Lucee 6.1, you also get the information from the parent thread as the cause. Consider the following example: + +```run + +thread name="testexception" { + throw "Upsi dupsi!" +} +threadJoin("testexception"); +dump(cfthread["testexception"].error.message); +dump(cfthread["testexception"].error.cause.Message); + +``` + +The error not only includes the exception information from within the cfthread tag but also provides information from outside, making debugging much easier as you can see where the tag was called from. + diff --git a/docs/recipes/exception-output.md b/docs/recipes/exception-output.md new file mode 100644 index 000000000..68c0c3a37 --- /dev/null +++ b/docs/recipes/exception-output.md @@ -0,0 +1,61 @@ + +# Output Exceptions + +How to catch and display exceptions + +## Example + +```run + +try { + throw "an error happened"; +} +catch ( any e ){ + dump(e); +} + +Go on with your code +``` + + [tag-Dump] shows the full exception structure without blocking your code. Dump includes all stack trace with it. + +## Example 2 + +```lucee + +try { + throw "an error happened again!"; +} +catch ( any e ){ + echo(e); +} + +Go on with your code +``` + +Here we simply echo the exception. It shows the normal exception without blocking your code. + +[https://www.youtube.com/watch?v=vM-4R2A-ZsM](https://www.youtube.com/watch?v=vM-4R2A-ZsM) \ No newline at end of file diff --git a/docs/recipes/file-extensions.md b/docs/recipes/file-extensions.md new file mode 100644 index 000000000..1becd4bae --- /dev/null +++ b/docs/recipes/file-extensions.md @@ -0,0 +1,67 @@ + +# File Extensions + +Lucee supports several file extensions for different types of templates and components. The most common extensions are `.cfm`, `.cfc`, `.cfml`, and `.cfs`. Each serves a specific purpose in CFML development. + +## .cfm + +The `.cfm` extension is used for CFML templates. These files contain CFML code that is processed by the server to generate dynamic web pages. + +### Example + +```cfm + + + + CFM Example + + + + +

#greeting#

+
+ + +``` + +## .cfc + +The `.cfc` extension is used for CFML Components (CFCs). These files define reusable components that encapsulate functionality in methods, similar to classes in object-oriented programming. + +### Example + +```cfc +component { + public string function greet(string name) { + return "Hello, " & name & "!"; + } +} +``` + +## .cfml + +The `.cfml` extension is an alternative to `.cfm` and can be used for CFML templates. It serves the same purpose as `.cfm` but is less commonly used. + +## .cfs + +Since version 6.0, Lucee supports templates with the extension `.cfs`. These templates contain script code, similar to `.js` files in the JavaScript world. This allows you to write direct script code without the need for the `` tag. + +### Example + +```cfs +writeOutput("Hello from a .cfs file!"); +``` \ No newline at end of file diff --git a/docs/recipes/filesystem-mapping-define-mapping.md b/docs/recipes/filesystem-mapping-define-mapping.md new file mode 100644 index 000000000..04f189668 --- /dev/null +++ b/docs/recipes/filesystem-mapping-define-mapping.md @@ -0,0 +1,105 @@ + +## How to define a regular Mapping + +Lucee allows you to define a mapping to a specific location in a filesystem, so you don't always have to use the full path. In most cases, the full path is not working anyway, for example with [tag-include] which does not work with a full path. + +This is supported with all the various [lucee-resources] supported (local, ftp, http, ram, zip, s3, etc.). + +## Create a regular Mapping in the Administrator + +The most common way to define a regular mapping is in the Lucee Server or Web Administrator. + +The only difference between the Web and Server Administrator is that a mapping defined in the Server Administrator is visible to all web contexts, and a mapping defined in the Web Administrator is only visible to the current web context. + +In your Administrator, go to the Page "Archives & Resources/Mappings" in the section "create new Mapping" that looks like this. + +![create-mapping.png](https://bitbucket.org/repo/rX87Rq/images/4035761629-create-mapping.png) + +With "Virtual" choose the virtual path for the mapping, this is the path you will later use to address this mapping. + +"Resource" is the physical location where the mapping is pointing to, for example `C:\Projects\whatever\test`. + +With "Archive" you can map a Lucee archive (more below) to the mapping. + +"Primary" defines where a template is searched first. Let's say you have set primary to "archive" and you execute the following code: + +```coldfusion + +``` + +In that case, Lucee first checks the archive associated with the "/myMapping" mapping for "test.cfm". If the template is not found there, Lucee also checks the physical location. + +"Inspect templates" defines the re-check interval for the physical paths. + +### Using the Mapping + +Now you can use that mapping in your code: + +```coldfusion + + +``` + +## Advanced + +In the previous example, we simply set a path. As you can see in the Administrator, a mapping can contain more data than only a physical path. Of course, you can use these settings also with a mapping done in the [tag-application]. + +```cfs +// Application.cfc +component { + this.mappings['/shop'] = { + physical: getDirectoryFromPath(getCurrentTemplatePath()) & 'shop', + archive: getDirectoryFromPath(getCurrentTemplatePath()) & 'shop.lar', + primary: 'archive' + }; +} +``` + +In that case, we not only define a physical path but also a Lucee archive (.lar). + +"primary" defines where Lucee checks first for a resource. Let's say you have the following code: + +```coldfusion + +``` + +In that case, Lucee first checks in the archive for "whatever.cfm". If not found there, it looks inside the physical path. + +### Side Note + +Of course, this can be done for all mapping types: + +```cfs +// Application.cfc +component { + this.componentpaths = [{archive: getDirectoryFromPath(getCurrentTemplatePath()) & 'testbox.lar'}]; // loading testbox from an archive + this.customtagpaths = [{archive: getDirectoryFromPath(getCurrentTemplatePath()) & 'helper.lar'}]; // a collection of helper custom tags +} +``` + +### See Also + +- [Forcing Lucee to re-check the physical paths of application defined mappings without a restart](https://blog.simplicityweb.co.uk/123/forcing-lucee-to-re-check-the-physical-paths-of-application-defined-mappings-without-a-restart) +- [Confusion Over this.mappings And expandPath() Not Working In Lucee](https://www.bennadel.com/blog/3718-confusion-over-this-mappings-and-expandpath-not-working-in-lucee-cfml-5-3-3-62.htm) \ No newline at end of file diff --git a/docs/recipes/filesystem-mapping.md b/docs/recipes/filesystem-mapping.md new file mode 100644 index 000000000..b1f8f6996 --- /dev/null +++ b/docs/recipes/filesystem-mapping.md @@ -0,0 +1,29 @@ + +## Mapping + +We distinguish 3 different mapping types: + +- "component" mappings used to map components (similar to a classpath in Java), +- "custom tag" mappings used to map custom tags +- "regular mappings" for the rest ([[tag-include]], [[tag-file]] ...). + +* [[cookbook-filesystem-mapping-define-mapping]] +* [[cookbook-application-context-set-mapping]] +* Define a component Mapping (TODO) +* Define a custom tag Mapping (TODO) \ No newline at end of file diff --git a/docs/recipes/flying_saucer.md b/docs/recipes/flying_saucer.md new file mode 100644 index 000000000..d558d600b --- /dev/null +++ b/docs/recipes/flying_saucer.md @@ -0,0 +1,125 @@ + +#PDF Engine (Flying saucer) +This document provides information about the new PDF engine, [Flying Saucer](https://github.com/flyingsaucerproject/flyingsaucer) (FS) in Lucee 5.3 + +Flying saucer is a new PDF engine in Lucee. PDF engines are mainly used to convert HTML to PDF format. + +### Benefits of moving to Flying Saucer from the old engine (PD4ML) ### + +* Full support for CSS 2.1 +* On average the generated PDFs are smaller +* Consume less Memory and CPU +* Engine in active development, +* Better Results + +### Downsides to Flying Saucer compared to the old engine (PD4ML) ### + +* The generated PDF does not always look exactly the same when generated with the new FC compared to files generated with the PD4ML. + +If it's important that the PDF output remains exactly the same as the old PD4ML-generated file, you will need to check it manually. + +If you don't have time to check all PDF outputs, or you really don't care about the fancy new engine, simply add the following code to use the old PDF engine. + +via Application.cfc, + +```luceescript +this.pdf.type = "classic"; +``` + +or if you are using an Application.cfm, + +```lucee + +``` + +and since the PDF Extension 1.0.0.92-SNAPSHOT you can specify the engine using type + +```lucee + + or + +``` + +### Features of Flying Saucer ### + +You can define a font directory where you have the fonts(.ttf,.otf) you are using in your PDF. + +### Define the font directory #### + +```lucee + +``` + +Define the font directory Application itself: + +via Application.cfc + +```luceescript +this.pdf.fontDirectory = "path/to/my/font"; +``` + +or via application.cfm + +```lucee + +``` + +If the font directory isn't specified, Lucee will look for fonts in /WEB_INF/lucee/fonts and uses them if they match. + +**Note**: Classic engine works using the font-family-name from pd4fonts.properties file. Modern (Flying saucer) engine works using the font-family-name from the .ttf file with the same case. + +#### Simplify Attributes #### + +Attributes with cfdocument are a mess. You can make it clearer using the following syntax: + +Example: + +```lucee + +``` + +In Lucee you can do the following: + +```lucee + +``` + +Or even simpler + +```lucee + +``` + +#### Additional Units #### + +In addition to "inch" and "cm", the attribute unit now supports "pixel" and "points". + +```lucee + +``` + +If you find any issues while using the new PDF engine, please ask a question on the [mailing list](https://dev.lucee.org/) + +### Footnotes ### + +You can see the details in this video: +[Flying saucer](https://www.youtube.com/watch?v=B3Yfa8SUKKg) \ No newline at end of file diff --git a/docs/recipes/function-listeners.md b/docs/recipes/function-listeners.md new file mode 100644 index 000000000..32c844b2f --- /dev/null +++ b/docs/recipes/function-listeners.md @@ -0,0 +1,237 @@ + +# Function Listener + +Lucee 6.1 introduced a new feature called "function listeners". This allows you to execute a function (built-in or user-defined) in parallel, so you do not have to wait for the result of the execution, similar to using the `cfthread` tag. Function listeners provide an easy syntax to not only execute the function in parallel but also include support to handle the result by simply adding a listener after the function call. This listener can handle the result or exception of a function. + +## Simple Version + +This example demonstrates a simple function listener that executes `mySuccess` in parallel and sets a variable with the result. + +```run + +function mySuccess() { + return "Susi Sorglos"; +} + +mySuccess():function(result,error) { + request.testFunctionListener=result; +}; +// wait for the thread to finish +sleep(100); +dump(request.testFunctionListener ?: "undefined1"); + +``` + +## Join thread thread + +Instead of "run and forget" you can also join the thread with help of the function `threadJoin` (or the tags ``). You get a name from the call and that name you can use to join it. The thread information is available in the scope `cfhread` like a regular thread. + +```run + +function mySuccess() { + return "Susi Sorglos"; +} +// the function call returns the name of the thread +threadName=mySuccess():function(result,error) { + thread.result=result; +}; +// you can then use the name to join the thread +threadJoin(threadName); + +// and for example see the result in cfthread +dump(cfthread[threadName].result); + +``` + +## Handling Exceptions + +In this case we see what happens when the function throws an exception. in that case the argument `error` is provided to the listener function, you can then for example send it to the log. + +```run + +// function that throws an exception +function myError() { + throw "Upsi dupsi!"; +} + +// storing the exception message in the thread scope +threadName=myError():function(result,error) { + thread.result=error.message; +}; + +// wait for the thread to finish +threadJoin(threadName); + +// see the result +dump(cfthread[threadName].result); + +``` + +## Listening on a Built-in Function (BIF) + +Instead of user defined functions, you can also listen to build in functions as well. + +```run + +threadName=arrayLen([1,2,3]):function(result,error) { + thread.result=result; +}; +// wait for the thread to finish +threadJoin(threadName); + +dump(cfthread[threadName].result); + +``` + +## Listening on a variable "Chain" + +You can also listen to variable "chain". + +```run + +function mySuccess() { + return "Susi Sorglos"; +} +// create a chain +a.b.c.d=mySuccess; +threadName=a.b.c.d():function(result,error) { + thread.result=result ?: error; +}; +// wait for the thread to finish +threadJoin(threadName); + +dump(cfthread[threadName].result); + +``` + + +## Listening on a Component Instantiation + +You can also listen to a component instantiation. + +```run + +threadName=new Query():function(result,error) { + thread.result=result; +}; + +// wait for the thread to finish +threadJoin(threadName); + +dump(getMetadata(cfthread[threadName].result).fullname); + +``` + +## Listening on a Static Component Function + +You can also listen to a static component function. + +```run + +threadName=Query::new(["columnName"]):function(result,error) { + thread.result=result; +}; + +// wait for the thread to finish +threadJoin(threadName); + +dump(cfthread[threadName].result.columnlist); + +``` + + +## Function Collection Listener + +A listener not necessarly has to be a function, it also can be a function collection (multiple functions inside a struct). +This way you can define a function for specific events like `onSuccess` or `onFaail` like this. + +```run + +function mySuccess() { + return "Susi Sorglos"; +} + +threadName1=mySuccess():{ + onSuccess:function(result) { + thread.success=result; + } + ,onFail:function(error) { + thread.fail=error.message; + } +}; + +// wait for the thread to finish +threadJoin(threadName1); +dump(cfthread[threadName1].success); + +``` + + +## Component Listener + +You can also define a component instance as a listener, in this case we do a inline component. + +```run + +function mySuccess() { + return "Susi Sorglos"; +} + +threadName1=mySuccess():new component { + function onSuccess(result) { + thread.success=result; + } +}; + +// wait for the thread to finish +threadJoin(threadName1); +dump(cfthread[threadName1].success); + +``` + + +## No Listener + +In case you wanna simply a asynchron exection, but you don't care about the outcome, you can define no listener at all, simply pass `null` (in this case i use the function `nullValue()`, because only with full null support enabled, the constant `null` is available). + +```run + +// writing some data to the request scope +function logAndFail(name,value) { + request.testFunctionListenerEcho[name]=value; + throw "Upsi dupsi!"; +} + +threadName1=logAndFail("testNull","Peter Lustig"):nullValue(); + +// wait for the thread to finish +threadJoin(threadName1); + +// reading the data stored to the request scope +dump(request.testFunctionListenerEcho.testNull); + +``` + +Instead of `null`, you can also simply pass a empty struct or a component not defining the function needed. + +```coldfusion + +// function collection with no listeners +threadName2=logAndFail("testStruct","Ruedi Zraggen"):{}; + +// function collection with no listeners +threadName2=logAndFail("testStruct","Ruedi Zraggen"):new component {}; +``` diff --git a/docs/recipes/gateways-overview.md b/docs/recipes/gateways-overview.md new file mode 100644 index 000000000..96ce18460 --- /dev/null +++ b/docs/recipes/gateways-overview.md @@ -0,0 +1,124 @@ + +## How does an Event Gateway work? + +An event gateway is a background process that continuously runs. + +While running, it is doing the following: `` for a specific time (the "interval"), then doing what it is designed for (checking changes in a directory, polling a mailserver, etc.), and after that, it goes to `` again. + +This looping and sleeping does not count for all types of event gateways. For example, a socket gateway just instantiates a Java socket server. + +This "doing what it is designed for" will be explained in more detail underneath. + +## Which gateways are available? + +Lucee comes with 2 gateways: a Directory watcher and a Mail watcher. + +### Directory watcher + +This event gateway checks a given directory for file changes. These changes (events) can be: + +* new files +* changed files +* removed files + +When this gateway starts, it first takes a snapshot of the current state of the directory. This is the starting point from where changes are calculated. + +Please note that the files in this first snapshot are NOT seen as changes! + +So if you already have some files in the directory you want to watch, these files are not seen as "new file" when the gateway starts. Also, when Lucee (or your whole server) restarts, any changes which happened within this time are not seen and will not be picked up when the Directory watcher starts up again. + +### Filters + +You can apply filters for what you exactly want to watch changes for: + +* **Watch subdirectories**: same as the "Recurse" option in `` and `directoryList()` +* **Extensions**: an optional list of comma-delimited file extensions. The default is "*", which obviously means "all files". + +Note: the Extensions setting might be changed in the near future, due to an enhancement request. + +### Mail watcher + +This gateway checks a given POP mailbox for new mail. Since it only checks for new mail, it is rather limited in what it can do, but this is what makes it fast. The Mail watcher will read the inbox, and then check all the emails found. + +### Logs + +Make sure you regularly check the logs, because when anything goes wrong, Lucee will report this in its logs. + +Lucee logs can be found here: + +* `{Lucee-server}/context/logs/` +* `{Lucee-web}/lucee/logs/` + +You can also view the logs in your web/server admin by installing the Log Analyzer plugin. + +Also, make sure that you wrap your Listener function code inside try-catch blocks and do something within the catch block. For example: + +```lucee + + + + + + + + + + +``` + +If you do not add these try-catch blocks, and anything goes wrong, it will be much harder to find out if anything went wrong! + +For example, the above code would crash if a file with the same name already exists in the directory "C:/backupfiles/". + +It might be even wiser to just email the complete error dump, so you will be semi-instantly notified of any errors. + +### Using cfadmin with Event gateways + +Instead of using the server/web admin, you can also use Lucee's `` tag. + +Add or update a gateway instance: + +```lucee + +``` + +### Remove a gateway instance + +```lucee + +``` \ No newline at end of file diff --git a/docs/recipes/global-proxy.md b/docs/recipes/global-proxy.md new file mode 100644 index 000000000..88b690c0b --- /dev/null +++ b/docs/recipes/global-proxy.md @@ -0,0 +1,57 @@ + +# Global Proxy + +Since version 6.0, Lucee allows you to define a global proxy in the Application.cfc. + +If set, it will affect all connections made to the "outside world". + +```lucee +this.proxy = { + server: "myproxy.com", + port: 1234, + username: "susi", + password: "sorglos" +}; +``` + +## Include + +You can also limit the proxy to specific hosts by defining a list (or array) of hosts it should be used for: + +```lucee +this.proxy = { + server: "myproxy.com", + port: 1234, + username: "susi", + password: "sorglos", + includes: "whatever.com,lucee.org" +}; +``` + +## Exclude + +Or you can do the opposite, defining for which hosts it should not apply: + +```lucee +this.proxy = { + server: "myproxy.com", + port: 1234, + username: "susi", + password: "sorglos", + excludes: ["lucee.org", "whatever.com"] +}; +``` \ No newline at end of file diff --git a/docs/recipes/hidden_gems.md b/docs/recipes/hidden_gems.md new file mode 100644 index 000000000..d3ae72432 --- /dev/null +++ b/docs/recipes/hidden_gems.md @@ -0,0 +1,123 @@ + +## Hidden Gems + +This document explains how to declare variables, function calls with dot and bracket notation, and passing arguments via URL/form scopes as an array. These concepts are explained with simple examples below: + +### Example 1: Declare Variables + +// test.cfc + +```luceescript +component { + function getName() { + return "Susi"; + } +} +``` + +// example1.cfm + +```luceescript +function test() { + var qry; + dump(qry); + query name="qry" datasource="test" { + echo("select 1 as one"); + } + dump(qry); +} +test(); +``` + +In the cfm page, we have a test() function with a local variable scope assigned as an empty string `var qry`. When executing this cfm, the qry returns "1". Dumping the `qry` below the var declaration returns an empty string. + +### Example 2: Dot and Bracket Notation for Function Calls + +Lucee allows you to use bracket notation to call a component function. + +// example2.cfm + +```luceescript +// UDF call via dot notation +test = new Test(); +dump( test.getName() ); +// Dynamic function name +funcName = "getName"; +dump(evaluate('test.#funcName#()')); +// UDF call via bracket notation +funcName = "getName"; +dump( test[funcName]() ); +``` + +These three different types of function calls are: + +- Calling the user-defined function `getName()` from the component. +- Dynamic function name with evaluate function. +- User-defined function via bracket notation. + +All three different function calls return the same content "Susi" as defined in the CFC page. + +### Example 3: Passing Arguments via URL/Form Scopes as Array + +Lucee allows passing URL and Form scope data as an array instead of a string list. + +// example3.cfm + +```lucee + + dump(label:"URL", var:url); + dump(label:"Form", var:form); + // current name + curr = listLast(getCurrentTemplatePath(),'\/'); + + + +

Countries

+
+
+			Countries Europe:	
+			Countries America:	
+			
+		
+
+
+``` + +// index.cfm + +```luceescript +directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +In this cfm page, URL and form scopes are available. The names are used twice. + +- The query string on the URL scope has the same name `country` twice. Similarly, the form also has two fields with the same name `country`. +- Execute this cfm page in the browser & submit the form. It shows a single URL string list in merged format instead of two fields & Form fields also merged as a single `country` field. +- Adding square brackets behind the name `country[]` means it returns two separate strings in array format. You will see the difference in the browser while dumping that name with square brackets. + +These simple methods are helpful for defining variables in different ways. + +### Footnotes + +Here you can see the above details in the video + +[Lucee Hidden Gems](https://youtu.be/4MUKPiQv1kAsss) \ No newline at end of file diff --git a/docs/recipes/index.json b/docs/recipes/index.json new file mode 100644 index 000000000..9a0d041f0 --- /dev/null +++ b/docs/recipes/index.json @@ -0,0 +1,380 @@ +[ + { + "file": "Externalizing_Strings.md", + "title": "Untitled", + "path": "/docs/recipes/Externalizing_Strings.md", + "hash": "3692c2187634611738569655ec2d1d81" + }, + { + "file": "QOQ_Sucks.md", + "title": "Untitled", + "path": "/docs/recipes/QOQ_Sucks.md", + "hash": "f17ec78bf0f0d984a48e233a4fde5d03" + }, + { + "file": "application-context-basic.md", + "title": "Untitled", + "path": "/docs/recipes/application-context-basic.md", + "hash": "b3d0ce76557077908974266dcfdc60c9" + }, + { + "file": "application-context-set-mapping.md", + "title": "Untitled", + "path": "/docs/recipes/application-context-set-mapping.md", + "hash": "b3d0ce76557077908974266dcfdc60c9" + }, + { + "file": "application-context-update.md", + "title": "Update Application Context", + "path": "/docs/recipes/application-context-update.md", + "hash": "1d2458d6f9588a69d5c0ae4f9036dfdc" + }, + { + "file": "basic-date.md", + "title": "Output the current date", + "path": "/docs/recipes/basic-date.md", + "hash": "809357e78cdb36ce7a37e691eec63d9a" + }, + { + "file": "cache-list.md", + "title": "Untitled", + "path": "/docs/recipes/cache-list.md", + "hash": "1b945113ef891419aeeedee94d49bf65" + }, + { + "file": "cached-within-request.md", + "title": "Untitled", + "path": "/docs/recipes/cached-within-request.md", + "hash": "b892be2e7faf4fb24ee97afa0499a38e" + }, + { + "file": "caches-in-application-cfc.md", + "title": "Untitled", + "path": "/docs/recipes/caches-in-application-cfc.md", + "hash": "cf425d1a69758abef18928e9ecfe366b" + }, + { + "file": "cfml-to-java.md", + "title": "Convert a CFML Function/Component to use in Java", + "path": "/docs/recipes/cfml-to-java.md", + "hash": "0a34744be52069b132aa3df6d4a4e0df" + }, + { + "file": "check-for-changes.md", + "title": "Check for changes in your configuration file automatically", + "path": "/docs/recipes/check-for-changes.md", + "hash": "c4e5fb1483292ba2ac9e7ddb5b4fe7c4" + }, + { + "file": "checksum.md", + "title": "Untitled", + "path": "/docs/recipes/checksum.md", + "hash": "cf4231c1e63a705a85f1da77feefaaeb" + }, + { + "file": "configuration-administrator-cfc.md", + "title": "Untitled", + "path": "/docs/recipes/configuration-administrator-cfc.md", + "hash": "0f7c973860dfd2cf9589aed06426271b" + }, + { + "file": "datasource-define-datasource.md", + "title": "How to define a Datasource", + "path": "/docs/recipes/datasource-define-datasource.md", + "hash": "72d343ac36721736d47dcee935c5963b" + }, + { + "file": "deploy-archives.md", + "title": "Untitled", + "path": "/docs/recipes/deploy-archives.md", + "hash": "8af685ebeb135e79527c76a638320a18" + }, + { + "file": "encryption_decryption.md", + "title": "Untitled", + "path": "/docs/recipes/encryption_decryption.md", + "hash": "31db9db5d16eb6460c82e73cdc16972f" + }, + { + "file": "event-gateway-create.md", + "title": "Untitled", + "path": "/docs/recipes/event-gateway-create.md", + "hash": "ab9bfbfa135dd0ec04aa164dbf823643" + }, + { + "file": "event-gateways-overview.md", + "title": "Untitled", + "path": "/docs/recipes/event-gateways-overview.md", + "hash": "0568f450c9368645e0add455a4e742c7" + }, + { + "file": "event-gateways.md", + "title": "Untitled", + "path": "/docs/recipes/event-gateways.md", + "hash": "8bc56d0c35fc03279d9014bb4f389cb3" + }, + { + "file": "exception-cause.md", + "title": "Exception - Cause (Lucee 6.1)", + "path": "/docs/recipes/exception-cause.md", + "hash": "98abc157a9882b0037daee19f70ae0e2" + }, + { + "file": "exception-output.md", + "title": "Output Exceptions", + "path": "/docs/recipes/exception-output.md", + "hash": "308d7c8bb816828345196300cf5ac047" + }, + { + "file": "file-extensions.md", + "title": "File Extensions", + "path": "/docs/recipes/file-extensions.md", + "hash": "4c50c5df979593151592a73898d27f42" + }, + { + "file": "filesystem-mapping-define-mapping.md", + "title": "Untitled", + "path": "/docs/recipes/filesystem-mapping-define-mapping.md", + "hash": "47bc95b01d84c9f1424a7e98c4d1c0f0" + }, + { + "file": "filesystem-mapping.md", + "title": "Untitled", + "path": "/docs/recipes/filesystem-mapping.md", + "hash": "cd87cdc45db078471a8f711f247a3e09" + }, + { + "file": "flying_saucer.md", + "title": "Untitled", + "path": "/docs/recipes/flying_saucer.md", + "hash": "2112724350ae392d1bd40ffe43c9d106" + }, + { + "file": "function-listeners.md", + "title": "Function Listener", + "path": "/docs/recipes/function-listeners.md", + "hash": "1c036e4c3566d30a20e51ae5e2f68520" + }, + { + "file": "gateways-overview.md", + "title": "Untitled", + "path": "/docs/recipes/gateways-overview.md", + "hash": "0568f450c9368645e0add455a4e742c7" + }, + { + "file": "global-proxy.md", + "title": "Global Proxy", + "path": "/docs/recipes/global-proxy.md", + "hash": "9c9e66206b05f71ea75ecc69c8881137" + }, + { + "file": "hidden_gems.md", + "title": "Untitled", + "path": "/docs/recipes/hidden_gems.md", + "hash": "de62288be566b022c8938c377e5673a9" + }, + { + "file": "inline-components.md", + "title": "Inline Component", + "path": "/docs/recipes/inline-components.md", + "hash": "bb048506a358e844ade28345398223fa" + }, + { + "file": "java-in-functions.md", + "title": "Java in Functions and Closures", + "path": "/docs/recipes/java-in-functions.md", + "hash": "eb86be0273c7e46a8371dfa7a5e8ea7d" + }, + { + "file": "labels.md", + "title": "Loop Labels", + "path": "/docs/recipes/labels.md", + "hash": "dd4d215c187f54e1fb9eca7504768ce8" + }, + { + "file": "lazy_queries.md", + "title": "Untitled", + "path": "/docs/recipes/lazy_queries.md", + "hash": "75c88bed5ea7ddc98523553a5c9760ef" + }, + { + "file": "loop_through_files.md", + "title": "Untitled", + "path": "/docs/recipes/loop_through_files.md", + "hash": "f1fb2d2e527f7592b62c600cc3263612" + }, + { + "file": "mail-listener.md", + "title": "Mail Listeners", + "path": "/docs/recipes/mail-listener.md", + "hash": "67637ed6ff0a361b9faff6ddfd4b2b14" + }, + { + "file": "mail-send.md", + "title": "Untitled", + "path": "/docs/recipes/mail-send.md", + "hash": "354b025c50774234e7d8544e9d31bd94" + }, + { + "file": "mathematical-precision.md", + "title": "Mathematical Precision", + "path": "/docs/recipes/mathematical-precision.md", + "hash": "f0f8a9e8dfdf71bb45600184cf6e0e53" + }, + { + "file": "monitoring.md", + "title": "Monitoring/Debugging", + "path": "/docs/recipes/monitoring.md", + "hash": "83f0fc803a72ec7fac7c261d8506f72d" + }, + { + "file": "null_support.md", + "title": "Untitled", + "path": "/docs/recipes/null_support.md", + "hash": "4e17d64e75d51075361bac51116f782f" + }, + { + "file": "precompiled-code.md", + "title": "Untitled", + "path": "/docs/recipes/precompiled-code.md", + "hash": "20314698d20b048c36cec1a2fd152497" + }, + { + "file": "query-async.md", + "title": "Query Async", + "path": "/docs/recipes/query-async.md", + "hash": "c34897c0d058b3b7107c7c5dbd99d18e" + }, + { + "file": "query-handling.md", + "title": "Untitled", + "path": "/docs/recipes/query-handling.md", + "hash": "25665af5991b704875419ab4a7cffda2" + }, + { + "file": "query-indexes.md", + "title": "Query Indexes", + "path": "/docs/recipes/query-indexes.md", + "hash": "845d8e3b4d135e50ca439465cc7441c6" + }, + { + "file": "query-listener.md", + "title": "Query Listeners", + "path": "/docs/recipes/query-listener.md", + "hash": "e2325c2ef8ac68da95653f7fd5e1b473" + }, + { + "file": "query-of-queries.md", + "title": "Untitled", + "path": "/docs/recipes/query-of-queries.md", + "hash": "c2ab9f515eda092288e30578364114cf" + }, + { + "file": "query_return_type.md", + "title": "Untitled", + "path": "/docs/recipes/query_return_type.md", + "hash": "75e2939902c9d6c68764dfa32ad498cc" + }, + { + "file": "request-timeout.md", + "title": "Request Timeout", + "path": "/docs/recipes/request-timeout.md", + "hash": "8f3386a44db2fe4e3885f7ac8071d7fc" + }, + { + "file": "retry.md", + "title": "Untitled", + "path": "/docs/recipes/retry.md", + "hash": "b36863bbebee833416f8a7ff0fb7f190" + }, + { + "file": "s3.md", + "title": "Untitled", + "path": "/docs/recipes/s3.md", + "hash": "4dc472f2c3feb0019809732d87a4a42c" + }, + { + "file": "sax.md", + "title": "Untitled", + "path": "/docs/recipes/sax.md", + "hash": "ed8def929c9f8e8266dd8214ee74f135" + }, + { + "file": "script-templates.md", + "title": "Script Templates", + "path": "/docs/recipes/script-templates.md", + "hash": "2a12bbaf30d398fd1dfd1974dcddb7c6" + }, + { + "file": "settings.md", + "title": "Environment Variables / System Properties for Lucee", + "path": "/docs/recipes/settings.md", + "hash": "b315d174fdbe7e634cbaeb91f267b4e1" + }, + { + "file": "startup-listeners-code.md", + "title": "Untitled", + "path": "/docs/recipes/startup-listeners-code.md", + "hash": "e84d76646ee9ae56ac4870ac3e763e55" + }, + { + "file": "static_scope.md", + "title": "Untitled", + "path": "/docs/recipes/static_scope.md", + "hash": "b8a0f18b890c7e02ccf4e75dcba7b3dc" + }, + { + "file": "sub-components.md", + "title": "Sub Component", + "path": "/docs/recipes/sub-components.md", + "hash": "fdce0dce415076cca6557352e5db9b13" + }, + { + "file": "supercharge-your-website.md", + "title": "Untitled", + "path": "/docs/recipes/supercharge-your-website.md", + "hash": "665da857e9a08d6809609c79a0df5648" + }, + { + "file": "systemoutput_function.md", + "title": "Function SystemOutput #", + "path": "/docs/recipes/systemoutput_function.md", + "hash": "ba5f56206b7604b3c964e60ebf695a0c" + }, + { + "file": "thread_task.md", + "title": "Untitled", + "path": "/docs/recipes/thread_task.md", + "hash": "1bc2ba605564ff9b120339cf9e5e4e9e" + }, + { + "file": "thread_usage.md", + "title": "Untitled", + "path": "/docs/recipes/thread_usage.md", + "hash": "439843142f39ba9a8ea251a676441e0a" + }, + { + "file": "timeout.md", + "title": "Timeout", + "path": "/docs/recipes/timeout.md", + "hash": "6c5108c34266748363a2da76111e99d0" + }, + { + "file": "types_lucee.md", + "title": "Types in Lucee", + "path": "/docs/recipes/types_lucee.md", + "hash": "b1db58d098bbab5a478ecf0fb1ab6440" + }, + { + "file": "virtual-file-system.md", + "title": "Untitled", + "path": "/docs/recipes/virtual-file-system.md", + "hash": "b5d57424b84ebf3653233da3c0f43b77" + }, + { + "file": "xml_fast-easy.md", + "title": "Untitled", + "path": "/docs/recipes/xml_fast-easy.md", + "hash": "64e2926998278f6a31fe50fb26805188" + } +] diff --git a/docs/recipes/inline-components.md b/docs/recipes/inline-components.md new file mode 100644 index 000000000..2bdaf4acc --- /dev/null +++ b/docs/recipes/inline-components.md @@ -0,0 +1,31 @@ + +# Inline Component + +Since Lucee 6.0, Lucee allows you to create inline components. These are components you can create directly in your CFML code, with no need to create a .cfc file for it. This feature allows you to directly use them, similar to closures. + +This example shows how to create an inline component and then use it: + +```run + +inline = new component { + function subTest() { + return "inline
"; + } +}; +dump("inline->" & inline.subTest()); +dump(inline); +
+``` \ No newline at end of file diff --git a/docs/recipes/java-in-functions.md b/docs/recipes/java-in-functions.md new file mode 100644 index 000000000..341a625a2 --- /dev/null +++ b/docs/recipes/java-in-functions.md @@ -0,0 +1,55 @@ + +# Java in Functions and Closures + +You can write CFML code directly in a function or a closure. + +## Function + +Inside the function, you write regular Java code. The arguments and return type definition must be Java types. + +```lucee +int function echoInt(int i) type="java" { + if (i == 1) throw new Exception("Upsi dupsi!!!"); + return i * 2; +} +``` + +## Component + +Of course, the function can also be part of a component. + +```lucee +component { + int function echoInt(int i) type="java" { + if (i == 1) throw new Exception("Test output!!!"); + return i * 2; + } +} +``` + +## Java Lambda Function + +If the interface of a function matches a functional Java interface (Lambda), Lucee automatically implements that interface. In the following example, we implement the `IntUnaryOperator` implicitly. You can then pass it to Java and use it as such. + +```lucee +int function echoInt(int i) type="java" { + if (i == 1) throw new Exception("Test"); + return i * 2; +} +dump(echoInt(1)); +``` \ No newline at end of file diff --git a/docs/recipes/labels.md b/docs/recipes/labels.md new file mode 100644 index 000000000..4a376de4d --- /dev/null +++ b/docs/recipes/labels.md @@ -0,0 +1,60 @@ + +# Loop Labels + +Lucee supports labels for all loop tags and statements, allowing you to control the flow of nested loops more precisely. + +Labels are particularly useful when using the `break` or `continue` statements to avoid affecting only the nearest enclosing loop. + +### Example + +Consider the following examples: + +### Tag-Based Example + +In this example, the `break` statement is used with a label to exit the outer loop: + +```run + + + time: #hour#:#minute#
+ +
+
+``` +Here, the `cfbreak` statement with the `outerLoop` label causes the loop to break out of the outer loop, not just the inner loop. As a result, only a single time value is printed. + +### Script-Based Example + +Similarly, you can use labels in script-based loops: + +```run + +outerloop:for(hour=0; hour<=4; hour++) { + for(minute=1; minute<=60; minute++) { + echo("time: " & hour & ":" & minute & "
"); + continue outerloop; + } +} +
+``` + +In this example, the `continue outerloop` statement causes the loop to skip to the next iteration of the outer loop, effectively restarting the outer loop and ignoring the remaining iterations of the inner loop. + +### Summary + +Using labels with `break` and `continue` statements in Lucee allows you to control nested loops more effectively by specifying which loop to break out of or continue. This provides greater flexibility and clarity in managing complex looping structures. + +If you have any questions or need further clarification, feel free to ask! diff --git a/docs/recipes/lazy_queries.md b/docs/recipes/lazy_queries.md new file mode 100644 index 000000000..b750a17f3 --- /dev/null +++ b/docs/recipes/lazy_queries.md @@ -0,0 +1,99 @@ + +## Lazy Queries ## + +This document explains about lazy queries with some simple examples as follows: + +### Example 1: ### + +**Regular query**: Regular query tags/functions load all data inside a two-dimensional structure to use. + +```luceescript +//regularQuery.cfm +query name="qry" returntype="query" { + echo("select * from lazyQuery"); +} +dump(numberFormat(qry.getColumnCount()*qry.getRowCount())); +loop query=qry { + dump(qry.val); + if(qry.currentrow==10) break; +} +``` + +1) In this example, we have a simple task. All statements return a result set with 200,000 records. We output the first ten. Then we make a break when we add ten rows. + +2) We execute this in the browser and we get the expected result. + +### Example 2: ### + +**Lazy query**: Lazy queries keep a pointer to the database and only load the data on demand. If you loop through a query, the data is loaded on the spot. It does not create a two-dimensional struct to store all the data beforehand. When the query tag is done, it keeps a pointer to the database. + +As a lazy query loops through, it loads the data on demand so you do not have to wait until it has loaded all the data. It is faster when you only load the first 10 rows as you don't have to wait until it's done loading everything. + +```luceescript +//lazyQuery.cfm +query name="qry" returntype="query" lazy=true { + echo("select * from lazyQuery"); +} +loop query=qry { + dump(qry.val); + if(qry.currentrow==10) break; +} +``` + +This example is similar to a regular query, but we define `lazy=true`. So that Lucee knows to do a lazy query. + +I have removed the column count from the example. Record count is no longer possible because it does not read all the data initially. It does not know how many records there are. When you loop, you can count the records, so you know the total number of records at the end, but not at the start. + +There is not really a difference between a regular query and a lazy query, just some limitations (you cannot get the record count in the beginning, and you cannot use cache) within a lazy query. + +With a lazy query, we do not have to wait until Lucee has loaded all the data into a two-dimensional structure, and it is also better for memory because you do not have to store all the older data in the memory until you are ready to use it. So there are some benefits. + +### Example 3: ### + +A comparison of lazy queries and regular queries follows: + +```luceescript +types=['regular':false,'lazy':true]; +results=structNew("ordered"); +loop struct=types index="type" item="lazy" { + loop from=1 to=10 index="i" { + start=getTickCount('nano'); + query name="qry" returntype="query" lazy=lazy { + echo("select * from lazyQuery"); + } + x=qry.val; + time=getTickCount('nano')-start; + + if(isNull(results[type]) || results[type]>time)results[type]=time; + } +} +// format results +results.regular=decimalFormat(results.regular/1000000)&"ms"; +results.lazy=decimalFormat(results.lazy/1000000)&"ms"; +dump(results); +``` + +This example compares lazy queries with regular queries. It has a loop that loops two times: once for a regular query and a second one for a lazy query. The `type` is used here with `lazy=lazy`, So it sets true or false and does that ten times, once for every time the loops execute. It stores the execution time but you only get the fastest execution time of the ten tries. + +Execute that example in the browser. The regular query takes 41 milliseconds and the lazy query takes 27 milliseconds. So we see the benefits of the lazy queries. + +### Footnotes ### + +You can see the details in this video: +[Lazy Query](https://youtu.be/X8_TB1py8n0) \ No newline at end of file diff --git a/docs/recipes/loop_through_files.md b/docs/recipes/loop_through_files.md new file mode 100644 index 000000000..bdf229615 --- /dev/null +++ b/docs/recipes/loop_through_files.md @@ -0,0 +1,71 @@ + +## Looping Through File ## + +This document explains how to handle big files in Lucee in a better way. The classic way that you are familiar with uses cffile tag, fileRead, and fileReadBinary functions to read the file into memory. This is a simple solution, but it consumes a lot of memory. + +### Example: Handle files with cffile tag, fileRead, and fileReadBinary functions + +```luceescript +include "_getPath2BigFile.cfm"; +NL=""; +// read the complete file into memory +content=fileRead(path); +// now we split, again everything lands in memory as an array +arr=listToArray(content,NL); +// now we loop over every single line +loop array=arr index="i" item="line" { + handle(line); +} +function handle(line) {} +dump(label:"String Size",var:len(content)); +dump(label:"Array Size",var:len(arr)); +``` + +In the example above, + +* Read the file into the memory +* Split into array +* Loop over the array + +It consumes a lot of memory. + +### Use Loop - File + +Use cfloop file. It allows you to read a file line by line, so you do not have to load an entire file into memory. You only load a line at a time into the memory. + +```luceescript + +``` + +### Example Using Loop + +```luceescript +include "_getPath2BigFile.cfm"; +// now we loop over every single line +loop file=path item="line" { + handle(line); +} +function handle(line) {} +``` + +In the above example, loop through the file and get each line, so in memory there is only ever the one line. This is not only faster, it also consumes less memory. + +### Footnotes + +Here you can see the above details in a video: + +[Looping through Files](https://www.youtube.com/watch?v=6w2Wr8snk50) \ No newline at end of file diff --git a/docs/recipes/mail-listener.md b/docs/recipes/mail-listener.md new file mode 100644 index 000000000..cf7cb4b08 --- /dev/null +++ b/docs/recipes/mail-listener.md @@ -0,0 +1,54 @@ + +# Mail Listeners + +Since Lucee 6.0, you can define a listener in the Application.cfc to listen to or manipulate every mail executed. + +## Global Listeners + +This example shows how to define a global mail listener in the Application.cfc: + +```lucee +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); + } +} +``` + +The listener can also be a component: + +```lucee +this.mail.listener = new MailListener(); +``` + +The component would look like this: + +```lucee +component { + function before(caller, nextExecution, created, detail, closed, advanced, tries, id, type, remainingtries) { + detail.from &= ".de"; + return arguments.detail; + } + + function after(caller, created, detail, closed, lastExecution, advanced, tries, id, type, passed, remainingtries) { + systemOutput(arguments.keyList(), 1, 1); + } +} +``` \ No newline at end of file diff --git a/docs/recipes/mail-send.md b/docs/recipes/mail-send.md new file mode 100644 index 000000000..7d92f9ce9 --- /dev/null +++ b/docs/recipes/mail-send.md @@ -0,0 +1,48 @@ + +## How to send a Mail + +The following example shows you how you can send a mail. Before you can use this functionality, you have to define a Mail server in the Lucee Administrator. + +### Tags + +```coldfusion + + Hi there, + This mail is sent to confirm that we have received your order. + +``` + +### Script + +```cfs +mail subject="Your Order" from="whatever@lucee.org" to="whatever@gmail.com" { + writeOutput('Hi there,'); + writeOutput('This mail is sent to confirm that we have received your order.'); +}; +``` + +That is all you need to do to send a mail. + +## Advanced + +TODO \ No newline at end of file diff --git a/docs/recipes/mathematical-precision.md b/docs/recipes/mathematical-precision.md new file mode 100644 index 000000000..0e3329eaf --- /dev/null +++ b/docs/recipes/mathematical-precision.md @@ -0,0 +1,30 @@ + +# Mathematical Precision + +So far, Lucee has handled numbers internally as “double”, but with Lucee 6 we have switched to “BigDecimal”. This makes math operations much more precise and there is no need anymore to use the function “PrecisionEvaluate”. + +Since version 6.0, all numbers Lucee uses in the runtime are by default BigDecimal based and no longer double as before. You can still change that fact in the Application.cfc as follows: + +```lucee +this.preciseMath = false; +``` + +## System Property / Environment Variable + +You can also change that behavior with the system property `-Dlucee.precise.math=false` or with the environment variable `LUCEE_PRECISE_MATH=false`. \ No newline at end of file diff --git a/docs/recipes/monitoring.md b/docs/recipes/monitoring.md new file mode 100644 index 000000000..5f6c9f49b --- /dev/null +++ b/docs/recipes/monitoring.md @@ -0,0 +1,131 @@ + +# Monitoring/Debugging + +Lucee 6.1 changed how you handle Monitoring/Debugging. + +## Old Behaviour +Previously, you could enable/disable Debugging in the Lucee admin, and enable/disable specific debug options like showing template execution. With the tag ``, you could define whether the debugging is shown or not. + +## New Behaviour +Lucee 6 has completely overhauled this functionality. Instead of having "Metrics" and "Reference" as part of the "Modern" Debug Template, they are now independently controlled under the new umbrella term "Monitoring". + +### Lucee Admin +The "debugging" settings are now under the group "Monitoring" and there is a new page called "Output". + +#### Page "Output" +On the "Output" page, you define which sections of monitoring are shown: +- Debugging +- Metrics +- Documentation (formerly "Reference") +- Test (available soon) + +This is similar to the action `` (more on that tag later). + +#### Page "Settings" +You no longer enable/disable debug as a whole, only the options. If no options are enabled, debugging is disabled. Thus, the general switch was/is not really needed anymore. + +#### Page "Debug Templates" +Here you can choose the debug template you want to use. You can limit it to a specific IP Range if you like, and you can also define different templates for different IP Ranges, though one template can cover all requests. + +#### Page "Logs" +This page allows you to show the last X requests (depending on your settings), which is useful if no debugging is shown on the output. + +### Application.cfc +Lucee 6.1 now allows you to overwrite all these settings in the Application.cfc, which was not possible in previous versions. + +You can define what is shown: +```lucee +this.monitoring.showDebug = true; +this.monitoring.showDoc = true; +this.monitoring.showMetric = true; +this.monitoring.showTest = true; // following soon +``` + +And also enable/disable debug options: +```lucee +this.monitoring.debuggingTemplate = true; +this.monitoring.debuggingDatabase = true; +this.monitoring.debuggingException = true; +this.monitoring.debuggingTracing = true; +this.monitoring.debuggingDump = true; +this.monitoring.debuggingTimer = true; +this.monitoring.debuggingImplicitAccess = true; +this.monitoring.debuggingThread = true; +``` + +You can also export all these settings in the Lucee Administrator on the Monitoring/Settings page. + +### In Your Code +Even after the Application.cfc, you can still change these settings. + +With the help of the tag ``: +```lucee + +``` + +Or with the tag `` you can change the "show" settings (not the debug options): +```lucee + +``` + +#### Downside/Upside +Of course, when you enable, for example, "debuggingTemplate" in your code, everything that happened before was not logged and is lost. But this can also be a benefit, as it allows you to do things like this: +```lucee +try { + application action="update" debuggingTemplate=false; + include "mysecretcode.cfm"; +} finally { + application action="update" debuggingTemplate=true; +} +``` + +This way, you can prevent Lucee from logging certain code. + +## Tab Documentation (formerly Reference) +This tab now not only gives you a function and tag reference, it also provides all kinds of "recipes" like this. + +## Backward Compatibility +These new features are fully backward compatible. + +### Tag cfsetting +The old attribute "showDebugOutput" is now an alias to the newly introduced "show" attribute. This means with this attribute you can still enable/disable Monitoring as a whole. + +So when you do: +```lucee + +``` +It will not show the monitoring at all, the same way as you would do: +```lucee + +``` + +## Conclusion +Lucee 6.1 gives you full control over Monitoring in your code, making it easier for every developer to use it. \ No newline at end of file diff --git a/docs/recipes/null_support.md b/docs/recipes/null_support.md new file mode 100644 index 000000000..9e8d1d675 --- /dev/null +++ b/docs/recipes/null_support.md @@ -0,0 +1,84 @@ + +## Null Support + +This document explains how to set null support in the Lucee server admin, assigning `null` value for a variable and how to use `null` and `nullvalue`. It is an annotation of the video found here: [https://www.youtube.com/watch?v=GSlWfLR8Frs](https://www.youtube.com/watch?v=GSlWfLR8Frs) + +### Enabling NULL support + +You can enable null support via the **Lucee Server Admin** --> **Language/compiler** and setting Null support to **complete support** (exclusive to Lucee) or **partial support** (default, same as Adobe CF). + +Or via [[tag-application]] + +```lucee +this.nullSupport = true; +``` + +### Explanation + +#### Illustration 1: + +```lucee + + function test() { + } + dump( test() ); + + t = test(); + dump(t); + + dump( isNull( t ) ); + dump( isNull( notexisting ) ); + +``` + +In this example, the function `test()` does not return a value. This, in effect, is the same as returning `null`. If you dump the result of the function (`dump( test() );`), you will see that the dump outputs `Empty: Null`. + +If we assign the function result to a variable, i.e. `t = test();`, and reference the variable, i.e. `dump( t );` an error will be thrown when using **partial support** for null: "the key [T] does not exist". If we enable **full support**, you will be able to reference the variable without error, the dump output will be `Empty: Null` and `IsNull( t )` will evaluate `true`. + +In all cases, `dump( isNull( notexisting ) );` will throw an error. + +#### Illustration 2: + +```luceescript +query datasource="test" name="qry" { + echo("select '' as empty, null as _null"); +} +dump( qry ); +dump( qry._null ); +``` + +With **partial support** for NULL enabled, `dump(qry._null);` will output an **empty string**. +With **full support**, `Empty: null` will be output and `IsNull( qry._null );` will evaluate `true`. + +### NullValue() function and null keyword + +With **partial support** for NULL, the `NullValue()` function must be used to explicitly return a null value (this will work in all scenarios). For example: + +```luceescript +var possibleVariable = functionThatMayOrMayNotReturnNull(); +return possibleVariable ?: NullValue(); +``` + +With **full support**, you are able to use the `null` keyword directly and, as illustrated above, can assign it to a variable directly: + +```luceescript +t = null; +dump( t ); +``` \ No newline at end of file diff --git a/docs/recipes/precompiled-code.md b/docs/recipes/precompiled-code.md new file mode 100644 index 000000000..f0fa40576 --- /dev/null +++ b/docs/recipes/precompiled-code.md @@ -0,0 +1,45 @@ + +## Precompiled Code + +This document explains how to pre-compile code for a production server while the source code is deployed. This method avoids compilation on the production server for security reasons. We explain this method with a simple example below: + +Example: + +```lucee +//index.cfm page in current instance location like \webapps\ROOT\sample\index.cfm + +Time is +writeoutput(now()); + +``` + +Run this index.cfm page in the browser. + +* When a cfm or cfc file is executed for the first time (or after the file has been edited), a class file holding the java byte-code representing that CFML file is automatically created within the cfclasses folder, webroot --> WEB-INF --> lucee --> cfclasses folder, in a sub-folder representing your application's context, for example `CFC__lucee_tomcat_webapps_ROOT4900`. (Differently from Adobe ColdFusion, a separate class file is not created for each method/function within a cfc/cfm file.) + +* After executing a request to our `/sample/index.cfm` example above, a class file named `index_cfm$cf.class` will be created within a `sample` folder of the cfclasses folder for our application's context. + +* To demonstrate how you can deploy that compiled byte-code for a cfm as if it was the cfm itself, you can copy that class file and paste it into your original application folder (\webapps\ROOT\sample). Since you already have the original index.cfm there, rename this class file to `test.cfm`. + +* Finally, run the `/sample/test.cfm` file in your browser. It should show the same results as the index.cfm file would. + +### Footnotes + +Here you can see the above details in a video: + +[Lucee Precompiled Code](https://www.youtube.com/watch?v=Yjy3bQJgphA) \ No newline at end of file diff --git a/docs/recipes/query-async.md b/docs/recipes/query-async.md new file mode 100644 index 000000000..ba482a8c6 --- /dev/null +++ b/docs/recipes/query-async.md @@ -0,0 +1,48 @@ + +# Query Async + +Since Lucee 6.0, you can define that a query gets executed asynchronously. Asynchronous execution of queries is useful in many cases where you don’t want to wait for a query to be executed. You can now set a simple flag to enable this feature. + +## Async Execution + +This example shows how to define a query for async execution: + +```lucee +query async=true { +~~~ +update user set lastAccess=now() +~~~ +} +``` + +But you may want to know when an exception is happening, so we use a local listener: + +```lucee +query datasource="mysql" async=true listener={ + error: function (args, caller, meta, exception) { + systemOutput(exception, true, true); + } +} { +~~~ + update user set lastAccess=now() +~~~ +} +``` + +## Other Option + +Since Lucee 6.1, this can also be done with the help of "function listeners". \ No newline at end of file diff --git a/docs/recipes/query-handling.md b/docs/recipes/query-handling.md new file mode 100644 index 000000000..a298671ed --- /dev/null +++ b/docs/recipes/query-handling.md @@ -0,0 +1,153 @@ + +This document explains how SQL queries are supported in Lucee. + +## Query tags + +[tag-query] different ways to use the tags in Lucee and how we can pass the value into the query. + +```lucee + + select * from Foo1890 + + +``` + +The above example just shows how to retrieve the data from the database. + +### Using QueryParam + +The [tag-QueryParam] is used inside the [tag-query] tag. It is used to bind the value with the SQL statement. + +```lucee + + select * from Foo1890 + where title= + + +``` + +Passing values with [tag-QueryParam] has two advantages: + +* The value you pass in QueryParam is very secure. +* Lucee is able to cache the query statement and reuse it as long as the value is unchanged. + +### Params + +Here we use params as part of [tag-cfquery] tag, used to pass the value with SQL statement. + +Pass the params value with struct. + +```lucee + + select * from Foo1890 + where title=:title + + +``` + +Referenced as `:key` in SQL. + +The below example shows how to pass more information using a struct. + +```lucee + + select * from Foo1890 + where title=:title + + +``` + +You can pass the params value using an array. It is referenced as `?` in SQL. + +```lucee + + select * from Foo1890 + where title=? + + +``` + +### Query Builder + +Query Builder is used as an extension; it will not come up with core. + +It is much easier to do a simple query. + +```lucee + +// Query Builder (creates SQL in dialect based on the datasource defined) +qb = new QueryBuilder("test") + .select("lastName") + .from("person") + .where(QB::eq("firstname", "Susi")); +qb.execute(); +dump(res); + +``` + +Use `QueryBuilder("test")` as constructor. + +* Define a datasource with constructor or `setDatasource('test')` function. +* Use `select("lastName")` to select the column. +* Use `from("person")` from which table you want to retrieve data. +* Where statement like `where(QB::eq("firstname", "Susi"))`. + +Use `qb.execute()` to obtain the result. + +You can change the selected column like in the example below. + +```lucee + +// change select +qb.select(["age", "firstname"]); +qb.execute(); +dump(res); + +``` + +You can also change the where condition as shown in the example below. + +```lucee + +// change where +qb.clear("where"); +qb.where( + QB::and( + QB::eq("firstname", "Susi"), + QB::neq("lastname", "Moser"), + QB::lt("age", 18) + ) +); +qb.execute(); +dump(res); + +``` + +### Footnotes + +Here you can see the above details in a video: + +[https://www.youtube.com/watch?time_continue=684&v=IMdPM58guUQ](https://www.youtube.com/watch?time_continue=684&v=IMdPM58guUQ) \ No newline at end of file diff --git a/docs/recipes/query-indexes.md b/docs/recipes/query-indexes.md new file mode 100644 index 000000000..4811504d9 --- /dev/null +++ b/docs/recipes/query-indexes.md @@ -0,0 +1,37 @@ + +# Query Indexes + +Since Lucee 6.0, you can set an index for a query result, which you can then use down the line. + +## Setting an Index + +This example shows how to define a query with an index: + +```lucee + + select 1 as id, 'Susi' as name + union all + select 2 as id, 'Peter' as name + +``` + +You can then access parts of the query like this: + +```lucee + + + + +``` \ No newline at end of file diff --git a/docs/recipes/query-listener.md b/docs/recipes/query-listener.md new file mode 100644 index 000000000..0e870ec3a --- /dev/null +++ b/docs/recipes/query-listener.md @@ -0,0 +1,61 @@ + +# Query Listeners + +Since Lucee 6.0, you can define a listener in the Application.cfc to listen to or manipulate every query executed. + +## Global Listeners + +This example shows how to define a global query listener in the Application.cfc: + +```lucee +this.query.listener = { + before: function (caller, args) { + dump(label: "before", var: arguments); + }, + after: function (caller, args, result, meta) { + dump(label: "after", var: arguments); + } +}; +``` + +The listener can also be a component: + +```lucee +this.query.listener = new QueryListener(); +``` + +The component would look like this: + +```lucee +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 + } +} +``` \ No newline at end of file diff --git a/docs/recipes/query-of-queries.md b/docs/recipes/query-of-queries.md new file mode 100644 index 000000000..6c4d8c65a --- /dev/null +++ b/docs/recipes/query-of-queries.md @@ -0,0 +1,126 @@ + +## Introduction + +Query of queries (QoQ) is a technique for re-querying an existing (in memory) query without another trip to the database. This allows you to dynamically combine queries from different databases. + +```lucee + + + SELECT * + FROM my_db_table + + + + + SELECT * + FROM sourceQry + +``` + +The above example isn't very useful, because `newQry` is a straight copy of the source query, but it demonstrates the two requirements of `QoQ`: + +* The dbtype="query" attribute +* A source query object name (e.g., sourceQry) instead of a table name in the FROM clause. + +### Example: Filtering + +Let's say you have the following database query, `myQuery`: + +```lucee + + SELECT Name, Age, Location + FROM People + +``` + +You would now have a list of names, ages, and locations for all the people in a query called `myQuery`. + +Say you want to filter out people under 18 and over 90, but you don't want to hit the database again: + +```lucee + + SELECT Name, Age, Location + FROM myQuery + WHERE Age >= 18 + AND Age <= 90 + +``` + +`filteredQry` contains the desired records. + +### Internals + +Lucee uses its own SQL implementation for QoQ; when that fails, HSQLDB is tried. + +Lucee's SQL implementation is a basic subset of ANSI92, but it is relatively fast. [HSQLDB is a more complete SQL implementation](http://hsqldb.org/doc/2.0/guide/sqlgeneral-chapt.html), but it is slow compared to Lucee's implementation. + +### Supported Constructs + +Even though under the hood, Lucee handles the fallback to HSQLDB automatically, it still can be useful to know what's possible with the fast Lucee SQL implementation versus the slower, fallback HSQLDB SQL implementation. + +### Lucee's SQL Implementation + +**Keywords and Operators** + +* <= +* <> +* = +* => +* = +* != +* ALL +* AND +* AS +* BETWEEN x AND y +* DESC/ASC +* DISTINCT +* FROM +* GROUP BY +* HAVING +* IN () +* IS +* IS NOT NULL +* IS NULL +* LIKE +* NOT +* NOT IN () +* NOT LIKE +* OR +* ORDER BY +* SELECT +* TOP +* UNION +* WHERE +* XOR + +Functions + +TODO: Flesh this out. + +### HSQLDB SQL Implementation + +This is the fallback for when Lucee's SQL implementation can't handle the QoQ syntax. See the [HSQLDB documentation](http://hsqldb.org/doc/2.0/guide/sqlgeneral-chapt.html) for details. + +### Footnotes + +Lucee Google Groups Post: SQL syntax supported by query-of-queries? \ No newline at end of file diff --git a/docs/recipes/query_return_type.md b/docs/recipes/query_return_type.md new file mode 100644 index 000000000..d7630ca8b --- /dev/null +++ b/docs/recipes/query_return_type.md @@ -0,0 +1,105 @@ + +## Query return type ## + +This document explains the different return types for a query with some examples. + +### Example 1: query ### + +First we start with the regular return type for a query. This query simply returns a result set. + +```luceescript +// index.cfm +directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +```luceescript +query name="qry" datasource="test" { + echo(" + select lastname,firstname from person + "); +} +dump(qry); +``` + +In this example we have a select statement with two columns in the `person` table. Execute the query in the browser and we get a simple result. + +### Example 2: Array ### + +Lucee can define the return type in a query tag. If we set array as follows: `returntype="array"`. We will get the result as an array. + +```luceescript +query name="arr" datasource="test" returntype="array" { + echo(" + select lastname,firstname from person + "); +} +dump(arr); +``` + +In this array, for each row there is an item in the array and it has a struct with all the columns. So this array is special because it returns an array of structs, and it has meta information about the SQL statement. So it shows the record count and execution time of the query. + +### Example 3: struct ### + +This example shows the same concepts that were shown in the previous Example 2, however instead of an array, we can do a struct. If you want to have a struct result, set the return type as struct. + +```luceescript +query name="sct" datasource="test" returntype="struct" columnKey="lastname" { + echo(" + select lastname,firstname from person + "); +} +dump(sct); +``` + +1) In this case you have to define which column is the key of the struct. Here I simply use the last name as the key of the struct. + +2) Execute it in the browser, and we get a struct as a result and the key is the last name. So you can directly choose one of these elements by writing the lastname. + +### Example 4: ### + +```luceescript +if(isNull(application.sex)) { + query name="application.sex" datasource="test" returntype="struct" columnKey="sex_id" { + echo(" + select sex_id,name from sex + "); + } +} +query name="qryPerson" datasource="test" { + echo(" + select sex_id,lastname,firstname from person + "); +} +loop query=qryPerson { + dump(qryPerson.lastname&" "&qryPerson.firstname&" ("&application.sex[qryPerson.sex_id].name&")"); +} +``` + +1) In this example we have two tables. We make a query to the `person` table. Notice that some fields are foreign key references too. We store `sex_id` in the application scope because we use this in the second query. In this, `sex_id` is the key of that struct, so we can simply address it in `"(&application.sex[qryPerson.sex_id].name&")"` this way. + +2) Execute this example in the browser and we get a result from the other table that is referenced by a foreign key. + +### Footnotes ### + +You can see these details in the video here: + +[Query return type](https://www.youtube.com/watch?v=b9YHhnAuNiw) \ No newline at end of file diff --git a/docs/recipes/request-timeout.md b/docs/recipes/request-timeout.md new file mode 100644 index 000000000..e1ce2c02c --- /dev/null +++ b/docs/recipes/request-timeout.md @@ -0,0 +1,81 @@ + + +# Request Timeout + +Lucee allows you to define a request timeout for every request made to Lucee. **Never accept request timeouts as a regular behavior of your application; always try to resolve any request timeout that occurs.** + +## Setting Request Timeout + +### Lucee Administrator +You can set the request timeout in the Lucee Administrator under "Settings/Request". + +### Application.cfc +You can also set the request timeout in the `Application.cfc` as follows: + +```luceescript +this.requestTimeout = createTimeSpan(0, 0, 0, 49); +``` + +### Tag cfsetting +Alternatively, you can set the request timeout using the `` tag: + +```luceetag + +``` + +## Thresholds + +Lucee includes several additional thresholds that requests must meet before they are terminated due to a timeout. These thresholds help prevent unnecessary termination of requests, which can pose risks such as deadlocks and open monitors. If a request timeout is reached but the thresholds are not met, Lucee will log the event in the "requesttimeout" log instead of terminating the request. + +### Concurrent Requests Threshold +This setting allows you to specify the number of concurrent requests Lucee can handle before enforcing request timeouts. Adjusting this threshold can help manage request timeouts under varying loads. A higher threshold allows more concurrent requests to be processed without enforcing timeouts, potentially improving performance under heavy loads at the risk of longer request times. The default threshold is set to 0, meaning request timeouts are enforced immediately for all requests. + +Set this threshold via the System Property: +```sh +-Dlucee.requesttimeout.concurrentrequestthreshold=100 +``` +or the Environment Variable: +```sh +LUCEE_REQUESTTIMEOUT_CONCURRENTREQUESTTHRESHOLD=100 +``` + +### CPU Usage Threshold +This option allows you to set a CPU usage threshold that Lucee monitors before enforcing request timeouts. The threshold value is a float ranging from 0.0 (0% CPU usage) to 1.0 (100% CPU usage). When the system's CPU usage is below this threshold, Lucee processes requests without applying the request timeout rule. This helps manage resource allocation and maintain application responsiveness during high demand or limited system resources. The default setting is 0.0, which means request timeouts are applied regardless of CPU usage. + +Set this threshold via the System Property: +```sh +-Dlucee.requesttimeout.cputhreshold=0.9 +``` +or the Environment Variable: +```sh +LUCEE_REQUESTTIMEOUT_CPUTHRESHOLD=0.9 +``` + +### Memory Usage Threshold +This setting allows you to establish a memory usage threshold, guiding Lucee on when to enforce request timeouts based on current memory consumption. The threshold value is a float from 0.0 (0% memory usage) to 1.0 (100% memory usage). By monitoring memory usage against this threshold, Lucee decides whether to enforce or relax request timeouts dynamically. This prevents system overloads and ensures stable performance by not strictly applying timeouts when memory usage is below the defined threshold. The default threshold is set to 0.0, meaning Lucee will apply request timeouts without considering memory usage. + +Set this threshold via the System Property: +```sh +-Dlucee.requesttimeout.memorythreshold=0.8 +``` +or the Environment Variable: +```sh +LUCEE_REQUESTTIMEOUT_MEMORYTHRESHOLD=0.8 +``` diff --git a/docs/recipes/retry.md b/docs/recipes/retry.md new file mode 100644 index 000000000..d459451a0 --- /dev/null +++ b/docs/recipes/retry.md @@ -0,0 +1,76 @@ + +## Retry ## + +This document explains how to use retry functionality with some simple examples. + +### Example 1: ### + +```luceescript +// example1.cfm + +path="test.txt"; +function fr(){ + dump(fileRead(path)); +} +try { + fr(); +} +catch(e) { + if(!fileExists(path)) { + fileWrite(path,"content of the file"); + fr(); + } + else echo(e); +} +if(fileExists(path)) fileDelete(path); +``` + +In this example, we have the "try" and "catch" blocks for reading a file and outputting the file content. Maybe that file does not exist in every case. So we have to check in advance if the file exists or not. In this example, the file read is a function `fr()`, and we will call this function in the "try" block. If the file does not exist, the "catch" block is executed and the `fr()` function is called again. + +This is not the best way. 'Retry' is a better option. The retry code looks like example2.cfm + +### Example 2: ### + +```luceescript +// example2.cfm + +path="test.txt"; +try { + dump(fileRead(path)); +} +catch(e) { + if(!fileExists(path)) { + fileWrite(path,"content of the file"); + retry; + } + echo(e); +} +if(fileExists(path)) fileDelete(path); +``` + +In this example, we use the retry functionality. Here we also still check if the file exists or not. If the file does not exist, we create a new file by using `fileWrite`. Then call retry to avoid duplicate code `fr()`. Retry points to the beginning of the try block and then it will read again the file and output the file content. + +We do not get an exception because if the file does not exist, we call retry (read the file again and output the file content). For this case, we simply use retry, and if it fails, we correct what is wrong. + +### Footnotes ### + +Here you can see these details in the video also: + +[Retry](https://youtu.be/zA9aAAimkk8) \ No newline at end of file diff --git a/docs/recipes/s3.md b/docs/recipes/s3.md new file mode 100644 index 000000000..4a0726107 --- /dev/null +++ b/docs/recipes/s3.md @@ -0,0 +1,72 @@ + +## S3 for source code ## + +This document explains how to use S3 as for your source code and how to use S3 for your artifacts when we look at the source code itself. + +### Example: + +```luceescript +// get an image directly from s3 +content file = "s3:///cfml1/lucee.png" type = "image/png" +"s3://##:#awsSecretKey#@/"; +``` + +//Application.cfc + +```luceescript +component{ + this.name = 'exampleS3'; + this.s3.accesskeyid = "JHKLJHGSGSGVSGVS"; + this.s3.awssecretkey = "Jgftiutry3uwiumcx4bvhjf9ksepu5wrwnvwbh9gj"; +} +``` + +1) In this example we directly call an S3 resource of the image using `file="s3:///cfml1/lucee.png"` and also define the mime type. Then we see the image while calling it in the browser. + +2) In this example, we define the credentials of the S3 in the Application.cfc. Here we give dummy data for the accesskey Id and secretkey. + +3) In this example, if you have an exception, it will display on the page exposing your credential information. So, we never use an error template that shows the exception. Best practice is to never use the credential with the password itself. Instead, always defined it in the application.cfc + +4) Another option is to map with the admin. + + - Virtual : /s3 + - Resource : s3://somethingLikeThis@/ + +But again, that would expose your credentials for everybody that sees an exception message. + +5) Instead, set the credentials in the environment variable or system properties (This is a new feature in S3 0.9.4.118). So, we can remove the resource in the mapping and just simply define `Resource : s3:///cfml1/` and save this mapping. + +6) Two important things in mapping. + + * When enabling the flag `Web Accessible`, this exposes that mapping directly to the user. So you can call it at /s3 in the browser. + * When removing the flag `Web Accessible`, you can only include that mapping. So, we always use cfinclude s3. + +7) If we select `Never` in 'Inspect Templates' this tells Lucee to pick up the file on first request from s3. It will compile the file to a local folder. Then it will only use that local compiled file and never check again if the file has changed. + +8) We go to `localhost:8888/s3/cfml1/index.cfm` in the browser. We get the source from S3 which comes directly from a stream, so we are catching that. It will not pick up any changes at all. For example, if we change that file a little bit and then update the file on s3, and then call it again in the browser, it does not pick up the latest changes. Because it is cached, what you now can do is flush the change with the help of the function `PagePoolClear()`. This function will create a complete page pool. + +9) Lucee will pick up the file including the new changes when we call and execute again. Here you have cached and flushed the cache manually. If you add new files to the s3, you can automate that step. + +It might be very useful to schedule a task that checks every five minutes or so to see if there are changes in the files on S3, and flush everything is there are changes. + +### Footnotes ### + +Here you can also see these details in the video: + +[S3 for source code](https://youtu.be/twQomRCbaCY) \ No newline at end of file diff --git a/docs/recipes/sax.md b/docs/recipes/sax.md new file mode 100644 index 000000000..940b33c57 --- /dev/null +++ b/docs/recipes/sax.md @@ -0,0 +1,147 @@ + +## Event driven XML reading (SAX) + +Lucee not only allows you to convert an XML file to an object tree (DOM) but also supports an event-driven model (SAX). + +The function `XMLParse` is handy to get an object representation of a complete XML document. However, for large XML documents, this can cause memory issues. This method is an overhead if you simply need to read some data from an XML file and convert it to something else. For this, the SAX event-driven model is a very handy and lightweight way to do this. Here is an example. + +Let's say we want to read in the following XML document: + +```lucee + + + + Empire Burlesque + Bob Dylan + USA + Columbia + 10.90 + 1985 + + + Hide your heart + Bonnie Tyler + UK + CBS Records + 9.90 + 1988 + + +``` + +To read this, we need to define a component that looks like the following, and you need to add functions that are listening to certain events of the XML parser (startDocument, startElement, body, endElements, ...). It is completely up to your code to store the data for later use. + +```cfs +component { + this.cds = []; + this.cd = {}; + this.insideCD = false; + this.currentName = ""; + this.filter = {}; + this.removeCD = false; + + /** + * constructor of the component that takes the path to the XML file and a simple custom-made filter + * @param xmlFile XML File to parse + * @param filter filter to limit content on certain records + */ + function init(string xmlFile, struct filter = {}) { + var xmlEventParser = createObject("java", "lucee.runtime.helpers.XMLEventParser"); + this.filter = filter; + // registering the event handlers + xmlEventParser.init( + getPageContext(), + this.startDocument, + this.startElement, + this.body, + this.endElement, + this.endDocument, + this.error + ); + xmlEventParser.start(xmlFile); + return this.cds; + } + + /** + * this function will be called on the start of parsing of an XML Element (Tag) + */ + function startElement(string uri, string localName, string qName, struct attributes) { + if (localName EQ "cd") { + this.cd = {}; + this.insideCD = true; + this.removeCD = false; + } else if (this.insideCD) { + this.currentName = localName; + } + } + + /** + * call with body of the tag + */ + function body(string content) { + if (len(this.currentName)) { + this.cd[this.currentName] = content; + if (structKeyExists(this.filter, this.currentName) and content NEQ this.filter[this.currentName]) + this.removeCD = true; + } + } + + /** + * this function will be called at the end of parsing an XML Element (Tag) + */ + function endElement(string uri, string localName, string qName, struct attributes) { + if (localName EQ "cd") { + if (!this.removeCD) + this.cds[arrayLen(this.cds) + 1] = this.cd; + this.insideCD = false; + } + this.currentName = ""; + } + + /** + * this function will be called when the document starts to be parsed + */ + function startDocument(string uri, string localName, string qName, struct attributes) {} + + /** + * this function will be called when the document finishes being parsed + */ + function endDocument(string uri, string localName, string qName, struct attributes) {} + + /** + * this function will be called when an error occurs + */ + function error(struct cfcatch) { + dump(cfcatch); + } +} +``` + +Now we simply can invoke that component to parse the XML file and get the result as an array of structs: + +```coldfusion + + + + +``` + +You can download the complete example [here](https://bitbucket.org/lucee/lucee/downloads/lucee-sax-example.zip). \ No newline at end of file diff --git a/docs/recipes/script-templates.md b/docs/recipes/script-templates.md new file mode 100644 index 000000000..02ce7941a --- /dev/null +++ b/docs/recipes/script-templates.md @@ -0,0 +1,19 @@ + +# Script Templates + +Since version 6.0, Lucee supports templates with the extension `.cfs`. The idea of these templates is that they contain script code, similar to `.js` files in the JavaScript world. + +This allows you to write direct script code without the need for the `` tag. \ No newline at end of file diff --git a/docs/recipes/settings.md b/docs/recipes/settings.md new file mode 100644 index 000000000..9dfd14b05 --- /dev/null +++ b/docs/recipes/settings.md @@ -0,0 +1,537 @@ + +# Environment Variables / System Properties for Lucee + +Below is a list of environment variables and system properties you can set for the Lucee Server. + +## Important Settings + +The following settings are very useful and important to know. + +**Environment Variable:** `LUCEE_ADMIN_ENABLED` +**System Property:** `-Dlucee.admin.enabled` +Should the Lucee Admin be available or not. + +**Environment Variable:** `LUCEE_ADMIN_PASSWORD` +**System Property:** `-Dlucee.admin.password` +Password used for the Lucee admin (when you run Lucee in multi mode, the password for the Server admin). + +**Environment Variable:** `LUCEE_DATASOURCE_POOL_VALIDATE` +**System Property:** `-Dlucee.datasource.pool.validate` +If enabled, Lucee will validate existing datasource connections reused from the datasource pool before using them. This protects from exceptions caused by connections dropped by the DB server but creates additional communication between Lucee and the DB server. + +**Environment Variable:** `LUCEE_DEBUGGING_OPTIONS` +**System Property:** `-Dlucee.debugging.options` +Debug options, a comma-separated list of the following possible debug options to enable: +- database +- exception +- template +- dump +- tracing +- timer +- implicit-access +- query-usage +- max-records-logged + +**Environment Variable:** `LUCEE_EXTENSIONS` +**System Property:** `-Dlucee.extensions` +Define a comma-separated list of Lucee extensions to install when starting up. This can be a simple list of IDs like this, then simply the latest versions get installed: + +```plaintext +99A4EF8D-F2FD-40C8-8FB8C2E67A4EEEB6, +671B01B8-B3B3-42B9-AC055A356BED5281, +2BCD080F-4E1E-48F5-BEFE794232A21AF6 +``` + +Or with more specific information like version and label (for better readability) like this: + +```plaintext +99A4EF8D-F2FD-40C8-8FB8C2E67A4EEEB6;name=MSSQL;label=MS SQL Server;version=12.2.0.jre8, +671B01B8-B3B3-42B9-AC055A356BED5281;name=PostgreSQL;label=PostgreSQL;version=42.7.3, +2BCD080F-4E1E-48F5-BEFE794232A21AF6;name=JDTsSQL;label=jTDS (MSSQL);version=1.3.1 +``` + +**Environment Variable:** `LUCEE_LOGINSTORAGE_ITERATIONS` +**System Property:** `-Dlucee.loginstorage.iterations` +Specifies the number of encryption iterations for `loginstorage`. The default is 0. + +**Environment Variable:** `LUCEE_LOGINSTORAGE_PRIVATEKEY` +**System Property:** `-Dlucee.loginstorage.privatekey` +A private key used to encrypt `loginstorage`. If not defined, a simple base64 encoding is used. + +**Environment Variable:** `LUCEE_LOGINSTORAGE_SALT` +**System Property:** `-Dlucee.loginstorage.salt` +The salt used for encrypting `loginstorage`. If no salt is defined, a hardcoded salt is used. + +**Environment Variable:** `LUCEE_PASSWORD_ENC_KEY` +**System Property:** `-Dlucee.password.enc.key` +The private encryption key used by Lucee to encrypt passwords stored in the configuration, such as for datasources. + +**Environment Variable:** `LUCEE_READ_CFID_FROM_URL` +**System Property:** `-Dlucee.read.cfid.from.url` +A boolean value. If true, Lucee allows reading the CFID from the URL query string. It is strongly recommended to disable this. + +**Environment Variable:** `LUCEE_REQUESTTIMEOUT` +**System Property:** `-Dlucee.requesttimeout` +A boolean value. If false, Lucee will disable request timeouts. + +**Environment Variable:** `LUCEE_REQUESTTIMEOUT_CONCURRENTREQUESTTHRESHOLD` +**System Property:** `-Dlucee.requesttimeout.concurrentrequestthreshold` +Concurrent request threshold to enforce a request timeout. If a request reaches the timeout, Lucee will only enforce it if this threshold is also reached. For example, setting it to `100` means the timeout is enforced only if there are at least 99 other requests running. + +**Environment Variable:** `LUCEE_REQUESTTIMEOUT_CPUTHRESHOLD` +**System Property:** `-Dlucee.requesttimeout.cputhreshold` +A floating-point number between 0 and 1. CPU threshold to enforce a request timeout. If a request reaches the timeout, Lucee will only enforce it if the CPU usage of the current core is at least the specified threshold. For example, setting it to `0.5` enforces the timeout if the CPU is at least 50%. + +**Environment Variable:** `LUCEE_SERVER_DIR` +**System Property:** `-Dlucee.server.dir` +Specifies the file directory for the Lucee server context. + +**Environment Variable:** `LUCEE_VERSION` +**System Property:** `-Dlucee.version` +Defines the version of Lucee to load. For example, setting it to `6.1.0.0` will load that version. If not available locally, Lucee will automatically download it from Maven. + +## Regular Settings + +Settings that are nice to know, but not that important. + +**Environment Variable:** `FELIX_LOG_LEVEL` +**System Property:** `-Dfelix.log.level` +Log level for the Felix Framework (OSGi). + +**Environment Variable:** `LUCEE_ALLOW_COMPRESSION` +**System Property:** `-Dlucee.allow.compression` +Allows compressing (GZIP) the HTTP response if the client explicitly supports it. + +**Environment Variable:** `LUCEE_APPLICATION_PATH_CACHE_TIMEOUT` +**System Property:** `-Dlucee.application.path.cache.timeout` +Lucee caches the path information to the template; this defines the idle timeout for these cache elements in milliseconds. + +**Environment Variable:** `LUCEE_CASCADE_TO_RESULTSET` +**System Property:** `-Dlucee.cascade.to.resultset` +When a variable has no scope defined (example: `#myVar#` instead of `#variables.myVar#`), Lucee will also search available resultsets (CFML Standard) or not. + +**Environment Variable:** `LUCEE_CLI_PRINTEXCEPTIONS` +**System Property:** `-Dlucee.cli.printExceptions` +Print out exceptions within the CLI interface. + +**Environment Variable:** `LUCEE_ENABLE_WARMUP` +**System Property:** `-Dlucee.enable.warmup` +Boolean to enable/disable Lucee warmup on start. + +**Environment Variable:** `LUCEE_EXTENSIONS_INSTALL` +**System Property:** `-Dlucee.extensions.install` +A boolean value to enable/disable the installation of extensions. + +**Environment Variable:** `LUCEE_FULL_NULL_SUPPORT` +**System Property:** `-Dlucee.full.null.support` +A boolean value to enable/disable full null support. + +**Environment Variable:** `LUCEE_LIBRARY_ADDITIONAL_FUNCTION` +**System Property:** `-Dlucee.library.additional.function` +Path to a directory for additional CFML-based functions Lucee should load to make these functions available in the application. For example, you create a file called `length.cfm` that looks like this: + +```lucee +function length(obj) { + return len(obj); +} +``` + +Then you copy that file into that directory, and you can use the function `length(any)` in all your code like a built-in function. + +**Environment Variable:** `LUCEE_LIBRARY_ADDITIONAL_TAG` +**System Property:** `-Dlucee.library.additional.tag` +Path to a directory for additional CFML-based tags Lucee should load as globally available tags following the custom tag interface. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_FUNCTION` +**System Property:** `-Dlucee.library.default.function` +Path to a directory for CFML-based functions Lucee should load to make these functions available in the application. For example, you create a file called `length.cfm` that looks like this: + +```lucee +function length(obj) { + return len(obj); +} +``` + +Then you copy that file into that directory, and you can use the function `length(any)` in all your code like a built-in function. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_TAG` +**System Property:** `-Dlucee.library.default.tag` +Path to a directory for CFML-based tags Lucee should load as globally available tags following the custom tag interface. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_TLD` +**System Property:** `-Dlucee.library.default.tld` +Tag Library Descriptor files (.tld or .tldx) Lucee should load to make these tags available in the application. + +**Environment Variable:** `LUCEE_LISTENER_MODE` +**System Property:** `-Dlucee.listener.mode` +Where/how does Lucee look for the Application Listener? +- `currenttoroot` - looks for the file "Application.cfc/Application.cfm" from the current up to the webroot directory. +- `currentorroot` - looks for the file "Application.cfc/Application.cfm" in the current directory and in the webroot directory. +- `root` - looks for the file "Application.cfc/Application.cfm" only in the webroot. +- `current` - looks for the file "Application.cfc/Application.cfm" only in the current template directory. + +**Environment Variable:** `LUCEE_LISTENER_TYPE` +**System Property:** `-Dlucee.listener.type` +Which kind of Application Listener is supported? +- `None` - no listener at all. +- `Classical (CFML < 7)` - Classic handling. Lucee looks for the file "Application.cfm" and a corresponding file "OnRequestEnd.cfm". +- `Modern` - Modern handling. Lucee only looks for the file "Application.cfc". +- `Mixed (CFML >= 7)` - Mixed handling. Lucee looks for a file "Application.cfm/OnRequestEnd.cfm" as well as for the file "Application.cfc". + +**Environment Variable:** `LUCEE_LOGGING_MAIN` +**System Property:** `-Dlucee.logging.main` +Name of the main logger used by Lucee, for example, a non-existing logger is defined. + +**Environment Variable:** `LUCEE_MAPPING_FIRST` +**System Property:** `-Dlucee.mapping.first` +Let's say you have the following code: + +```lucee + +``` + +And you have the following mappings defined: +- `/foo/bar` +- `/foo` + +Then Lucee will look for `/index.cfm` in `/foo/bar` and for `/bar/index.cfm` in `/foo` and invoke the first `index.cfm` it finds, which could be in both mappings. If this setting is set to `true`, Lucee will only check `/foo/bar` for `index.cfm`. + +**Environment Variable:** `LUCEE_MVN_REPO_RELEASES` +**System Property:** `-Dlucee.mvn.repo.releases` +Endpoint used by Lucee >= 6 to load releases, by default this is `https://oss.sonatype.org/service/local/repositories/releases/content/`. + +**Environment Variable:** `LUCEE_MVN_REPO_SNAPSHOTS` +**System Property:** `-Dlucee.mvn.repo.snapshots` +Endpoint used by Lucee >= 6 to load snapshots, by default this is `https://oss.sonatype.org/content/repositories/snapshots/`. + +**Environment Variable:** `LUCEE_PAGEPOOL_MAXSIZE` +**System Property:** `-Dlucee.pagePool.maxSize` +Max size of the template pool (page pool), the pool Lucee holds loaded `.cfm`/`.cfc` files. By default, this is `10000`; no number smaller than `1000` is accepted. + +**Environment Variable:** `LUCEE_PRECISE_MATH` +**System Property:** `-Dlucee.precise.math` +A boolean value. If enabled, this improves the accuracy of floating-point calculations but makes them slightly slower. + +**Environment Variable:** `LUCEE_PRESERVE_CASE` +**System Property:** `-Dlucee.preserve.case` +A boolean value. If true, Lucee will not convert variable names used in "dot notation" to UPPER CASE. + +**Environment Variable:** `LUCEE_QUEUE_ENABLE` +**System Property:** `-Dlucee.queue.enable` +A boolean value. If true, Lucee will enable the queue for requests. + +**Environment Variable:** `LUCEE_QUEUE_MAX` +**System Property:** `-Dlucee.queue.max` +The maximum concurrent requests that the engine allows to run at the same time before the engine begins to queue the requests. + +**Environment Variable:** `LUCEE_QUEUE_TIMEOUT` +**System Property:** `-Dlucee.queue.timeout` +The time in milliseconds a request is held in the queue. If the time is reached, the request is rejected with an exception. If you set it to 0 seconds, the request timeout is used instead. + +**Environment Variable:** `LUCEE_REQUEST_LIMIT_CONCURRENT_MAXNOSLEEP` +**System Property:** `-Dlucee.request.limit.concurrent.maxnosleep` +The maximal number of threads that are allowed to be active on the server. If this is reached, requests get forced into a "nap" (defined by `lucee.request.limit.concurrent.sleeptime`). + +**Environment Variable:** `LUCEE_REQUEST_LIMIT_CONCURRENT_SLEEPTIME` +**System Property:** `-Dlucee.request.limit.concurrent.sleeptime` +How long a request should "nap" in milliseconds if it reaches the `lucee.request.limit.concurrent.maxnosleep`. + +**Environment Variable:** `LUCEE_REQUESTTIMEOUT_MEMORYTHRESHOLD` +**System Property:** `-Dlucee.requesttimeout.memorythreshold` +A floating-point number between 0 and 1. Memory threshold to enforce a request timeout. If a request reaches the request timeout, Lucee will only enforce that timeout if this threshold is also reached. For example, setting it to `0.5` enforces the timeout if the memory consumption of the server is at least 50%. + +**Environment Variable:** `LUCEE_RESOURCE_CHARSET` +**System Property:** `-Dlucee.resource.charset` +Default character set for reading from/writing to various resources (files). + +**Environment Variable:** `LUCEE_SCRIPT_PROTECT` +**System Property:** `-Dlucee.script.protect` +Script protect setting used by default. Consult the Lucee admin page `/Settings/Request` for details on possible settings. + +**Environment Variable:** `LUCEE_SECURITY_LIMITEVALUATION` +**System Property:** `-Dlucee.security.limitEvaluation` +A boolean value. If enabled, limits variable evaluation in functions/tags. If enabled, you cannot use expressions within `[ ]` like this: `susi[getVariableName()]`. This affects the following functions: `IsDefined`, `structGet`, `empty` and the following tags: `savecontent attribute "variable"`. + +**Environment Variable:** `LUCEE_SSL_CHECKSERVERIDENTITY` +**System Property:** `-Dlucee.ssl.checkserveridentity` +A boolean value. If enabled, checks the identity of the SSL certificate with SMTP. + +**Environment Variable:** `LUCEE_STATUS_CODE` +**System Property:** `-Dlucee.status.code` +A boolean value. If disabled, returns a 200 status code to the client even if an uncaught exception occurs. + +**Environment Variable:** `LUCEE_STORE_EMPTY` +**System Property:** `-Dlucee.store.empty` +A boolean value. If enabled, does not store empty sessions to the client or session storage. + +**Environment Variable:** `LUCEE_SUPPRESS_WS_BEFORE_ARG` +**System Property:** `-Dlucee.suppress.ws.before.arg` +A boolean value. If enabled, Lucee suppresses whitespace defined between the `cffunction` starting tag and the last `cfargument` tag. This setting is ignored when there is different output between these tags as whitespace. + +**Environment Variable:** `LUCEE_SYSTEM_ERR` +**System Property:** `-Dlucee.system.err` +Where is the error stream of the JVM sent? Possible values are: +- `null` (the stream is ignored) +- `class:` - the data is sent to an instance of that class that must implement the `java.io.PrintStream` interface +- `file:` - an absolute path to a file name the stream is written to +- `log` - stream is written to `err.log` in `context/logs/` + +**Environment Variable:** `LUCEE_SYSTEM_OUT` +**System Property:** `-Dlucee.system.out` +Where is the out stream of the JVM sent? Possible values are: +- `null` (the stream is ignored) +- `class:` - the data is sent to an instance of that class that must implement the `java.io.PrintStream` interface +- `file:` - an absolute path to a file name the stream is written to +- `log` - stream is written to `out.log` in `context/logs/` + +**Environment Variable:** `LUCEE_TEMPLATE_CHARSET` +**System Property:** `-Dlucee.template.charset` +Default character set used to read templates (`.cfm` and `.cfc` files). + +**Environment Variable:** `LUCEE_TYPE_CHECKING` +**System Property:** `-Dlucee.type.checking` +A boolean value. If enabled, Lucee enforces types defined in the code. If false, type definitions are ignored. + +**Environment Variable:** `LUCEE_UPLOAD_BLOCKLIST` +**System Property:** `-Dlucee.upload.blocklist` +Default block list for the tag `cffile action="upload"`. A comma-separated list of extensions that are allowed when uploading files via forms. + +**Environment Variable:** `LUCEE_USE_LUCEE_SSL_TRUSTSTORE` +**System Property:** `-Dlucee.use.lucee.SSL.TrustStore` +Specifies the file location of the trust store that contains trusted Certificate Authorities (CAs) for SSL/TLS connections in Java applications. + +**Environment Variable:** `LUCEE_WEB_CHARSET` +**System Property:** `-Dlucee.web.charset` +Default character set for output streams, form-, URL-, and CGI scope variables, and reading/writing the header. + +## Edge Case Settings + +These settings are normally not needed in a regular environment. + +**Environment Variable:** `FELIX_LOG_LEVEL` +**System Property:** `-Dfelix.log.level` +Log level for the Felix Framework (OSGi). + +**Environment Variable:** `LUCEE_ALLOW_COMPRESSION` +**System Property:** `-Dlucee.allow.compression` +Allows compressing (GZIP) the HTTP response if the client explicitly supports it. + +**Environment Variable:** `LUCEE_APPLICATION_PATH_CACHE_TIMEOUT` +**System Property:** `-Dlucee.application.path.cache.timeout` +Lucee caches the path information to the template; this defines the idle timeout for these cache elements in milliseconds. + +**Environment Variable:** `LUCEE_CASCADE_TO_RESULTSET` +**System Property:** `-Dlucee.cascade.to.resultset` +When a variable has no scope defined (example: `#myVar#` instead of `#variables.myVar#`), Lucee will also search available resultsets (CFML Standard) or not. + +**Environment Variable:** `LUCEE_CLI_PRINTEXCEPTIONS` +**System Property:** `-Dlucee.cli.printExceptions` +Print out exceptions within the CLI interface. + +**Environment Variable:** `LUCEE_ENABLE_WARMUP` +**System Property:** `-Dlucee.enable.warmup` +Boolean to enable/disable Lucee warmup on start. + +**Environment Variable:** `LUCEE_EXTENSIONS_INSTALL` +**System Property:** `-Dlucee.extensions.install` +A boolean value to enable/disable the installation of extensions. + +**Environment Variable:** `LUCEE_FULL_NULL_SUPPORT` +**System Property:** `-Dlucee.full.null.support` +A boolean value to enable/disable full null support. + +**Environment Variable:** `LUCEE_LIBRARY_ADDITIONAL_FUNCTION` +**System Property:** `-Dlucee.library.additional.function` +Path to a directory for additional CFML-based functions Lucee should load to make these functions available in the application. For example, you create a file called `length.cfm` that looks like this: + +```lucee + +function length(obj) { + return len(obj); +} + +``` + +Then you copy that file into that directory, and you can use the function `length(any)` in all your code like a built-in function. + +**Environment Variable:** `LUCEE_LIBRARY_ADDITIONAL_TAG` +**System Property:** `-Dlucee.library.additional.tag` +Path to a directory for additional CFML-based tags Lucee should load as globally available tags following the custom tag interface. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_FUNCTION` +**System Property:** `-Dlucee.library.default.function` +Path to a directory for CFML-based functions Lucee should load to make these functions available in the application. For example, you create a file called `length.cfm` that looks like this: + +```lucee + +function length(obj) { + return len(obj); +} + +``` + +Then you copy that file into that directory, and you can use the function `length(any)` in all your code like a built-in function. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_TAG` +**System Property:** `-Dlucee.library.default.tag` +Path to a directory for CFML-based tags Lucee should load as globally available tags following the custom tag interface. + +**Environment Variable:** `LUCEE_LIBRARY_DEFAULT_TLD` +**System Property:** `-Dlucee.library.default.tld` +Tag Library Descriptor files (`.tld` or `.tldx`) Lucee should load to make these tags available in the application. + +**Environment Variable:** `LUCEE_LISTENER_MODE` +**System Property:** `-Dlucee.listener.mode` +Where/how does Lucee look for the Application Listener? +- `currenttoroot` - looks for the file "Application.cfc/Application.cfm" from the current up to the webroot directory. +- `currentorroot` - looks for the file "Application.cfc/Application.cfm" in the current directory and in the webroot directory. +- `root` - looks for the file "Application.cfc/Application.cfm" only in the webroot. +- `current` - looks for the file "Application.cfc/Application.cfm" only in the current template directory. + +**Environment Variable:** `LUCEE_LISTENER_TYPE` +**System Property:** `-Dlucee.listener.type` +Which kind of Application Listener is supported? +- `None` - no listener at all. +- `Classical (CFML < 7)` - Classic handling. Lucee looks for the file "Application.cfm" and a corresponding file "OnRequestEnd.cfm". +- `Modern` - Modern handling. Lucee only looks for the file "Application.cfc". +- `Mixed (CFML >= 7)` - Mixed handling. Lucee looks for a file "Application.cfm/OnRequestEnd.cfm" as well as for the file "Application.cfc". + +**Environment Variable:** `LUCEE_LOGGING_MAIN` +**System Property:** `-Dlucee.logging.main` +Name of the main logger used by Lucee, for example, a non-existing logger is defined. + +**Environment Variable:** `LUCEE_MAPPING_FIRST` +**System Property:** `-Dlucee.mapping.first` +Let's say you have the following code: + +```lucee + +``` + +And you have the following mappings defined: +- `/foo/bar` +- `/foo` + +Then Lucee will look for `/index.cfm` in `/foo/bar` and for `/bar/index.cfm` in `/foo` and invoke the first `index.cfm` it finds, which could be in both mappings. If this setting is set to `true`, Lucee will only check `/foo/bar` for `index.cfm`. + +**Environment Variable:** `LUCEE_MVN_REPO_RELEASES` +**System Property:** `-Dlucee.mvn.repo.releases` +Endpoint used by Lucee >= 6 to load releases. By default, this is `https://oss.sonatype.org/service/local/repositories/releases/content/`. + +**Environment Variable:** `LUCEE_MVN_REPO_SNAPSHOTS` +**System Property:** `-Dlucee.mvn.repo.snapshots` +Endpoint used by Lucee >= 6 to load snapshots. By default, this is `https://oss.sonatype.org/content/repositories/snapshots/`. + +**Environment Variable:** `LUCEE_PAGEPOOL_MAXSIZE` +**System Property:** `-Dlucee.pagePool.maxSize` +Max size of the template pool (page pool), the pool Lucee holds loaded `.cfm`/`.cfc` files. By default, this is `10000`; no number smaller than `1000` is accepted. + +**Environment Variable:** `LUCEE_PRECISE_MATH` +**System Property:** `-Dlucee.precise.math` +A boolean value. If enabled, this improves the accuracy of floating-point calculations but makes them slightly slower. + +**Environment Variable:** `LUCEE_PRESERVE_CASE` +**System Property:** `-Dlucee.preserve.case` +A boolean value. If true, Lucee will not convert variable names used in "dot notation" to UPPER CASE. + +**Environment Variable:** `LUCEE_QUEUE_ENABLE` +**System Property:** `-Dlucee.queue.enable` +A boolean value. If true, Lucee will enable the queue for requests. + +**Environment Variable:** `LUCEE_QUEUE_MAX` +**System Property:** `-Dlucee.queue.max` +The maximum concurrent requests that the engine allows to run at the same time before the engine begins to queue the requests. + +**Environment Variable:** `LUCEE_QUEUE_TIMEOUT` +**System Property:** `-Dlucee.queue.timeout` +The time in milliseconds a request is held in the queue. If the time is reached, the request is rejected with an exception. If you set it to 0 seconds, the request timeout is used instead. + +**Environment Variable:** `LUCEE_REQUEST_LIMIT_CONCURRENT_MAXNOSLEEP` +**System Property:** `-Dlucee.request.limit.concurrent.maxnosleep` +The maximal number of threads that are allowed to be active on the server. If this is reached, requests get forced into a "nap" (defined by `lucee.request.limit.concurrent.sleeptime`). + +**Environment Variable:** `LUCEE_REQUEST_LIMIT_CONCURRENT_SLEEPTIME` +**System Property:** `-Dlucee.request.limit.concurrent.sleeptime` +How long a request should "nap" in milliseconds if it reaches the `lucee.request.limit.concurrent.maxnosleep`. + +**Environment Variable:** `LUCEE_REQUESTTIMEOUT_MEMORYTHRESHOLD` +**System Property:** `-Dlucee.requesttimeout.memorythreshold` +A floating-point number between 0 and 1. Memory threshold to enforce a request timeout. If a request reaches the request timeout, Lucee will only enforce that timeout if this threshold is also reached. For example, setting it to `0.5` enforces the timeout if the memory consumption of the server is at least 50%. + +**Environment Variable:** `LUCEE_RESOURCE_CHARSET` +**System Property:** `-Dlucee.resource.charset` +Default character set for reading from/writing to various resources (files). + +**Environment Variable:** `LUCEE_SCRIPT_PROTECT` +**System Property:** `-Dlucee.script.protect` +Script protect setting used by default. Consult the Lucee admin page `/Settings/Request` for details on possible settings. + +**Environment Variable:** `LUCEE_SECURITY_LIMITEVALUATION` +**System Property:** `-Dlucee.security.limitEvaluation` +A boolean value. If enabled, limits variable evaluation in functions/tags. If enabled, you cannot use expressions within `[ ]` like this: `susi[getVariableName()]`. This affects the following functions: `IsDefined`, `structGet`, `empty` and the following tags: `savecontent attribute "variable"`. + +**Environment Variable:** `LUCEE_SSL_CHECKSERVERIDENTITY` +**System Property:** `-Dlucee.ssl.checkserveridentity` +A boolean value. If enabled, checks the identity of the SSL certificate with SMTP. + +**Environment Variable:** `LUCEE_STATUS_CODE` +**System Property:** `-Dlucee.status.code` +A boolean value. If disabled, returns a 200 status code to the client even if an uncaught exception occurs. + +**Environment Variable:** `LUCEE_STORE_EMPTY` +**System Property:** `-Dlucee.store.empty` +A boolean value. If enabled, does not store empty sessions to the client or session storage. + +**Environment Variable:** `LUCEE_SUPPRESS_WS_BEFORE_ARG` +**System Property:** `-Dlucee.suppress.ws.before.arg` +A boolean value. If enabled, Lucee suppresses whitespace defined between the `cffunction` starting tag and the last `cfargument` tag. This setting is ignored when there is different output between these tags as whitespace. + +**Environment Variable:** `LUCEE_SYSTEM_ERR` +**System Property:** `-Dlucee.system.err` +Where is the error stream of the JVM sent? Possible values are: +- `null` (the stream is ignored) +- `class:` - the data is sent to an instance of that class that must implement the `java.io.PrintStream` interface +- `file:` - an absolute path to a file name the stream is written to +- `log` - stream is written to `err.log` in `context/logs/` + +**Environment Variable:** `LUCEE_SYSTEM_OUT` +**System Property:** `-Dlucee.system.out` +Where is the out stream of the JVM sent? Possible values are: +- `null` (the stream is ignored) +- `class:` - the data is sent to an instance of that class that must implement the `java.io.PrintStream` interface +- `file:` - an absolute path to a file name the stream is written to +- `log` - stream is written to `out.log` in `context/logs/` + +**Environment Variable:** `LUCEE_TEMPLATE_CHARSET` +**System Property:** `-Dlucee.template.charset` +Default character set used to read templates (`.cfm` and `.cfc` files). + +**Environment Variable:** `LUCEE_TYPE_CHECKING` +**System Property:** `-Dlucee.type.checking` +A boolean value. If enabled, Lucee enforces types defined in the code. If false, type definitions are ignored. + +**Environment Variable:** `LUCEE_UPLOAD_BLOCKLIST` +**System Property:** `-Dlucee.upload.blocklist` +Default block list for the tag `cffile action="upload"`. A comma-separated list of extensions that are allowed when uploading files via forms. + +**Environment Variable:** `LUCEE_USE_LUCEE_SSL_TRUSTSTORE` +**System Property:** `-Dlucee.use.lucee.SSL.TrustStore` +Specifies the file location of the trust store that contains trusted Certificate Authorities (CAs) for SSL/TLS connections in Java applications. + +**Environment Variable:** `LUCEE_WEB_CHARSET` +**System Property:** `-Dlucee.web.charset` +Default character set for output streams, form-, URL-, and CGI scope variables, and reading/writing the header. \ No newline at end of file diff --git a/docs/recipes/startup-listeners-code.md b/docs/recipes/startup-listeners-code.md new file mode 100644 index 000000000..e5c311e74 --- /dev/null +++ b/docs/recipes/startup-listeners-code.md @@ -0,0 +1,105 @@ + +## Startup Listeners Code ## + +Lucee has two kinds of startup listeners. + +- **Server.cfc** which runs when the Lucee Server starts up. It exists only once per Lucee instance. +- **Web.cfc** which can be used for each web context. + +### Server.cfc ### + +Create a `Server.cfc` file in `lucee-server\context\context` directory. + +```lucee + +// lucee-server\context\context\Server.cfc + +component{ + public function onServerStart( reload ){ + if ( !arguments.reload ){ + systemOutput("-------Server Context started -----",true); + // this is when the server is first started. + // you can for example, use configImport() to import a .cfConfig.json setting file + var config_web = "/www/config/lucee_server_cfConfig.json"; + configImport( + type: "server", + data: deserializeJSON(fileRead(config_web)), + password: "your lucee server admin password" + ); + } else { + // the server config is reloaded each time an extension is installed / or the config is updated + systemOutput( "-------Server Context config reloaded -----", true ); + } + } +} +``` + +- Here, Server.cfc has one function ``onServerStart()`` +- Start Lucee Server. +- The server console or `out.log` should show the above systemOutput's which means it has run the `Server.cfc` + +### Web.cfc ### + +Create a `Web.cfc` file in `webapps\ROOT\WEB-INF\lucee\context\` directory, or the context webroot. + +```lucee + +// webapps\ROOT\WEB-INF\lucee\context\Web.cfc + +component { + public function onWebStart( reload ){ + if ( !arguments.reload ){ + systemOutput("-------Web Context started -----",true); + // this is when the web content is first started. + // you can for example, use configImport() to import a .cfConfig.json setting file + var config_web = "/www/config/lucee_web_cfConfig.json" + configImport( + type: "web", + data: deserializeJSON( fileRead( config_web ) ), + password: "your lucee web content admin password" + ); + } else { + // the web context is reloaded each time an extension is installed / or the config is updated + systemOutput("-------Web Context config reloaded -----",true); + } + } +} +``` + +Here `Web.cfc` has one function ``onWebStart()`` and one argument ``reload`` that indicates if the web context is a new startup of the server. + +Here ``reload`` is used to reload the web context. We see the difference when setting reload to true or false. + +- Start your Lucee server. +- Here we see the server context output first, then the web context output next. So you can see that both listeners get triggered by Lucee. +- Next, change the **settings --> charset** for web charset "UTF-8" in web admin. +- After setting the charset in web admin, the web context only is reloaded and we do not have the server context. So this feature is used to stop/prevent any difficulties with the server context. + +This is a simple way to stop the server context. It is never triggered because there is no event happening inside java. + +### Footnotes ### + +Here you can see above details in video + +[Lucee Startup Listeners](https://youtu.be/b1MWLwkKdLE) \ No newline at end of file diff --git a/docs/recipes/static_scope.md b/docs/recipes/static_scope.md new file mode 100644 index 000000000..1df130065 --- /dev/null +++ b/docs/recipes/static_scope.md @@ -0,0 +1,214 @@ + +## Static scope in components + +Static scope in components is needed to create an instance of cfc and call its method. It is used to avoid creating an instance each time you use the cfc. + +You can create an object in the Application init() function, and make it at application scope, so you can directly call the methods. + +We explain this methodology with a simple example below: + +### Example: + +```luceescript +// index.cfm +directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +1) Create a constructor of the component. It is the instance of the current path and also create new function hey(). + +```luceescript +// Example0.cfc +Component { + public function init() { + dump("create an instance of " & listLast(getCurrentTemplatePath(),'\/')); + } + public function hey() { + dump("Salve!"); + } +} +``` + +2) Next, we instantiate the component four times, and then call the hey() function. Run this example00.cfm page in the browser. It shows five dumps. Four dumps coming from inside of the constructor and the fifth dump is from hey(). Note that the init() function is private, so you cannot load it from outside the component. Therefore, you have no access to the message within init() from the cfscript in the example below. + +```luceescript +// example0.cfm +new Example0(); +new Example0(); +new Example0(); +cfc = new Example0(); +cfc.hey(); +``` + +### Example 1: + +As our code gets more complicated, we need to make some additions to it. + +* One option is to create the Component in the application scope or server scope, or to use the function GetComponentMetaData to store components in a more persistent manner. + +The static scope for components was introduced in Lucee 5.0. This is a scope that is shared with all instances of the same component. + +Here is an example showing the use of static scope: + +```luceescript +// Example1.cfc +Component { + static var counter = 0; + public function init() { + static.counter++; + dump("create an instance of " & listLast(getCurrentTemplatePath(),'\/') & " " & static.counter); + } + public function getCount() { + return static.counter; + } +} +``` + +Here, the variable `counter` is defined in the static scope. This means that all instances of Example1.cfc share this variable. + +2) In the following example, we call the Example1() function three times. Each time, the `counter` variable is incremented and shared across all instances. + +```luceescript +// example1.cfm +new Example1(); +new Example1(); +new Example1(); +``` + +### Example 2: + +1) Another example is using the static scope to store the result of a time-consuming operation that does not need to be recomputed every time. + +```luceescript +// Example2.cfc +Component { + static var data = []; + public function init() { + if (arrayLen(static.data) == 0) { + for (i = 1; i <= 100; i++) { + arrayAppend(static.data, i * i); + } + } + dump(static.data); + } +} +``` + +Here, the array `data` is defined in the static scope, which means it will be computed only once and shared across all instances. + +2) In the following example, we call the Example2() function twice. The array `data` is computed only once and reused in the second instance. + +```luceescript +// example2.cfm +new Example2(); +new Example2(); +``` + +### Example 3: + +1) The static scope can also be used for functions. In this example, we define a static function that is available to all instances. + +```luceescript +// Example3.cfc +Component { + public static function hello() { + return "Hello, World!"; + } +} +``` + +2) In the following example, we call the static function `hello` without creating an instance of Example3. + +```luceescript +// example3.cfm +dump(Example3::hello()); +``` + +### Example 4: + +1) The static scope can be used to count the number of instances created from a component. + +```luceescript +// Example4.cfc +Component { + static var counter = 0; + public function init() { + static.counter++; + dump(static.counter & " instances used so far"); + } +} +``` + +2) In the following example, we call the Example4() function five times. Each time the function is called, the count of `counter` in the static scope increases. + +```luceescript +// example4.cfm +new Example4(); +new Example4(); +new Example4(); +new Example4(); +new Example4(); +``` + +### Example 5: + +1) We can also use the static scope to store constant data like HOST, PORT. + +* If we store the instance in the variable scope, you will run into problems when you have a thousand components or it gets loaded a thousand times. This is a waste of time and memory storage. +* The static scope means that a variable only exists once and is independent of how many instances you have. So it is more memory efficient to do it that way. You can also do the same for functions. + +```luceescript +// Example5.cfc +Component { + static { + static.HOST = "lucee.org"; + static.PORT = 8080; + } + public static function splitFullName(required string fullName) { + var arr = listToArray(fullName, " "); + return {'lastname': arr[1], 'firstname': arr[2]}; + } + public function init(required string fullName) { + variables.fullname = static.splitFullName(fullName); + } + public string function getLastName() { + return variables.fullname.lastname; + } +} +``` + +2) In the following example, we call the Example5() function in two ways. It has a function splitFullName() that does not need to access anything (read or write data from the disks) and a variable scope that doesn't have to be part of the instance. It returns the firstname and lastname. + +```luceescript +// example5.cfm +person = new Example5("Sobchak Walter"); +dump(person.getLastName()); +dump(Example5::splitFullName("Quintana Jesus")); +``` + +### Footnotes + +[Lucee 5 features reviewed: static](https://dev.lucee.org/t/lucee-5-features-reviewed-static/433) + +[Video: Lucee Static Scopes in Component Code](https://www.youtube.com/watch?v=B5ILIAbXBzo&feature=youtu.be) \ No newline at end of file diff --git a/docs/recipes/sub-components.md b/docs/recipes/sub-components.md new file mode 100644 index 000000000..57295bef6 --- /dev/null +++ b/docs/recipes/sub-components.md @@ -0,0 +1,45 @@ + +# Sub Component + +Since Lucee 6.0, Lucee allows you to create sub components. These are additional components created in a .cfc after the main component. + +After the main component, simply add as many additional components as you like with the attribute "name" like this: + +```lucee +component { + function mainTest() { + return "main"; + } +} +component name="Sub" { + function subTest() { + return "sub"; + } +} +``` + +This is useful when a component needs sub components, like an Address component could have a Person component inside. + +Here is an example of how you can address/load these sub components: + +```lucee +cfc = new MyCFC(); +echo("main->" & cfc.mainTest()); +echo("
"); +cfc = new MyCFC$Sub(); +echo("sub->" & cfc.subTest()); +echo("
"); +``` \ No newline at end of file diff --git a/docs/recipes/supercharge-your-website.md b/docs/recipes/supercharge-your-website.md new file mode 100644 index 000000000..289ba3aa4 --- /dev/null +++ b/docs/recipes/supercharge-your-website.md @@ -0,0 +1,44 @@ + +## Supercharge your website ## + +This document explains how you can improve the performance of your website in a very short time with Lucee. + +### Example: ### + +```luceescript +// index.cfm +writeDump(now()); +``` + +Run the above index.cfm, and you get a timestamp. Whenever we call our file, Lucee checks once at every request if a file has changed or not (for files currently residing in the template cache). If a file has changed, Lucee recompiles it, and executes it. Checking the files at every request takes time. If you have a published server, and you know your server does not produce or change any files, you can simply avoid that check that happens all the time. + +### Using Admin ### + +* Go to _Admin -> Performance/ Caching -> Inspect Templates (CFM/CFC) -> Never_ + +* The default is "Once", checking any requested files one time within each request. You should check "Never" to avoid the checking at every request. + +* Change the index.cfm and run it again. No changes happen in the output because Lucee does not check if the file changed or not. Now, let's see the faster execution and less performance memory being used. + +* You can clear the cache by code using `pagePoolClear()`. This clears all template cache so that Lucee will check again if the template has changed. On the next request, Lucee will check initially for the file. + +* Another option to clear the template cache is to use clear cache via the admin by clicking the button in _Admin -> Settings -> Performance/ Caching -> Page Pool Cache_. + +### Footnotes ### + +Here you can see these details on video also: + +[Charge Your Website](https://youtu.be/w-eeigEkmn0) \ No newline at end of file diff --git a/docs/recipes/systemoutput_function.md b/docs/recipes/systemoutput_function.md new file mode 100644 index 000000000..dbc9a9aee --- /dev/null +++ b/docs/recipes/systemoutput_function.md @@ -0,0 +1,80 @@ + +# Function SystemOutput # + +This document explains the systemoutput function with some simple examples. + +## Example 1: ## + +```luceescript + +directory action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +
+``` + + +```lucee + +dump(cgi); // or writedump if you love to write more +echo(now()); // or writeoutput if you love to write more + +``` + +This example has a simple dump with CGI. It displays normally in the browser while we are running example1.cfm. + +## Example 2: ## + +```luceescript +// example2.cfm +systemOutput(now(),true); // with new line +``` + +systemOutput() creates output content in the web browser (console). Here the systemoutput has two arguments: first argument _now()_ for current date and time, and second argument _true_ for a new line. Run this in the browser and see the content in the console. + +## Example 3: ## + +```luceescript +// example3.cfm +systemOutput(now(),true,true); // send to error stream +``` + +This example uses three arguments: first argument `now()` for current date and time, second argument `true` for a new line, and third argument for the stream. The stream argument indicates which stream the output should go. There are two streams to choose from: "Output stream" and "Error stream". A value of true in the third argument indicates the output should go to the error stream. Run this in the browser and see the contents with the output stream in the console. + +## Example 4: ## + +```luceescript +// example4.cfm +systemOutput(cgi,true); // complex object +``` + +In addition to simple strings or simple values, you can also pass complex objects to the SystemOutput() function. In this example we pass CGI as the argument. When you run this in the browser, you get a serialized output in the console. + +## Example 5: ## + +```luceescript +// example5.cfm +systemOutput("Here we are:",true); // complex object +``` + +SystemOutput() has another good feature too. There is `` used to show helpful information (where it is, which template, on which line number) while we mouse over the dump content. Lucee will detect and show the stack-trace if we add `` to the SystemOutput() function in our code. When we run this in the browser, we see the stack-trace in the console. + +## Footnotes ## + +Here you can see these details on video also: + +[Function SystemOutput](https://www.youtube.com/watch?v=X_BQPFPD320) \ No newline at end of file diff --git a/docs/recipes/thread_task.md b/docs/recipes/thread_task.md new file mode 100644 index 000000000..f88fafdc4 --- /dev/null +++ b/docs/recipes/thread_task.md @@ -0,0 +1,121 @@ + +## Thread Task ## + +This document explains about the thread tasks. It is useful to know the differences between regular threads and task threads. + +When a regular thread throws an exception, the default exception type is `type='daemon'`. After an exception, the thread is dumped. If a regular thread is terminated due to an error, the information from the thread will be output with a 'terminated' status. + +Regular Threads have the following characteristics: + +1) **Bound to current request**: With the help of CFThread you can always see what the thread is doing. With `action='join'` you can wait until the thread ends and join it. You can also call `action='terminate'` and end the thread. You always have control over the thread with the various actions. + +2) **Runs only once**: The thread runs only once. It ends at the end of the Cfthread tag or if there is an exception. + +3) **It fails when it fails**: There is no special exception handling so when the thread fails it fails unless you have Cftry, cfcatch inside the thread and you have exception handling there. + +### Example 1: ### + +In addition to daemon (regular) threads, Lucee also supports task threads. The main differences is that task threads execute completely independently of the request that is calling the thread. We log the thread and can re-execute it. + +This example shows a task thread. It is similar to the daemon thread, but we do not have the join and output of the thread because these are not allowed with a task thread. + +```luceescript +thread name="test" type="daemon" { + throw "hopala!"; +} +thread action="join" name="test"; +dump(cfthread.test); +``` + +Note that when you execute the example code, you will get no output. This is expected since no output was written in the code. + +However, view the Lucee _Admin --> Services --> Tasks_ and see the name of the tasks and their `Type` is `daemon` and the status is `terminated`. + +### Example 2: ### + +Next we show a similar example using the task type: + +```luceescript +thread name="test" type="task" { + throw "hopala!"; +} +sleep(1000); +dump(getPageContext().getTasks().getTask("test")); +``` + +This example shows the task type. This example is similar to the daemon thread. It waits 1 second before outputting the task details. The task type thread will continue to run independently. + +### Example 3: ### + +Task threads can be retried multiple times if they fail. This is different from daemon threads which fail permanently after an exception. Below example shows a task thread that retries multiple times. + +```luceescript +thread name="test" type="task" + retryinterval=[ + {tries:3, interval:createTimeSpan(0,0,0,1)}, + {tries:5, interval:createTimeSpan(0,0,0,5)}, + {tries:10, interval:createTimeSpan(0,0,0,10)}, + {tries:10, interval:createTimeSpan(0,0,1,0)}, + {tries:20, interval:createTimeSpan(0,0,10,0)} + ] { + throw "hopala!"; +} +sleep(1000); +dump(getPageContext().getTasks().getTask("test")); +``` + +This example creates a task thread named `test` that retries according to the defined intervals. Initially, the task thread throws an exception. The retry intervals are defined in the array with `tries` and `interval` attributes. + +### Example 4: ### + +Another example for getting the admin component and task is physically created on the system. + +```luceescript +thread name="test" type="task" + retryinterval=[ + {tries:3, interval:createTimeSpan(0,0,0,1)}, + {tries:5, interval:createTimeSpan(0,0,0,5)}, + {tries:10, interval:createTimeSpan(0,0,0,10)}, + {tries:10, interval:createTimeSpan(0,0,1,0)}, + {tries:20, interval:createTimeSpan(0,0,10,0)} + ] { + throw "hopala!"; +} +sleep(1000); +admin=new Administrator(type:"web",password:"server"); +tasks=admin.getTasks(); +dump(tasks); +admin.executeTask(tasks.id); +admin.removeTask(tasks.id[tasks.recordcount]); +admin.removeAllTask(); +``` + +1) `admin.getTasks()` is used to list out all existing tasks. When executed, it returns a query that contains the information from the task. + +2) `admin.executeTask()` is used to execute the task and we see it in the browser. It throws an exception. + +3) `admin.removeTask()` and `admin.removeAllTask()` are used to remove tasks from the administrator. + +### Footnotes ### + +Here you can see the details in the video: +[Thread Task](https://youtu.be/-SUbVWqJRME) \ No newline at end of file diff --git a/docs/recipes/thread_usage.md b/docs/recipes/thread_usage.md new file mode 100644 index 000000000..386a06df8 --- /dev/null +++ b/docs/recipes/thread_usage.md @@ -0,0 +1,192 @@ + +## Thread Scope + +This document explains how to use threads in Lucee. Threads are mainly used for executing code in parallel. + +### Example 1 + +The below example shows normal execution, waiting for all code to resolve. Here it takes 1000 milliseconds to complete the execution. + +```lucee + +function napASecond() localmode=true { + sleep(1000); +} +start = getTickCount(); +napASecond(); +dump("done in #getTickCount()-start#ms"); + +``` + +The below example shows thread execution. Code within the thread tag runs in parallel with the execution of the other code in the cfscript. Here execution does not wait for sleep. + +```lucee + +function napASecond() localmode=true { + thread { + sleep(1000); + } +} +start = getTickCount(); +napASecond(); +dump("done in #getTickCount()-start#ms"); + +``` + +Threads run independently of other threads or code in Lucee. Lucee does not the thread first and then the following code. + +Threads are mainly used to retrieve data from the database, cfhttp, or webservice. Threads are used when it does not matter how much time it takes to execute. + +### Example 2 + +Here we see an example using multiple threads in Lucee. All threads run in parallel. + +```lucee + +function napASecond(index) localmode=true { + thread { + thread.start = now(); + sleep(1000) + thread.end = now(); + } +} + +start = getTickCount(); +loop from=1 to=5 index="index" { + napASecond(index); +} + +// show threads +dump(var:cfthread, expand=false); +// wait for all threads to finish +thread action="join" name=cfthread.keyList(); +// show threads +dump(var:cfthread, expand=false); + +dump("done in #getTickCount()-start#ms"); + +``` + +The above example shows five threads running in parallel. The thread action="join" line waits for all threads to finish. `cfthread` returns struct info of all thread statuses. + +### Example 3 + +Threads can also be used to perform long-running tasks asynchronously. + +```lucee + +// normal array +list = ["Susi", "Harry"]; +list.each( + function(value, key, struct) { + systemOutput(arguments, true); + } + ,true +); +// struct +list = {"name": "Susi", "age": 47}; +list.each( + function(value, key, struct) { + systemOutput(arguments, true); + } + ,true +); +// list +list = "Susi,Harry"; +list.each( + function(value, key, struct) { + systemOutput(arguments, true); + } + ,true +); +// query +persons = query( + 'firstName': ['Susi', 'Harry'], + 'lastName': ['Sorglos', 'Hirsch'] +); +queryEach( + persons, + function(value, row, query) { + systemOutput(arguments, true); + } + ,true +); +persons.each( + function(value, row, query) { + systemOutput(arguments, true); + } + ,true +); +dump("done"); + +``` + +### Example 4 + +Lucee members often discuss how to extend functionality to make Lucee easier to use or adding other new functionality. + +This example shows a future implementation of threads in Lucee. + +```lucee + +// Thread Pool +tasks.each( + function(value, key, struct) { + systemOutput(arguments, true); + } + ,true + ,20 // ATM default for max threads is 20, instead we plan to use a smart thread pool in the future (Java ExecutorService) +); + +``` + +Currently, the default max threads is 20. In the future, we plan to use a smart thread pool based on your JVM (Java ExecutorService). So you will not have to take care of how many threads are being used. The system will do that, and provide the best choice for your code. + +```lucee + +thread /* action="thread" name="whatever" */ { + sleep(); +} + +``` + +In the future, we will make threads smarter by also using a pool for threads. + +This feature is something we are planning. Changes will be implemented on the backend so that nothing changes on the front end. + +```lucee + +// Extend parallel +loop from=1 to=10 index="i" parallel=true { + ... +} +// ??? +for(i=0; i<10; i++; true) { +} + +``` + +Another planned enhancement is to extend parallel to the loop by simply adding `parallel=true`. It will execute the body of the loop in parallel. + +### Footnotes + +Here you can see the above details in a video: + +[Lucee Threads](https://www.youtube.com/watch?v=oGUZRrcg9KE) \ No newline at end of file diff --git a/docs/recipes/timeout.md b/docs/recipes/timeout.md new file mode 100644 index 000000000..257f14e72 --- /dev/null +++ b/docs/recipes/timeout.md @@ -0,0 +1,49 @@ + +# Timeout + +Since version 6.0, Lucee supports the tag ``. This tag allows you to define a timeout specific to a code block. + +## Basic Usage + +This example shows how to define a timeout for a code block: + +```lucee + + + +``` + +You define how long the code within the tag is allowed to run and a listener (closure) that is called in case the timeout is reached. In this case, the listener `onTimeout` simply dumps the timespan. + +## Error Handling + +You can also add an additional listener `onError` that is called in case an exception occurs within the timeout block. If you want to escalate the exception, you simply rethrow the exception like in the following example: + +```lucee + + + +``` \ No newline at end of file diff --git a/docs/recipes/types_lucee.md b/docs/recipes/types_lucee.md new file mode 100644 index 000000000..7809cd9ce --- /dev/null +++ b/docs/recipes/types_lucee.md @@ -0,0 +1,109 @@ + +# Types in Lucee + +This document explains types in Lucee. Lucee is still an untyped language. Types are only a check put on top of the language. The language is not based on types, however there are different places where types come into Lucee. This is explained with some simple examples below: + +### Example 1 : Function Argument and Return Value + +For functions, the return value is returned with the specific type that was defined in that function. + +```luceescript + +// function1.cfm + + param application.names={}; + boolean function recExists(string name, number age) { + var exists = application.names.keyExists(name); + application.names[name]=age; + dump(age); + return exists; + } + dump(recExists("Susi","15")); + void function test(array arr) { + arr[2]="two"; + } + arr={'1':'one'}; + test(arr); + dump(arr); +``` + +* This example function has two arguments: name, age (One is a string, the other is a number). When this example is executed, the recExists() function checks if a certain record exists or not and It returns the boolean value `true`. +* When dumping the function recExists() with arguments, if we give age as a string format in the argument, `dump(recExists("Susi","15"))`, it shows `string 15` even though we defined it as a number data type in the arguments. +* The test() function takes an array, but in this example I do not pass an array into the function. I have passed a struct `arr={'1':'one'}` value into the test() function. The test() function contains an array value `arr[2]= "two"`, so Lucee converts this array value into a structure. So the struct has two values as per keys are 1, 2 and values are one, two. +* Lucee can handle an array as long as the keys are all numbers, meaning it considers a struct `'1' and [2]`. Execute this cfm page, the dump shows the structure format. + +### Example 2 : CFParam + +```luceescript + +// param.cfm + +param name="url.age" type="numeric" default=10; +param name="url.name" type="string" default="Susi"; +param name="url.mails" type="array" default=["Susi@lucee.org"]; +dump(url); + +// convert string to date + d=dateAdd("d",0,"12/1/2018"); + dump(d); +// convert date to number + n=d+1; + dump(n); +// convert number to date + d=dateAdd("d",0,n); + dump(d); + sb=createObject("java","java.lang.StringBuilder").init("Susi Sorglos"); + dump(sb); + dump(sb.substring("5")); + dump(sb.substring(JavaCast("int","5"))); +//sb=createObject("java","java.lang.StringBuilder").init("1"); + sb=createObject("java","java.lang.StringBuilder").init(javaCast("int","1")); + dump(sb); +``` + +* Internally every object has a type and Lucee automatically takes care of converting from one type to another if necessary. For example when you define a function with a string, but then pass a number into that function, Lucee automatically converts the number to a string. +* The above example is useful for converting "string to date", "date to number", "number to date" formats. +* We have loaded a Java library and string builder. We pass a string into a constructor and execute this. We see that the string builder contains that value. We refer to this `string builder` method in the Java Doc. The method is called `substring`. This substring takes an int as its argument. For example, we pass a string value instead of an int value `sb.substring("5")`. Lucee returns a substring properly. +* Two constructors are available for string builder. There are `StringBuilder(int), StringBuilder(string)`. + +```luceescript + +//index.cfm + +directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; +loop query=dir { + echo('#dir.name#
'); +} +``` + +```luceescript + +// test.cfc + +component { + function getName() { + return "Susi"; + } +} +``` + +These types are on top of the language. Lucee contains some other different types too. Therefore, it is always good to do type checking in your code. + +## Footnotes + +Here you can see above details in video + +[Types of Lucee](https://youtu.be/02kMrN4PByc) \ No newline at end of file diff --git a/docs/recipes/virtual-file-system.md b/docs/recipes/virtual-file-system.md new file mode 100644 index 000000000..9f65ec155 --- /dev/null +++ b/docs/recipes/virtual-file-system.md @@ -0,0 +1,171 @@ + +## Virtual File Systems + +Lucee supports the following virtual file systems: + +- ram +- file +- s3 +- http / https +- zip +- tar + +### Local File System + +You may already be familiar with local file systems. The local file system is the default file system in Lucee. That means if there is no other definition, Lucee will always use the local file system. + +A simple example: + +```lucee + + sct.file = getCurrentTemplatePath(); + sct.directory = getDirectoryFromPath(sct.file); + dump(sct); + + dump(fileRead(sct.file)); + dump(directoryList(sct.directory)); + +``` + +* `getCurrentTemplatePath` returns the current template path. +* `getDirectoryFromPath` returns the directory. +* Pass the file path to `fileRead` to read the content of the file. +* Pass the directory to `directoryList` to list all the directories in the given folder path. + +However, you can explicitly define the file system you want to use. To use a local file system, use the `file://` prefix. + +As seen in the example code above, the local file system is the default, so it is not necessary to explicitly define it. The example below shows how you can explicitly define a local file system. The result is the same as the code above. + +```lucee + + sct.file = getCurrentTemplatePath(); + sct.directory = getDirectoryFromPath(sct.file); + dump(sct); + + sct.file = "file://" & sct.file; + sct.directory = "file://" & sct.directory; + + dump(fileRead(sct.file)); + dump(directoryList(sct.directory)); + +``` + +### ZIP File System + +Another file system you can use in Lucee is the ZIP file system. To tell Lucee to use the ZIP file system, use the prefix `zip://` and end with `!`. + +Now the file path will look like `zip://xxx/xxx/xxx/xxx/test.zip!/...`. + +```lucee + + sct.zip = "zip://" & expandPath("zip/test.zip") & "!"; + dump(directoryList(sct.zip)); + dump(fileRead(sct.zip & "/hello.txt")); + +``` + +### RAM File System + +RAM is a virtual file system that allows you to treat memory as a file system. + +This is useful for storing temporary files, and it is very fast since it uses the system's RAM. This data will be lost when the server restarts. + +The RAM file system is configured with the `ram://` prefix. + +```lucee + + sct.ram = "ram://"; + dump(sct); + dump(directoryCreate(sct.ram & "/heidi/")); + fileWrite(sct.ram & "susi.txt", "sorglos"); + dump(directoryList(sct.ram)); + +``` + +### S3 File System + +S3 is a remote file system you can use from Amazon S3 storage. You will need access credentials for accessing the S3 bucket. + +Set up the S3 file system using the prefix `s3://`. + +In Lucee, we can define an S3 file system in two ways: + +* Define credentials in the Application.cfc file. +* Pass the credentials with the S3 URL. + +```lucee + + // In Application.cfc + this.s3.accesskeyid = "xxxxxxx" + this.s3.awssecretkey = "xxxxxxx" + + sct.s3 = "s3://" & request.s3.accesskeyid & ":" & request.s3.awssecretkey & "/"; // simply as file path + sct.s3 = "s3:///"; // If you define in cfc use like this + + dir = directoryList(sct.s3); + dump(dir); + + dir = directoryList(sct.s3 & "testcasesS3/"); + dump(dir); + + dir = directoryList(sct.s3 & "testcasesS3/a"); + dump(dir); + + c = fileRead(sct.s3 & "testcasesS3/a/foo.txt"); + dump(c); + +``` + +### FTP File System + +Lucee allows you to treat a remote FTP server as a virtual filesystem. + +You will need access credentials for accessing FTP. Set up an FTP file system using the prefix `ftp://`. + +```lucee + + /* How to configure default FTP settings via Application.cfc */ + this.ftp.username = "..."; + this.ftp.password = "..."; + this.ftp.host = "ftp.lucee.org"; + this.ftp.port = 21; + + /* index.cfm */ + ftp = "ftp://" & request.ftp.user & ":" & request.ftp.pass & "@ftp53.world4you.com:21/"; + // ftp = "ftp:///"; // take credentials and host/port from Application.cfc + // ftp = "ftp://ftp53.world4you.com:21/"; // take credentials from Application.cfc + // ftp = "ftp://" & request.ftp.user & ":" & request.ftp.pass & "@/"; // take host/port from Application.cfc + + dir = directoryList(ftp); + dump(dir); + +``` + +### Footnotes + +Here you can see the above details in a video: + +[Lucee virtual File System](https://www.youtube.com/watch?time_continue=693&v=AzUNVYrbWiQ) \ No newline at end of file diff --git a/docs/recipes/xml_fast-easy.md b/docs/recipes/xml_fast-easy.md new file mode 100644 index 000000000..7bb79029d --- /dev/null +++ b/docs/recipes/xml_fast-easy.md @@ -0,0 +1,429 @@ + +This document explains how to use XML parsing in Lucee. + +I have XML as shown below: + +```luceescript +//catalog.xml + + + +Empire Burlesque +Bob Dylan +USA +Columbia +10.90 +1985 + + +Hide your heart +Bonnie Tyler +UK +CBS Records +9.90 +1988 + + +Greatest Hits +Dolly Parton +USA +RCA +9.90 +1982 + + +Still got the blues +Gary Moore +UK +Virgin records +10.20 +1990 + + +Eros +Eros Ramazzotti +EU +BMG +9.90 +1997 + + +One night only +Bee Gees +UK +Polydor +10.90 +1998 + + +Sylvias Mother +Dr.Hook +UK +CBS +8.10 +1973 + + +Maggie May +Rod Stewart +UK +Pickwick +8.50 +1990 + + +Romanza +Andrea Bocelli +EU +Polydor +10.80 +1996 + + +When a man loves a woman +Percy Sledge +USA +Atlantic +8.70 +1987 + + +Black angel +Savage Rose +EU +Mega +10.90 +1995 + + +1999 Grammy Nominees +Many +USA +Grammy +10.20 +1999 + + +For the good times +Kenny Rogers +UK +Mucik Master +8.70 +1995 + + +Big Willie style +Will Smith +USA +Columbia +9.90 +1997 + + +Tupelo Honey +Van Morrison +UK +Polydor +8.20 +1971 + + +Soulsville +Jorn Hoel +Norway +WEA +7.90 +1996 + + +The very best of +Cat Stevens +UK +Island +8.90 +1990 + + +Stop +Sam Brown +UK +A&M +8.90 +1988 + + +Bridge of Spies +T'Pau +UK +Siren +7.90 +1987 + + +Private Dancer +Tina Turner +UK +Capitol +8.90 +1983 + + +Midt om natten +Kim Larsen +EU +Medley +7.80 +1983 + + +Pavarotti Gala Concert +Luciano Pavarotti +UK +DECCA +9.90 +1991 + + +The dock of the bay +Otis Redding +USA +Stax Records +7.90 +1968 + + +Picture book +Simply Red +EU +Elektra +7.20 +1985 + + +Red +The Communards +UK +London +7.80 +1987 + + +Unchain my heart +Joe Cocker +USA +EMI +8.20 +1987 + + +Forever Man +Eric Clapton +UK +Duck +7.90 +1989 + + +Brother in Arms +Dire Straits +UK +Vertigo +7.90 +1985 + + +20 Greatest Hits +Frank Sinatra +UK +Reprise Records +9.90 +1987 + + +Surface Tension +Roger Hodgson +UK +A&M +8.70 +1984 + + +Get a Grip +Aerosmith +USA +Geffen +8.90 +1993 + + +Morning Glory +Oasis +UK +Chrysalis +8.90 +1995 + + +Falling into you +Celine Dion +USA +Epic +8.90 +1996 + + +Escapology +Robbie Williams +UK +Chrysalis +8.90 +2002 + + +One night only +Bee Gees +UK +Polydor +10.90 +1998 + + +``` + +To read this XML, we need to define a component that implements certain listener functions to handle events from the XML parser. + +```luceescript +component { +variables.cds=[]; +variables.cd={}; +variables.insideCD=false; +variables.currentName=""; +variables.filter={}; +variables.removeCD=false; + +/** +* Constructor of the component that takes the path to the XML file and a simple custom-made filter. +* @param xmlFile XML File to parse. +* @param filter Filter to limit content to certain records. +*/ +public any function init(required string xmlFile, struct filter={}) { +variables.filter=filter; +xmlEventParser=createObject("java","lucee.runtime.helpers.XMLEventParser"); +xmlEventParser.init( +getPageContext(), +this.startDocument, +this.startElement, +this.body, +this.endElement, +this.endDocument, +this.error +); +xmlEventParser.start(xmlFile); +return this; +} + +public array function execute(struct filter=variables.filter) { +variables.cds=[]; +variables.filter=filter; +xmlEventParser.start(); +return variables.cds; +} + +/** +* This function will be called at the start of parsing an XML Element (Tag). +*/ +public void function startElement(string uri, string localName, string qName, struct attributes) { +if(qName == "cd") { +variables.cd={}; +variables.insideCD=true; +variables.removeCD=false; +} +else if(variables.insideCD) { +variables.currentName=qName; +} +} + +/** +* Call with body of content. +*/ +public void function body(string content) { +if(len(variables.currentName)) { +variables.cd[variables.currentName]=content; +if(structKeyExists(variables.filter,variables.currentName) && content != variables.filter[variables.currentName]) { +variables.removeCD=true; +} +} +} + +/** +* This function will be called at the end of parsing an XML Element (Tag). +*/ +public void function endElement(string uri, string localName, string qName, struct attributes) { +if(qName == "cd") { +if(!variables.removeCD) { +arrayAppend(variables.cds,variables.cd); +} +variables.insideCD=false; +} +variables.currentName=""; +} + +/** +* This function will be called at the start of parsing a document. +*/ +public void function startDocument() {} + +/** +* This function will be called at the end of parsing a document. +*/ +public void function endDocument() {} + +/** +* This function will be called when an error occurs. +*/ +public void function error(struct cfcatch) { +echo(cfcatch); +} +} +``` + +You can pass the filter struct when you execute the function. + +Example: + +```luceescript + +file=GetDirectoryFromPath(GetCurrentTemplatePath())&'catalog.xml'; +catalog=new XMLCatalog2(file); + +dump(catalog.execute({year:"1995"})); + +``` + +The example above executes and returns a result array which contains only the year equal to 1995. + +You can modify the component as you like. Instead of storing the array, you can store the result in a database or mail, or whatever you like. + +### Footnotes ### + +You can see the details in this video: +[Xml-Fast and Easy](https://www.youtube.com/watch?v=_AP6GpVk7TE) \ No newline at end of file