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&#40;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!

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

  • 1,653