Subscribing to multiple ActiveMQ topics in a Coldfusion Event Gateway - coldfusion

I was wondering if I could use the Coldfusion example ActiveMQ event gateway to subscribe to multiple topics.
Currently I can set
destinationName=dynamicTopics/topic1
however I would have assumed I could set some kind of Composite Destination
destinationName=dynamicTopics/topic1,topic2
or
destinationName=dynamicTopics/topic1,dynamicTopics/topic2
This does not seem to work. Is this just not possible out of the box, or am I missing something about how JNDI works?
Obviously the alternative is creating multiple event gateways, but I do not like that idea at all.
Also it would be important to have access to the incoming message's topic's name in the onIncomingMessage handler

The way that I accommodate multiple destinations in a single ActiveMQ event gateway is to use an "action" as my qualifier. Rather than have multiple queues or topics, I instead include the target in my payload like:
payload = {action: "notify", foo: "bar"};
sendGatewayMessage('gw', {status = "SEND",
topic="dynamicTopics/sync",
message = serializeJson(payload)});
Then in onIncomingMessage, I fork based on the action:
<cffunction name="onIncomingMessage">
<cfargument name="event" type="struct" required="true" />
<cfset var msg = deserializeJson(arguments.event.data.msg) />
<cfif msg.action EQ "verify">
<cfset verify(argumentCollection = msg) />
<cfelseif msg.action EQ "notify">
<cfset notify(argumentCollection = msg) />
</cfif>
</cffunction>
And I use private methods to implement each of the routines as needed. A bonus of pulling the code out of onIncomingMessage, it can be implemented in a standalone CFC which can be unit tested on its own using something like MxUnit or TestBox.

Related

cffunction httpMethod attribute validation?

Is there an administrative option that controls whether the httpMethod attribute of an access='remote' cffunction is validated / required?
I have two server instances, dev and prod, both CF11; on the dev box I can successfully call a remote function defined like:
<cffunction name="foo" access="remote" returnFormat="json">
...
</cffunction>
But on the prod box it returns 500 invalid endpoint definition: no httpMethod declared.
Easy to fix—just place the attribute on it—but I'd like to bring my setups closer together in terms of configuration.

Exempting Coldfusion page from authentication

I need to post from a non-secure CF page to a secure CF page. I don't want to have to go through and implement the user authentication on the page sending the values because its a rather cumbersome process due to the way this legacy site was setup and secondly because the page sending the values is acting as a service between two unrelated order management systems as opposed to a user.
Right now, when I try to post to, the response result is a redirect to the login of the homepage. Is there a way to make an exception for a posting or receiving page from forcing user authentication?
I'm using <cfhttp> to post the values to post page which has a series of <cfparam>'s that I'm passing the values to. Once I pass those values into the post page is when the post page triggers a redirect to the home page because the post page is an internal page in the order management system and is displayed as a client logs in and a session is created for them.
Since you did not provide any code, here is a guess what it might look like and how you could add an exception for specific requests:
<cffunction name="onRequestStart" access="public" output="false" returnType="boolean">
<cfargument name="targetPage" type="string" required="true">
<!--- treat initialized SESSION or matching request token (rtoken) as successful authentication --->
<cfset LOCAL.isAuthenticated = (
isDefined("SESSION.userID")
or
( structKeyExists(FORM, "rtoken") and (FORM["rtoken"] eq "some-secret-only-you-know") )
)>
<cfif LOCAL.isAuthenticated>
<!--- do something... --->
<!--- not authenticated --->
<cfelse>
<!--- redirect to login --->
<cflocation url="login.cfm" statusCode="303" addToken="false">
</cfif>
</cffunction>
Now you could simple add the key-value-pair rtoken=some-secret-only-you-know (i.e. <input type="hidden" name="rtoken" value="some-secret-only-you-know" />) to your POST to bypass the session based authentification.
Disclaimer: Only use this method if the POST parameters (form fields) are not public/editable by the user.
Feel free to provide actual context so I can assist in a more concrete way.
I have written a couple of apps with similar, but not identical requirements. Here is how I handled those requirements in the last one I wrote. All this code is the Application.cfc file in the methods specified.
In onApplicationStart:
application.securityNotNeededPages =
"somePage.cfm,someOtherPage.cfm,someMorePages.cfm";
In onRequestStart
var ThisPage = listlast(cgi.PATH_INFO, "/");
...
if (ListFindNoCase(application.securityNotNeededPages, ThisPage) is false) {
security related code
}
else {
code for when the page does not to be secured
}

Setting session variables with JavaScript in ColdFusion

I have a website with multiple tabs. Each tab runs a separate report based on a set of filters that take their values from session variables.
How things work now:
While the user is inside a report tab they can open a filter menu to select the options that they need to run their report (doctor names, locations, date, etc) and then they can hit the run button to get their report. When the user clicks "run" the form is saving the variables inside the session where they are available to run other reports without having to click "run" or define them again and again.
What I am trying to do:
Instead of having only a "run" button inside the form I need an "Apply" button that will set the session variables from the form without running the current report. This way the user can pre-define their variables without being forced to run a report they don't need.
I tried using ajax that calls a function outside my application which is setting up variables based on the user's selection.
My challenge is to get those variables back from the function in some format where I could use them in updating the current session variables.
This is a sample of my code:
The Apply button:
Apply
My Ajax Function:
function setSession(){
var formData = $('form').serialize();
$.ajax({
url:'/mod_example/components/exampleCFCs/xUtility.cfc?method=setSessionVariables',
data: formData
});
};
And part of my function:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfargument name="docid" type="string" required="no">
<cfif isDefined('docid')>
<cfset session.doctorids = docid>
</cfif>
<cfif isDefined('docid')>
<cfreturn session.doctorids>
<cfelse>
<cfreturn 0>
</cfif>
</cffunction>
</cfcomponent>
What I need is to get the value of session.doctorids to be able to update my session variables with the new value.
It sounds like you have this utility cfc in a shared directory and you are calling it directly. As you've noticed, the problem with that is that you end up with multiple sessions. You can get around this issue be setting up a Facade cfc within your application and make your ajax calls to that cfc.
If you only want to expose the setSessionVariables then you could use this cfc:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfset var xUtility = createObject('component','mod_example.components.exampleCFCs.xUtility')>
<cfreturn xUtility.setSessionVariables(argumentCollection=ARGUMENTS)>
</cffunction>
</cfcomponent>
If you want to expose all methods of the utility cfc, then you can extend it:
<cfcomponent output="no" extends="mod_example.components.exampleCFCs.xUtility">
</cfcomponent>
This would allow you to call methods on the utility cfc while maintaining a single session scope (per user of course).
EDIT:
Been a while since i've worked in wheels...but i remember not liking AJAX in the wheels framework. If you create a new subfolder and call it 'remoting' and put the facade in there, and drop an application.cfc in there that looks like this:
<cfcomponent >
<cfset this.name = 'whatever_your_wheels_app_name_is'>
<cfset this.SessionManagement=true>
</cfcomponent>
You should be able to use that facade and this application.cfc will piggyback on the existing application with the same name. The problem with this approach would be if the application times out, and a remote call is the first request to the application, then the wheels application scope might not get set up properly.
It would be best if you could extend the root application.cfc and just override the onRequestStart method so that the framework will ignore the request. To do that you would need to make a mapping in the cfadmin to the root of your project and use this for your remoting/application.cfc
<cfcomponent extends="mappingName.Application">
<cffunction name="onRequestStart">
<cfargument name="requestname" required="true" />
<cfset structDelete(this,'onRequest')>
<cfset structDelete(this,'onRequestEnd')>
<cfset structDelete(VARIABLES,'onRequest')>
<cfset structDelete(VARIABLES,'onRequestEnd')>
<cfreturn true>
</cffunction>
</cfcomponent>
The way that wheels uses `cfinclude' all over the place, you may need to look at this post about extending the appliciation: http://techblog.troyweb.com/index.php/2011/09/cfwheels-workarounds-numero-uno-application-proxy/
There are some wheels plugins (http://cfwheels.org/docs/1-1/chapter/wheels-ajax-and-you) that allow you to use the controller actions / views / routes via ajax so you could look into those also.

Is there a way to (per request) set a non-persistent Database Bean in Coldbox

I am looking to migrate from a custom framework to Coldbox.
The application has 3 datasources
Core
Common
Site
The Core datasource stores information about the sites, the common datasource stores shared information, like the states table, and the Site datasource stores the data relevant to the website.
The Site datasource is changed per request based on the URL of the request, allowing each site to be sandboxed into its own database.
From my testing it seems that the DatasourceBeans generated by Coldbox and used in it's autowiring are stored/cached in the application scope. This is what I'm thinking to do, but the change to the datasource is persisted across requests.
In Coldbox.cfc
datasources = {
Core = {name="DSNCore", dbType="mssql", username="", password=""},
Common = {name="DSNCommon", dbType="mssql", username="", password=""},
Site = {name="", dbType="mssql", username="", password=""}
};
and
interceptors = [{
class="interceptors.Website",
properties={}
}];
Interceptor named Website.cfc
<cfcomponent name="Website" output="false" autowire="true">
<cfproperty name="dsncore" inject="coldbox:datasource:Core">
<cfproperty name="dsn" inject="coldbox:datasource:Site">
<cffunction name="Configure" access="public" returntype="void" output="false" >
</cffunction>
<cffunction name="preProcess" access="public" returntype="void" output="false" >
<cfargument name="event" required="true" type="coldbox.system.web.context.RequestContext">
<cfargument name="interceptData" required="true" type="struct">
<cfset var q="" />
<cfdump var="#dsn.getMemento()#" label="DSN before change" />
<cfquery name="q" datasource="#dsncore.getName()#">
SELECT
Datasource
FROM
Websites
WHERE
Domain = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#cgi.http_host#" />
</cfquery>
<cfscript>
dsn.setName(q.Datasource);
</cfscript>
<cfdump var="#dsn.getMemento()#" label="DSN after change" />
<cfdump var="#q#" label="Results of query" /><cfabort />
</cffunction>
</cfcomponent>
Is there any way to do this in a way that I can use the Coldbox autowire datasource beans?
Honestly, this is just the way I thought I would do it, if anyone has any other ideas on how to get my model to use NON-hardcoded different datasource per request, I would love to understand the framework better.
This question also extends to ORMs. Is there a way for, say, Transfer to use a different datasource per request? What if the databases can have potentially different schemas? Lets say that one database has been updated to a newer version, but another still uses an older version and I essentially have some if statements in the code to provide enhanced functionality to the updated database.
You may be reading these questions and thinking to yourself "You shouldn't do that." Well i am, so please no answers saying not to do it. If you have ideas on better ways to have single codebase attached to different databases then I'm all ears though.
Another way you could do it is by using the requestStartHandler in Coldbox.cfc
<!---config/Coldbox.cfc--->
requestStartHandler = "Main.onRequestStart"
<!---handlers/Main.cfc--->
<cffunction name="onRequestStart" returntype="void" output="false">
<cfargument name="event" required="true">
<cfquery name="q" datasource="#dsncore.getName()#">
SELECT
Datasource
FROM
Websites
WHERE
Domain = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#cgi.http_host#" />
</cfquery>
<cfset rc.dataSource = q.Datasource />
</cffunction>
Then you just have your dataSource stored in the Request Collection becuase onRequestStart will fire on every Request.

How can I avoid using SESSION variables in CFCs when these are used for DataSource and Database Schemas?

I'm trying to refactor all of my CFCs to avoid using SESSION and APPLICATION variables (not an easy task).
However, in this application, SESSION variables are used in every database call, since different logged in users may be accessing different databases and schemas:
<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
I don't want to go through the trouble of passing these two SESSION variables to every method call that accesses the database. This is especially the case since I don't want to pass DSNs and Schema Names in remote AJAX calls.
What is best practice for doing this - for all Scopes that shouldn't be used in CFCs?
I think that since the datasource truly is variable I'd pass it into every function as an optional parameter and set the default value to a variables scoped dsn attribute. I'd set the variables scoped DSN in the CFC's constructor. That way you only have to pass in the DSN for the AJAX calls.
<cffunction name="doFoo" access="remote"...>
<cfargument name="dsn" type="String" required="false" default="#variables.datasource#" />
</cffunction>
I'd use the session scope of your app to store the users dsn name and use that var to pass to the AJAX call.
You should create an "init" method that will serve as a constructor for your CFC. You can then instantiate the CFCs and store them in a shared scope, most likely the application scope. From here, to use this CFC via AJAX, I typically will create a remote facade. Basically this is another CFC that will directly access the CFC instance in the application scope. It will implement the methods you need to access via Ajax, expose them using access="remote" giving your application access to the access="public" methods from the actual CFC. In this case it is generally accepted that the remote facade can access the application scope directly as part of the design pattern.
A simple example:
example.cfc:
<cfcomponent output="false">
<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="dsn" type="string" required="true" />
<cfset variables.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="doStuff" access="public" output="false" returntype="query">
<cfset var q = "" />
<cfquery name="q" datasource="#variables.dsn#">
select stuff from tblStuff
</cfquery>
<cfreturn q />
</cffunction>
</cfcomponent>
In your Application.cfc onApplicationStart() method:
<cfset application.example = createObject("component","example").init(dsn = "somedsn") />
remote.cfc:
<cfcomponent output="false">
<cffunction name="doStuff" access="remote" returntype="query">
<cfreturn application.example.doStuff() />
</cffunction>
</cfcomponent>
Can you set your datasource variables in the onRequest or onRequestStart functions in your Application.cfc
<cffunction name="onSessionStart">
<cfset session.dsn = _users_personal_dsn_ />
</cffunction>
<cffunction name="onRequestStart" >
<cfset dsn = "#session.dsn#" />
</cffunction>
<cfquery name="qEmployees" datasource="#dsn#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
etc.
not sure if that will work [not tested - actually feels a bit sloppy]
-sean
The scope you choose (for any variation of this question, not just for DSNs) should be based on whether the lifetime of the value is the same as the lifetime of the scope.
In our application, the DSN is just set once in the lifetime of the application, so we have an application.config struct that gets created (parsed from a file) in onApplicationStart, and within it is application.config.dsn
If your value really does change between sessions, but not over the life of a session, go ahead and use the session scope.
If your value could change for any given request, but not in the middle of a request, put it in the request scope.
That said, still heed ryan's advice and add optional arguments that only default to this value: being flexible is always the best.
My suggestion for this is to create a base class and then have your components that need database access extend that component. It doesn't have to be in the immediate parent hierarchy but somewhere down the line.
They goal is to do two things, keep the cfc abstracted from the main program and keep it easily configurable. This accomplishes both.
So your CFC that queries the database would look something like this :
<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
<cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>
The key above is the extends="DataAccessBase" portion. This adds the layer of abstraction where you can control the data access at one configurable point, but it's not tied to the application itself, leaving the component abstracted from where it's implemented.
Your DataAccessBase.cfc could look something like this:
<cfcomponent>
<cffunction name="loadSettings">
<cfparam name="request.settings" default="#structNew()#">
<cfparam name="request.settigns.loaded" default="false">
<cfif request.settings.loaded eq false>
<!--- load settings from resource bundle etc --->
<cfset request.settings.dsn = 'myDSN'>
<cfset request.settings.loaded = true>
</cfif>
</cffunction>
<cffunction name="getDsn" access="public" returntype="string">
<cfset loadSettings()>
<cfreturn request.settings.dsn>
</cffunction>
You can of course get more intricate with how you configure and store the settings etc, but that's out of scope of the question I think. :)
I don't see any reason to pass the DSN with every method call. Yes, it works, but it's not necessary. The components are developed with a built-in assumption of the datastructure so you know that it is not going to change from a addItem() call to a updateItem() call, thus its duplication of work which means additional points of failure. :P
Make sense?