Creating WebSphere Portal Apps Using the Visual Studio IDE
|
Overview
|
|
Unlike remoting solutions based on Web Services for Remote Portlets (WSRP), Mainsoft®,
Portal Edition, enables you to use your expertise in Visual Studio®
development and the C#/Visual Basic languages to create portlets that run natively
on IBM WebSphere® Portal and integrate tightly with the Java stack
of the portal server itself.
A comparison between WSRP vs Mainsoft is fully discussed in a Java Developer's Journal article written by Laurence Moroney on approaches to integrating
.NET and Java components within WebSphere Portal.
In this article, we’ll show you how to create a fully featured ASP.NET portfolio
portlet that runs locally on IBM WebSphere Portal as a
JSR 168 compliant portlet. Your C# portlet will have direct access to IBM's
rich set of end-user features, including single sign-on, people awareness, branding,
and inter-portlet communications. At the end of the day, end users won’t know whether
the portlets were created in C#, Visual Basic, or Java, because they behave exactly
the same within the portal environment.
You can find the code for the sample ASP.NET portlet when you install Mainsoft,
Portal Edition, at: <Mainsoft_install_dir>\Samples\{CS|VB}\WebSpherePortal\ASPNETFolio.
|
Getting started
|
|
|
|
 |
|
|
Figure 1. Configuring the IDE for WebSphere Portal server.
|
|
|
 |
|
|
Figure 2. Creating a new portal application.
|
Before we begin, set up an environment with WebSphere Portal 6.0, the Visual Studio
development environment, and Mainsoft, Portal Edition, installed. Check the
system requirements for details.
Then, in the Visual Studio IDE, add WebSphere Portal as a target server for portlet
development. You do this using the Tools > Options menu, and then
selecting Mainsoft for Java EE. If WebSphere Portal is not on the list, click
the Add button to add it. You can see this dialog in Figure 1.
Now you are ready to create a new portal application using the Visual Studio New
Project wizard (see Figure 2).
The project contains the necessary files to build and run your portal application.
In this example, the application is a simple portfolio manager that updates stock
prices based on a publicly available Web service.
In addition to seeing all your familiar files for .NET development, including Default.aspx,
- that we renamed in this sample to ViewForm.aspx - and Web.config,
you'll also find a few new files that are required by the JSR 168 specifications
to deploy your portlet application on WebSphere Portal. You'll see a META-INF
directory that contains the application manifest, a file used to describe your application
to the portal server, and a WEB-INF directory that contains the deployment
descriptor files used by the portal server at deployment time. You'll be editing
some of these files a little later.
|
Creating the Web reference
|
|
|
|
 |
|
|
Figure 3. Adding a Web reference.
|
To create the Web reference, right-click References in the Solution Explorer
and select Add Web Reference. You'll see the Add Web Reference dialog
(Figure 3) into which you need to enter the URL:
http://www.webservicex.net/stockquote.asmx?wsdl. When you are done, select
Add Reference and the Visual Studio IDE will create the Web service proxy
for you.
Using WebSphere Portal theme
Next, we’ll brand the ASP.NET server controls automatically using WebSphere Portal
themes. The ASP.NET controls style elements inherit the stylesheet elements of the
Portal theme (e.g. fonts, colors, table styles, etc.) using the ASP.NET WebSphere
Portal theme file that is part of Mainsoft Portal Application template.
|
|
 |
|
|
Figure 4. WebSphere Portal theme that binds the ASP.NET controls to the appropriate
stylesheet classes WebSphere Portal.
|
Figure 4 shows the WebSphere Portal theme that is part of every portal application
you develop using Mainsoft.
Step 1. Creating the Data Grid and binding it to the portfolio data
Let’s create a data grid that displays portfolio information that is retrieved through
an object data source from an XML file.
First, create the XML file by adding new XML file called portfolios.xml
to your project. Fill out this file with the XML from Listing 1.
Next, create a data source that will load the information from the XML file. Add
a new class file named portfolio.cs. You can find the code for portfolio.cs
in Listing 2.
Change the code to match the namespace of your Web reference:
using ASPNETFolio.net.webservicex.www;
In portfolio.cs, the following function will be used by the ObjectDataSource
control to get the data:
public DataSet GetPortfolioData(String exchange)
Now, create an ObjectDataSource that will fetch the data from the Portfolio object
(through the GetPotfolioData method) and bind it to a GridView. Open the ViewForm.aspx
designer, drop an ObjectDataSource ASP.NET control on it, and set the following
properties:
|
|
 |
|
|
Figure 5. Running the basic portfolio application locally in WebSphere Portal.
|
- TypeName: ASPNETFolio.Portfolio -- This is the data source object.
- SelectMethod: GetPortfolioData -- This is the method that returns the DataSet.
- SelectParameters: Add a new Parameter named "Exchange" with the following settings:
- Parameter source: ProfileParameter // This will fetch only the tickers
from the NASDAQ exchange
- PropertyName: Exchange
- DefaultValue: NASDAQ
If you switch the source view of ViewForm.aspx, your ObjectDataSource control
should look like this:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="GetPortfolioData" TypeName="ASPNETFolio.Portfolio">
<SelectParameters>
<asp:ProfileParameter DefaultValue="NASDAQ" Name="Exchange"
PropertyName="Exchange" />
</SelectParameters>
</asp:ObjectDataSource>
Finally, drop a GridView ASP.NET control on ViewForm.aspx and set its DataSourceID
property to match the ID of the ObjectDataSource you just created (e.g. ObjectDataSource1).
When you run the application, you’ll see the results in Figure 5.
|
|
Step 2. Making the grid visually pleasing – using C#
|
|
|
|
 |
|
|
Figure 6. The GridView property builder.
|
|
|
 |
|
|
Figure 7. Enhancing the grid using your existing ASP.NET skills.
|
The column names in Figure 5 are not intuitive because GridView gets its column
names from the field names by default. So we’ll use the ASP.NET GridView control
to create BoundColumn settings, filter out unwanted fields, and enter user-friendly
text on the column header rather than the data column name.
Go to the property builder by selecting the columns property of the GridView
control. If you are familiar with configuring standard ASP.NET GridView properties,
this dialog box will look very familiar to you. In fact, it is the same property
builder you use whenever you develop ASP.NET projects for Windows (see Figure 6.)
Make sure that the Auto-generate fields checkbox is not selected, and create
new Bound Columns for each field you want to render at runtime. For each of these
columns, enter a Header text entry to set the header text and a Data Field
entry to indicate the data item. So, if you want the column called Ticker
to have the header Ticker or Stock Ticker, enter the text as shown.
Then, run the application again, and you will see the new Column Headings. You can
also use the Data Formatting Expression field to enter a data formatting command
code. Since we are using currency values, the value to enter here for the monetary
fields is ${0:C}. See Figure 7 for the results.
Now the grid looks presentable, thanks to your expertise in .NET development and
ASP.NET!
|
Accessing user profile information
|
|
User customization is an important part of every Web application that serves authenticated
users. Microsoft created the ASP.NET Profile APIs with its built-in storage to make
it easier to manage and store user profile data. Mainsoft extends these capabilities
to the Java EE portal, mapping .NET Profile APIs to the portal user profile management.
In the sample, we fetch profile information from the logged-in portal user using
ASP.NET Profile API. Let’s see how this mapping works.
Mainsoft’s portal application template generates a web.config file that
configures the Mainsoft WebSphere specialized profile:
<profile enabled="true" defaultProvider="WPProfileProvider"
inherits="Mainsoft.Web.Profile.WPUserProfile">
Then, we can use the Mainsoft Profile namespace that defines the WPUserProfile
base profile class in ViewForm.aspx.cs:
using Mainsoft.Web.Profile;
Now, we can access the namespace in the page_load of ViewForm.aspx:
WPUserProfile WPProfile = (WPUserProfile) Context.Profile;
String GivenName = WPProfile.user.name.given;
String FamilyName = WPProfile.user.name.family;
String Email = WPProfile.user.business_info.online.email;
|
|
 |
|
|
Figure 8. The portlet.xml designer.
|
|
|
 |
|
|
Figure 9. Selecting the JSR 168 user profile attributes that we would like to access
|
The WPUserProfile properties are derived from the JSR 168 specifications, which
define the user attribute names. The JSR 168 specifications also require that we
declare the user profile attributes that are accessed by the application in the
portlet.xml deployment descriptor.
Mainsoft provides a Visual Studio based
Portlet Designer that will save you the time and energy you would otherwise
need in learning the JSR 168 specification details. To fetch the user profile vales,
simply open the portlet.xml file (under the WEB-INF directory
in the Solution Explorer), and Visual Studio will open it in Mainsoft Portlet Designer.
Click the Edit User Attributes... button (see Figure 8). This dialog allows
you to declare JSR 168 user profile attributes that are used by your application
(see figure 9).
We will use the user profile values in the next section.
|
Adding people awareness to your application
|
|
Let’s see how to use WebSphere Portal’s people awareness, a WebSphere Portal feature
that integrates with Lotus SameTime collaboration products and is in high demand.
One feature, the presence awareness system, alerts you when your colleagues are
online, so you can collaborate more easily and get your work done more efficiently.
Mainsoft, Portal Edition, makes it easy to use people awareness in ASP.NET portlets.
Mainsoft provides an ASP.NET server control named wpPerson that can be dragged
and dropped to the ASPX designer from one of Mainsoft’s toolboxes, called IBM WP
Web Forms.
Drag a Label control to ViewForm.aspx and label it Welcome.
Next to it, add people awareness to your application by dragging-and-dropping the
wpPerson control into your application. The following code in the page_load event
uses the user profile you already fetched to feed the wpPerson control:
WpPerson1.DisplayName = ((GivenName != null) || (FamilyName != null)) ?
GivenName + " " + FamilyName : Context.User.Identity.Name;
if (Email != null)
{
WpPerson1.ValueType = Mainsoft.Web.Controls.WebSphere.
wpPersonValueType.EMAIL;
WpPerson1.Value = Email;
}
else
WpPerson1.Value = WpPerson1.DisplayName;
Note that this user is the identity of a user in Workplace client technology. If
your system is integrated with Workplace, you will get a number of additional features,
which users can use to interact with each other.
|
Integrating with portal preferences and using Edit mode
|
|
|
|
 |
|
|
Figure 10. Supporting the portlet Edit mode to personalize the portlet.
|
|
|
 |
|
|
Figure 11. Editing your portlet.
|
|
|
 |
|
|
Figure 12. Creating the Edit Form.
|
Let’s now add another page to allow the application users to customize their ASPNETFolio
application by selecting a stock exchange. We will leverage the JSR 168 Edit
mode for this purpose and the integrated Mainsoft Portlet Designer. The JSR 168
specifications define several portlet modes: the default (and mandatory) mode that
you have used up to now is called the View mode. In addition, there are also
Edit, Config, Help, and edit_defaults that are optional
modes.
Open the Portlet Designer and select the Portlet Modes property to open the
Select Portlet Modes dialog box (see figure 10). Enable the Edit mode
and create its start page, just type EditForm.aspx, and press OK.
This will add a new ASP.NET page into your project named EditForm.aspx.
It also tells the JSR 168 portlet container that your portlet supports the Edit
mode, and the portal will display the Personalize menu item in the portlet
control toolbox.
Now, whenever you select the Personalize menu item in your portlet control toolbox,
the EditForm.aspx will be launched (see Figure 11).
Next, add code to the edit page that will get called when your users select Personalize
for the portlet.
Add two labels - a drop-down list and a button - to this Web Form as in Figure 12.
In the Page_Load event of the EditForm.aspx, add the code:
DropDownList1.Items.Add("NASDAQ");
DropDownList1.Items.Add("AMEX");
DropDownList1.Items.Add("NYSE");
if (!IsPostBack)
DropDownList1.SelectedValue =
(String)Context.Profile["Exchange"];
|
The JSR 168 Portlet Preferences is meant for storing portlet customization data.
Mainsoft has mapped the ASP.NET profile custom properties to the JSR 168 portlet
preferences as part of WebSphere Portal’s specialized ASP.NET Profile provider.
So we just have to define a new ASP.NET profile property in our web.config
inside the property section:
<profile enabled="true" defaultProvider="WPProfileProvider"
inherits="Mainsoft.Web.Profile.WPUserProfile">
<properties>
<add name="Exchange" defaultValue="NASDAQ"></add>
</properties>
</profile>
In the click event handler for the Submit button, we need to update the profile
property, stored as a portlet preference, with the drop-down list selected value:
Context.Profile["Exchange"] = DropDownList1.SelectedValue;
Now, your users will expect to return to View mode and to a NORMAL
window state (the Edit mode will run by default in MAXIMIZED) once they click
on Submit. To programmatically switch portlet modes and window states, you need
to invoke the JSR 168 APIs. Mainsoft provides you with a portlet-api reference
that exposes all the JSR 168 classes and all the WebSphere Portal public APIs and
SPIs (Service Portlet Interfaces). Additionally, to ease the access to the inner
JSR 168 objects from the ASP.NET page, Mainsoft also provides you with a specialized
portal page that defines properties for all the JSR 168 objects you need to access:
javax.portlet.ActionResponse ar =
(javax.portlet.ActionResponse)this.PortletResponse;
ar.setPortletMode(javax.portlet.PortletMode.VIEW);
ar.setWindowState(javax.portlet.WindowState.NORMAL);
Notice that your ASPX pages inherit from this Mainsoft.Web.Portal.Page.
public partial class EditForm : Mainsoft.Web.Portal.Page
The stock exchange property stored in the portlet preferences is also used in the
View mode Form to display the stock exchange that is currently selected.
Add a label to your ViewForm.aspx file that will fetch the ASP.NET property:
Label1.Text = "selected stock exchange is " +
Context.Profile["Exchange"];
You can see the entire interaction with the ASPNetFolio portlet in Figures 13a,
13b, and 13c. You can find the code for the ASPNetFolio portlet under the Mainsoft
samples folder: <Mainsoft_install_dir>\Samples\{CS|VB}\WebSpherePortal\ASPNETFolio.
 |
 |
 |
Figure 13a. I am signed into my portal and seeing the portlet in view mode.
|
Figure 13b. I enter Edit mode and change the stock exchange.
|
Figure 13c. I am selecting the people awareness control.
|
|
Summary
|
|
This article took you step-by-step through the process of using the Visual Studio
IDE, C#, and ASP.NET server controls to develop an ASP.NET portlet that runs natively
on IBM WebSphere Portal as a JSR 168 compliant portlet.
We also ventured into new territory, enabling tight integration and interoperability
with the JSR 168 and the WebSphere Portal stack. Once you’ve mastered these development
techniques, you can access WebSphere Portal’s many end-user features from your ASP.NET
portlets, including branding, people awareness, portlet modes, user profiles, and
portlet preferences.
And that’s it! You have the tools you need to build a fully integrated WebSphere
Portal environment with all the rich end-user functionalities that make WebSphere
Portal the industry’s leading portal environment.
Enjoy!
|
|
Listing 1:
|
|
<portfolios>
<portfolio exchange='NASDAQ'>
<item>
<ticker>SUNW</ticker>
<pp>5.15</pp>
<qty>100</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>EBAY</ticker>
<pp>32.20</pp>
<qty>50</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>INTC</ticker>
<pp>18.20</pp>
<qty>1000</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
</portfolio>
<portfolio exchange='AMEX'>
<item>
<ticker>XLE</ticker>
<pp>58.51</pp>
<qty>100</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>IWM</ticker>
<pp>77.45</pp>
<qty>500</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>IIP</ticker>
<pp>1.39</pp>
<qty>1000</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
</portfolio>
<portfolio exchange='NYSE'>
<item>
<ticker>DIS</ticker>
<pp>29.19</pp>
<qty>1000</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>LU</ticker>
<pp>2.77</pp>
<qty>50</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
<item>
<ticker>TWX</ticker>
<pp>17.04</pp>
<qty>100</qty>
<cp>0</cp>
<cv>0</cv>
<del>0</del>
</item>
</portfolio>
</portfolios>
|
|
Listing 2:
|
|
using System;
using System.Data;
using System.Configuration;
using System.Xml;
using System.IO;
using ASPNETFolio.net.webservicex.www;
namespace ASPNETFolio
{
public class Portfolio
{
private String portfolioFile = "portfolios.xml";
private XmlDocument portfolioXml;
public Portfolio()
{
portfolioXml = new XmlDocument();
portfolioXml.Load(portfolioFile);
}
public DataSet GetPortfolioData(String exchange)
{
XmlNode portfolioNode = portfolioXml.SelectSingleNode(
"//portfolios/portfolio[@exchange='" + exchange + "']");
this.GetCurrentValues(portfolioNode);
DataSet ds = new DataSet();
ds.ReadXml(XmlReader.Create(new StringReader(
("<data>" + portfolioNode.InnerXml + "</data>"))));
return ds;
}
private void GetCurrentValues(XmlNode portfolioNode)
{
foreach (XmlNode item in portfolioNode.SelectNodes("//item"))
{
string ticker = item.SelectSingleNode("ticker").InnerText;
Decimal purchase = Convert.ToDecimal(
item.SelectSingleNode("pp").InnerText);
int qty = int.Parse(item.SelectSingleNode("qty").InnerText);
Decimal currentPrice = GetQuote(ticker);
item.SelectSingleNode("cp").InnerText =
currentPrice.ToString();
item.SelectSingleNode("cv").InnerText =
(currentPrice * qty).ToString();
item.SelectSingleNode("del").InnerText =
((currentPrice - purchase) * qty).ToString();
}
}
private Decimal GetQuote(string ticker)
{
try
{
StockQuote sq = new StockQuote();
string stockXml = sq.GetQuote(ticker);
XmlDocument stock = new XmlDocument();
stock.LoadXml(stockXml);
return Decimal.Parse(
stock.SelectSingleNode("//Last").InnerText);
}
catch
{
return 0;
}
}
}
}
|