Error switching to client management - coldfusion

This is embarrassing. I've been a ColdFusion developer for 13 years and I've never had a reason to use client management over session management. Anyway, I've set up a database and datasource in CFAdmin, then selected that datasource as the default storage mechanism for client sessions.
Then I went through all my code and global-replaced all my session scopes with clients scope.
Top of my application.cfm looks like:
<CFAPPLICATION
NAME="blah"
SESSIONMANAGEMENT="No"
SESSIONTIMEOUT=#CreateTimeSpan(0,2,0,0)#
CLIENTMANAGEMENT="Yes"
CLIENTSTORAGE="sys_blah"
>
In my application.cfm, I had previously set a few variables globally related to session authentication, i.e.
<cfparam name="session.user.authenticated" default="0">
<cfparam name="session.user.id" default="">
However, now that these are written as
<cfparam name="client.user.authenticated" default="0">
<cfparam name="client.user.id" default="">
I get an error:
Element USER is undefined in CLIENT.
What might I be doing wrong?
I can see the client vars going into the newly-created database.
I'm on CF12

Client variables must be simple data types: strings, numbers, lists,
Booleans, or date and time values. They cannot be arrays, recordsets,
XML objects, query objects, or other objects. If you must store a
complex data type as a client variable, you can use the cfwddx tag to
convert the data to WDDX format (which is represented as a string),
store the WDDX data, and use the cfwddx tag to convert the data back
when you read it. For more information on using WDDX, see Using WDDX.
http://help.adobe.com/en_US/ColdFusion/10.0/Developing/WSc3ff6d0ea77859461172e0811cbec0c35c-7fd5.html
So... WDDX or JSON would work.

Related

ColdFusion - SOAP Service Request Error "Web service operation cannot be found."

So I'm very new to SOAP and I am trying to connect to the National Weather Service's SOAP service in order to pull forecast data to display on my webpage. Here is my short code for this process:
<cfinvoke
webservice="http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl"
method="NDFDgen"
returnvariable="aTemp">
<cfinvokeargument name="latitude" value="37.94"></cfinvokeargument>
<cfinvokeargument name="longitude" value="-75.47"></cfinvokeargument>
<cfinvokeargument name="product" value='"glance"'></cfinvokeargument>
<cfinvokeargument name="startTime" value="2014-05-02T12:00"></cfinvokeargument>
<cfinvokeargument name="endTime" value="2014-05-05T12:00"></cfinvokeargument>
<cfinvokeargument name="Unit" value='"e"'></cfinvokeargument>
<cfinvokeargument name="weatherParameters" value="maxt = TRUE"></cfinvokeargument>
</cfinvoke>
The problem is, when I try and run my webpage, I'm getting the following error:
Web service operation NDFDgen with parameters {Unit={"e"},startTime={2014-05-02T12:00},endTime={2014-05-05T12:00},product={"glance"},longitude={-75.47},weatherParameters={maxt = TRUE},latitude={37.94}} cannot be found.
I'm a bit confused since the NDFDgen operation does indeed exist in the WSDL file I am retrieving, and I have addressed all of the required parameters for the NDFDgen operation.
Link to the WSDL file I am trying to use:
http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl
Link to the functions page with required parameters:
http://graphical.weather.gov/xml/#use_it
Can anyone see anything wrong with my code? Is it perhaps something with my arguments I'm passing to SOAP? I even tried following the advice of this Stack Overflow question (Consuming ColdFusion webservice - Web service operation with parameters {} cannot be found) and added the
refreshwsdl="yes"
attribute to my <cfinvoke>, but I am still getting the same error.
With complex web services, it is often easier to go the xml + cfhttp route, as Chester suggested. However, to answer your question, there are a few things wrong with the arguments. That is what the error message means. A method by that name may exist, but its signature does not match the values you have supplied. There is either mismatch in the number of arguments, or in this case, the type of arguments.
According to the wsdl, the start/endTime values must be dates. While CF can implicitly convert a variety of U.S. date strings, it cannot parse the format you are using: yyyy-MM-ddThh:mm. So either use date objects, or use "parseable", date strings such as yyyy-MM-dd hh:mm:ss.
The "weatherParameters" argument should be structure (or complex type) not a string:
<cfset weather = {maxt=true}>
...
<cfinvokeargument name="weatherParameters" value="#weather#">
The Product and Unit values have too many quotes. By using value='"glance"' you are actually including the double quotes as part of the value. That will probably cause an error because the remote web service expects to receive glance (no quotes).
While it will not cause an error, you do not need to include closing tags: </cfinvokeargument>. If you prefer to close it, it is cleaner to use the shortcut <cfinvokeargument ... />
With those changes, your call should work as expected. Though you might want to consider switching to createObject, rather than cfinvoke. Then you can dump the web service object for debugging purposes. It is also less bulky IMO.
<cfscript>
ws = createObject("webservice", "http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl");
//show web service methods for debugging purposes
writeDump(ws);
// construct arguments
args = {latitude="37.94"
, longitude="-75.47"
, product="glance"
, startTime="2014-05-02 12:00:00"
, endTime="2014-05-05 12:00:00"
, Unit="e"
, weatherParameters={maxt=true}
};
// call the method
result = ws.NDFDgen(argumentCollection=args);
writeDump(result)
</cfscript>

How can you get a list of the datasources used to generate a page in coldfusion?

When you add a debug IP to review the debug information from the collection of templates that are parsed to present the page, it includes a list of all queries from that page.
Is it possible to get access to that object for inspection?
I'm looking at trying to automate the documentation which data sources are in use for which page requests. We have a large number of small web apps that access different databases and have different dependencies. I'm hoping to find a way to automate the documentation of these dependencies rather than having to manually review all code for all of the webapps.
Not sure if the object doesn't get created until after the page creation is too far gone to actually do anything with the data, but who knows...
Here is a snippet of code that you can add to the end of your template to get a list of datasources used on the page:
<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="factory">
<cfset cfdebugger = factory.getDebuggingService()>
<cfset qEvents = cfdebugger.getDebugger().getData()>
<cftry>
<cfquery dbtype="query" name="cfdebug_qryDSN">
SELECT DISTINCT DATASOURCE FROM qEvents WHERE type = 'SqlQuery'
</cfquery>
<cfcatch type="Any">
<cfset cfdebug_qryDSN = queryNew('DATASOURCE')>
</cfcatch>
</cftry>
<cfdump var="#cfdebug_qryDSN#" label="cfdebug_qryDSN">
PS: most of the inspiration for this snippet came from {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm. You can get some good ideas on how to gain access to debugger objects/data from this file.
For anyone stumbling across this....
If your [cfroot]/cfusion/lib/neo-datasource.xml file is WDDX encoded and you're not sandboxed, you can use the following (tested on CF2021)
<cflock type="readonly" scope="Server" timeout="5">
<CFSET LibPath=Server.System.Properties["coldfusion.libPath"]>
</cflock>
<CFFILE action="Read" file="#LibPath#/neo-datasource.xml" variable="DatasourcesWDDX">
<cfwddx action="wddx2cfml" input="#DatasourcesWDDX#" output="Datasources">
<cfoutput>#StructKeyList(Datasources[1])#</cfoutput>
<cfdump var=#Datasources#>
The first position of the Datasources array holds a structure containing information on each configured datasource with the main key being the name of the datasource.
Here's an idea that'll work for each application which uses an Application.cfc.
Enable Request Debugging Output in CF Administrator.
Configure Debugging IP Addresses so that every page receives debugging information.
Assuming that Select Debugging Output Format is set to classic.cfm, short circuit {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm by making <cfreturn> the first executable statement in classic.cfm. This will prevent any pages from seeing the debug output.
In Application.cfc::OnRequestEnd() do what Scott Jibben suggested. You can wrap Scott's idea in an <cfif IsDebugMode()>.

Export to Excel - Hidden Field not generating results

I've a simple CFC file that contains the different functions for different queries & a separate function that displays the reports dynamically based on the queries.
All the queries work except one which returns approx. 50k rows. Its just a blank screen & I get no error. When I dump the query results, they do get dumped on the screen but while displaying it in a tabular report it gives nothing.
I've a another CFM file that returns 100k rows & works fine.
Below is CFC code that is not working.
<cfcomponent>
<cfparam name="qry1" default="">
<cffunction name="showqry1" access="remote">
<cfquery name="qry1" dataSource="myds" cachedwithin="#CreateTimeSpan(0, 2, 0, 0)#">
<!--- myquery --->
</cfquery>
<cfset Display()>
</cffunction>
<cffunction name="showqry2" access="remote">
<cfquery name="qry1" dataSource="myds" cachedwithin="#CreateTimeSpan(0, 2, 0, 0)#">
<!--- myquery --->
</cfquery>
<cfset Display()>
</cffunction>
<cffunction name="Display" access="private">
<cfdump var="#rptQry#" top="20">
<cfsavecontent variable="myrpt">
<table>
<!--- make a tabular report here using cfloop over the query--->
</table>
</cfsavecontent>
<cfform action="test.cfm" method="post" name="ExcelData">
<cfoutput>#myrpt#</cfoutput>
<cfinput type="hidden" name="excel_data" value="#myrpt#"/><!---This is giving the error. --->
<cfinput type="submit" name="test" value="Export" />
</cfform>
</cffunction>
</cfcomponent>
Any idea why CFM works fine but CFC doesn't? I need my CFC to work & dont want it to convert it to CFM...
UPDATE:
I've added a comment("This is giving the error") in the above code that is cause of the error. Irrespective of CFC/CFM this doesn't work.
I use the hidden field to pass data to another file which exports data to excel. Any alternate suggestions??
Any help is highly appreciated.
Thanks
You still need to read that doc I put in the comment about how to ask questions clearly.
However you are putting your recordset into a variable qry1, but trying to dump a variable rptQry. But that would just error, unless there's some code you're not showing us that populates rptQry.
Also, from a coding practice POV, you shouldn't really be outputting stuff in your functions: that's best done in a CFM page. Get your data with a CFC method; display it with a CFM.
I also recommend you read up on how to do OO with CFML (or in general). Perhaps get Matt Gifford's book "Object-Oriented Programming in ColdFusion"
Your Display function has cfsavecontent with tabular data and you are putting it into a cfform inside a cfc. I don't know why you are doing that. Insted, simply do an ajax call which return that cfsavecontent and then show it in the cfm.
Else, I guess you may have to output the cfform in the Display function. I may be wrong, but I don't think you can simply place a cfform inside a cfc and expect it to show up on the browser. CFC is not for browser rendering, it should be in a cfm.
Regarding the comment, "This is because I need to export to excel on click of a button for which I'd need cfform. Can you suggest some alternate to this functionality?", I will give you some things to think about.
First, you talk about recordsets containing several thousand rows and you have code where you attempt to display that in a browser. Quite simply, that will take an enormous amount of time to render. So, it's a bad idea.
Next, your code has functions for various queries but just one display function. Unless that's a cleverly written function that figures out the column names, it will only work if all the queries have the same columns. If that's the case, maybe you only need one query and some variables.
My suggestion is to start with a form where the user sends the appropriate information which determines what sql gets written. This form should also include a way for them to choose whether they want the results rendered in excel or html. If they choose html, do something to ensure that the data being returned does not overwhelm their browser.
By the way, re-useable code for displaying query results is a good idea. However, a custom tag might be a more conventional way to do it.

Coldfusion StructDelete creates blank value

I'm using Codlfusion 9's StructDelete() method to remove a value from a cookie. But after using
StructDelete(cookie,"selector12")
I'm getting a value of [empty string] for cookie.selector12 despite deleting it
This is the code I'm using:
<cfdump var="#cookie#">
<!--- kill any existing selector cookie when looking at a profile --->
<cfset structDelete(cookie,'selector#URL.clk#')>
<cfdump var="selector#URL.clk#">
<cfdump var="#cookie#">
To give the following output:
Does StructDelete actually just set blank values? Or have I missed something obvious and simple... again
The behavior of StructDelete() when applied to the COOKIE structure pushes the following header to your browser:
Set-Cookie SELECTOR12=;expires=Wed, 14-Dec-2011 10:06:02 GMT;path=/
which in turn will (for the remainder of the life of that request) produce a browser cookie with a blank value -- which mimics the behavior you're seeing when calling cfdump on that COOKIE structure.
This is simply a side-effect of dealing with the COOKIE structure, which in reality, wraps access to your browser's cookies--and is therefore limited by how cookies are defined/managed in web-browsers.

Can we pass a whole Structure over an URL?

I am opening a new popup window on click of an URL in my page.
My question over here is Can i pass whole structure to the new page ?
If thats not possible is there any simple method to do that ?
Is the page being opened in the URL part of the same application?
If so a better way would be to save the structure in the user's session and pull in the information in that way. Cleaner URLs, code and more secure.
Cheers,
James
Expanding on James Buckingham's answer...
(This assumes you have session management set to true.)
In the calling page, simply copy your structure to a session variable:
<cfset session.myTempStruct=variables.myTempStruct />
Then, in the popup, copy the structure back to the local scope for that request:
<cfset variables.myTempStruct=session.myTempStruct />
If you don't want that structure to hang around in the session, you can have the request for the popup remove it from the session right after copying it to the local scope.
<cfset structDelete(session, "myTempStruct") />
Although HIGHLY NOT recommended, you could do this:
<cfset tmp = {} />
<cfset tmp.name="Marcos" />
<cfset tmp.lname="Placona" />
<cfwddx action="cfml2wddx" input="#tmp" output="tmpWDDX">
link
If you decide to take this approach, I'd suggest sending the information via form as opposed to URL.
You always have the option to store the data in a persistent object such as a bean, or a more simple approach such as a session.
Hope this helps you
You can add your data points as parameters to the end of the URI, but I do not suggest using the method you see as it would be highly subject to injection.
Serializing Struct (with serializeJSON() or something) and puttin git in URL seems reasonable in case struct is not too big (read: less than 3-4k characters in total).
Other solution would be to put this in some shared scope: session, application etc.
Third, would be to call cfm with POST request which can handle larger structs then GET.