Calling Java JAR functions - coldfusion

I'm attempting to connect to Java (JAR) libraries to communicate with the "QuickBooks Merchant Services API".
Each call to the API requires an OAuth2 access token. My first step is to use the Java SDK that QuickBooks provides to get the access token.
I downloaded the "jar-with-dependencies.jar" option from this page. According to Maven, 6.0.1 is the current version.
https://search.maven.org/search?q=a:oauth2-platform-api
I created a folder named "java" off of the root of the web site and stored this file in that folder.
oauth2-platform-api-6.0.1-jar-with-dependencies.jar
In Application.cfc, I added this line of code:
<!--- Application.cfc snippet --->
<cfset this['javaSettings'] = {
LoadPaths = ["#expandPath('/java')#"]
,loadColdFusionClassPath = true
,reloadOnChange = true
,watchInterval = 100
,watchExtensions = "jar,class,xml"
}>
In a test.cfm file I have the following lines of code in a function:
variables['oauth2Config'] = createObject("java", "com.intuit.oauth2.config.OAuth2Config").OAuth2ConfigBuilder(arguments.clientId, arguments.clientSecret).callDiscoveryAPI(Environment.SANDBOX).buildConfig();
In an example on the developer.intuit.com web site, the Java code looks like this:
OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder("clientId", "clientSecret").callDiscoveryAPI(Environment.SANDBOX).buildConfig();
When I run the code, I get the following error message.
Class not found: com.intuit.oauth2.config.OAuth2Config
I have swapped out the "LoadPaths" key with several different variants but all result in the same error.
LoadPaths = ["/java"]
LoadPaths = ["./java"]
LoadPaths = ["./java/oauth2-platform-api-6.0.1-jar-with-dependencies.jar"]
I have tried modifying the initialization call:
local['oauth2Config'] = createObject("java", "OAuth2Config").OAuth2ConfigBuilder(arguments.clientId, arguments.clientSecret).callDiscoveryAPI(Environment.SANDBOX).buildConfig();
That resulted in basically the same kind of error:
Class not found: OAuth2Config
I'm pretty sure that I'm doing something wrong in the this.javaSettings struct in Application.cfc. I was hoping that the Java JAR approach would be easier, but I may end up using the REST API that Intuit has. I still have to use OAuth2 and I'm not sure if the cfoauth tag/function will work with this system.
Let me know if you see anything that will help me get past this problem.

CF can't find OAuth2ConfigBuilder because it's inner class, which requires a slightly different syntax:
createObject("java", "com.intuit.oauth2.config.OAuth2Config$OAuth2ConfigBuilder");
Environment is also a class within the jar. You must create a reference to it before using the constant Environment.SANDBOX
Even after fixing those issues, a brief test with CF2016 threw a LinkageError due to the inclusion of slf4j within the custom jar, while also loading the jars from CF/lib (which includes slf4j too). Switching to loadColdFusionClassPath = false seemed to get past that error.
java.lang.LinkageError: loader constraint violation: when resolving
method
"org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
the class loader (instance of
coldfusion/runtime/java/JavaDynamicClassLoader)
...
Application.cfc
this['javaSettings'] = {
LoadPaths = ["#expandPath('/java')#"]
,loadColdFusionClassPath = false
, ...
};
Code:
variables['environ'] = createObject("java", "com.intuit.oauth2.config.Environment");
variables['builder'] = createObject("java", "com.intuit.oauth2.config.OAuth2Config$OAuth2ConfigBuilder");
variables['oauth2Config'] = variables['builder'].init( arguments.clientId, arguments.clientSecret).
callDiscoveryAPI( variables['environ'].SANDBOX ).buildConfig();

Related

Rewring the connection for lucee

I have this code working for coldfusion, but when i port it to lucee it fails, i know that macromedia does not work in lucee, how can i make this a possibility in lucee and want to remove the connection string, but the result should be exactly like the how the function is returning me
// open a connection to the database
Class = createObject("java", "java.lang.Class");
Class.forName("macromedia.jdbc.sqlserver.SQLServerDriver");
manager = createObject("java", "java.sql.DriverManager");
connectionURL = "jdbc:macromedia:sqlserver://"& SESSION.USER.dbServer &":"& SESSION.USER.dbPortNumber & ";EncryptionMethod=SSL;ValidateServerCertificate=false;";
connection = manager.getConnection(connectionURL, SESSION.USER.dbUser, SESSION.USER.dbPass);
You might define this data source in the Admin instead of in the application code. One thing that screams "big red flag" in your existing code is that the database server name is defined in a cookie. Makes me wonder what other things are in cookie values that need to be refactored.
Read the documentation for defining a data source:
this.datasources["myds"] = {
class: 'org.gjt.mm.mysql.Driver'
, connectionString: 'jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true'
, username: 'root'
, password: "encrypted:5120611ea34c6123fd85120a0c27ab23fd81ea34cb854"
};
The example is for MySQL, but you need to find the correct driver name for SQL Server used by Lucee. Go to the admin and create the DSN there to find the driver name.

Is there an equivalent way of doing 'lb soap' programmatically with loopback?

According to documentation on loopback, lb soap creates models of underlying soap based datasource. Is there a programmatic way to do this? I want to do it programmatically to facilitate a dynamic soap consumption through dynamically created models and datasources.
Disclaimer: I am a co-author and maintainer of LoopBack.
Here is the source code implementing the command lb soap:
soap/index.js
soap/wsdl-loader.js
Here is the code that's generating models definition and method source code:
exports.generateAPICode = function generateAPICode(selectedDS, operationNames) { // eslint-disable-line max-len
var apis = [];
var apiData = {
'datasource': selectedDS,
'wsdl': selectedWsdl,
'wsdlUrl': selectedWsdlUrl,
'service': selectedService.$name,
'binding': selectedBinding.$name,
'operations': getSelectedOperations(selectedBinding, operationNames),
};
var code = soapGenerator.generateRemoteMethods(apiData);
var models = soapGenerator.generateModels(apiData.wsdl, apiData.operations);
var api = {
code: code,
models: models,
};
apis.push(api);
return apis;
};
As you can see, most of the work is delegated to soapGenerator, which refers to loopback-soap - a lower-level module maintained by the LoopBack team too. In your application, you can use loopback-soap directly (no need to depend on our CLI tooling) and call its API to generate SOAP-related models.
Unfortunately we don't have much documentation for loopback-soap since it has been mostly an internal module so far. You will have to read the source code to build a better understanding. If you do so, then we would gladly accept contributions improving the documentation for future users.

Coldfusion - Cant find testbox

I have a coldfusion application that i would like to start writing unit tests for. I found testbox, and think it would be a great testing library to use. I followed the installation instructions, but my cold fusion app is throwing an error when i try to run my first test.
I downloaded the zip file and put in in my C drive, added the mapping to my application.cfc, but it throws the following error (see below). Can anyone help me debug why it can't find textbox?
Error
Invalid CFML construct found on line 2 at column 1.
ColdFusion was looking at the following text:
testbox
The error occurred in C:/inetpub/wwwroot/tests/main.cfc: line 2
1 : // Create TestBox object
2 : testbox = new testbox.system.TestBox();
3 : ​
4 : // You can add fluent specs via addDirectory(), addDirectories(), addBundles()
Application.cfc
component {
this.name = "A TestBox Runner Suite " & hash( getCurrentTemplatePath() );
// any other application.cfc stuff goes below:
this.sessionManagement = true;
// any mappings go here, we create one that points to the root called test.
this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() );
// Map back to its root
this.mappings[ "/testbox" ] = expandPath( "C:/testbox/" );
// any orm definitions go here.
// request start
public boolean function onRequestStart( String targetPage ){
return true;
}
}
main.cfc
// Create TestBox object
testbox = new testbox.system.TestBox();
​
// You can add fluent specs via addDirectory(), addDirectories(), addBundles()
testbox.addDirectory( "specs" );
​
// Run tests and produce reporter results
testbox.run()
​
// Run tests and get raw testbox.system.TestResults object
testbox.runRaw()
​
// Run tests and produce reporter results from SOAP, REST, HTTP
testbox.runRemote()
test box directory.
Try changing your mapping to C:\testbox\testbox\ , or move the contents of your \testbox\testbox folder up one level. I think you may have unzipped to one too many folders. Is there a system folder inside the second testbox folder? You want to make sure that your mapping points to the folder containing your actual TestBox files.

Use wsse security header in soap message (Visual Studio 2015, .Net Framework 4.5)

I would like to consume a Soap Service provided by DHL. You can find the wsdl here: https://wsbexpress.dhl.com/sndpt/expressRateBook?WSDL
Therefore I created a new ClassLibrary in Visual Studio 2015 targeting .net framework 4.5.
Then I added a Web Reference to the created project by providing the wsdl address. I generated a proxy file with all types and ports in it but my first problem is, that the generated Service extends from System.Web.Services.Protocols.SoapHttpClientProtocol. As I read in recent posts it is not possible to get the wsse header to that proxy. Some posts advise to add wse but it seems wse is not supported by newer Visual Studio versions.
I tried to generate my proxy by svcutil. After that I added the generated .cs file to the project and copied the content of the generated config file to app.config. (of cause I removed the web reference)
Now the Service class extends System.ServiceModel.ClientBase. (I thought the generator in VS uses svctool internally. If microsoft want people to use wcf why does the generator generate non-wcf proxy files.
I also created a nunit testproject which should test my service, but If I use the version with the svcutil generated version I get an error. I try to translate it to english as the error is displayed in german:
Could not find a default endpoint element which points to the service contract. As I figured out this is because the proxy is in its own class library and therefor doesn't really have an app.config. But my test project is a class library too.
What would be the actual way to consume a web service which needs ws security Username/Password auth these days?
You can add the Web Reference in compatibility mode (I am guessing you are doing so). If you are not adding the reference in compatibility mode, do the following:
Right click on references Add Service Reference-> Advanced -> Add Web Reference (Below the compatibility section), type the URL of the WS and add the reference.
The WSE2.0 extensions are available as a Nuget Package at:
https://www.nuget.org/packages/Microsoft.Web.Services2/
Install the nuget package on the package manager console running the following nugget command:
Install-Package Microsoft.Web.Services2
After you installed the nuget package, you need to make sure your project is referencing the following DLL's:
System.Web
System.Web.Services
Microsoft.Web.Services2 (This will be added after you install the nuget package)
In order to use the WSE2.0 extensions, you need to actually modify the Proxy class that was created when you added the WebReference to inherit from "Microsoft.Web.Services2.WebServicesClientProtocol" instead of "System.Web.Services.Protocols.SoapHttpClientProtocol". Be aware that if you update the WebReference, the Proxy class will inherit againfrom SoapHttpClientProtocol.
Add the following using clauses to the code consuming the Proxy class:
using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Security;
using Microsoft.Web.Services2.Security.Tokens;
After you make this changes, you code should look something like this:
var token = new UsernameToken("theUser", "thePassword", PasswordOption.SendHashed);
var serviceProxy = new ExpressRateBook.gblExpressRateBook();
SoapContext requestContext = serviceProxy.RequestSoapContext;
requestContext.Security.Timestamp.TtlInSeconds = 60;
requestContext.Security.Tokens.Add(token);
//The rest of the logic goes here...
I added the screenshot down below for your reference:
NOTE: I was unable to test the code since I am unfamiliar with the actual methods that you need to consume, the code displayed is just an example of what I saw in the proxy class, update it according to your needs. It should work fine if you follow the steps described before. Check the following link for more detailed instructions:
https://msdn.microsoft.com/en-us/library/ms819938.aspx
You can configure you Service Reference to add the Security Header as AW Rowse describes at http://cxdeveloper.com/article/implementing-ws-security-digest-password-nonce-net-40-wcf:
private void Configure()
{
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
defaultBinding = new BasicHttpBinding
{
Security =
{
Mode = BasicHttpSecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Digest
}
}
};
defaultToken = new UsernameToken(UserName, Password, PasswordOption.SendHashed);
defaultSecurityHeader = MessageHeader.CreateHeader(
"Security",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
defaultToken.GetXml(new XmlDocument())
);
}
And create you client/proxy like this:
public consulta_informacao_respttClient CriaConsultaClinicaClient()
{
var client = new consulta_informacao_respttClient(defaultBinding, new EndpointAddress("https://resqa.homologacao.unimed.coop.br/chs-integration-external-services-ptu-clinical/proxy-services/execute-query/execute-query-proxy-service"));
client.ClientCredentials.UserName.UserName = UserName;
client.ClientCredentials.UserName.Password = Password;
var scope = new OperationContextScope(client.InnerChannel);
OperationContext.Current.OutgoingMessageHeaders.Add(defaultSecurityHeader);
return client;
}
The properties you will need to create in your class are:
private BasicHttpBinding defaultBinding;
private UsernameToken defaultToken;
private MessageHeader defaultSecurityHeader;
You won't need to configure anything in app/web.config.

Upload file to SharePoint WSS 3.0 with WebRequest PUT

Hey, I've got this nice little piece of code, much like all the other versions of this method of upload using WSS WebServices. I've got one major problem though - once I have uploaded a file into my doc list, and updated the list item to write a comment/description, the file is stuck there. What I mean is that this method will not overwrite the file once I've uploaded it. Nobody else out there seems to have posted this issue yet, so .. anyone?
I have another version of the method which uses a byte[] instead of a Stream .. same issue though.
Note: I have switched off the 'require documents to be checked out before they can be edited' option for the library. No luck tho .. The doc library does have versioning turned on though, with a major version being created for each update.
private void UploadStream(string fullPath, Stream uploadStream)
{
WebRequest request = WebRequest.Create(fullPath);
request.Credentials = CredentialCache.DefaultCredentials; // User must have 'Contributor' access to the document library
request.Method = "PUT";
request.Headers.Add("Overwrite", "t");
byte[] buffer = new byte[4096];
using (Stream stream = request.GetRequestStream())
{
for (int i = uploadStream.Read(buffer, 0, buffer.Length); i > 0; i = uploadStream.Read(buffer, 0, buffer.Length))
{
stream.Write(buffer, 0, i);
}
}
WebResponse response = request.GetResponse(); // Upload the file
response.Close();
}
Original credits to: http://geek.hubkey.com/2007/10/upload-file-to-sharepoint-document.html
EDIT -- major finding .. when I call it from my nUnit test project it works fine. It seems it only fails when I call it from my WCF application (nUnit running under logged on user account, WCF app has app pool running under that same user -- my account, which also has valid permissions in SharePoint).
Nuts. "Now where to start?!", I muses to myself.
SOLVED -- I found a little bug - the file was being created in the right place, but the update path was wrong.. I ended up finding a folder full of files with many, many new versions.. doh!
Why not use the out-of-the-box SharePoint webservice, Lists.asmx? You'll find it in
http://SITEURL/___vti_bin/Lists.asmx
Edit, I checked out the link and it seems you are calling the out of the box web service. This has got be versioning related then. Can you check out the different versions that exist in the doc lib of the specific file? see if it perhaps gets added as a minor version through the service?
Have you tried using a capital T? SharePoint's webdav header processing is not very likely to be case-sensitive, but the protocol does specify a capital T. Oh, and what is the response? A 412 error code or something altogether different?