I’ve been experimenting with the new Java integration in Lucee 6.2.1 and appreciate the direction it’s heading. However, I’ve encountered several issues that have made it challenging to continue development using this feature:
Error Messages Lack CFML Line Numbers
When exceptions occur within type="java" functions, the error messages don’t indicate the specific line number in the .cfm file where the error happened. This omission makes debugging difficult, as I often have to guess the source of the problem.
Use of var Keyword Causes Errors
Attempting to use var for local variable declarations within type="java" functions results in the following error: Local variable type inference NYI; Failed in C:\lucee\tomcat\webapps\ROOT\java_test.cfm:3
(java 11 support var Local Variable Type Inference)
Type Casting Parameters Fails
Passing parameters of certain types to Java functions leads to casting errors. This issue was also discussed in this thread.
Calling Other type="java" Functions
It’s unclear how to invoke other type="java" functions or standard Lucee functions from within a type="java" function.
Declaring Custom Java Classes
Unlike Adobe ColdFusion’s <cfjava> tag, Lucee doesn’t seem to offer a straightforward way to declare and use custom Java classes directly. For reference, here’s the Adobe documentation on <cfjava>.
Thank you again for the ongoing work on Lucee. I’m looking forward to seeing how the Java integration continues to evolve, and I’d really appreciate any advice or clarification on the points above.
When using type="java" if you aren’t running a jdk, Lucee falls back on the jamino compiler, which has some limitations. (docs updated to reflect that)
With a JDK, the error I’m getting back does have the correct context (yes it’s 7, but the 6 throws the same exception)
I’d like to highlight an issue I’ve encountered with type="java" functions: when runtime errors occur, the error messages often lack specific line numbers in the .cfm file, making debugging challenging.
For example:
<cfscript>
java.math.BigDecimal function getBigNumberAdd(string A, string B) type="java" {
java.math.BigDecimal a = new java.math.BigDecimal(A);
java.math.BigDecimal b = new java.math.BigDecimal(B);
return a.add(b);
}
echo(getBigNumberAdd("", ""));
</cfscript>
<cfscript>
all_travelers=queryExecute("select * from travelers",{},{datasource="lucee"});
japen=queryExecute("select * from all_travelers where county='japen'",{},{dbtype="query"});
korea=queryExecute("select * from all_travelers where county='korea'",{},{dbtype="query"});
echo(getBigNumberAdd(japen.count,korea.count));
</cfscript>
java_test_cfm$cf$getBigNumberAddb: line 25 but code less then 25 line
If A or B is empty strings, the BigDecimal constructor throws a NumberFormatException. However, the error message doesn’t indicate the exact line in the .cfm file where the error occurred… Even if it doesn’t indicate the exact line, having a reference point a few lines before or after the problematic code would greatly aid in debugging.
Is there a way to enhance error reporting in such scenarios, perhaps by ensuring a JDK is present or through other configurations? Improved error messages with precise line numbers would greatly aid in debugging.
Thank you for your continued efforts in improving Lucee.
TBH the experience with type=“java” will never be quite as nice as with native cfml as we have more control over the bytecode for lucee and java is way stricter than cfml
<cfscript>
echo(server.lucee.version &"<br>");
echo("java: " & server.java.version &"<br>");
dump(server.java);
b = new component {
import java.math.BigDecimal;
function getBigNumberAdd(string A, string B) {
var a = new BigDecimal(A);
var b = new BigDecimal(B);
return a.add(b);
}
};
echo(b.getBigNumberAdd(2, 3));
flush;
echo(b.getBigNumberAdd("", ""));
</cfscript>
I understand that the experience with type="java" functions may not be as seamless as with native CFML due to inherent differences. However, I wanted to highlight that when using Java directly, error messages typically include precise line numbers when runtime errors, which greatly aids in debugging, especially in larger codebases.
java
BigDecimalCalculator.java
import java.math.BigDecimal;
public class BigDecimalCalculator {
public static BigDecimal add(String A, String B) {
BigDecimal a = new BigDecimal(A);
BigDecimal b = new BigDecimal(B);
return a.add(b);
}
}
javac BigDecimalCalculator.java -encoding utf8
jar cf BigDecimalCalculator.jar BigDecimalCalculator.class
<cfscript>
echo(server.lucee.version &"<br>");
echo("java: " & server.java.version &"<br>");
dump(server.java);
dump(createObject("java","System").getenv("JAVA_HOME"));
object function getBigNumberAdd(string A, string B) type="java"{
java.math.BigDecimal a = new java.math.BigDecimal(A);
java.math.BigDecimal b = new java.math.BigDecimal(B);
return a.add(b);
}
echo(getBigNumberAdd("2",""));
</cfscript>
I get a slightly different error with the error template, rather than a dump?
btw, newer versions of java like 21 produce more informative stack traces (like NPEs include more information) (tho currently, lucee is using the janino compiler over the jdk one, but this is a runtime exception, so it’s java 21)
Thank you for sharing the decompiled Java class—it was very insightful.
It appears that all runtime errors point to line 26, which corresponds to the try block. This makes it challenging to identify the exact location of errors within the original CFML code, especially in larger codebases.
It would be immensely helpful if Lucee could enhance runtime error reporting to include the corresponding Java code and its line numbers. This addition would greatly aid in debugging complex applications.
Additionally, I have a question: How can I invoke a standard CFML function or another type="java" function from within a type="java" function? I’m looking for guidance or examples on how to achieve this.
Thank you again for your assistance and for the continuous improvements to Lucee.
@micstriit does have it on his long TODO list to dive and address your questions about working with type=“java”.
I’m not sure how good your java skills are, you could always try diving in and improving the exception handling yourself. I’d start by setting some breakpoints in the error handling code you see in the stacktraces.
If you checkout the 7 branch into vscode with java extensions installed, it’s quite easy to step debug lucee, you just run /bin/tomcat/catalina.bat jpda run (or .sh)
I usually edit the catalina bat to make suspend default to y and the bundled vscode debugging profile defaults to port 5000, rather than 8000
When you are working with java directly in Lucee, your code should only interact with the loader interfaces, i.e. the APIs exposed in our javadocs, they are stable, that’s what the extensions use.
The using the loader API guarantees that any underlying changes to the Lucee core won’t break your code, it’s also why Lucee, unlike Adobe Coldfusion, can easily be upgraded between major versions, without a full uninstall etc.
The loader interface has been stable for a long time, with 7 we have revved the loader interface version, adding the AI interfaces and merging the various hacks we have added overtime, back into the loader interfaces, to work around the limitations it causes.
As you can see from the bytecode, you’ll find the pageContext is the gateway to accessing other variables, functions, mappings, logging etc from the current request.