Cold Brews
Getting Started with Java
in Your ColdFusion Apps
Matthew Clemente
Adobe ColdFusion Summit 2021
Follow Along
What it's not...
It's not...
...how to write Java
It's not...
Java Upgrades
It's not...
Java Versions / Distros
It's not...
...JVM Configuration / Tuning
...Memory / Heap Analysis
What we'll cover...
Using Java in your ColdFusion Apps
When? How? 🤔
And when you don't need Java!
With lots of code examples!
Relationship of Java and CMFL
ColdFusion / Java
Relationship Status
Java can be intimidating
One step
at a time
Lets Get Started!
When...
Do you need Java?
Take advantage of a Java Library
Performance Optimization
Extended Functionality
Version Limitations
ColdFusion
ColdFusion
Java
How?
Creating Java Objects
system = createObject("java", "java.lang.System");
writeDump( system.getenv() );
Creating Java Objects
// Introduced in ColdFusion 2018
system = new java( "java.lang.System" );
writeDump( system.getenv() );
Standard Library
What Version of Java is ColdFusion Using?
🤔
java.lang.System
system = createObject( "java", "java.lang.System" );
writeDump( system.getProperty("java.version") );
Give it a try!
Pause ⏸
// Introduced in ColdFusion 2018
writeDump( server.system.properties.java.version );
Do you need Java here?
How do I access Environment variables?
🤔
java.lang.System
var system = createObject('java', 'java.lang.System');
for ( var key in secrets ) {
var envValue = system.getenv( secrets[key] );
if ( !isNull(envValue) && envValue.len() ) {
variables[ key ] = envValue;
continue;
}
var propValue = system.getProperty( secrets[key] );
if ( !isNull(propValue) && propValue.len() ) {
variables[ key ] = propValue;
}
}
Pause ⏸
// Introduced in ColdFusion 2018
writeDump( server.system.environment );
Do you need Java here?
How do I write to stdout?
🤔
java.lang.System
stdout = createObject( "java", "java.lang.System" ).out;
stderr = createObject( "java", "java.lang.System" ).err;
stdout.println( "ahhhhh... information." );
stderr.println( "AHHHHH! ERRORS!" );
java.lang.System.out
function writeToConsole(required message) {
var args = arguments.copy();
args["timestamp"] = now().dateTimeFormat("iso");
stdout = createObject( "java", "java.lang.System" ).out;
stdout.println(serializeJSON(args));
}
writeToConsole( 'testing' );
How can I write a newline in a string in ColdFusion?
🤔
java.lang.System
system = createObject( "java", "java.lang.System" );
nl = system.getProperty("line.separator");
How can I tell if it's a leap year?
🤔
java.util.GregorianCalendar
<CFOBJECT ACTION="CREATE" TYPE="Java"
CLASS="java.util.GregorianCalendar" NAME="myCalendar">
<CFSET is2001LeapYear = myCalendar.isLeapYear(2001)>
<CFSET theYear = myCalendar.get(myCalendar.YEAR)>
<CFSET theMonth = myCalendar.get(myCalendar.MONTH) + 1>
<CFSET theDay = myCalendar.get(myCalendar.DATE)>
<cfoutput>
<html>
<body>Is 2001 a leap-year? <b>#is2001LeapYear#</b>
<p>Today is the <b>#theMonth# / #theDay# / #theYear#</b>
</body>
</html>
</cfoutput>
That code was 20 years old!
🤯
Pause ⏸
// Introduced in some time after 2001
writeDump( isLeapYear(2001) );
You don't need Java here!
Can I preserve the order of keys in a struct?
🤔
java.util.LinkedHashMap
serial = createObject("java","java.util.LinkedHashMap").init();
serial.a = 1;
serial.b = 2;
serial.c = 3;
writeDump( serial );
Pause ⏸
// Introduced in ColdFusion 2016
serial = structNew( "ordered" );
serial.a = 1;
serial.b = 2;
serial.c = 3;
writeDump( serial );
Do you need Java here?
How do I create a standardized UUID?
🤔
java.util.UUID
uuid = createObject( "java", "java.util.UUID" );
writeDump( uuid.randomUUID().toString() );
How do I return the local hostname of the server?
🤔
java.net.InetAddress
function getHostname(){
return createObject( 'java', 'java.net.InetAddress' )
.getLocalHost()
.getHostName();
}
writeDump( getHostname() );
How do I work with Unix (Epoch) timestamps?
🤔
java.time.Instant
timestamp = 1637249609000;
instant = createObject("java","java.time.Instant").ofEpochMilli(timestamp);
date_time = createObject("java","java.util.Date").from( instant );
result = dateTimeFormat( date_time, 'full' );
writeDump(result);
So can I generate a Unix (Epoch) timestamp?
🤔
java.time.Instant
date_time = "2007-12-03T10:15:30.00Z";
instant = createObject("java","java.time.Instant").parse(date_time);
writeDump(instant.toEpochMilli());
instant = createObject("java", "java.time.Instant");
writeDump( instant.now().getEpochSecond() );
Can I gzip text strings?
🤔
java.util.zip.GZIPOutputStream
function gzip(required string str) {
if (!str.length()) {
return str;
}
var obj = createObject('java', 'java.io.ByteArrayOutputStream').init();
var gzip = createObject('java', 'java.util.zip.GZIPOutputStream').init(obj);
gzip.write(str.getBytes('UTF-8'));
gzip.close();
return binaryEncode(obj.toByteArray(), 'base64');
}
str = '“It is not the critic who counts; not the man who points out how the strong man stumbles, or where the doer of deeds could have done them better. The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood; who strives valiantly; who errs, who comes short again and again, because there is no effort without error and shortcoming; but who does actually strive to do the deeds; who knows great enthusiasms, the great devotions; who spends himself in a worthy cause; who at the best knows in the end the triumph of high achievement, and who at the worst, if he fails, at least fails while daring greatly, so that his place shall never be with those cold and timid souls who neither know victory nor defeat.”';
compressed = gzip(str);
writeDump(compressed);
size = [
"Original": arrayLen(str.getBytes()),
"Gzipped": arrayLen(compressed.getBytes())
];
writeDump(size);
Can I un-gzip compressed text strings?
🤔
java.util.zip.GZIPInputStream
function ungzip(required string str) {
var buffReader = createObject('java', 'java.io.BufferedReader').init(
createObject('java', 'java.io.InputStreamReader').init(
createObject('java', 'java.util.zip.GZIPInputStream').init(
createObject('java', 'java.io.ByteArrayInputStream').init(
toBinary(arguments.str))
)
)
);
var result = buffReader.readLine();
return result;
}
str = 'H4sIAAAAAAAAAFVS25HcMAxrhQV4roGtIP9pQLboFSd67Ii0Pf67QpLmrpKA0k7m8mWZhEAA1Nfn7x9GolSbkSWmrYvJRldqtLWjmj7+tUqoo/5qgjq1wyi1a7TUeqvPgVA7yppZF2odcO48ELFxp7ZTZI7q1DlSCqc36kAUWtmM+wf9HDI4iqGUwatk7T8F0Bs2O0LON0kdrdC5hsW7yrSHjR1UQgcPrTfFQ41CjaQXh3lac2vxMfggX05WOkOWUC3fs8y96/KOoqCtqXXcfQbMdIZxWiByC4cOE51nlsT77thLLHlOYEIaY75zgE7q80ErWk6PcL45mmrcc5y2R2ZT0q/aLqVndxNcLR0qQQtUOm6WI5/NpNX3DX1xReJJinLePa5AFzSkm4bsiQpzxSsjpjnjnSsujy80HeWVfIVJnglqk/DJBSKWYewbC+gVVQHUdyEZ+tDKHMA+/oGWDGOhI4epO98LqfsFMiHEV/YlakIkVDGpQ9zIExDf8dbwgnywSRHkihelQ0Rl8U0MG3TKZq3fWElHMDvmfHx9/vkLKBERbfYCAAA=';
writeDump(ungzip(str));
How can I easily parse URLS?
🤔
regex????
😧
java.net.URI
uri = 'https://blog.mattclemente.com/etc';
parsed_uri = createObject( "java","java.net.URI" ).init( uri );
uri_components = {
"Scheme": parsed_uri.getScheme(),
"Scheme Specific Part": parsed_uri.getSchemeSpecificPart(),
"Authority": parsed_uri.getAuthority(),
"User Info": parsed_uri.getUserInfo(),
"Host": parsed_uri.getHost(),
"Port": parsed_uri.getPort(),
"Path": parsed_uri.getPath(),
"Query": parsed_uri.getQuery(),
"Fragment": parsed_uri.getFragment()
}
writeDump( uri_components );
java.net.URI ⚔ java.net.URL
🧐
Can I generate URL Safe Base64 Encoding?
🤔
java.util.Base64
function urlSafeBase64Encode(str) {
return createObject("java", "java.util.Base64")
.getUrlEncoder()
.withoutPadding()
.encodeToString(str.getBytes("UTF-8"));
}
str = "subjects?_d=1"
base64 = toBase64(str);
writeDump({ "Standard Base64": base64});
urlSafeBase64 = urlSafeBase64Encode(str);
writeDump({ "URL Safe Base64": urlSafeBase64 });
java.util.Base64
function urlSafeBase64Encode(str) {
return createObject("java", "java.util.Base64")
.getUrlEncoder()
.withoutPadding()
.encodeToString(str.getBytes("UTF-8"));
}
function urlSafeBase64Decode(str) {
var bytes = createObject("java", "java.util.Base64")
.getUrlDecoder()
.decode(str);
return createObject("java", "java.lang.String").init(bytes);
}
urlSafeBase64 = urlSafeBase64Encode("subjects?_d=1");
writeDump({ "URL Safe Base64": urlSafeBase64 });
writeDump({ "Decoded": urlSafeBase64Decode(urlSafeBase64)});
How can I randomly shuffle elements in a list?
🤔
It is best to shuffle (randomize) the list of fallback hosts at init time in order to ensure load balancing across clients.
books = [
"Pilgrim at Tinker Creek",
"Long Day's Journey into Night",
"We are in a Book!",
"A Confederacy of Dunces",
"Pride and Prejudice"
];
createObject('java','java.util.Collections').Shuffle(books);
writeDump(books);
java.util.Collections.Shuffle()
What if I need to cycle through the list?
🤔
books = [
"Pilgrim at Tinker Creek",
"Long Day's Journey into Night",
"We are in a Book!",
"A Confederacy of Dunces",
"Pride and Prejudice"
];
writeDump(books);
collection = createObject('java','java.util.Collections');
collection.Rotate(books,-1);
writeDump(books);
collection.Rotate(books,-1);
writeDump(books);
java.util.Collections.Rotate()
What else can I do with these "Collections"?
🤔
books = [
"Pilgrim at Tinker Creek",
"Long Day's Journey into Night",
"We are in a Book!",
"A Confederacy of Dunces",
"Pride and Prejudice"
];
writeDump(books);
createObject('java','java.util.Collections').Reverse(books);
writeDump(books);
min = createObject('java','java.util.Collections').min(books);
writeDump({"min": min});
max = createObject('java','java.util.Collections').max(books);
writeDump({"max": max});
createObject('java','java.util.Collections').swap(books,0,1);
writeDump(books);
java.util.Collections
Can I perform DNS lookups?
🤔
com.sun.jndi.dns.DnsContextFactory
function dnsLookup(domain, type, dnsServer = "8.8.8.8", timeout = 2000, retries = 1){
var dnsRecords = [];
var env = CreateObject('java','java.util.Hashtable');
env.put('java.naming.factory.initial','com.sun.jndi.dns.DnsContextFactory');
env.put('java.naming.provider.url', 'dns://#dnsServer#');
env.put('com.sun.jndi.dns.timeout.initial', javaCast('string', timeout));
env.put('com.sun.jndi.dns.timeout.retries', javaCast( 'string', retries));
var dirContext = CreateObject('java', 'javax.naming.directory.InitialDirContext');
dirContext.init( env );
try {
var records = dirContext.getAttributes( domain, [ type ] ).get( type ).getAll();
while( records.hasMore() ) {
var record = records.next().ToString();
dnsRecords.append( record );
}
} catch ( any e ){} //if there are no records, the lookup fails
return dnsRecords;
}
mxRecords = dnsLookup( "mattclemente.com", "MX" );
writeDump( mxRecords );
How can I do faster string concatenation?
🤔
start = getTickCount();
sys = createObject( "java", "java.lang.System" );
nl = sys.getProperty("line.separator");
csvstr = "Email,Rand" & nl;
cfloop( from=1, to=50000, index="i" ){
csvstr &= "#i#@test.com,";
csvstr &= "#randRange(1,1000)#"
csvstr &= nl;
}
result = {"Time": getTickCount() - start}
writeDump(result);
The Problem
start = getTickCount();
sys = createObject( "java", "java.lang.System" );
nl = sys.getProperty("line.separator");
csvstr = "Email,Rand" & nl;
cfloop( from=1, to=50000, index="i" ){
csvstr &= "#i#@test.com,#randRange(1,1000)##nl#";
}
result = {"Time": getTickCount() - start}
writeDump(result);
ColdFusion Solution #1
start = getTickCount();
sys = createObject( "java", "java.lang.System" );
nl = sys.getProperty("line.separator");
csvstr = createObject("java","java.lang.StringBuffer");
csvstr.append("Email,Rand" & nl );
cfloop( from=1, to=50000, index="i" ){
csvstr.append("#i#@test.com," & randRange(1,1000) & nl);
}
result = {"Time": getTickCount() - start}
writeDump(result);
java.lang.StringBuffer
Pause ⏸
Do you need Java here?
start = getTickCount();
arr = ["Email,Rand"];
cfloop( from=1, to=50000, index="i" ){
arr.append("#i#@test.com,#randRange(1,1000)#");
}
sys = createObject( "java", "java.lang.System" );
nl = sys.getProperty("line.separator");
csvstr = arr.toList( nl );
result = {"Time": getTickCount() - start}
writeDump(result);
ColdFusion Solution #2
start = getTickCount();
sys = createObject( "java", "java.lang.System" );
nl = sys.getProperty("line.separator");
savecontent variable="csvstr" {
writeOutput("Email,Rand");
cfloop( from=1, to=50000, index="i" ){
writeOutput("#i#@test.com" & "," & randRange(1,1000) & nl);
}
};
result = {"Time": getTickCount() - start}
writeDump(result);
ColdFusion Solution #3
More to learn!
Community Libraries
How to Load Jars
What's a .jar?
Java Archive
It's basically a zip.
this.javaSettings
// defaults
this.javaSettings = {
// paths to directories containing Java jars / classes
loadPaths: [], // only required setting
loadColdFusionClassPath: false,
reloadOnChange: false,
watchInterval: 60, //seconds
watchExtensions: "jar,class"
}
this.javaSettings
// Application.cfc
this.javaSettings = {
loadPaths: [
".\java\myjar.jar",
".\java_lib\"
]
}
// Application.cfc
this.javaSettings = {
loadPaths: directoryList(
path = expandPath( "/lib" ),
recurse = true,
type = 'file' )
};
JSOUP
HTML parser, built for HTML editing, cleaning, scraping, and XSS safety
Application Structure
Jsoup = createObject("java", "org.jsoup.Jsoup");
uri = "https://blog.mattclemente.com";
doc = Jsoup.connect( uri ).get();
title = doc.title();
writeDump(title);
links = doc.select("a[href]");
// writeDump( var='#links#', abort='true' );
for( link in links ){
parsed = {
"Text": link.wholeText(),
"href": link.attr("abs:href")
}
writeDump( parsed );
}
Parsing HTML
Parsing HTML - Output
Antisamy
Fast, configurable cleansing of HTML coming from untrusted sources.
Application Structure
Sanitizing HTML
sanitizer = createObject("java","org.owasp.validator.html.AntiSamy");
policyFile = expandPath( 'config/default.xml' );
str = "<p> This is a test for
<h1 onclick= “alert(‘malicious code’);” > AntiSamy </h1>";
clean_results = sanitizer.scan( str, policyFile );
result = [
"Input": str,
"Cleaned": clean_results.getCleanHTML(),
"Errors": clean_results.getErrorMessages()
]
writeDump(result);
Sanitizing HTML - Result
// Input
<p> This is a test for <h1 onclick= “alert(‘malicious code’);” > AntiSamy </h1>
// Cleaned
<p> This is a test for </p> <h1> AntiSamy </h1>
// Error message
The h1 tag contained an attribute that we could not process. The onclick attribute has been filtered out, but the tag is still in place. The value of the attribute was "alert(malicious".
Pause ⏸
// Introduced in ColdFusion 11
unsafeHtml = "<p> This is a test for <h1 onclick= “alert(‘malicious code’);” > AntiSamy </h1>";
writeDump( isSafeHTML(unsafeHtml) );
writeDump( getSafeHTML(unsafeHtml) );
Do you need Java here?
eo-yaml
Read and generate YAML
# Project Info. This comment refers to the whole document.
---
firstName: John
lastName: Doe
age: 20
grades:
CS: 10
Math: 9
other:
- 1
- 2
- 3
Reading Yaml Files
eoyaml = createObject("java", "com.amihaiemil.eoyaml.Yaml");
yaml_file = createObject("java", "java.io.File").init("example.yaml");
yml_parsed = eoyaml.createYamlInput(yaml_file).readYamlMapping();
iterator = yml_parsed.keys().iterator();
while( iterator.hasNext() ) {
key = iterator.next();
writedump(key.value());
writedump(yml_parsed.value(key.value()));
}
Reading Yaml Files
writeDump( var='#yml_parsed.comment().value()#', abort='true' );
Reading Yaml Files - Result
yaml = eoyaml.createYamlMappingBuilder()
.add("name", "Elizabeth")
.add(
"siblings",
eoyaml.createYamlSequenceBuilder()
.add("Jane")
.add("Mary")
.add("Kitty")
.add("Lydia")
.build()
).add(
"suitors",
eoyaml.createYamlSequenceBuilder()
.add("Darcy")
.add("Wickham")
.add("Collins")
.build()
).build();
Writing Yaml Files
# Output of previous slide
name: Elizabeth
siblings:
- Jane
- Mary
- Kitty
- Lydia
suitors:
- Darcy
- Wickham
- Collins
Writing Yaml Files
AWS Java SDK
Interact with all things AWS
Resources
Mustache
Utilize the Mustache templating language
}
flexmark-java
Markdown parser and more.
Security Stuff
Argon2id, BCrypt, PBKDF2, and more
More to explore!
- BoltHTTP (Apache HttpComponents)
- Playwright Java Library with ColdFusion (Gist)
- LaunchDarkly CFML SDK
- pdfbox.cfc
Cold Brews
Getting Started with Java
in Your ColdFusion Apps
Matthew Clemente
Adobe ColdFusion Summit 2021
Resources
Adobe Documentation
Additional
Cold Brews: Getting Started with Java in Your ColdFusion Apps
By mjclemente
Cold Brews: Getting Started with Java in Your ColdFusion Apps
Presentation for Adobe ColdFusion Summit 2021
- 2,028