Android : Android Consuming Dynamics NAV SOAP Web Service

on Monday, March 23, 2015


I am trying to establish a connection for my android app to dynamics nav 2013 r2 with SOAP web service.


I´ve tried several tutorials found in different forums but failed to establish the connection.


NTLM is enabled on the NST.


This is are the java classes I´ve tried:


AndroidManifest.xml



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="as.myapplication" >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>

</manifest>


MainActivity.java



package as.myapplication;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;


public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//ohneNTLM();
WithNTLM();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}


public void WithNTLM()
{
String namespace = "urn:microsoft-dynamics-schemas/page/customercard";
String url = "http://172.16.1.15:7047/DynamicsNAV71/WS/CRONUS/Page/CustomerCard";
String soap_action = "urn:microsoft-dynamics-schemas/page/customercard:Read";
String method_name = "Read";
String great;
try
{
SoapObject request = new SoapObject(namespace, method_name);

String custID = "323111";
PropertyInfo custProp = new PropertyInfo();
custProp.setName("No");
custProp.setValue(custID);
custProp.setType(String.class);
request.addProperty(custProp);

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

envelope.dotNet = true;
envelope.setOutputSoapObject(request);
// HttpTransportSE transport = new HttpTransportSE(url);

NtlmTransport ntlm = new NtlmTransport();
ntlm.setCredentials(url, "USER", "PW", "DOMAIN","EMPTY??");
ntlm.call(soap_action, envelope); // Receive Error here!
SoapObject result = (SoapObject) envelope.getResponse();
great = result.toString();
}
catch (Exception e)
{
e.printStackTrace();
great = e.toString();
System.out.println(great);
Toast.makeText(this, great, Toast.LENGTH_LONG).show();
}
}


}


JCIFSEngine.java



package as.myapplication;

import jcifs.ntlmssp.NtlmFlags;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;

import java.io.IOException;

/**
* Class taken from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
*/
public final class JCIFSEngine implements NTLMEngine {

private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56 |
NtlmFlags.NTLMSSP_NEGOTIATE_128 |
NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NtlmFlags.NTLMSSP_REQUEST_TARGET;

public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return Base64.encode(type1Message.toByteArray());
}

public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, password, domain,
username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}


NTLMTransport.java



package as.myapplication;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.ServiceConnection;
import org.ksoap2.transport.Transport;
import org.xmlpull.v1.XmlPullParserException;

/**
* A transport to be used with NTLM.
*
* Inspired by http://hc.apache.org/httpcomponents-client-ga/ntlm.html
* @author Lian Hwang lian_hwang@hotmail.com
* @author Manfred Moser <manfred@simpligity.com>
*/
public class NtlmTransport extends Transport {

static final String ENCODING = "utf-8";

private final DefaultHttpClient client = new DefaultHttpClient();
private final HttpContext localContext = new BasicHttpContext();
private String urlString;
private String user;
private String password;
private String ntDomain;
private String ntWorkstation;

public void setCredentials(String url, String user, String password,
String domain, String workStation) {
this.urlString = url;
this.user = user;
this.password = password;
this.ntDomain = domain;
this.ntWorkstation = workStation;

}

public List call(String targetNamespace, SoapEnvelope envelope, List headers)
throws IOException, XmlPullParserException {
return call(targetNamespace, envelope, headers, null);
}

public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
throws IOException, XmlPullParserException {
if (outputFile != null) {
// implemented in HttpTransportSE if you are willing to port..
throw new RuntimeException("Writing to file not supported");
}
HttpResponse resp = null;

setupNtlm(urlString, user, password);

try {
// URL url = new URL(urlString);
HttpPost httppost = new HttpPost(urlString);
// UrlEncodedFormEntity byteArrayEntity =
// new UrlEncodedFormEntity(new ArrayList<BasicNameValuePair>());
// httppost.setEntity(byteArrayEntity);
setHeaders(soapAction, envelope, httppost, headers);

resp = client.execute(httppost, localContext);
HttpEntity respEntity = resp.getEntity();

InputStream is = respEntity.getContent();
parseResponse(envelope, is);

} catch (Exception ex) {
// ex.printStackTrace();
}

if (resp != null) {
return Arrays.asList(resp.getAllHeaders());
} else {
return null;
}
}

private void setHeaders(String soapAction, SoapEnvelope envelope, HttpPost httppost, List headers) {
byte[] requestData = null;
try {
requestData = createRequestData(envelope);
} catch (IOException iOException) {
}
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
httppost.setEntity(byteArrayEntity);
httppost.addHeader("User-Agent", org.ksoap2.transport.Transport.USER_AGENT);
// SOAPAction is not a valid header for VER12 so do not add
// it
// @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
if (envelope.version != SoapSerializationEnvelope.VER12) {
httppost.addHeader("SOAPAction", soapAction);
}

if (envelope.version == SoapSerializationEnvelope.VER12) {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
} else {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_XML_CHARSET_UTF_8);
}

// Pass the headers provided by the user along with the call
if (headers != null) {
for (int i = 0; i < headers.size(); i++) {
HeaderProperty hp = (HeaderProperty) headers.get(i);
httppost.addHeader(hp.getKey(), hp.getValue());
}
}
}

// Try to execute a cheap method first. This will trigger NTLM authentication
public void setupNtlm(String dummyUrl, String userId, String password) {
try {

((AbstractHttpClient) client).getAuthSchemes().register("ntlm", new NTLMSchemeFactory());

NTCredentials creds = new NTCredentials(userId, password, ntWorkstation, ntDomain);
client.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);

HttpGet httpget = new HttpGet(dummyUrl);

HttpResponse response1 = client.execute(httpget, localContext);
HttpEntity entity1 = response1.getEntity();

Header[] hArray = response1.getAllHeaders();
int size = hArray.length;
for (int i = 0; i < size; i ++) {
Header h = hArray[i];
if (h.getName().equals("WWW-Authenticate")) {
entity1.consumeContent();
throw new Exception("Failed Authentication");
}
}

entity1.consumeContent();
} catch (Exception ex) {
// swallow
}
}

//NTLM Scheme factory
private class NTLMSchemeFactory implements AuthSchemeFactory {
public AuthScheme newInstance(final HttpParams params) {
// see http://www.robertkuzma.com/2011/07/
// manipulating-sharepoint-list-items-with-android-java-and-ntlm-authentication/
return new NTLMScheme(new JCIFSEngine());
}
}

public ServiceConnection getServiceConnection() throws IOException
{
throw new IOException("Not using ServiceConnection in transport");
}

public String getHost() {
String retVal = null;
try {
retVal = new URL(url).getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}

public int getPort() {
int retVal = -1;
try {
retVal = new URL(url).getPort();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}

public String getPath() {
String retVal = null;
try {
retVal = new URL(url).getPath();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
}


After running this application I got following message/error:



java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.ksoap2.serialization.SoapObject.toString()' on a null object reference


The access to the web service works when I try to open the page with the android browser.


Any help would be appreciated.


0 comments:

Post a Comment