This is a showcase review for our sponsors at The Code Project. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.
Many organizations have departments or units that use the Windows® SharePoint® Server for ad-hoc collaboration environments while the main enterprise portal infrastructure is built on IBM WebSphere® Portal. If you find yourself in this situation, and you have SharePoint data you want to expose to broader audiences inside and outside the organization, then read on!
This article introduces you to Mainsoft's SharePoint Federator for WebSphere Portal, a Visual Studio®-based software development kit (SDK) that enables you to deliver an integrated user experience across Microsoft® SharePoint sites, .NET and Java™ applications, without sacrificing your workgroup's autonomy. You'll learn how to use your .NET and Visual Studio proficiency to connect Windows SharePoint Services (WSS) and SharePoint sites with a Google Maps™ portlet running on WebSphere Portal.
Figure 1: The Mainsoft SharePoint Federator Web controls
Mainsoft's SharePoint Federator for WebSphere Portal is an add-on to Mainsoft's Portal Edition that integrates federated SharePoint content with .NET and Java applications running on WebSphere Portal. It features ready-to-use portlets that can be easily configured to expose your SharePoint document libraries and your SharePoint lists. These portlets are available in source form and can be tailored to your specific organization's needs. Now you can provide a single point of personalized access to your enterprise applications and contents within a single sign-on, role-based environment. Federating content and services onto WebSphere Portal further enhances SharePoint's capabilities to scale up, and integrate with legacy and back-end systems.
At the heart of the SharePoint Federator SDK are two ASP.NET Web controls (see Figure 1):
- SharePointDataSource
- SaveCredentials
Using them in your ASP.NET forms gives you seamless access to your SharePoint contents. You can surface the SharePoint data into your WebSphere Portal environments using the familiar ASP.NET development framework.
Figure 2: The CAML Visual Designer
The SharePointDataSource control features a design-time wizard for configuring to which SharePoint server you are connecting. At design time, the control lets you specify the SharePoint list or view to retrieve. It also lets you drill down into the data and customize which columns and rows to fetch, using Collaborative Application Markup Language (CAML), the XML-based language used to query SharePoint sites, as well as define the sort order and result set grouping. You can use either a Visual Designer (see Figure 2) or an IntelliSense-enabled XML Editor to make these customizations. The SharePointDataSource control behaves like the standard DataSource controls (e.g. SqlDataSource or ObjectDataSource) you are used to, making it simple to view the SharePoint data in the standard ASP.NET Web data-bound controls.
If you do not have a trusted connection between your WebSphere Portal and your SharePoint site, the users or the administrator will need to configure SharePoint credentials in order to authenticate against the SharePoint site. You can build personalization or configuration forms to capture these credentials and store them in WebSphere Portal Credential Vault. WebSphere Portal Credential Vault is the portal service used by Mainsoft's SharePoint Federator portlets to safely store and retrieve credentials for connecting to SharePoint in a non-trusted environment. The credential slots stored in WebSphere Portal belong to one of two types: user or admin. With a user Credential Vault, your portlet users will enter their own credentials through the Edit form. Using admin slot, your portal administrator will be able to configure shared credentials for all portlet users. The SaveCredentials control (see Figure 3) lets you prompt the users (or administrator) for SharePoint credentials on the first visit and store them in WebSphere for single sign-on access in future sessions.
Figure 3: The SaveCredentials control
The SaveCredentials control is typically added to the portlet's Edit mode page.
The shared Credential Vault slot is best used in scenarios where your portal users will access the SharePoint site without having to configure their own SharePoint credentials. In some situations, your corporate portal users may not have direct access to a departmental SharePoint site. Then, the portal administrator can use shared credentials and will rely exclusively on WebSphere Portal authentication to authorize access to the SharePoint data.
You are not limited to using the visual controls to access the stored credentials of the SharePoint user that is currently logged in. The CredentialVaultSlot
class offers a simple point of access to specific credential vault slot data. This can be useful in scenarios where you need to authenticate with different back-end systems that use the SharePoint credentials (e.g.: an e-mail server) or simply present the username of the currently logged in SharePoint user that is currently logged in:
CredentialVaultSlot cvs = CredentialVaultSlot.Open(PortletRequest, "SharePointAccess");
Label1.Text = cvs.Username;
One of the more important benefits of surfacing your SharePoint content into portlets is the synergy your users gain by wiring together, or combining, different portlets on the same page. For example, you can have one portlet present an items listing, while a second portlet displays the selected item's details. These wires, called InterPortlet Communication (IPC), are defined by the portal user. While IPC is not a part of the original JSR 168 portlet specification, it is integrated in the upcoming JSR 286 specifications and will be an integral part of the WebSphere Portal end-user experience.
Below, I will show you how to integrate SharePoint content into an enterprise workflow, based on a fictional but realistic coding scenario. The sample application will show the SharePointDataSource, Credential Vaults, and IPC in action.
The SharePoint Federator in Action
A large law firm wants to make life a little easier for their hard-working associates. The senior partners have decided to use their portal for presenting the firm's client list, integrated with Google Maps and simple ways of communicating with clients. Although the firm is using WebSphere Portal for serving their portal solution, they are using Microsoft SharePoint to maintain their client contact list. Also, the in-house .NET development team has plenty of Visual Studio experience, and the firm is eager to make good use of their familiarity with Microsoft's IDE and Framework as the new project kicks off.
The technology parameters in this background story form a common scenario in many of today's organizations.
Let's take a look at the actual requirements. To make the portal site more useful, while at the same time spicing it up a little, the page will feature three portlets:
Figure 4: The New Project dialog with Visual C# and Visual Basic for Java EE project types
- Law Firm Client List - listing the clients using a DataList .NET control.
- Google Maps - a map for viewing the client's location using Google Maps JavaScript API.
- Contact Info - a detailed view of the selected contact. The user can contact client directly, using the information stored in SharePoint.
By wiring the portlets together, the map and contact info portlets will automatically refresh as the user selects different contacts.
This example is written in Visual C#®, but as with all of Mainsoft's interoperability solutions, it would work just as well using Visual Basic®.
Full source code is available for download. Before building and deploying the source code, you must modify the connection string to point to your own SharePoint Server. The connection string is stored in the file Web.config.
Let's begin with firing up Visual Studio, installed with Mainsoft's Portal Edition. If you do not already have Mainsoft's Visual Studio add-in installed, you can download a fully functional 30-day trial version. I start out by creating a new project using the template ASP.NET Portal Application, under the new project type Visual C# for Java EE (see Figure 4). Mainsoft's add-on also enables you to take an existing ASP.NET solution, and deploy it as a Java EE application onto WebSphere Portal. To learn more about converting existing .NET projects to Java, read the article, "Creating WebSphere Portal Apps Using the Visual Studio IDE".
Connecting the main portlet to the SharePoint list data with the client's contact details could not be simpler. In the Visual Studio toolbox, locate the SharePointDataSource control in the Mainsoft SharePoint Federator group. Add the control to the design surface of ClientList.aspx
, the file that makes up the default view mode for the new portlet.
The SharePointDataSource control features a smart tag (a small arrow in the upper right corner, see Figure 5) that enters the configuration wizard for the SharePoint data source. Using the wizard, customize the SharePoint data that will be retrieved. The first page of the wizard hosts the connection string parameter. You have the option to specify different authentication schemes at runtime and design time, which you need to do if you are using the WebSphere Credential Vault since it is not accessible from within Visual Studio. Typically, you authenticate using either a trusted connection (with your Windows user credentials) or a WebSphere Portal Credential Vault, specifying the resource name.
Credential Vault authentication requires a few more lines of code, compared to trusted connections that work out-of-the-box. Credential Vault authentication comes in two flavors: user and admin. The site administrator can add a shared admin credential slot, containing valid SharePoint login information, which is available to all WebSphere Portal users. Private credential slots are defined by and for each user. I am using the private credential vault in the sample application.
I fill in the SharePoint URL and the private Credential Vault's resource name (here named "SharePointAccess") in the new data source dialog (see Figure 6).
The wizard's second page is where you specify the SharePoint list you will display. Next up you have the option to custom-select the results using standard CAML query syntax in the wizard. Here, I use a custom query, because I only want U.S. customers returned. In the wizard's fifth page, I define the query in Figure 7, using the Visual Query Designer:
You can construct very complex queries using the Visual Designer, and in case you want to fine-tune them, you can use the IntelliSense-enabled XML Editor. Later on, you can modify any aspects of your SharePoint connection using the control's properties.
Now, I need a way of displaying the data fetched in the SharePointDataSource. I choose to use a standard ASP.NET DataList Web control, which I connect to the SharePoint data source in the same way as I would connect it to any other standard .NET data source.
Figure 7: The CAML query defined in the Visual Designer
One of the differences between a DataList control and the more common GridView control is that the DataList will not display any columns by default (GridView displays all columns from the data source). Using the DataList's Edit Template command, I choose the data rows. Here, I'll present the name and company of each client in our SharePoint list using the format "<full name> at <Company>". This is achieved by adding a Label control to the DataList's ItemTemplate. In the Label's DataBindings properties, I use Custom Binding with the following Code Expression:
Eval("FullName") + " at " + Eval("Company")
Figure 8: The Portlet Designer
The Eval() method takes the name of the SharePoint data source column as a parameter and returns the value of the current row.
I've also added two hidden labels that will be used for sending data to other portlets. One contains the current address of the client, while the other holds the e-mail address. I will return to these fields when it is time to use them. I also add an ImageButton to the ItemTemplate, which will ensure that the user can select items in the list. Any command-type button control (Button, LinkButton, or ImageButton) in the ItemTemplate enables selections.
Now, let's make it easier for end users to register their SharePoint credentials in WebSphere's Credential Vault on the first visit. Even though it is possible for portal users to add private Credential Vault slots using the administration interface, it is much more user-friendly to prompt them for their SharePoint usernames and passwords using the portlet's Edit mode. Add Edit mode to the portlet by opening the WEB-INF\portlet.xml
file, which shows the project portlets' properties using Mainsoft's Portlet Designer (see Figure 8). Here, you can configure the portlet completely, without having to learn the JSR 168 portlet specification.
In Portlet Designer, click the "...
" button in Portlet Modes, enable Edit mode, and specify a new .aspx
filename to associate with Edit mode. The new ASPX file is, at this point, generated automatically.
I add the SaveCredentials Web control (available from the Mainsoft SharePoint Federator control group) to the new form (see Figure 9).
In the control's properties, I set ResourceNameto "SharePointAccess", which is the same resource name I configured the SharePointDataSource to use to retrieve data at runtime.
Figure 9: The SaveCredentials Web control on the designer surface
First-time visitors to the portlet will run into the problem that when they will load the View mode (which is the default mode) no user credential will have been set and a SharePointDataSource exception will be thrown. I need to catch this exception and let the user know that before the portlet can be used, the SharePoint credentials must first be set in the Edit mode (named Personalize in the browser). One way to skin this cat is to override the Page_Error() method and display an informative message. Using this technique, I add the following implementation to ClientList.aspx
, containing the portlet's View mode code:
public void Page_Error(object sender, EventArgs e)
{
Exception ex = Context.Error.GetBaseException();
if (ex is UnauthorizedAccessException || ex.Message.Contains("Unauthorized"))
{
Context.ClearError();
Context.Response.Write("<b>SharePoint authentication
failed.</b><br />Please add your SharePoint login information in the
portlet's personalize mode to access the client list.");
Context.Response.End();
}
}
Figure 10: The SharePoint client list running on WebSphere Portal
That's it for the first iteration of development. Running the application at this point will present us with the following portlet, after entering the SharePoint credentials (see Figure 10):
This is actual data from the SharePoint server, aggregated in WebSphere Portal within a single sign-on environment. All accomplished in just a few simple steps, manually adding only a few lines of error-handling code!
So far, so good. Now it's time to make things more interesting by adding IPC support, which enables the portlets to communicate with each other. To enable the client list portlet to transmit information about the selected item, I need to make the following modifications:
- Specify the datatype and other meta-information of the transmitted data in a Web Services Description Language (WSDL) file.
- Map the portlet to the WSDL file.
- Enable the ProcessAction property.
- Add parameters to the PostBackUrl property for the DataListItems' ImageButton.
- Handle the DataList's ItemCommand event to transmit the actual value over IPC.
Knowing that our two target portlets will render a map and display contact information, using the wpPerson Web control, I specify two output parameters from the portlet. Below are two snippets from the WSDL file, which I add to the project:
[…]
<types>
<xsd:schema targetNamespace="http://www.ibm.com/wps/p2psample3">
<xsd:simpleType name="AddressType">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
</types>
[…]
<binding
name="GoogleMapSearchBinding"
type="tns:GoogleMapSearch_Service">
<portlet:binding/>
<operation name="ClientDetails">
<portlet:action name="ClientDetailsAction"
type="standard"
caption="ClientDetails.action"
description="Get the details of the selected client"
actionNameParameter="ClientDetailsActionParam"/>
<output>
<portlet:param name="ClientDetailsText"
partname="ClientDetails_Text"
boundTo="request-attribute"
caption="ClientDetails.text"/>
</output>
</operation>
</binding>
Going into the details of WSDL is outside the scope of this text. Mainsoft's Developer Zone has a good introductory article about IPC, called "Developing Cooperative Portlets Using Mainsoft, Portal Edition". All you really need to know for now is that the "types" element specifies a string datatype, and that data being communicated between portlets must use the same datatype on the sending and receiving end. This datatype is used to bind two output parameters from the LawFirmClient portlet, one is the selected item's work address ("WorkAddressActionParam"), and the other the name and email address ("ClientDetailsActionParam") separated with a semi-colon. The full contents of the WSDL file are available in the download of the project source code.
Figure 11: Directing WebSphere Portal to associate a WSDL file with a portlet
I use the Portlet Designer to connect the WSDL file to the portlet. A new key value pair in the Preferences collection tells WebSphere Portal to make the association (see Figure 11).
While still in Portlet Designer, I enable the Process Action property. This will cause any postbacks from the portlet to the Web server to use the ProcessAction phase, where IPC messages are handled.
In order to enable the client list portlet to send information to its fellow portlets, the event fired when a user selects a DataListItem needs to be handled. As all underlying IPC infrastructure is in place, I only need to add a few lines of code to set the correct value of the appropriate IPC parameter. I add a handler to the DataList's ItemCommand event, which is fired when the item selection changes:
protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
{
string name = ((Label)e.Item.FindControl("NameLabel")).Text;
string address = ((Label)e.Item.FindControl("HiddenAddress")).Text;
string email = ((Label)e.Item.FindControl("HiddenEmail")).Text;
Client client = new Client(name, address, email);
PortletRequest.setAttribute("ClientDetailsText", client.ToString());
}
The implementation of the event takes the name, work address and e-mail address for the selected client and inserts it as string into a PortletRequest attribute. The PortletRequest object is used to carry the IPC data between the portlets. I am using a helper class (Client) for building a string from the client details.
To enable the IPC action parameter, I need to add an input field, which I make hidden. The input field contains the name and actionNameParameter from the WSDL's action element as follows:
<input type="hidden" name="ClientDetailsActionParam" value="ClientDetailsAction" />
I have now enabled the portlet with the ability to send IPC data to other portlets.
Next up, I will add two additional portlets and have them receive and parse the data sent by the client list portlet, and render their own representation of it.
Figure 12: Configuring additional portlets - LawFirmClients, GoogleMaps and ClientDetails
The Portlet Designer is used to create and configure two additional portlets in our portal application. The Add Portlet button creates a new portlet. I let each portlet's View mode be implemented in two new ASPX pages by setting the View mode in the Portlet Modes property (see Figure 12).
The Google Maps portlet will use the Google JavaScript API to render the client's location in a portlet. In order to embed the Google Maps service in a portlet, you will have to sign up for a Google Maps API key. This key will be used in the portlet's JavaScript code to initialize the API access. Although the downloadable LawFirmClient application contains a Google Maps API key, I recommend that you apply for your own and use it.
A minimalist HTML design is sufficient to implement the map portlet. A single div tag with the "map" ID is the placeholder the Google Maps API will use to render the requested map. I need to handle the IPC data sent from the client list portlet and resend it to the Google Maps API as a JavaScript function parameter.
The first step is to add a WSDL file for the map portlet, containing the same datatype as the WSDL file for the client list. The main difference is that this file defines input elements instead of output elements. Second I create a new mapping between the WSDL file and the portlet using Portlet Designer as previously described. Third, I add the logic for retrieving and processing the parameter value. This step is added to the map portlet's class as follows:
public partial class GoogleMaps : Mainsoft.Web.Portal.Page
{
protected string searchAddress = "";
protected void Page_Load(object sender, EventArgs e)
{
SetRenderParameter("RenderClientDetailsText", Request.Params["ClientDetailsText"]);
}
}
PageLoad()
is called twice on callbacks containing IPC data for portlets that have the ProcessAction
property set to True. The first call contains the IPC data as a HTTPRequest
parameter, while the second is the standard PageLoad()
call. In order to separate the logic between the two calls, you can use the IsInProcessAction
, a property inherited from Mainsoft.Web.Portal.Page
. IsInProcessAction
is true for the IPC call and false for the standard call. In the source code above, the member variable SearchAddress
is used to store the address retrieved from IPC in the HTTPRequest parameter "GoogleMapSearchText" (the name is defined in the WSDL file). This value is then injected into the JavaScript code communicating with the Google Maps API, using the code below that I insert into the markup (which is only executed during the render phase). It also sets the input field to display the actual address text in the portlet.
<%
string clientData = (string)Request.Params["RenderClientDetailsText"];
if (!String.IsNullOrEmpty(clientData))
{
String[] properties = clientData.Split(
if (properties.Length == 3)
this.searchAddress = properties[(int)LawFirmClients.eClientDetails.Address];
}
%>
Figure 13: The wpPerson Web control
For the ClientDetails portlet, which will be used to enable communication, I use the Mainsoft's wpPerson Web control (see Figure 13). The wpPerson control provides contextual collaboration functionality related to a named person, and it is similar in syntax and behavior to the WebSphere Portal person tag. It displays the online status for a portal user, taking into account Domino and Extended products and servers installed and enabled in the portal environment.
Configuring the ClientDetails portlet follows the same steps as the map portlet. I add a WSDL file, which defines the IPC input bindings, hook it up to the portlet using the Portlet Designer, and add the logic. Do you remember that the two hidden labels in the client list portlet contain the work address and e-mail address of the selected user? This is the data, together with the client's full name, that is being sent over IPC. I encode the data as a string, and on the receiving end, the string is decoded, and it uses the fields relevant for the target portlet. The code below shows how the ClientDetails portlets decodes the IPC string and uses the data to set the control’s properties. The first section comes from the code-behind, while the second code snippet is inserted into the markup:
protected void Page_Load(object sender, EventArgs e)
{
SetRenderParameter("RenderClientDetailsText", Request.Params["ClientDetailsText"]);
}
<%
string clientData = (String)Request.Params["RenderClientDetailsText"];
if (!String.IsNullOrEmpty(clientData))
{
String[] properties = clientData.Split(';');
if (properties.Length == 3)
{
WpPerson1.DisplayName = properties[(int)LawFirmClients.eClientDetails.Name];
WpPerson1.Value = properties[(int)LawFirmClients.eClientDetails.Email];
}
}
%>
Figure 14: The Portlet Wiring Tool in WebSphere Portal Server
The implementation is now ready for end-user consumption. It is important to notice that once the application has been deployed, the portal user needs to activate the wires himself. This is done by using the Portlet Wiring Tool (see Figure 14), accessible from the Wires tab under Edit Page Layout in WebSphere Portal Server.
After adding the two wires (one from the client list to the map; the other one from the client list to the client details), we are good to go. Figure 15 shows a screen-shot of WebSphere Portal running our application with SharePoint contents, Google Maps data and the WebSphere Portal person-enabling communication features.
Figure 15: The final result
Thank you for reading this walkthrough! The full source code for the LawFirmClient project can be downloaded from the menu in the left-hand column of this page. You are welcome to download the evaluation version of Mainsoft, Portal Edition, which contains SharePoint integration, and see for yourself what it's like to integrate SharePoint data with enterprise Java applications, without having to rely on complex interoperability layers or external development teams.