Wednesday, February 24, 2010

Access external data in SharePoint with BDC (business data catalog), SSO (Single Sign On) and WCF (Windows Communication foundation)

 



image



High Level Summary



Accessing external systems from SharePoint portals is a typical integration scenario. One reality that has not changed is that there are three basic components to any interoperability scheme.




  1. Communication–You must be able to connect and communicate with the foreign system, either through code or through infrastructure.


  2. Identity Management–You must have some way to authenticate users against the foreign system.


  3. Composition–You must be able to assemble discrete pieces of data (from tables, function calls, and so on) into an entity that is meaningful and useful to business users.



There are challenges in all three of these activities, regardless of what you are connecting.



The architecture tackles the four issues elegantly. WCF provides the communication. Single Sign On provides the Identity management. Business data catalog allows different ways to compose such as DataViewWebPart, BDC object model, search and out-of-box BDC web parts.



Comparing with directly accessing WCF web service, my favorite  argument to support this architecture is that BDC is a layer of security boundary in addition to its support to search. BDC allows you to authorize the access to BDC entities through Shared Service web pages. With direct WCF access, there is no easy way to achieve this level of seamless integration and flexibility to control the authorization.



Another advantage of this architecture is to let BDC access external data through SSO, which allows the management of  identities in SharePoint central admin web pages. Invoking WCF or Web Service directly from code will run into the famous double hop issue in a distributed environment unless you hard code the user credentials.  The matter will be even more complicated if you use SharePoint  Form Authentication. BDC has very nice integration with SSO so it is a very nature approach to marry those two technologies together for accessing external data.



Implementation Details



Notice, it is not a step by step tutorial. Instead, it covers important ideas and possible catches during implementation.



Tools




  1. Visual Studio 2008 for WCF development


  2. Microsoft Application Definition Designer for BDC definition.


  3. Microsoft MOSS server for SSO and BDC configuration





Ideas/Catches





  • Use BDC to control the authorization. In the home page of the ShareService administration web site, you will see the “Business data catalog permission” at the bottom-right corner as the following






image





  • Use WCF to encapsulate your external system. BDC supports “basichttpbinding” only. The following is a sample web.config. Here is one of the biggest catch. If running the WCF inside VS2008, use “NTLM”. After deploying the WCF to production, use “Windows”. It appears that VS2008 debug web server has different behavior in terms of WCF authentication. Also, in VS2008, you cannot test WCF authorization through [PrincipalPermission(SecurityAction.Demand, Name = “”)], it won’t work.




<service behaviorConfiguration="WindowsAuthorizationBehavior"
name="BDCContractImplementation">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="WindowsAuthenticationBinding"
contract="IBDCContract">
<identity>
<dns value="host" />
</identity>
</endpoint>
</service>
<behaviors>
<serviceBehaviors>
<behavior name="WindowsAuthorizationBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<windowsAuthentication includeWindowsGroups="true" allowAnonymousLogons="false" />
</serviceCredentials>
<serviceAuthorization principalPermissionMode="UseWindowsGroups"
impersonateCallerForAllOperations="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="WindowsAuthenticationBinding"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"/> <!-- works in VS2008 debug -->
<!--<transport clientCredentialType="Windows" /> --> <!-- works after deploying to IIS -->
</security>
</binding>
</basicHttpBinding>
</bindings>









    • The catch of the return value from IDEnumerator in BDC. If the ID is of type string, you can not return list<string> from IDEnumerator. You must implement a simple class to encapsulate the ID; otherwise, for some reason, Microsoft Application Definition Designer won’t able to handle the method


      /* The small class to encapsulate the string */
      [DataContract]
      public class MeetingID
      {
      [DataMember]
      public string MeetingIDStr { get; set; }
      }
      [ServiceContract]
      public interface IMeetingBDC
      {
      [OperationContract]
      List<Meeting> GetAllMeetings(string filter);
      [OperationContract]
      Meeting GetMeetingSpecificFinder(string meetingId);
      [OperationContract] /* the signature for IDEnumerator */
      List<MeetingID> GetMeetingIdEnumerator();
      }









    • BDC configuration using SSO. Copy paste and modify the following code:




    <LobSystemInstance Name="BDCMeeting_Instance">
    <Properties>
    <Property Name="LobSystemName" Type="System.String">LobMeeting</Property>
    <Property Name="WebServiceAuthenticationMode" Type="Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.WebService.HttpAuthenticationMode">WindowsCredentials</Property>
    <Property Name="SsoProviderImplementation" Type="System.String">Microsoft.SharePoint.Portal.SingleSignon.SpsSsoProvider, Microsoft.SharePoint.Portal.SingleSignon, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Property>
    <Property Name="WebServiceSsoApplicationId" Type="System.String">your_sso_application_id</Property>
    </Properties>
    </LobSystemInstance>

    Monday, February 8, 2010

    Creating custom dialog in Android

    This blog shows you an example on how to create custom dialog. For more information on creating different kind of dialogs, please visit http://developer.android.com/guide/topics/ui/dialogs.html
    1. Create layout for the dialog
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="10sp">
    <EditText android:text=""
    android:id="@+id/categoryEditText"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:singleLine="true">
    </EditText>
    </LinearLayout>




    2. Override onCreateDialog in the Activity



    @Override
    protected Dialog onCreateDialog(int id) {
    switch (id) {
    case CATEGORY_DETAIL:
    LayoutInflater li = LayoutInflater.from(this);
    View categoryDetailView = li.inflate(R.layout.category_detail, null);

    AlertDialog.Builder categoryDetailBuilder = new AlertDialog.Builder(this);
    categoryDetailBuilder.setTitle("Edit Category");
    categoryDetailBuilder.setView(categoryDetailView);
    AlertDialog categoryDetail = categoryDetailBuilder.create();

    categoryDetail.setButton("OK", new OnClickListener(){

    @Override
    public void onClick(DialogInterface dialog, int which) {
    AlertDialog categoryDetail = (AlertDialog)dialog;
    EditText et = (EditText)categoryDetail.findViewById(R.id.categoryEditText);
    if (categories.get(selectedIndex)!=null){
    //... some code
    }
    });

    categoryDetail.setButton2("Cancel", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
    return;
    }});

    return categoryDetail;
    default:
    break;
    }
    return null;
    }



    3. Override onPrepareDialog to dynamically set default value before the dialog is open



    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    switch (id) {
    case CATEGORY_DETAIL:
    AlertDialog categoryDetail = (AlertDialog)dialog;
    EditText et = (EditText)categoryDetail.findViewById(R.id.categoryEditText);
    et.setText(defaultValue);
    break;
    default:
    break;
    }
    }


    4. Call showDialog to display the dialog

    static final private int CATEGORY_DETAIL = 1;
    //... some code
    showDialog(CATEGORY_DETAIL);



    Monday, February 1, 2010

    Passing a list of objects between Activities

    In Android, you can use Bundle to pass data between activities. There are many functions to pass Integer, String, etc. To pass an object, you will need to implement Parcelable interface and override some functions to serialize/de-serialize the object. Here is an example:

    public class Category implements Parcelable {
    private String category;
    private ArrayList<Item> items;

    public String getCategory() {
    return category;
    }

    public void setCategory(String category) {
    this.category = category;
    }

    public ArrayList<Item> getItems() {
    return items;
    }

    public void setItems(ArrayList<Item> items) {
    this.items = items;
    }

    public Category(){

    }

    public Category(String _category){
    category = _category;
    }

    @Override
    public int describeContents() {
    return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(category);
    Bundle b = new Bundle();
    b.putParcelableArrayList("items", items);
    dest.writeBundle(b);

    }

    public static final Parcelable.Creator<Category> CREATOR =
    new Parcelable.Creator<Category>() {
    public Category createFromParcel(Parcel in) {
    Category category = new Category();
    category.category = in.readString();
    Bundle b = in.readBundle(Item.class.getClassLoader());
    category.items = b.getParcelableArrayList("items");

    return category;
    }

    @Override
    public Category[] newArray(int size) {
    return new Category[size];
    }
    };
    }




    To pass an arraylist  of Category to another activity,

    intent i = new Intent(_av.getContext(), ItemList.class);
    Bundle b = new Bundle();
    b.putParcelableArrayList("categories", categories);
    b.putInt("index", _index);
    i.putExtras(b);
    startActivityForResult(i, ITEM_LIST);


    To retrieve the data,

    Bundle b = this.getIntent().getExtras();

    ArrayList<Category> cats = b.getParcelableArrayList("categories");
    int index = b.getInt("index");


    How to add MessageBox in Android?

    Simple message box with no buttons
    AlertDialog alertDialog;
    alertDialog = new AlertDialog.Builder(this).create();
    alertDialog.setTitle("Packing List");
    alertDialog.setMessage("Could not find the file.");
    alertDialog.show();


    Message box with buttons
    AlertDialog deleteAlert = new AlertDialog.Builder(this).create();
    deleteAlert.setTitle("Delete List");
    deleteAlert.setMessage("Are you sure you want to delete this list?");
    deleteAlert.setButton("Yes", new OnClickListener(){

    @Override
    public void onClick(DialogInterface dialog, int which) {
    //...
    }
    });
    deleteAlert.setButton2("No", new OnClickListener(){

    @Override
    public void onClick(DialogInterface dialog, int which) {
    //...
    }
    });
    deleteAlert.show();