Coldfusion opencsv csvWriter - I get writeNext method was not found - coldfusion

I have the following code:
<cfset csvFilename = 'myCSV.csv'>
<cfset fileWriter = createobject("java","java.io.FileWriter").init("#csvFileName#")>
<cfset csvWriter = createObject("java","com.opencsv.CSVWriter").init(fileWriter, ",")>
<cfset csvWriter.writeNext('{"1","2"}', true)>
<cfset csvWriter.flush()>
<cfset fileWriter.close()>
<cfset csvWriter.close()>
When I run the page I get this error message:
The writeNext method was not found.
Either there are no methods with the specified method name and
argument types or the writeNext method is overloaded with argument
types that ColdFusion cannot decipher reliably. ColdFusion found 0
methods that match the provided arguments. If this is a Java object
and you verified that the method exists, use the javacast function to
reduce ambiguity.
I have searched the internet and cannot seem to find any examples of using csvWriter with Coldfusion and I am not sure why it is not working. I have a working example of csvReader and ReadNext, but not WriteNext. Any ideas of what I am doing wrong? I have tried to do a javacast but that didn't work either.
<cfset csvWriter.writeNext(javacast("string",'1,2'))>
I am using Coldfusion 11 with opencsv-3.8.jar

According to the API, that overload of writeNext() expects a String[], or an array of Strings. CF arrays are a little different than Java's, but they are compatible. Either of these would work:
<cfset csvWriter.writeNext( ["1","2"], true)>
<cfset csvWriter.writeNext( javacast("String[]", ["3","4"]), javacast("boolean", true))>
As an aside, skip the call to fileWriter.close(). When you call CSVWriter.close(), it closes the underlying writer for you. Calling both will cause an error.
<cfset csvFilename = 'myCSV.csv'>
Without a full path, I am not sure where that file will end up. Always specifying a full path can save a lot of head scratching later on ;-)

Related

Does CF not support Java constructors with variable number of arguments and any known workarounds?

We have a project that deals with files of various encodings. I am using BOMInputStream to skip UTF-8 byte order markers. The existing code works but needs to also support UTF-16 variations. The most straight forward approach is to pass the BOMInputStream constructor multiple ByteOrderMarkers.
Per documentation...
BOMInputStream bomIn = new BOMInputStream(in,
ByteOrderMark.UTF_16LE,
ByteOrderMark.UTF_16BE,
ByteOrderMark.UTF_32LE,
ByteOrderMark.UTF_32BE);
The constructor signature uses variable arguments:
public BOMInputStream(InputStream delegate,
ByteOrderMark... boms)
However, when I try to call this constructor using the following code
<cfset var fis = createObject("java", "java.io.FileInputStream").init(arguments.filePath) />
<cfset var boms = createObject("java", "org.apache.commons.io.ByteOrderMark") />
<cfset var bomins = createObject("java", "org.apache.commons.io.input.BOMInputStream").init(fis, boms.UTF_8, boms.UTF_16LE, boms.UTF_16BE) />
I get the following error...
Unable to find a constructor for class org.apache.commons.io.input.BOMInputStream that accepts parameters of type ( java.io.FileInputStream, org.apache.commons.io.ByteOrderMark, org.apache.commons.io.ByteOrderMark, org.apache.commons.io.ByteOrderMark ).
I have tried just one BOM argument as well and get same error with fewer arguments in the error. So it appears that CF can't call Java constructors with unlimited arguments. Is that correct and, if so, is there any known work around?
Of course the moment I post an idea comes to me. It looks like these variables are accessed through an array in the Java object being called. I just changed the CF code to pass an array of BOMs instead of individual arguments and it worked as expected.
<cfset var bomins = createObject("java", "org.apache.commons.io.input.BOMInputStream").init(
fis,
[boms.UTF_8, boms.UTF_16LE, boms.UTF_16BE]
) />

Element VAR is undefined in ATTRIBUTES ColdFusion 11

I am trying to call a method from a java class but I am getting an exception I haven't seen before.
This is what I get back when I am calling the class and one of the methods and how I got this
<cfdump var="#nlp#">
<cfdump var="#nlp.run()#">
And this is the exception I got when I am trying to dump the method
19:12:31.031 - Expression Exception - in Z:/Sites/xamplifier/views/surveyreporting/wordcloud.cfm : line 157
Element VAR is undefined in ATTRIBUTES.
Am I calling the method in a wrong way? This is how we had the code on CF9 and everything works but CF 11 seems to have issues...
It looks like the Open_NPL run() method is generating an exception, which is caught and causes it to return null. See here: Open_NPS Source
Agree with the other answers, you'll just have to test for NULL to avoid the CF exception, and dig into the Java to determine the root cause.
The Java method is returning NULL, which in ColdFusion is the same as not being defined. You need to capture the result and test it.
<cfset local = {}><!--- if inside a function, this isn't necessary --->
<cfset local.result = nlp.run() >
<cfif not isNull( local.result ) >
<cfdump var="local.result">
<cfelse>
NULL!
</cfif>

Invoking function with dynamic array of arguments

If I have
<cfset arr_arguments = ["a","b","c"]>
<cfunction name="someFunction">
<cfargument name="someArgumentOne">
<cfargument name="someArgumentTwo">
<cfargument name="someArgumentThree">
</cffunction>
Is there any way to invoke someFunction with the arguments arr_arguments, similar to someFunction("a","b","c")? I of course know I can use argumentCollection to pass a (keyed) structure to a function, but I am specifically asking about passing in an (keyless) array. In JS this could be easily done with someFunction.apply(this,arr_arguments), but in coldfusion I just can't find any way of doing this.
Unnamed arguments are passed into a function as a structure with numeric keys matching the positions of the arguments defined in the function arguments. So instead of passing the named arguments, you can convert your array to a struct with numeric keys, and then pass the struct in with argumentCollection:
<cfset arr_arguments = {"1"="a","2"="b","3"="c"}>
<cfset someFunction(argumentCollection=arr_arguments)>
You can easily convert an array to a struct with numeric keys like this:
<cfset args = {}>
<cfloop from="1" to="#arrayLen(arr_arguments)#" index="i">
<cfset args[i] = arr_arguments[i]>
</cfloop>
In Coldfusion 10, you can use the invoke function to do this. It appears to be an undocumented way to pass an array of arguments in order. Example in cfscript:
invoke('', 'someFunction', ['a','b','c']);
The first argument of invoke is the component name (or an empty string for UDFs). The second is the function name, and third is an argument array. Note that both component and function names must be passed as strings.
I tested this both with and without defined argument names and the order of arguments was preserved.
Ok if you are looking to call a function with an arbitary array of arguments the best option I can suggest is to make a wrapper function. See below example code
<cfscript>
function testMe(
required string arg1,
required string arg2
) {
writedump(arguments);
}
function invokeFunction(
required inFunction,
required array functionArguments
) {
var stcArguments = {};
// Introspect to get the method parameters
var arrFunctionArguments = GetMetaData(arguments.inFunction).parameters;
// Now figure out what we are iterating to
var numArgumentsToParse = Min(ArrayLen(arguments.functionArguments),ArrayLen(arrFunctionArguments));
// Populate the arguments structure
for (var i=1;i<=numArgumentsToParse;i++) {
stcArguments[arrFunctionArguments[i].name] = arguments.functionArguments[i];
}
// And try to call it
return arguments.inFunction(
argumentCollection = stcArguments
);
}
invokeFunction(testMe,[1,2]); // Works fine
invokeFunction(testMe,[1,2,3,4]); // Just skips 3 and 4
// invokeFunction(testMe,[1]); // Errors due to not enough arguments
// invokeFunction(fakeFunctionName,[1]); // Errors due to undefined function
</cfscript>
This will of course error if either of the following happen
One or more arguments are not of correct type
The function doesn't actually exist
Not all required arguments are passed in
But you can handle that outside. This will use introspection to figure out the argument names, populate the structure accordingly, and call the requested function.
Here's a UDF which will do that for you:
<cfscript>
function test_function(a,b,c){
writeOutput("arguments.a"&arguments.a&"<br>");
writeOutput("arguments.b"&arguments.b&"<br>");
writeOutput("arguments.c"&arguments.c&"<br>");
return arguments.a & "::" & arguments.b & "::" & arguments.c;
}
function invoke_positional(func,arr){
var metaData = GetMetaData(func);
var args={};
for(var pos=1;pos<=ArrayLen(arr);pos++){
args[metaData.parameters[pos].name]=arr[pos];
}
return arguments.func(argumentCollection=args);
}
data = ["StringOne","StringTwo",22];
result = invoke_positional(test_function,data);
</cfscript>
<cfoutput>
<p>
Returned: #result#
</p>
</cfoutput>
invoke_positional works by using the UDF's metadata to build up a named set of parameters and then uses them with argumentCollection=
So, one option I have found out to work is to create an arguments object separately from the actual real arguments scope and simply add the values to it using a numeric index.
<cfset args_input = createObject("java","com.naryx.tagfusion.cfm.engine.cfArgStructData").init()>
<cfloop from="1" to="#arraylen(arr_arguments)#" index="i">
<cfset args_input[i] = arr_arguments[i]>
</cfloop>
<cfset someFunction(argumentCollection=args_input)>
If you encounter this situation using adobe coldfusion I would advice you to try
<cfset correctClass = arguments.getClass().getName()>
To get the string to pass to the createObject. Or if that doesn't work just have a dedicated function to return an empty arguments object
<cfset args_input = this.generateArgumentObject()>
and
<cffunction name="generateArgumentObject">
<cfreturn arguments>
</cffunction>
Oh well, all I know for sure is that it will work for sure in OpenBD and based on a post I saw in the past on Ben Nadel's blog I am pretty sure this should work in Adobe ColdFusion as well.

coldfusion 9 dynamically call method

I'm attempting to build a method call from strings that have been passed into an object that refer to another object.
normally when calling an object we write the code like this:
application.stObj.oNewsBusiness.getNews(argumentCollection=local.stArgs);
However what I have done is created an array that contains the object name, the method name and the argument collection.
<cfscript>
local.stArgs = {};
local.stArgs.nNewsID = 19;
local.stArgs.sAuthor = "John";
local.aData = [];
local.aData[1] = local.stArgs;
local.aData[2] = "stObj.oNewsBusiness";
local.aData[3] = "getNews";
</cfscript>
however i am struggling to recombine all this to be a method call.
UPDATE using suggestion but still with issue
While cfinvoke seems to work for:
<cfinvoke component="#application.stObj.oNewsBusiness#" method="#local.sMethod#" argumentcollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it doesn't work when doing something like:
<cfscript>
local.stArgs = local.aData[1];
local.sObject = local.aData[2];
local.sMethod = local.aData[3];
</cfscript>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#" argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it generates an error:
Could not find the ColdFusion component or interface application.stObj.oNewsBusiness
CFInvoke is generally used to handle dynamic method calls.
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7e0a.html
CFInvoke has an argumentcollection attribute so you can pass your arguments in the way you are used to.
Dan is correct CFInvoke is the way to go
<cfinvoke component="#mycomponentname#" method="get" arg1="#arg1#" arg2="#arg2#" arg3=..>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
from your update won't work because there are no # signs around the component variable.
You could do
<cfset local.componentName = "application." & local.sObject>
<cfinvoke component="#local.componentName#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
There's probably an inline way of combining application. with the variable on the cfinvoke call, but I don't know off the top of my head.
Edit: Dan Wilson's comment does it better in an inline way.

access JRUN jndi environment vaiables from coldfusion (java)

I want to put some instance specific configuration information in JNDI. I looked at the information here:
http://www.adobe.com/support/jrun/working_jrun/jrun4_jndi_and_j2ee_enc/jrun4_jndi_and_j2ee_enc03.html
I have added this node to the web.xml:
<env-entry>
<description>Administrator e-mail address</description>
<env-entry-name>adminemail</env-entry-name>
<env-entry-value>admin#mystore.com</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
In coldfusion I have tried several different approaches to querying the data:
<cfset ctx = createobject("java","javax.naming.InitialContext") >
<cfset val = ctx.lookup("java:comp/env") >
That lookup returns a jrun.naming.JRunNamingContext. If i preform a lookup on ctx for the specific binding I am adding I get an error.
<cfset val = ctx.lookup("java:comp/env/adminemail") >
No such binding: adminemail
Preforming a listBindings returns an empty jrun.naming.JRunNamingEnumeration.
<cfset val = ctx.listBindings("java:comp/env") >
I only want to put a string value (probably several) into the ENC (or any JNDI directory at this point).
Never used it, but I got curious so I decided to try ... with no success.
I found this though, hopefully it helps you.
http://www.adobe.com/livedocs/coldfusion/7/htmldocs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=ColdFusion_Documentation&file=00001570.htm