Where to find which jars Lucee is using

I’ve run into an issue using an external resource. I’m trying to use the Apache POI Library. I’ve downloaded all the jars and added them to the Lucee-server > context > lib folder. The binaries that I downloaded are the latest version of the POI library, version 5.2.3.

The POI functionality that I need is in WEB-INF/lucee-server/context/lib/poi-ooxml-5.2.3.jar. I set up the following code to load a test.docx:

classPath = expandPath("../WEB-INF/lucee-server/context/lib/poi-ooxml-5.2.3.jar")
docPath = expandPath("./asset/test.docx")

 if( ! fileExists( docPath) ) {echo("<h1>Doc doesn't exist</h1>"); abort;}

 FIS = createObject("java", "java.io.FileInputStream").init( docPath )

 try 
{  
 DOC = createObject("java", "org.apache.poi.xwpf.usermodel.XWPFDocument", "# classPath #" ).init( FIS )
} catch(any err)
{
  dump( err )
}

My catch statement is firing and the message is: org.apache.xmlbeans.XmlException

From what I’ve read, this is a ClassNotFoundException. . . Maybe??

I came across a post on stackoverflow. It was for a java application, but the dev was running into similar issues. One resolution mentioned was to add the xmlbeans-{{latest-version}}.jar to his /lib folder. I’ve already got that in my Lucee-server/context/lib folder as xmlbeans-5.1.1.jar.

I’m no java developer. I’m trying to understand how to get the classes I’ve loaded through createObject() to reference the correct jars. I started adding the path argument (3rd arg) to the createObject function thinking the initiated objects would be looking for the objects it needs, from the same directory.

I don’t know if I’m even asking the right questions here.

These are the files I added to the Lucee-server > context > lib folder:

commons-io-2.11.jar
poi-5.2.3.jar
poi-examples-5.2.3.jar
poi-excelant-5.2.3.jar
poi-javadoc-5.2.3.jar
poi-ooxml-5.2.3.jar
poi-ooxml-full-5.2.3.jar
poi-ooxml-lite-5.2.3.jar
poi-scratchpad-5.2.3.jar
xmlbeans-5.1.1.jar

I was running Lucee 6.1.0.243 but “upgraded” to 6.2.0.36-SNAPSHOT to see if it would make a difference.

I’m running on macOS Sonoma 14.5
Servlet Container Apache Tomcat/9.0.59
Java 19.0.1 (Oracle Corporation) 64bit

I’m no java dev either, but I have some experience integrating POI with CFML and one thing I’ve learned is that invoking classes isn’t always as easy as dropping the jars in your /lib folder and using CreateObject(). It can work fine with small java libraries that have few dependencies, but POI is quite complex.

The problem often comes down to a lack of “isolation” when classes are loaded, meaning you may get conflicts between different versions or their dependencies that are already on your class path.

For many years now, the best solution to this problem has been JavaLoader, which has a clever mechanism for loading classes so they don’t clash.

But more recently we’ve had OSGi, which allows jars to be put in bundles which are also loaded in such a way that they don’t clash with other versions that may be running. See this blog post for more details:

Many 3rd party java libraries are already packaged for OSGi or can be converted easily, but POI currently isn’t one of them.

It can be done though… and fortunately it has been! I needed to do it for the Spreadsheet-CFML library and as a result you should be able to use the library’s createJavaObject() method to call any of the bundled POI classes (not just the spreadsheet related ones) and be confident there won’t be any clashes.

As a bonus, you’re also getting the latest POI version, which is 5.3.0 at the time of writing.

I posted the following example of generating a Word doc on Slack in answer to your question there, but I’ll repeat it for posterity.

//Instantiate the library
spreadsheet = new spreadsheet.Spreadsheet() //adapt to where you have put the library
// Create a Word docx object
document = spreadsheet.createJavaObject( "org.apache.poi.xwpf.usermodel.XWPFDocument" )
// Add some text
para = document.createParagraph()
textRun = para.createRun()
textRun.setText( "A programmatically written Word doc!" )
// Save to a Word file
filepath = "c:/temp/word.docx" //adapt to where you want to save the file
try{
	outputStream = CreateObject( "java", "java.io.FileOutputStream" ).init( filepath )
	document.write( outputStream )
}
finally{
	outputStream.close()
	document.close()
}

word.docx (2.2 KB)

3 Likes

Thanks @Julian_Halliwell1

When I had my conversation with denny on slack and he suggested cfsimplicity my initial response was positive. But then I was thinking to myself I can figure this out.

Bang, bang, bang. That’s my head against the wall.

Thanks for posting such a clear and concise response. Twice even!

Things seem to be working well. Just working on how to find and replace text within the word documents now. I’m looking for an iterator that will, take line by line through the documents and replace something like {{client-name}} with “John Doe”.

Now I’m headed over to slack to thank you again.

1 Like

XWPFDocument objects have a paragraph iterator and within each paragraph you can loop over each “run” of text. I got this quick’n’dirty code working but it will obviously need refactoring depend on the complexity of your documents, especially if they have tables.

spreadsheet = new spreadsheet.Spreadsheet()
templatePath = ExpandPath( "template.docx" )
resultPath = ExpandPath( "result.docx" )
needle = "{{client.name}}"
replacement = "Gregor Hamsa"
function replaceTextInPara( required para, string needle, string replacement ){
	var runs = arguments.para.getRuns()
	for( var run in runs ){
		var text = run.getText( 0 )
		if( IsNull( text ) )
			continue;
		text = text.Replace( arguments.needle, arguments.replacement, "all" )
		run.setText( text, 0 )
	}
}
try{
    inputStream = CreateObject( "java", "java.io.FileInputStream" ).init( templatePath )
    doc = spreadsheet.createJavaObject( "org.apache.poi.xwpf.usermodel.XWPFDocument" ).init( inputStream )
    paras = doc.getParagraphsIterator()
    while( paras.hasNext() ){
    	replaceTextInPara( paras.next(), needle, replacement )
    }
    outputStream = CreateObject( "java", "java.io.FileOutputStream" ).init( resultPath )
    doc.write( outputStream )
}
finally{
	if( !IsNull( doc ) )
		doc.close()
	if( !IsNull( inputStream ) )
   		inputStream.close()
   	if( !IsNull( outputStream ) )
    	outputStream.close()
}

template.docx (5.8 KB)
result.docx (5.7 KB)

1 Like