twitter4j.auth.AccessToken instantiating in ColdFusion - coldfusion

I'm working with ColdFusion 9.0.1 and latest (for current date) stable build of twitter4j library - twitter4j-core-2.2.4. I'm trying to create functionality which allows users to login or register at our site using their twitter accounts.
I was able to create authorization part: user click on the link on our site and system redirects him to twitter page. On this page he able to "Authorise" our application. After that system redirecting him back using callBackURL.
But I have a problem with next step. When I'm trying to setOAuthAccessToken and for that trying to instantiate AccessToken object with follow part of code:
accessToken = createObject( 'java', 'twitter4j.auth.AccessToken' ).init( 'myStoredRequestToken', 'myStoredRequestTokenSecret' );
But I have follow error:
An exception occurred while instantiating a Java object. The class
must not be an interface or an abstract class. Error: ''.
Any ideas?
Update:
The start part of stacktrace:
'coldfusion.runtime.java.JavaObjectInstantiationException: Object instantiation exception. at coldfusion.runtime.java.JavaProxy.CreateObject(JavaProxy.java:171) at coldfusion.runtime.java.JavaProxy.invoke(JavaProxy.java:80) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2360) at cftwitter2ecfc2084917956$funcGETUSERCREDENTIALS.runFunction(C:\inetpub\wwwroot_test\twPlayGrnd_com\twitter.cfc:36) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55) at ...
...cut here, not sure this is important...
the last part is
cfapplication2ecfc665259542$funcONREQUEST.runFunction(C:\inetpub\wwwroot_test\twPlayGrnd\application.cfc:55) ... 55 more Caused by: java.lang.IllegalArgumentException: Invalid access token format. at twitter4j.auth.AccessToken.(AccessToken.java:50) ... 60 more'
I saw the message about wrong format, but based on documentation at http://twitter4j.org it should accept two agruments (strings with keys). Am I wrong?
Update 2
*just find that out - I am sorry that I brought you into confusion with my first post and example... of course I used myStoredRequestToken, myStoredRequestTokenSecret, not a consumer key/secret *
*there are relevant parts of code I'm using for this functionality*
application.cfc ("onApplicationStart" function, instantiating components on start of application)
<cffunction name="onApplicationStart" access="public" returntype="boolean" output="false">
...
<cfset application.com.twitterInstance = server.javaloader.create("twitter4j.TwitterFactory").getInstance() />
<cfset application.com.twitter = createObject("component","_com.twitter").init() /> *<!--- cfc component which will be listed below --->*
...
</cffunction>
twitter.cfc (corresponding coldfusion component)
<cfcomponent displayname="twitter" output="false">
<cffunction name="init" access="public" output="false">
<cfreturn this>
</cffunction>
<cffunction name="authorizeTwitter" access="public" output="false">
<cfargument name="callBackURL" type="string" required="false" default="#request.twtCallBackURL#" />
<cfset var requestToken = "" />
<cfset application.com.twitterInstance.setOAuthConsumer(request.twtConsumerKey,request.twtConsumerSecret) />
<cfset requestToken = application.com.twitterInstance.getOAuthRequestToken(arguments.callBackURL) />
<cflock scope="session" type="exclusive" timeout="10">
<cfset session.oAuthRequestToken = requestToken.getToken()>
<cfset session.oAuthRequestTokenSecret = requestToken.getTokenSecret()>
</cflock>
<cflocation url="#vLocal.requestToken.getAuthorizationURL()#" addtoken="No" />
</cffunction>
<cffunction name="getUserCredentials" access="public" output="true">
<cfset var vLocal = {} />
<cfset vLocal.accessToken = "" />
<cfset vLocal.userData = "" />
<cfset vLocal.requestToken = "" />
<cfset vLocal.accessToken = server.javaloader.create("twitter4j.auth.AccessToken").init(session.oAuthRequestToken,session.oAuthRequestTokenSecret)>
<cfset application.com.twitterInstance.setOAuthAccessToken(vLocal.accessToken) />
<cfset vLocal.userData = application.com.twitterInstance.verifyCredentials() />
<cfdump var="#vLocal.userData#" label="User Credentials">
</cffunction>
First function is for first step - requesting twitter for autorization page (where user can autorize or deny application). Call back URL runs the page what calls the second function and I have problem only at this step (line for generation accessToken).
I have the same result if Im using createObject function instead of javaloader.
*So, my main question is still the same - to obtain the users unique Access Token? Please point me, what I'm doing wrong? What is a correct format for unique user's accessToken generation? Should I place oauth_verifier parameter there? if so, how?*

You are passing consumer key/secret instead of access token/secret.
You can generate your access token/secret at dev.twitter.com.
https://dev.twitter.com/apps » create my access token
Best,
Yusuke

I think I figured out what is wrong with the help of the examples 8. Sign in with Twitter and
Adding support for automated tweets with OAuth. Only tested with my own account though ..
Before you redirect to the authorization page, save the whole RequestToken object in a session variable. You will need it to extract the AccessToken. Note: I am storing the TwitterFactory in the application scope - not the instance
<cfset Twitter = application.TwitterFactory.getInstance()>
<cfset Twitter.setOAuthConsumer(application.TwitterConsumerKey, application.TwitterConsumerSecret)>
<cfset Session.RequestToken = Twitter.getOAuthRequestToken( YourCallBackURL )>
On callback, twitter adds a parameter named oauth_verifier to the URL. Use that value and the saved RequestToken to extract the AccessToken.
<cfset AccessToken = Twitter.getOAuthAccessToken(Session.RequestToken, URL.oauth_verifier)>
<cfset session.StoredAccessToken = AccessToken.getToken()>
<cfset session.StoredAccessSecret = AccessToken.getTokenSecret()>
Once you have the AccessToken/Secret you can access user details (update status,...) anywhere.
<cfset Twitter = application.TwitterFactory.getInstance()>
<cfset Twitter.setOAuthConsumer(application.TwitterConsumerKey,application.TwitterConsumerSecret)>
<cfset AccessToken = createObject("java", "twitter4j.auth.AccessToken")>
<cfset OAuthToken = AccessToken.init(session.StoredAccessToken, session.StoredAccessSecret)>
<cfset Twitter.setOAuthAccessToken(OAuthToken)>
<cfset userData = Twitter.verifyCredentials()>
<cfoutput>
id = #userData.getId()#<br>
name = #userData.getName()#<br>
followers = #userData.getFollowersCount()#<br>
friends = #userData.getFriendsCount()#<br>
</cfoutput>

Related

Issue migrating from Adobe Coldfusion 10 to Lucee 4.5.1 - accessing structure

I'm currently attempting to migrate my site from Adobe Coldfusion 10 to Lucee 4.5.1.
I'm getting the following error: key [TITLE] doesn't exist.
The code I was using was:
<cfset variables.title = ress.title.welcome>
The code that I need to fix the issue seems to be:
<cfset variables.title = ress["title.welcome"]>
I'm using JavaRB and loading a properties file (onRequestStart()) and setting it to the variable ress.
<cfset ress = utilObj.getResourceBundle()>
Is there an alternative other than going through my code to fix all the references? Is there a setting in the server to exhibit the old behavior?
Update #1
Properties files looks like this:
# #comment
title.welcome=Content here
Update #2
This currently works on CF10 Developer on Windows 2008 R2 and CF10 on my shared host which is also Windows Server. I will also acknowledge that this is old code :)
JavaRB returns a structure from the content of the file:
var resourceBundle=structNew(); // structure to hold resource bundle
...
<cfreturn resourceBundle />
Partial CFC and method calls...
<cfcomponent name="utils" output="false">
<cfset this.ress = "">
<cffunction name="init">
<cfscript>
this.ress = loadResourceBundle();
</cfscript>
<cfreturn this>
</cffunction>
<cffunction name="loadResourceBundle" access="public" output="true">
<!--- Get javaRB --->
<cfinvoke component="#application.cfcPath#.javaRB" method="init" returnvariable="rb">
</cfinvoke>
<cfscript>
rbFile = GetDirectoryFromPath(expandpath("/resources/")) & "mgs.properties";
</cfscript>
<cfreturn rb.getResourceBundle("#rbFile#")>
</cffunction>
...
</cfcomponent>
<cfcomponent displayname="javaRB" output="no">
<cffunction access="public" name="init" output="No">
<cfscript>
rB=createObject("java", "java.util.PropertyResourceBundle");
fis=createObject("java", "java.io.FileInputStream");
msgFormat=createObject("java", "java.text.MessageFormat");
locale=createObject("java","java.util.Locale");
</cfscript>
<cfreturn this>
</cffunction>
<cffunction access="public" name="getResourceBundle" output="No" returntype="struct" hint="reads and parses java resource bundle per locale">
<cfargument name="rbFile" required="Yes" type="string" />
<cfargument name="rbLocale" required="No" type="string" default="en_US" />
<cfargument name="markDebug" required="No" type="boolean" default="false" />
<cfscript>
var isOk=false; // success flag
var keys=""; // var to hold rb keys
var resourceBundle=structNew(); // structure to hold resource bundle
var thisKey="";
var thisMSG="";
var thisLang=listFirst(arguments.rbLocale,"_");
var thisDir=GetDirectoryFromPath(arguments.rbFile);
var thisFile=getFileFromPath(arguments.rbFile);
var thisRBfile=thisDir & listFirst(thisFile,".") & "_"& arguments.rbLocale & "." & listLast(thisFile,".");
if (NOT fileExists(thisRBfile)) //try just the language
thisRBfile=thisDir & listFirst(thisFile,".") & "_"& thisLang & "." & listLast(thisFile,".");
if (NOT fileExists(thisRBfile))// still nothing? strip thisRBfile back to base rb
thisRBFile=arguments.rbFile;
if (fileExists(thisRBFile)) { // final check, if this fails the file is not where it should be
isOK=true;
fis.init(thisRBFile);
rB.init(fis);
keys=rB.getKeys();
while (keys.hasMoreElements()) {
thisKEY=keys.nextElement();
thisMSG=rB.handleGetObject(thisKey);
if (arguments.markDebug)
resourceBundle["#thisKEY#"]="****"&thisMSG;
else
resourceBundle["#thisKEY#"]=thisMSG;
}
fis.close();
}
</cfscript>
<cfif isOK>
<cfreturn resourceBundle />
<cfelse>
<cfthrow message="#e.message#" detail="#e.detail#" type="#e.type#" />
</cfif>
</cffunction>
...
</cfcomponent>
Update #3
FWIW, I used the Eclipse IDE and did a find replace using a regex and replaced it with a value...
regex: ((ress\.){1}(([a-z\.])+))
value: ress["$3"]
Update #4
So, using Lucee and MySQL, table names are case sensitive!?
Welcome to Adobe ColdFusion, where syntactical mistakes are not punished immediately.
<cfset ress = { "title.welcome": "Content here" }>
<cfoutput>#ress.title.welcome#</cfoutput>
<!---
>> outputs "Content here" in Adobe ColdFusion
>> throws an exception in Lucee/Railo
--->
The behavior in Adobe ColdFusion is misleading and plain wrong. "title.welcome" is a key that is supposed to be put in the struct ress. Instead the key is split into two structs with the keys "title" and "welcome", linked to each other and then put into the struct ress.
Your only chance to fix this issues is by adapting your getResourceBundle function. Here you need to refactor the lines with resourceBundle["#thisKEY#"] so that thisKEY creates a struct chain.

Need suggestion on one small thing about soap web service in cf

Please suggest me what is the correct way to get method arguments in CF web service with SOAP
Below is my sample code for web service method where I didn't used <cfargument> but parsing xml request
<cffunction name="Method1" displayname="method name" description="method description
"access="remote" output="true" returntype="xml">
<cfset isSOAP = isSOAPRequest()>
<cfif isSOAP>
Get the first header as a string.
<cfset reqxml = GetSOAPRequest()>
<cfset reqxml1 = XmlParse(reqxml)>
<cfset responseNodes = xmlSearch(#reqxml1#,"soapenv:Envelope/soapenv:Body") />
<cfset responseNodes = xmlparse(responseNodes[1]) />
<cfif structKeyExists( responseNodes.xmlroot, "AgentID" )>
<CFSET AgentID=trim(responseNodes.xmlroot.AgentID.xmltext)>
<cfelse>
<cfset process = 0 >
<cfset responce['ErrorCode'] = "MG1000">
<cfset responce['ErrorMessage'] = "Agent ID not found in request">
<cfset AnythingToXML = createObject('component', 'AnythingToXML.AnythingToXML').init() />
<cfset myXML = AnythingToXML.toXML(responce,"StatusResponse") />
<cfset result = xmlparse(myXML)>
<cfreturn result>
</cfif>
But I think I should use
<cfargument> in place of parsing xml request.
Please suggest what is the correct way to do it.
Thanks in advance
Your ColdFusion SOAP method can be as simple as:
<cffunction name="yourMethod" access="remote" output="false" returntype="struct">
<cfargument name="testString" type="String" >
<cfset local.out = structNew()>
<cfset local.out.argIn = arguments.testString>
<cfset local.out.additionalValue = "Hello World">
<cfreturn local.out>
</cffunction>
ColdFusion will allow remote function to be accessed via SOAP requests. You don’t need to rely on the SOAP methods like isSOAPRequest() and GetSOAPRequest() unless you’re doing more complex tasks, such as requiring data in the SOAP header. You can view the SOAP wsdl by appending ?wsdl to the name of your component, such as localhost\yourService.cfc?wsdl
You can return any data type that ColdFusion can serialize. In my example I opted to return a structure, which will return a map in SOAP.

Setting up Session variable in Application.cfc

Hi I'm very new with ColdFusion 10 especially with Application.cfc. It is very confusing.
I created Application.cfc and inside this cfc I created the following:
My questions are:
1. Why do I get session error? (see my codes below)
2. What should I put in sessionEnd function?
<!--- Application.cfc --->
<CFCOMPONENT displayname="Application" output="true">
<cfset THIS.Name = "MyTestApp" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan(0,0,60,0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 30, 0 ) />
<cfset THIS.SetClientCookies = false />
<cfset THIS.SetClientCookies = false />
<cffunction name="OnApplicationStart" access="public"
returntype="boolean" output="false">
<cfset application.Main_DSN = "MyTestDB">
</cffunction>
<cffunction name="onApplicationEnd" output="false">
<cfargument name="applicationScope" required="true">
</cffunction>
<cffunction name="OnSessionStart" access="public" returntype="void" output="false"
hint="Fires when user session initializes">
<cfset session.loggedin = "NO">
<cfset session.username = "">
<cfset session.userrights = "">
<cfset session.usergroup = "">
</cffunction>
<!--- After user login, I have checklogin.cfm --->
<cfif mylogin NEQ true><!--- Login failed, go redirect to login page --->
<cflocation url="login.cfm">
<cfelse>
<cfset session.loggedin = "YES"><!--- ??? error: see below --->
<cfset session.username = "#Trim(Form.username)#">
<cfset session.userrights = "#Trim(Form.userrights )#">
<cfset session.usergroup = "#Trim(Form.usergroup)#">
</cfif>
<cffunction name="onSessionEnd">
<!--- Not sure what can I put in here????? --->
</cffunction>
Your login code is outside any of the event handlers in your Application.cfc file, so that code will run first... before any of the event handlers!
The execution sequence of code in Application.cfc is:
code outside of any event handler (irrespective of where it is in the file)
onApplicationStart()
onSessionStart()
onRequestStart()
etc
So you cannot have code referencing the session scope (or application scope for that matter) outside the other event handlers. You can only start using session variables once onSessionStart() has fired. Not before.
What is the error message that you are getting?
You can really put anything in the session end. Anything that you would need to run to clean up after a session.
Maybe you want to update a log or send an email, clear a cookie, or redirect to a certain page.
EDIT:
Is everything else in the Application.cfc working for you? It seems like the onSessionStart event is not firing.
EDIT 2:*
Sorry for doing this via an answer but I need more rep to leave comments.
The session should start as soon as you connect to the application, you do not need to wait to be "logged in" as in verify credentials.
If the session variables do not exist then the event is not firing for some reason. Make sure you have the file name correctly and it is in the root folder of the application.
EDIT 3:
Your login code is not in the onSessionStart function, not in any function. Unless I misunderstood your comment and you were saying that code section is in the "checklogin.cfm" file and not in the Application.cfc file.
EDIT 4:
I need to jump on a client call but I can help some more after. We might need to take a look at part of the checklogin.cfm file.
EDIT 5:
Any luck?
EDIT 6:
Sorry for the delay, volume is ramping up this afternoon.
At this point I think I would either need to see the code that is doing the validation checking or ask if you are sure the validation is working correctly.
I assume you are connecting to a database, if you take the query you are using to verify the login credentials and run it in SQL server or whatnot inserting the values you would type into the form, do you get any results?
EDIT 7:
Figure it out, OP?

How to display <cfreturn> output from a CFC being called from a form

I have a web form which uses the action attribute to call a CFC like this:
<form action="mycfc.cfc?method=registeruser">
The CFC processes the data in the form and then I want it to return a variable telling the user if their form submission has been successful or not.
So within my CFC, I put this:
<cffunction name="registeruser" access="remote" hint="registers a new user" returnformat="JSON">
... PROCESSES FORM HERE THEN...
<cfset Msg = 'Success'>
<cfreturn Msg>
<cflocation url = "/registrationpage.cfm">
</cffunction>
How do I display the Msg variable in the registrationpage.cfm page? My output is set to JSON so I guess I have to DeSerialize but I have no idea how to actually reference/access this output from the method.
My whole answer is for educationnal purposes only and I strongly advise you to use an existing framework rather than reinventing the Wheels. Have a look at Picking a ColdFusion MVC Framework
You can store the value in the session scope. A lot of frameworks does it using a flash memory concept, which is a temporary memory (implemented as a struct) that destroys members when accessed.
Have a look at http://cfwheels.org/docs/1-1/chapter/using-the-flash it's quite straight forward to implement an API that does this.
Client code could look like (depending on your implementation):
<cfset session.flash.set('importantMsg', 'some important msg')>
<cflocation ...>
Then from the other page:
<cfif session.flash.has('importantMsg')>
<!--- The following line should also destroy the 'importantMsg' key --->
#session.flash.get('importantMsg')#
</cfif>
Here's an implementation example (not that the implementation is not thread-safe):
FlashMemory.cfc
<cfcomponent>
<cffunction name="init" returntype="FlashMemory">
<cfset variables.instance = {flash = {}}>
</cffunction>
<cffunction name="set" returntype="void">
<cfargument name="key" type="string" required="true">
<cfargument name="value" type="any" required="true">
<cfset variables.instance.flash[arguments.key] = arguments.value>
</cffunction>
<cffunction name="get" returntype="any">
<cfargument name="key" type="string" required="true">
<cfset var v = variables.instance.flash[arguments.key]>
<cfset structDelete(variables.instance.flash, arguments.key)>
<cfreturn v>
</cffunction>
<cffunction name="has" returntype="boolean">
<cfargument name="key" type="string" required="true">
<cfreturn structKeyExists(variables.instance.flash, arguments.key)>
</cffunction>
</cfcomponent>
onSessionStart
<cfset session.flash = new FlashMemory()>
Also please note that in your case, your remote CFC methods shouldn't return anything. You will use the flash memory to pass data around instead. That means when the method has finished it's work you can simply redirect the client.
You probably shouldn't use remote CFC methods in this particular case:
I have never really used remote CFC methods as stateful web services. The various advantages of remote CFC methods like their ability to spit out data in multiple data-interchange formats (JSON, WDDX...) is lost with your implementation.
You could simply do something like:
registration.cfm
<cfset errors = session.flash.get('registrationErrors')>
<cfif arrayLen(errors)>
<!--- Display errors --->
</cfif>
<form method="post" action="register.cfm">
...
</form>
register.cfm
<cfset registration = new Registration(argumentcollection = form)>
<cfset validator = new RegistrationValidator(registration)>
<cfif validator.validate()>
<cfset application.userService.register(registration)>
<!--- You can also redirect to a page that represents the correct state --->
<cflocation url="registered.cfm" addtoken="no">
<cfelse>
<!--- Store the errors collection in the flash memory --->
<cfset session.flash.set('registrationErrors', validator.errors())>
<!--- Redirect to the page the user came from --->
<cflocation url="#cgi.http_referer#" addtoken="no">
</cfif>
Take the cflocation tag out of your function. It will not execute anyway because it's after the cfreturn tag.
Also, post your form to a .cfm page, not the .cfc. From that .cfm page, do this:
<cfset MyObject = CreateObject(stuff for your cfc goes here)>
<cfset MessageFromFunction = MyObject.registeruser()>
<cfoutput>#MessageFromFunction</cfoutput.

Using Onclick on an HREF tag in Coldfusion

I was wondering if someone can tell me what is wrong with this code because it doesn't work and it does not give me any errors either.
With this code I am trying to access a component that updates a DB table each time someone press on the link.
<cfajaxproxy bind="url:http://www.example.com/admin/CRM/linktracking.cfc" />
<cfscript>
SSLtype = (CGI.HTTPS EQ 'off')?'http://':'https://';
</cfscript>
<cfset domainname = CGI.SERVER_NAME>
<cfset domainURL = SSLtype&CGI.SERVER_NAME&CGI.SCRIPT_NAME&'?'&CGI.QUERY_STRING>
<script>
function insertTracking(href) {
var instance = new ajaxjsclass();
instance.setCallbackHandler();
<cfoutput>
instance.insertTrack(href,'#surveymain.contactid#','#domainname#','#domainURL#');
</cfoutput>
}
</script>
This is the component I am trying to access.
<cfcomponent>
<cffunction name="insertTrack" access="remote" returntype="void" >
<cfargument name="clickedURL" required="yes">
<cfargument name="contactid" required="yes">
<cfargument name="domainName" required="yes">
<cfargument name="domainURL" required="yes">
<cfquery name="q_inserttrack" datasource="dpsigweb">
update survey_tracking
set surveystarted = <cfqueryparam value="#now()#" cfsqltype="CF_SQL_TIMESTAMP">
where contactid= '#contactid#'
</cfquery>
<cfif ARGUMENTS.contactid NEQ ''>
<cfscript>
additionalInfo = '<b>Clicked URL</b> - <i>#ARGUMENTS.clickedURL#</i><br><br><b>From Site</b> - <i>#ARGUMENTS.domainURL#</i>';
gaCFC = CreateObject("component","mod_sigweb.components.guestaccount");
gaCFC.AddCorrespondenceCurDoctorProcedureRemote(
functionPassword = 'password',
contactid = '#ARGUMENTS.contactid#',
theMessage = additionalInfo,
statustype = 'Survey Started',
contactresult ='Survey Started'
);
</cfscript>
</cfif>
</cffunction>
</cfcomponent>
This is where I am trying to access the function from:
<a href="http://dev.example.com/surveys/survey.cfm?id=#id#&contactid=#contactid#&doctorid=#doctorid#" onClick="insertTracking(this.href)" >
I am suspecting that my <cfajaxproxy> tag may have a syntax error but when I am pressing the link I am not getting any errors.
I change my cfajaxproxy to this
<cfajaxproxy cfc="linktracking" jsclassname="ajaxjsclass" />
but still the function does not seem to work. I even moved the component and the cfm file in the same folder but it still doesn't work.
Edit:
I forgot to mention that i am sending this code in an email template. i don't know if that matters in any way. I created a test page that I am testing locally and my code works just fine. If there is something that I need to change because of that please let me know
Solution Found. I put the update query code directly on the page so when the user presses on the link and opens the page the query runs from there instead from the link.
The above code is working fine I just had issues with it because I was sending the page via email to the user. At least this is what I suspect.