As my blog identity indicates, I am the .Net Ninja – self proclaimed, which is definitely a little weak…but how else do we form a web persona?  

Outsiders may see the persona name, or company I work for, and assume that I only do .Net or work with Microsoft (GP) products; this is simply not the case.  So naturally, when a client requests a project that is outside that box, I am intrigued.   One or my recent project requirements can be simply stated:  “We must integrate data to and from Great Plains using Java. ” 

The requirement is best met using GP web services, as interoperability and logistics are of the utmost importance.   This was my recommendation to the client, which was met with some hesitation.  I was surprised.  I began to recall my early days as a developer – I spent 4-5 years working with Java.  It was the first language I had ever used.  My familiarity led to confidence – I proposed a proof of concept project, in which I would help build a sample Java client.  Googling this topic (GP Web Services and Java) does not turn up many positive results, largely because the Java and .Net communities do not seem to communicate effectively…not sure why, and on many occasions, the “opposing members” meet with apprehension. 

My online research came up with many threads that said things like “I am a .Net developer…I do not know Java.  Or, I am a Java developer…I do not know .Net”.  So, what do we really need?  Someone who knows both (at least to some degree).  Enter the .Net Ninja, who surprisingly knows Java…after all, Java made his transition to C# a breeze.  Can you percieve my ego?  What good consultant does not have one?   Well, I will admit, I did make some decisions that would make my life easier.

I built a simple java Swing application (Image 1) in Eclipse that would provide data entry for standard GL Journal Entry transactions.  I constucted business objects for use with the client; my purpose was to plug-in the Java Client Proxy after having all the other pieces in place…plus, it would be a good exercise in recollection.  When I got to the point that I was gonna make the Client Proxy using Eclipse, which uses Axis libraries, I ran into my first road block.

GP2010 Web Services do not run out of IIS.  Instead, they use the newer Service Host model and run directly Administrative Tools->Services.  Additionally, Microsoft built two endpoints for connecting: Legacy and Standard.  The Legacy endpoint basically boils down to the ASP.NET 2.0 implementation many are familiar with – basicHTTP binding and an .asmx file.  The Standard endpoint uses wsHTTP binding and a WCF service -newer technology.

I tried connecting Eclipse to both using the Eclipse interface – neither would connect.  I always got an error regarding a naming conflict in the WSDL.  Some research indicated that I could alter the web service config files to get by the error – no such luck.  I should admit here, that a colleague was able to generate the proxy from Eclipse using the wsdl2java tool from the command line, but I am not aware of his environment, Eclipse version…I didn’t even bother to ask, as I already had a solution when he told me.

I decided to change my Java toolset…enter Project Tango, NetBeans, and Glassfish Metro.  Project Tango is around for this specific purpose – interoperability between .Net web services and Java.  After changing to the latest NetBeans tools (Metro included), I was able to generate the Client Proxy using just the interface…no command lines, no issues.  I did bind to the Legacy endpoint, as opposed to the Standard endpoint.  This decision also simplified my project. (Image 2)

With this hurdle out of the way, I was ready to start sending transactions to Great Plains 2010.  The solution worked immediately after I plugged in the remaing web services client logic (Image 3). 

Of course, I was running under administrative context -  able to connect to GP as God and do anything I want.  When I tested as a different user…failure.

This is where the important information comes into play…a little bit of knowledge about .Net services and Java Proxy Clients goes a long.  NetBeans uses the JAX-WS toolset, which uses java.net for its protocols, authentication, and handshaking.  The issue boils down to, “How can I impersonate a windows user account with java.net progammatically when connecting to GP2010 Legacy Web Serices?” (Image 4)

The solution involves three components:

  1. Dynamics Security Console (Image 5): This component is installed on the same machine that Web Services are installed.  It can be located by going to Administrative Tools->Dyanmics Security Console.  This piece is important because it allows use to assign a windows user account to specific security roles, granting access to only what we need.  We can use the pre-assigned windows accounts as our “java” impersonation account.  Configuration of a windows user account to a security is trivial if you have the correct domain permissions.
  2. java.net.authenticator and javax.xml.ws.BindingProvider :  These are the java libraries I used to override to default user account used during web service handshaking.  Code has been provided below.  Basically, we must create a custom Authenticator, set it as the default Authenticator used by java.net, and then override the user name and password in the service client using the BindingProvider functionality.  After much research, I found that Authenticator did not provide good support for NTLM authentication…maybe it can be done, but I did not find it to be simple.  I did find that Authenticator worked great for Basic and Digest authentication.  In that case, I would use Digest, which is considered more secure than NTLM anyway.
  3. WSBinding.config: This is a configuration file located in a subdirectory in the GP2010 Web Services root folder.  It is located in the ServiceConfigs folder.  This config file determines what authentication mechanism GP2010 web services use when a client connects.  There is separate configuration for both the Legacy and Standard endpoints.  By default, the Legacy service is set to use NTLM, but is easy to change.  I tested NTLM, Windows, Basic, Digest, and None authentication types.  NTLM and Windows did not work using this solution.  Basic and Digest did.  None is not applicable, as Web Services should require an authenticated user to perform its own security set by the Dynamics Security Console in #1.  My config file has been provided below.  Just look for the node  “transport” with attribute “clientCredentialType”

For #2, here is the code I used for my Authenticator:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class CustomAuthenticator extends Authenticator{

    private String myUserName;
    private String myPassword;

    public CustomAuthenticator(String username,String password)
    {
        super();
        myUserName = username;
        myPassword = password;
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication()
    {
        char[] pwdChar = myPassword.toCharArray();
        return new PasswordAuthentication(myUserName,pwdChar);
    }
}

For #2, here is the code I used when override the default Authenticator and Binding Provider properties:

import javax.xml.ws.BindingProvider;
import java.util.Map;
import java.net.URL;
import java.net.Authenticator;

 protected void btnTestClick()
    {
        //My SuperUser credentials
        String uName = “DEMO\\Administrator”;
        String uPass = “XYZ123″;

        //Create a CustomAuthenticator and establish that is the default
        //java.net Authenticator
        Authenticator.setDefault(new CustomAuthenticator(uName, uPass));
        URL dgpWsdlUri;
        String dgpuri = “http://DEMO:48620/Metadata/Legacy/Full/DynamicsGP.wsdl“;
        try
        {
            dgpWsdlUri = new URL(dgpuri);

            //Get the service interface and legacy endpoint 

            DynamicsGP_Service service = new DynamicsGP_Service(dgpWsdlUri);
            System.out.println(“** Got DynamicsGP Service interface ***”);
            DynamicsGP dynamicsGP = service.getLegacyDynamicsGP();

            //Override the URL; I only put this in to show how.
            BindingProvider bp = (BindingProvider)dynamicsGP;
            bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, jftfURL.getText());

            //Override default BindingProvide information
            Map<String, Object> map = bp.getRequestContext();
            map.put(BindingProvider.USERNAME_PROPERTY, uName);
            map.put(BindingProvider.PASSWORD_PROPERTY, uPass);

            //Begin Web Services Work

            //……

            //End Web Services Work

        } catch (Exception e)
        {
            //Handle Exceptions
        }

    }

For #3, here is my WSBinding.Config:

<?xml version=”1.0″ encoding=”utf-8″?>
<bindings>
  <basicHttpBinding>
    <binding name=”BasicHttpBindingTarget”>
      <readerQuotas maxDepth=”2147483647″ maxStringContentLength=”2147483647″ maxArrayLength=”2147483647″ maxBytesPerRead=”2147483647″ maxNameTableCharCount=”2147483647″/>
      <security mode=”TransportCredentialOnly”>
        <transport clientCredentialType=”Digest”/>
      </security>
    </binding>
  </basicHttpBinding>
  <wsHttpBinding>
    <binding name=”WSHttpBindingTarget” maxBufferPoolSize=”524288″ maxReceivedMessageSize=”128896″ messageEncoding=”Text” textEncoding=”utf-8″ useDefaultWebProxy=”true”>
      <security mode=”Message”>
        <message clientCredentialType=”Windows”/>
      </security>
    </binding>
  </wsHttpBinding>
  <customBinding>
    <binding name=”CustomBinding”>
      <textMessageEncoding>
        <readerQuotas maxDepth=”2147483647″ maxStringContentLength=”2147483647″ maxArrayLength=”2147483647″ maxBytesPerRead=”2147483647″ maxNameTableCharCount=”2147483647″/>
      </textMessageEncoding>
      <httpTransport maxBufferPoolSize=”2147483647″ maxReceivedMessageSize=”2147483647″ maxBufferSize=”2147483647″/>
    </binding>
  </customBinding>
</bindings>

Remember to place this config file in the correct location, and then restart the services using Administrative Tools->Services before trying to connect. 

Hopefully, this article will help people that come across this “niche” situation, which shouuld help prevent un-necessary hair loss and cursing.  There are many things not discussed here (how to use Proxy objects, lower security layers, firewalls, etc.), but this solution should get many people off the ground and running.

“Those who realize their folly are not true fools.” – Chuang Tzu

My Sample Application - Not Too Pretty JAX-WS Web Service Client Proxy Generated By NetBeans The Sample Transaction Integrated Into GP Simplified Security Scenario The Dynamics Security Console

Post to Twitter Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to MySpace Post to Ping.fm Post to Reddit Post to StumbleUpon