Sandrino Di Mattia

Content-type: Microsoft

Social

Follow me on Twitter! LinkedIn! RSS! SilverlightShow Contributor

Recent posts

Tags

Categories

Archive

Blogroll

Belgian eID and .NET - Part 3: Using the eID in Dynamics CRM 2011

Dynamics CRM 2011 allows you to manage all kinds of data (xRM) but when you start working with actual people it might come in handy to work with eID cards. Especially in healthcare, education, ... where the end user has to work with a lot of people during the day. Instead of manually entering the data over and over again you could use the eID card to automate this process. That's what we'll build today!
 
Before we get started there are some prerequisites:
Note: This post will explain how to do this in a regular deployment. For IFD I'll write a post in some time explaining how to do this with Windows Azure. 

Java?

Sigh... Java. I never thought I'de mention this in one of my posts but yes... we'll be using Java. On code.google.com you'll find a project named eid-javascript-lib. This project allows you to use the eID Java Applet to make the connection between your websites using Javascript and the Belgian eID. And since Dynamics CRM 2011 relies heavily on Javascript this is the perfect match.
This is how the solution will look like:
 
In CRM 2011 you are able to add some web resources, but the system only allows a few types like HTML, CSS, JS, XML, PNG, ... You might think, why not change the extension to one of these types... but this won't work since the MIME type used to serve the Java applets will not be the right one (like application/x-java-jnlp-file for example, more on this later). That's why we'll need a seperate site to host the Java specific stuff.

Hosting the eid-javascript-lib and the applets

Before we can dive into Dynamics CRM 2011 we need to host the required Javascript and Java files. Remember that this is only suited for regular deployments, the guide to host this in an IFD will follow soon.
 
First we'll create an IIS website that will contain all the data. If your CRM deployment is https be sure to also make the IIS website an https website. If you don't do this, you'll get some nasty popups saying only the secure parts of the page are displayed etc...
 
So I've created a website in IIS with HTTPS binding (https://localhost:100/). In this website I'll be hosting 3 types of files: *.js, *.jar and *.jnlp. If you want these to work correctly you'll need to check if the MIME Types have been configured in your IIS website. Go to your website and open the MIME Types option:
 
 
Be sure the following MIME Types exist: 
 
 
Now the site is ready to host our files. I assume you installed the Belgian eID SDK (see prerequisites). If you did, you should have a directory like this: 'C:\Documents and Settings\<user>\My Documents\Belgium Identity Card SDK\beidlib\Java\bin'. This directory should contain the following files:
  • applet-launcher.jar
  • beid.jnlp
  • beid35JavaWrapper-linux.jar
  • beid35JavaWrapper-mac.jar
  • beid35JavaWrapper-win.jar
  • beid35libJava.jar
  • BEID_Applet.jar
Copy these files to the directory of your newly created website. Now go to the download page of the eid-javascript-lib (http://code.google.com/p/eid-javascript-lib/downloads/list) and download the following files (also in the website's directory):
  • be_belgium_eid.js
  • beid_java_plugin.jnlp
Modify both *.jnlp files, the codebase (2nd. line) should contain the address of the IIS website containing all these files. Here is an example of the beid.jnlp file:
 
<?xml version="1.0" encoding="utf-8"?>
<jnlp codebase="https://localhost:100/" href="beid.jnlp">
  <information>
    <title>Java Binding to Belgium eID Middleware 3.5</title>
    <vendor>Fedict</vendor>
    <offline-allowed/>
  </information>
  <security>
      <all-permissions/>
  </security>
  <resources os="Windows">
      <nativelib href = "beid35JavaWrapper-win.jar" />
    </resources>
    <resources os="Linux">
      <nativelib href = "beid35JavaWrapper-linux.jar" />
    </resources>
    <resources os="Mac OS X">
      <nativelib href = "beid35JavaWrapper-mac.jar" />
    </resources>
  <component-desc />
</jnlp>
 
Finally create a new file in your website called web.config containing the following (this will make sure the Java Applets are hosted correctly):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <staticContent>
            <mimeMap fileExtension=".jnlp" mimeType="application/x-java-jnlp-file" />
        </staticContent>
    </system.webServer>
</configuration>
This should be the end result:
 

Creating the Dynamics CRM 2011 solution

Now that we've done the heavy lifting it's time to do some work in CRM 2011. I've started by adding a new button in the Ribbon of the contact page. Since this post is focused on the eID I will not explain how to modify the Ribbon. Take a look at http://msdn.microsoft.com/en-us/library/gg309639.aspx to get started with Ribbon modifications.
This is my button on the contact page:
 
 
Now the goal is that the user clicks the button and a dialog opens. In this dialog the user will be able to read the eID card and if required store the information in the current contact. First we'll write the code for the dialog. The dialog is actually very simple. We'll just write some HTML to display the form and some Javascript to interact with it:
 
<html>
<head>
	<title>eID: Read</title>
	<script type="text/javascript" src="https://localhost:100/be_belgium_eid.js"></script>
	<script language="javascript" type="text/javascript">
	    // Initialize the reader.
	    var cardReader = new be.belgium.eid.CardReader();

	    // Bind error events.
	    function noCardPresentHandler() { window.alert("No card present!"); }
	    cardReader.setNoCardPresentHandler(noCardPresentHandler);
	    function noReaderDetectedHandler() { window.alert("No reader detected!"); }
	    cardReader.setNoReaderDetectedHandler(noReaderDetectedHandler);
	    function appletNotFoundHandler() { window.alert("Applet not found!"); }
	    cardReader.setAppletNotFoundHandler(appletNotFoundHandler);
	    function appletExceptionHandler(e) { window.alert("Error reading card!\r\nException: " + e + "\r\nPlease try again."); }
	    cardReader.setAppletExceptionHandler(appletExceptionHandler);

	    // Read the card.
	    function readCard() {
	        document.getElementById("content").value = "Please wait ...";
	        window.card = cardReader.read();
	        if (window.card != null) {
	            document.getElementById("content").value = window.card.toString();
	        } else {
	            document.getElementById("content").value = "No card returned.";
	        }
	    }

	    // Save the card to contact.
	    function saveCard() {
	        var data = { firstName: card.getFirstName1().toString(),
	            lastName: card.getSurname().toString(),
	            street: card.getStreet().toString(),
	            streetNumber: card.getStreetNumber().toString(),
	            zipCode: card.getZipCode().toString(),
	            country: card.getCountry().toString(),
	            municipality: card.getMunicipality().toString()
	        };
	        self.opener.onUpdateContact(data);
	        self.close();
	    }
	</script>
</head>
<body style='background-color: rgb(233, 237, 241)'>
<table>
	<tr>
	<td valign='top'>
		<applet code="org.jdesktop.applet.util.JNLPAppletLauncher" codebase="https://localhost:100" 
                width="140" height="200" name="BEIDAppletLauncher" id="BEIDAppletLauncher" archive="applet-launcher.jar,beid35libJava.jar,BEID_Applet.jar">
			<param name="codebase_lookup" value="false">
			<param name="subapplet.classname" value="be.belgium.beid.BEID_Applet">
			<param name="progressbar" value="true">
			<param name="jnlpNumExtensions" value="1">
			<param name="jnlpExtension1" value= "https://localhost:100/beid.jnlp">
			<param name="debug" value="false"/>
			<param name="Reader" value=""/>
			<param name="OCSP" value="-1"/>
			<param name="CRL" value="-1"/>
			<param name="jnlp_href" value="https://localhost:100/beid_java_plugin.jnlp" />
		</applet>
	</td>
	<td valign='top'>
		<textarea id='content' wrap='virtual' cols='80' rows='20'> </textarea>
		<br>
		<input title="Read Card" onclick=javascript:readCard() value="Read Card" type=button> 
		<input title="Save" onclick=javascript:saveCard() value="Save" type=button> 
	</td>
	</tr>
</table>
</body>
</html>
There isn't actually that much to it:
  • Line 4: This references the Javascript file hosted on our IIS Website.
  • Line 7-28: Loads the card and the card reader. It actually interacts with the Java Applet to talk to the hardware.
  • Line 31-42: Puts all the data in an object and returns it to the window that created this dialog (the contact page).
  • Line 49-61: Displays the Java Applet on the page. Mind lines 49, 55 and 60 as they also reference our IIS website.
That's it for the dialog. I've saved the HTML page as a Web Resource and called it contacteid_page (write this down, you'll need this later on).
 
Now we still need to write some code for the Ribbon button and for updating entity. I've created a new Web Resource holding this code.
// Open the card reader page.
function readCard() {
  window.open ("https://crm2011.some.url//WebResources/new_contacteid_page", 'ReadEid', 'width=850, height=450'); 
}

// Update the contact based on the eID data.
function onUpdateContact(data)
{
   Xrm.Page.getAttribute("firstname").setValue(data.firstName);
   Xrm.Page.getAttribute("lastname").setValue(data.lastName);
   Xrm.Page.getAttribute("address1_line1").setValue(data.street + ' ' + data.streetNumber);
   Xrm.Page.getAttribute("address1_city").setValue(data.municipality);
   Xrm.Page.getAttribute("address1_postalcode").setValue(data.zipCode);
   Xrm.Page.getAttribute("address1_country").setValue(data.country);
   Xrm.Page.getAttribute("address1_stateorprovince").setValue('');
};
Again this code is pretty straightforward:
  • The readCard function is called when I click the Ribbon button and will show the dialog. Note that the URL ends with the name of the Web Resource I created for the HTML page. 
  • The onUpdateContact function will be called when I press the Save button in my dialog. Finally the data is used to update the entity.

Our Customizations In Action!

First I've opened my contact list:
 
 
Now I've opened the contact Cathan Cook and pressed the Connect eID button:
 
 
If all goes well you should see a security warning. Press Run to continue:
 
 
To read the card I press the Read button and this is the result:
 
 
To update the CRM contact press Save and you'll see that the dialog will close and your contact will be updated:
 
 
That wasn't so hard was it?

Troubleshooting Javascript

We're working with 2 websites, Javascript, Java Applets, ... and this can sometimes be a pain to troubleshoot. Here are some quick tips!
 
 
When saving the dialog to the entity you might bump into a Permission Denied error caused by the same origin policy (http://en.wikipedia.org/wiki/Same_origin_policy). To fix this issue, make sure the hostname of the dialog's URL is the same as the hostname of the CRM server. In my example I had the following URLs:
Do you see the difference? One link has 'api' in the hostname and the other one doesn't.

Troubleshooting the Java Applet

The following issue took me a while to troubleshoot... when pressing the Read Card button I suddenly got an Error reading card! error. And a few minutes before everything just worked!
 
 
After investigating a bit I stumbled upon the following:
<TD vAlign=top><APPLET id=BEIDAppletLauncher name=BEIDAppletLauncher archive=applet-launcher.jar,beid35libJava.jar,BEID_Applet.jar codeBase="https://localhost:100" code=org.jdesktop.applet.util.JNLPAppletLauncher width=140 height=200><PARAM NAME="_cx" VALUE="3704"><PARAM NAME="_cy" VALUE="5291"></APPLET> </TD>
<TD vAlign=top><TEXTAREA id=content wrap=virtual cols=80 rows=20> </TEXTAREA> <BR><INPUT title="Read Card" onclick=javascript:readCard() value="Read Card" type=button> <INPUT title=Save onclick=javascript:saveCard() value=Save type=button> </TD></TR></TBODY></TABLE></BODY></HTML>
It looked like the code of my HTML page (for the dialog) suddenly changed. Everything was wrapped on two lines and some parts were actually missing. The reason why this happened was because I modified the file using the Text Editor in the Web Resource page. Don't use the Text Editor because it will change your page! Always modify the file locally and upload it using the browse button.

Conclusion

With a bit of magic you can now use the Belgian eID in Dynamics CRM 2011. Think about the possibilities here... this is great for all healthcare, education, ... projects. Projects where you'll want the users to sign in visitors, register people, ...
Enjoy!

Early binding tips and tricks for Dynamics CRM 2011

When you're working with Dynamics CRM 2011 and you need to write plugins or other kinds of integration with .NET code you should make use of early binding. This gives you a context that contains a typed .NET representation of all your entities, relations, ...

    var contacts = from c in context.ContactSet
                    where c.FirstName == "Sandrino"
                    select c;

    foreach (var contact in contacts)
    {
        Console.WriteLine(contact.FullName);
    }

The fact that you have a 1 on 1 representation of all your entities and relations has a lot of advantages:

  • You no longer have to work with magic strings when accessing attributes
  • Every property is typed the correct way. No need to cast attribute values anymore.
  • When entities or attributes are removed, renamed, ... and you update your context your code won't build and you can fix each problem immediately. No more runtime errors.
  • Less bugs
  • Less manual work
  • ...

This context is created based on the metadata of your organization. To view the metadata of your organization you just need to navigate to the following URL: http://server/organization/XRMServices/2011/OrganizationData.svc/$metadata

In Visual Studio you can use this URL to add a Service Reference and have some code generated but I wouldn't advise this. Instead you should use a tool that comes with the CRM SDK named CrmSvcUtil.exe When you start using this tool you'll quickly bump into the same error over and over again:

Exiting program with exception: The caller was not authenticated by the service.
Enable tracing and view the trace files for more information.

Let's run over a few tips on how you could solve this issue:

Proxy Settings

When you're working inside a corporate network that only allows network access via a proxy server you'll need to create a new file in the same directory as CrmSvcUtil.exe and name it crmsvcutil.exe.config Now this file should contain the proxy settings for the application, and it most cases it should look like this:

<?xml version="1.0"?>
<configuration>
  <system.net>
    <defaultProxy enabled="true" useDefaultCredentials="true"/>
  </system.net>
</configuration>

This forces the application to use the proxy and to use the default credentials. If you are still having problems and you're working behind an ISA Proxy you might want to install the Microsoft Firewall Client Management tool from Microsoft. This will help you with most other authentication problems.

Finding the actual error

The error message specifies that you should take a look at the trace files to find the actual error. This trace file is located in the C:\Program Files\Microsoft Dynamics CRM\Trace directory.

Look for the file with the most recent date modified that looks like the file on the screenshot. This will contain the trace message with a detailed exception:

<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
  <TraceIdentifier>http://msdn.microsoft.com/nl-BE/library/System.ServiceModel.Diagnostics.ThrowingException.aspx</TraceIdentifier>
  <Description>Throwing an exception.</Description>
  <AppDomain>/LM/W3SVC/2/ROOT-1-129423140458251760</AppDomain>
  <Exception>
    <ExceptionType>System.ComponentModel.Win32Exception, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
    <Message>The Security Support Provider Interface (SSPI) negotiation failed.</Message>
    <StackTrace>
      at System.ServiceModel.Security.WindowsSspiNegotiation.GetOutgoingBlob(Byte[] incomingBlob, ChannelBinding channelbinding, ExtendedProtectionPolicy protectionPolicy)
      >   at System.ServiceModel.Security.SspiNegotiationTokenAuthenticator.ProcessNegotiation(SspiNegotiationTokenAuthenticatorState negotiationState, Message incomingMessage, BinaryNegotiation incomingNego)>
    </StackTrace>
    <ExceptionString>System.ComponentModel.Win32Exception (0x80004005): The Security Support Provider Interface (SSPI) negotiation failed.</ExceptionString>
    <NativeErrorCode>8009030C</NativeErrorCode>
  </Exception>
</TraceRecord>

In this case the exception string will help me to find the solution.

The Security Support Provider Interface (SSPI) negotiation failed.

If you're working with a virtual machine that is part of an other domain you might get this error (cross domain call). To solve this you'll need to change the way you pass the authentication arguments to CrmSvcUtil.exe Instead of calling CrmSvcUtil.exe using the following line:

CrmSvcUtil.exe /url:"http:/srv/org/XRMServices/2011/Organization.svc" /out:Context.cs
     /username:"sandrino" /password:"pass" /domain:"somedomain" /serviceContextName:Context

Change it to the following:

CrmSvcUtil.exe /url:"http:/srv/org/XRMServices/2011/Organization.svc" /out:Context.cs
     /username:"sandrino@somedomain" /password:"pass" /serviceContextName:Context

By removing the domain argument and appending the domain to the username (separated with the @ sign) you'll solve the cross domain problem.

Other authentication problems

This might be a no-brainer but it will help. If for some reason you find yourself with authentication problems it might have to do with one of the arguments you are passing to CrmSvcUtil.exe That's why it's best to wrap all argument values with double quotes.

CrmSvcUtil.exe /url:"http:/srv/org/XRMServices/2011/Organization.svc" /out:"Context.cs"
     /username:"sandrino@somedomain" /password:"pass" /serviceContextName:"Context"

Creating a useful batch file

To make sure you can easily update your context class you should automate this process. In my case I created a batch file with only 1 command:

CrmSvcUtil\CrmSvcUtil.exe /url:http://server/Contoso/XRMServices/2011/Organization.svc
     /out:"..\Sandrino.CRM.Contoso.Common\ContosoContext.cs" /username:sandrino@CONTOSO
     /password:"mypwd" /serviceContextName:ContosoContext

Now each time I execute the batch file it will call CrmSvcUtil with the correct arguments and overwrite the existing ContosoContext.cs file in my Sandrino.CRM.Contoso.Common project. I don't need to memorize the arguments and I don't need to copy paste the file around. Remember if you are working with TFS you need to check out the file first!

This is how my solution looks like:

Download the sample project: Sandrino.CRM.Contoso.zip (423.70 kb)

CRM 2011 launch: Video of our WP7 demo application



If you attended the Dynamics CRM 2011 launch event in Belgium you might have seen the RealDolmen 'booth' where you could play with the Mobile CRM application on Windows Phone 7. But if you couldn't make it, you can watch the video of the Mobile CRM application:

This video shows a demo application we created for Windows Phone 7 and it uses Windows Azure to connect to Dynamics CRM 2011 Online. The reason why we used Windows Azure was simple, when you're working with a mobile platform you have to consider data transfer and local resources. In the application you have features like finding the closest contacts or calculating a route to a contact. All these calculations happen in the cloud and only the relevant results are sent over to the phone.

If you would like to know more about what CRM 2011 has to offer you might be interested in the RealDolmen CRM 2011 Launch Event on tuesday February 15th

Update: Tweeted by the Dynamics CRM 2011 team

.

Enjoy the video!
Posted: Jan 26 2011, 08:29 by Sandrino | Comments (0) RSS comment feed |
Filed under: .NET | Dynamics CRM

Dynamics CRM 2011 Installation: "The instance name must be the same as computer name."

After having issues with VMware and the corporate network during my last presentation I decided to go back to VirtualBox. VirtualBox seems to work seamlessly with our ISA proxy. This also gave me the opportunity to try out Dynamics CRM 2011 RC1.

Now before installing CRM I first prepared a few base virtual machines. After installing SQL Server I also renamed the machine to VM-CRM2011. Then I finally got to installing CRM 2011, but the system checks failed for SQL Server:

The reason I got this error was because I renamed my machine after installing SQL Server. The fix is quite simple, run the following SQL script to update from the old machine name to the new machine name:

sp_dropserver 'VM-2008R2-AD'
GO
sp_addserver 'VM-CRM2011', local
GO

Finally restart your SQL Server and the system checks should pass without any problem.

Enjoy

Slide deck for my session at Belgian Dynamics Community - Connection Day

Last week I got the opportunity to speak at the Belgian Dynamics Community Connection Day: The Inevitable Cloud

19:30 - 21:00
CRM track - Integrating CRM Online and ERP systems using Windows Azure

RealDolmen

As of January 2011, Microsoft Dynamics CRM will also be available as a SAAS solution. Organizations are ever more attracted to moving out a vast series of business applications to the cloud. This trend however introduces a new level of complexity when it comes to systems integration. This session will give you step by step guidance on how to integrate an on premise ERP application with Microsoft Dynamics CRM Online using the Windows Azure Service Bus.

The live demo with Windows Phone 7, Azure AppFabric and CRM Online turned this into a fun session where everything actually worked like we wanted.

Download: RealDolmen BDC 2010-12-09 - CRM Track.zip (1.92 mb)