Grasshopper 2.0 fully integrates with Visual Studio 2005 and offers an implementation of new ASP.NET 2.0 features. In this article, we'll see how you can use the Grasshopper 2.0 and ASP.NET 2.0 controls to develop a Web-based accounting application with membership and role-based security and to deploy it on Linux. We will use Master Pages and new controls, including: Login, which is an out-of-the-box authentication feature; Wizard, which is used to split large forms and maintain state consistency; and the powerful GridView, a data control offering GUI-manageable support for paging and sorting. The sample application, originally developed for the annual JavaDay in Rome, uses the new ASP.NET 2.0 providers featured with Grasshopper. These Grasshopper 2.0 providers are based on Apache Derby™, an embedded database that offers a pure Java runtime alternative to MS SQL Express for ASP.NET 2.0 Membership and Role Profile providers. Moreover, we show in this sample how to access application data, which remains stored in MS SQL Express 2005. Finally, we'll show you how to take advantage of the integrated debugger, based on open standard JPDA (Java Platform Debugger Architecture), and how to create a deployment package that can be used over multi-platform application servers. |
|
It All Begins With a Good Database |
| |
| Figure 1: The JDExpenses Database. | There is no denying it: creating a good application database makes application development go much faster. JDExpenses is an imaginary accounting application, developed for JavaDay, which allows the conference speakers to input their expenses, and the administrator to view each person's balances. Note that there is no need for authentication and role database tables since this Grasshopper version features the ASP.NET 2.0. Membership and Role API. The balance, defined as a BigInteger, demonstrates how Grasshopper handles a native Java type such as BigDecimal. Besides which, I prefer big expense accounts! :-) In order for us to use the SQL Server from a Grasshopper application, we need to configure the database. Obviously, Windows Authentication will not work for an application that can potentially run on any operating system for which a JVM is available. We are therefore going to log in using SQL Server authentication and enable Mixed Mode Authentication in the database properties. (See Figure 2.) To do this, you can use Microsoft’s SQL Server Management Studio Express (SSMSE) tool, freely available for download from Microsoft. Launch SSMSE, sign in, select the database, and right-click. From the context menu, select Properties. You'll see the SQL Server Properties dialog. Select Security and make sure that the SQL Server and Windows Authentication mode radio button is selected. Next, we need to enable the preset sa account for access via SQL Server Authentication since it is disabled in the default setting (see Figure 3). From the same tool, expand Security > Logins > sa. Right-click and select Properties. Switch to Status page and set Login to Enabled. This sample assumes that you have a blank password for sa user. If not, update the password in the JavaDayConnectionString property in the Web.Config file. To deliver multi-platform portability, Grasshopper's JDBC driver for SQL Server (the same one provided by Microsoft) supports connections via TCP/IP only, and not via Named Pipes. We therefore need to run the SQL Server Configuration Manager from Start > Program Files > Microsoft SQL Server 2005 > Configuration Tools, and enable connectivity via TCP/IP (Figure 4). Restart the SQL Server after making these changes. | Figure 2: Naturally, an application that can potentially run on any operating system cannot resort to Windows Authentication; it is therefore necessary to enable SQL Server's Mixed Mode Authentication. | | | Figure 3: It may be necessary to enable the "sa" user. | | | Figure 4: The JDBC driver used to access SQL Server supports TCP/IP connectivity only; so we'll need to enable the protocol in the SQL Server Configuration Manager. | | |
All for One and One Template for All: The Master Pages |
| |
| Figure 5: Let's create a new Grasshopper application. | The Master Pages of ASP.NET 2.0 allows you to create a consistent look and feel for our applications by defining a template that will be used for each page. First, create a new application from File > New > Project > Visual C# for Java EE project template (Figure 5). Make sure the Tomcat Application Server that comes with Grasshopper 2.0 has been launched. From the Solution Explorer of Visual Studio, add a new Master Page. We can work on this graphic template directly in Design mode, adding all the necessary elements (Figure 6). The ContentPlaceHolder element will receive the personalized content from each page to which the template is applied. In this case, I am certain you will have no trouble recognizing the two characters in the column on the left; they may even work in your company's Accounting Department! :-) We can now use the template for all pages. In Default.aspx, in the @Page directive, add the new attribute MasterPageFile, whose value will match the Master Page that was just created (Visual Studio will suggest this automatically). Select the Title attribute, which will overwrite the same attribute on the Master Page. In the Source view of Default.aspx, delete all the code under the @Page directive and replace it with an control: | | Figure 6: Once a Master Page has been added to the Visual Studio project, it will be possible to define the template entirely in Visual mode. Pay special attention to the ContentPlaceHolder element that will receive each page's specific content. | Figure 7: Once a Master Page has been defined, it is possible to edit its content, though the Master Page area itself cannot be modified. | Naturally, the content will be associated with a ContentPlaceHolder with the same name defined in the Master Page. Next, start adding content to the page (Figure 7). Notice that the area defined in the Master Page cannot be edited in the pages that use it. Making changes to the template is a simple matter of updating the Master Page. A preview of Default.aspx shows that the title is also replaced in runtime mode. |
Tell Me Who You Are and I Will Tell You What You Can Do: Role-Based Access Control |
| |
| Figure 8: The Grasshopper 2.0 version of the WSAT, executed from within the application server. |
| |
| Figure 9: The Grasshopper version of the administration tool for the Membership, Role, and Profile API database: the creation of roles... |
| |
| Figure 10: ...and of users. Notice how roles are assigned to a user upon creation. | The time has come to "witness in action" the extraordinary role-based authentication features provided by the ASP.NET 2.0 controls, thanks to the infrastructure available for each application through the Membership and the Role APIs, which can be configured graphically with the Web Site Administration Tool (WSAT). The operation of the ASP.NET 2.0 Membership is the subject of a series of articles on MSDN, and for issues concerning implementation, I recommend reading this tutorial: Walkthrough: Creating a Web Site with Membership and User Login (Visual Studio). On Microsoft .NET, the WSAT graphics tool creates an ASPNETDB.MDF MS SQL Express database in the default App_Data application folder in a manner that is transparent to the user. Grasshopper 2.0 creates, in the same folder, an ASPNETDB database based on Apache Derby, a pure Java open source database and offers its own version of the WSAT tool. Whereas ASP.NET 2.0 offers an AspNetSqlMembershipProvider that frees the developer from the detailed handling of the SQL Server database containing the Membership data, Grasshopper 2.0 provides an analogous AspNetDerbyMembershipProvider defined in the Mainsoft.Web.Security namespace. For JDExpenses, we need to set up two components: ExpensesManagement, which enables speakers to input their expenses, and BalancesReport, which will be used by the accounting team to retrieve the account data. The first component will be accessible to all speakers, who are going to be associated with the role of speaker and to the accounting team; the second component will be accessible only to the accounting team, defined by the role of admin. From the Solution Explorer, create two folders by the same names. These will contain the pages pertaining to the two elements. Then, compile the application and launch the Grasshopper 2.0 version of the WSAT, which can be launched from the Visual Studio IDE or can be accessed from within the application runtime via the URL http://localhost:8090/JDExpenses/aspnetconfig/Default.aspx (Figure 8). Create the roles (Figure 9) and the users (Figure 10) for the application. When creating the users, we will assign specific roles by checking the appropriate boxes in the form. The last link in the administration page, Manage Users, allows modification of these settings after the users have been created (Figure 11). To set up access control and determine which roles have access to which parts of the application, we will use ASP.NET 2.0 to define appropriate allow and deny rules in the web.config file. In this case, our tasks are to: - Deny access to the application subfolders to all users, by default.
- Allow access to the ExpensesManagement subfolder to both the speaker and admin roles.
- Allow access to the BalancesReport subfolder to the admin role only.
The rule set will be as follows: |
|
| |
| Figure 11: The Grasshopper 2.0 version of the WSAT's user management page. | At runtime, Grasshopper's ASP.NET 2.0 providers, based on Derby, will create an aspnetdb folder containing the Derby store that corresponds to the SQL Server database used by Microsoft ASP.NET 2.0. By default, the folder App_Data is used, and during development, it will be looked up from the project folder so projects can be shared or copied together with their membership data. When deploying the application as a WAR file, App_Data will be looked up from the deployment directory. When necessary, the system administrator responsible for application deployment will be able to change this setting, thanks to a new context parameter in the web.xml deployment file: |
|
At runtime, the application developer will be able to retrieve the value of this parameter in the program itself: |
(string)AppDomain.CurrentDomain.GetData("DataDirectory") AppDomain.CurrentDomain.SetData("DataDirectory","/usr/tmp"); |
Controls for Accessing JDExpenses |
| |
| Figure 12: In the application's main page, add two HyperLink controls that connect to two JDExpenses elements by assigning a value to the NavigateUrl property. |
| |
| Figure 13: The Login control of ASP.NET 2.0 handles form-based authentication. | Now we are ready to take advantage of the authentication tools offered by ASP.NET 2.0, which are accessible from the Login group of the Visual Studio Toolbox. In Default.aspx, add two HyperLink controls and connect them to the ExpensesManagement.aspx and BalancesReport.aspx pages, two elements of JDExpenses, through the NavigateUrl property (Figure 12). Since both pages require authentication (they belong to two folders that require it), the user will be redirected to the page that contains the login form, which by default is Login.aspx. It is possible to change the default page from web.config, but it is unnecessary and would complicate this example. Let's create a Login.aspx page in which we will drag the Login control from the Toolbox. As is customary for the ASP.NET 2.0 controls, we can personalize its aspect using the associated Smart Tag, which is accessible through the black arrow on the edge of the control. The Login control manages the handling of username and password input errors through the integrated Validator controls in a way that is transparent to the developer, and it can use a cookie to remember the user's data for future logins via the Remember Me check box. We would normally assign a value to DestinationPageUrl and MembershipProvider, but in this case it will be unnecessary. The destination page will be handled by the HyperLink that calls up Login.aspx, handled autonomously by Grasshopper 2.0. Launch the application, click on either of the two HyperLinks in the Home Page and type the credentials in the login page. This should allow you to access the login page and the application itself (Figure 14). You'll notice the classic "welcome message" and logout links are personalized with the username. These were achieved out-of-the-box thanks to three controls in the Login group: LoginView, which provides a personalized message depending on the authentication state of the user; LoginName; and LoginStatus. The last two require very little work: LoginName is a pure drag-and-drop control, and LoginStatus requires you to simply personalize the messages that appear before the user has logged in or is already authenticated, using the LoginText and LogoutText properties, respectively. LoginView requires a little personalization of the views for anonymous or registered users by means of the associated Smart Tag (Figure 15). Ultimately, we could have achieved the same results working in the Code view of the page and inputting the simple code below: |
Authenticated! Welcome, You're not logged. Click over "Login"
|
As mentioned, the Login control also handles credential input failure and failed authentications (Figure 16); by selecting the Remember Me Next Time check box (associated with the RememberMeSet property), users will not be asked for their data the next time the application is launched. | | | Figure 14: Access to areas that require authentication is illustrated. Note the welcome message and the logout link, obtained using the LoginView, LoginName, and LoginStatus controls. | Figure 15: The LoginView control requires only minimal personalization of the templates for anonymous or registered users using the associated Smart Tag. | Figure 16: Through ValidatorControls, the Login control handles failure to input username, password, or both, and shows an error message if the authentication has failed. | |
Inputting Data: The Wizard Control and the Data Sources |
| |
| Figure 17: The Wizard control offers various means of personalizing from the GUI, in terms of both graphical and functional aspects. | In order to sample the authentication features, we used a simple stub page for ExpensesManagement.aspx. We can certainly do better. We will use this page to allow users to input a good deal of data, thanks to the Wizard control. This new ASP.NET 2.0 control can be found in the Standard group of the Visual Studio Toolbox. It is designed to allow the creation of a large number of fields, which are shown to the user a few at a time. This feature offers an out-of-the-box menu and a series of buttons to navigate between the various pages, while maintaining their state of course. Drag a Wizard control onto the page and have a look at the outline of the generated code: |
|
| |
| Figure 18: The DropDownList control Smart Tag allows association of a data source by the Wizard. | The wizard consists of a series of steps, each of which allows a series of personalization thanks to the GUI provided by the associated Smart Tag. It is almost needless to say that from the Smart Tag itself we can choose a series of graphical templates thanks to the Auto Format feature (Figure 17). Let us begin to populate the first step of the control. From the Toolbox, drag the Label and Calendar controls that the speaker will use to select the date of JavaDay, that he or she has taken part in. There is no reason in particular to select a date using a Calendar control; but, oh, what would a Grasshopper tutorial be, without a Calendar? :-) Let's insert another Label and a DropDownList, used to select the host city from the corresponding table in the SQL Server 2005 Express database. Here again, Visual Studio comes to our aid by allowing us to connect the drop-down box to a data source by means of a wizard (Figure 18). |
| |
| Figure 20: ...the type will be SqlDataSource. |
| |
| Figure 19: In the first step of the Data Source wizard, choose to create a new data source... | Select Choose Data Source. The wizard's first step appears. Choose to create a new data source (Figure 19), whose type will be SqlDataSource (Figure 20). Click OK. It will now be possible to create a new database connection through New Connection (Figure 21). The Add Connection dialog box will appear (Figure 22). Select the SQL Server instance to be used, the authentication via SQL Server Authentication, and the database that contains the data. In order to be able to use the SQL Server 2005 Express data source from a Java application server, we still have one last step to complete: enabling the use of TCP/IP instead of the Named Pipes. |
Click the Advanced button. From the connection properties, choose to use TCP/IP under Network Library. Also, note how Integrated Security is set automatically to False. Click on Test Connection to ensure that everything works correctly. Our connection string is now ready (Figure 24). When the time comes for deployment on a non-Windows platform, one last change needs to be made — this involves substituting the named instance of SQL Server (STEFANENKO\MSSMLBIZ in the figure) with the IP address of the database server. To do this, you will have to open the Web.Config file, and replace Data Source=.\SqlExpress with Data Source=\SqlExpress. Click Next to persist the connection string in web.config, and visually create the SQL query (Figure 25), whose testing will take place in the last step of the Data Source wizard. In much the same way, associate a DropDownList to the database Accounts table in order to select the name of the speaker. Finally, add a TextBox so that the total number of JavaDay participants can be inputted. The final result is shown in Figure 26. |
| Figure 21: New Connection allows us to start creating the database connection. | | | Figure 22: From within the Add Connection dialog, choose the SQL Server 2005 Express instance to be used, the SQL Server Authentication, and the database. Finally, click "Avanzate" (Advanced) to configure the connection via TCP/IP. | | | Figure 23: In the connection properties, choose TCP/IP under Network Library. | | | Figure 24: Our connection string is now ready. At the time of deployment on Linux, we will have to substitute the name of the SQL Server instance with the IP address of the machine that hosts the database server. | | | Figure 25: The visual editor of SQL queries, provided by Visual Studio. | | | Figure 26: The first page of the Data Input wizard is now complete. The connection to SQL Server works perfectly, with the JDBC driver provided by Grasshopper 2.0. | | |
Travel Expenses, or: How I Learned to Love External Java Classes |
We are not going to fill in the second page of the Wizard control with data. Instead, we'll use a series of labels that contain the travel expenses incurred by the speaker selected on the first page. Note, first of all, that there is no need to retrieve the value from a DropDownList, since the control handles the persistence of data in a manner that is transparent to the developer. However, in a real application, the travel expense database would, in all likelihood, be populated by another application, or better still, by several applications: one hosted by the travel agency, another by the hotel chain where the speaker resides, yet another hosted by the restaurant chain where the speaker eats, and so forth. So why not add a "touch of realism" to JDExpenses? The data pertaining to travel expenses are in fact sourced from another application, JDTravel, provided by the agency that takes care of the logistics for this event. Let us take a look at the application's source code: |
package JDTravel; import java.lang.*; import java.util.*;
public class TravelExpenses { private String speaker; private int expensesTravel; private int expensesLodging; private int expensesBoard; public TravelExpenses() { }
public int[] calculateExpenses(String surnameToSearch) { int[] speakerExpenses = {0,0,0}; HashMap expenses = getExpensesByERP(); if (expenses.containsKey(surnameToSearch)) { speakerExpenses = expenses.get(surnameToSearch); } return speakerExpenses; }
protected HashMap getExpensesByERP() { HashMap expensesTable = new HashMap(); int[] DeNictolisExpenses = {1243, 234, 4985}; int[] LandiniExpenses = {4324, 3487, 7893}; int[] CutriExpenses = {2362, 9832, 3489}; int[] BaccanExpenses = {348, 6895, 3894}; String DeNictolis = "De Nictolis"; String Landini = "Landini"; String Cutri = "Cutrì"; String Baccan = "Baccan"; expensesTable.put(DeNictolis, DeNictolisExpenses); expensesTable.put(Landini, LandiniExpenses); expensesTable.put(Cutri, CutriExpenses); expensesTable.put(Baccan, BaccanExpenses); return expensesTable; } } Listing 1: A Java class developed using NetBeans. |
There is no denying it: this is not a typical .NET application! In fact, we created it with the lean, educational version of NetBeans 5.0,BlueJ. The public method calculateExpenses takes care of an array of integers containing the speaker expenses, retrieving them from a HashMap and using a surname passed on as a parameter as the key. The HashMap is returned by an internal method, getExpensesByERP. While this method simply populates the HashMap with a series of value put assigned in a hard-coded manner, its name hints at a deeper notion: if the map were really produced by a complex application, such as an ERP, the wrapper calculateExpenses method would make the results available within our .NET application. To enable and use application classes, right-click the project name in the Solution Explorer and choose Add Java Reference. Click the Browse tab and select the BlueJ JAR - JDTravel.jar - executing the build of the JDTravel (Figure 27) project. To make the project internally consistent, I added the JAR to a new ext (external) folder under the Visual Studio JDExpenses project. Adding the JAR to the project references is a little time-consuming, and if we look at the reference properties when the job is done (Figure 28) we will realize why: Grasshopper records the JAR as if it were a native .NET assembly, assigning values to many of the properties of a "common" assembly. In order to be able to use the TravelExpenses class, an instance of it must be created in the transition to the wizard's second page. In practical terms, we must handle the Wizard control's NextButtonClick event. Add the instruction — OnNextButtonClick="LoadExpenses" — to the attribute of the Wizard control in the Code view of ExpensesManagement.aspx (Visual Studio features auto-completion with IntelliSense) and write the handling code, in the LoadExpenses method, in the page's code-behind, which is to say in ExpensesManagement.aspx.cs. This is an event handler with the following signature: protected void LoadExpenses (object sender, WizardNavigationEventArgs e) |
| |
| Figure 27: The Add Java Reference dialog allows us to add a Java class library packaged in a JAR file that will be regarded as a .NET assembly. |
| |
| Figure 28: Grasshopper assigns to the JAR, which was added to the project, several of the properties of a native .NET assembly, thanks to its own assembly management based on the Java EE application server shared classpath. | | |
Having defined it as protected, we will not have to add the instruction AddHandler in Page_Load(). In the code-behind, add the following directive: |
|
And create an instance of the class, as well as a private variable that will receive the array of integers returned by the calculateExpenses method: |
private TravelExpenses ExpensesJD = new TravelExpenses(); private int[] expenses; |
Go back to ExpensesManagement.aspx and in Design mode move to the second step of the Wizard control by selecting the corresponding Various expenses item defined in the control's menu. Bear in mind that before the compilation stage, you will need to re-select the first Wizard step, and then the execution will begin from the page highlighted during compilation. Then, add three labels that will help us show the user's travel, room, and board expenses: |
Text="Travel expenses: " Font-Size="X-Large"> | | Text="Lodging expenses: " Font-Size="X-Large"> | | Text="Board expenses: " Font-Size="X-Large"> | |
|
Populate them using the following code: protected void LoadExpenses(object sender, WizardNavigationEventArgs e) { expenses = ExpensesJD.calculateExpenses(this.ddlSpeaker.SelectedValue); this.lblTravelExpenses.Text = "Travel expenses: " + expenses[0].ToString() + " dollars"; this.lblLodgingExpenses.Text = "Lodging expenses: " + expenses[1].ToString() + " dollars"; this.lblBoardExpenses.Text = "Board expenses: " + expenses[2].ToString() + " dollars"; }
| |
| Figure 29: A Java class has populated our ASP.NET 2.0 page: the beauty of Grasshopper. | Besides the great simplicity of the code, notice what actually happened when the expenses variable was assigned its value: a Java array, which is an instance of java.lang.reflect.Array, was transformed into an array, which is to say an instance of System.Array implemented .NET's ICollection. It would be rather difficult to achieve this transformation in such an efficient way if we resorted to the Web Services as an integration technology. The result is shown in Figure 29. Cross-Platform Debugger To see Grasshopper's integrated debugger in action, insert a breakpoint in the row in which the expenses variable received its value, clicking as usual on the gray bar beside the row numbers, and launch the application by pressing F5. The code flow will be suspended at the breakpoint, as expected, and we will be able to use all our familiar debugging tools: call stack, local variables window, and Step Over, as well as the opportunity to verify the value of a variable by mouse-over (Figure 30).
| |
| Figure 30: Grasshopper 2.0's debugger, based on JPDA, incorporates all Visual Studio's familiar tools, including breakpoints, call stack, local variables window, Step Over, and the possibility to verify variable values. | As a final task for the ExpensesManagement element, we need to handle the data gathered in the Wizard control. This control does not execute the data POST, not even in the type-safe form of the ASP.NET 2.0 cross-page posting. Rather, it reveals the FinishButtonClick event, where we shall execute not only the persistence of the inputted data, but the management of the state as well. The most suitable device among those offered by ASP.NET 2.0 for a self-contained and cross-platform application is the Session object. We shall therefore add this call to the event handler - OnFinishButtonClick="WizardDataManagement" - and write the persistence code in ExpensesManagement.aspx.cs, as follows: |
protected void WizardDataManagement(object sender, WizardNavigationEventArgs e) { int DollarsForPresent = 10; int generalExpenses = 0; IEnumerator it = expenses.GetEnumerator(); while ((it.MoveNext()) && (it.Current != null)) generalExpenses = generalExpenses + (Decimal)it.Current; BigDecimal generalExpensesBD = PrimitiveTypeUtils.DecimalToBigDecimal(generalExpenses); BigDecimal presents = new BigDecimal(this.txtPresent.Text); BigDecimal unitaryFee = new BigDecimal(DollarsForPresent); BigDecimal fee = presents.multiply(unitaryFee); fee = fee.add(generalExpensesBD); updateBalance(fee);
Session["Speaker"] = this.ddlSpeaker.SelectedValue; Session["City"] = this.ddlCity.SelectedValue; Session["JDate"] = this.JDCalendar.SelectedDate.ToShortDateString(); Session["Fee"] = fee; }
private void updateBalance(BigDecimal amount) { string connectionString = ConfigurationManager.ConnectionStrings["JavaDayConnectionString"] .ConnectionString; SqlConnection conn = new SqlConnection(connectionString); try { conn.Open(); string sQuery = "UPDATE Accounts SET Balance = Balance + " + amount + " WHERE SurnameAccount = '"; sQuery = sQuery + this.ddlSpeaker.SelectedValue + "'"; SqlCommand comm = new SqlCommand(sQuery, conn); comm.ExecuteNonQuery(); conn.Close(); } catch (SqlException err) { Console.WriteLine(err.Message); } } |
The actual persistence in the SQL Server database is executed by the updateBalance private method that receives the speaker's total remuneration. This method is nothing new for developers who use ADO.NET. It is far more interesting to see how the application calculates this remuneration - that is to say the sum of the overall expenses (retrieved by means of a simple Iterator on the expenses array of integers, whose elements are cast at Decimal) plus a fixed amount (DollarsForPresent) for each speaker. Actually, the number of participants is a little unrealistic, since it amounts to more than ten thousand times the planet's population, but the use of a BigDecimal, that is to say a native Java type (requiring using java.math directive) is an excellent way of demonstrating Grasshopper 2.0's integration potential at the code level. To convert generalExpenses from a .NET Decimal to a Java BigDecimal, we will use PrimitiveTypeUtils.DecimalToBigDecimal, a helper method provided by Grasshopper and available in the vmw.common namespace (subject to using), once we add the J2SE.Helpers.jar classes among the project references through Add Java Reference. This kind of conversion would be quite difficult to achieve if we chose to integrate .NET and Java applications using the Web Services! The number of participants and the remuneration per person are retrieved respectively by the TextBox of the Wizard control and by an internal variable, since the BigDecimal builder accepts either a String or an int. At this point, we carry out "simple" arithmetic operations on very large numbers via the BigDecimal multiply and add methods, invoke the persistence method in the database, and memorize the values input in the Wizard controls through as many Session variables. The application's last page, Review.aspx, allows us to simply verify the persistence of the remuneration in the database as well as verify that the Session variables have been actually assigned their values. Having pressed the Finish button of the Wizard control, after the execution of the method associated to the FinishButtonClick event, we shall be redirected automatically to this page, as long as it has been specified as a value in the FinishDestinationPageUrl property of the Wizard control. Let us now retrieve the speaker's balance on the Page_Load of Review.aspx event: |
protected void Page_Load(object sender, EventArgs e) { string connectionString = ConfigurationManager.ConnectionStrings["JavaDayConnectionString"] .ConnectionString; SqlConnection conn = new SqlConnection(connectionString); try { conn.Open(); string sQuery = "SELECT Balance FROM Accounts WHERE " + "SurnameAccount = '" + Session["Speaker"] + "'"; SqlCommand selComm = new SqlCommand(sQuery, conn); SqlDataReader r = selComm.ExecuteReader(System.Data.CommandBehavior .SingleRow); if (r.HasRows) { while (r.Read()) this.lblBalance.Text = r.GetValue(0).ToString(); } else { this.lblBalance.Text = "zero"; } r.Close(); conn.Close(); } catch(SqlException err) { Console.WriteLine(err.Message); this.lblBalance.Text = "ERROR. Data not recovered"; } }
|
| |
| Figure 31: The Review.aspx page serves the purpose of showing us that the speaker's remuneration persisted in SQL Server and that the Session variables, which are meant to receive the values of the Wizard controls, have actually received these values. | And show it to the user in a Label, as well as retrieve the values in the Session variables code. The final result is shown in Figure 31. The Speaker's Account Report and the GridView Control All that remains to do now is to use the GridView control to generate the speaker's current account report, in other words the contents of the Accounts table, which will be shown in the BalancesReport.aspx page. Once the Master Page has been defined in the usual manner, switch to Design mode and drag GridView from the Toolbox Data control onto the work area. Personalize the graphical aspect of the GridView with the usual Auto Format command from the associated Smart Tag. Also, from the Smart Tag, choose the database Accounts table as a data source for the control using the Data Source wizard. Once the wizard is completed, select the Enable Paging and Enable Sorting check boxes of the Smart Tag to enable the corresponding features. There is nothing else to do - the reporting page is ready (Figure 32). Clicking on a column's header allows sorting of the values according to the column in question (Figure 33), whereas we can see the pagination at the bottom of the control, nice and proper (Figure 34). The GridView control is one of the best new features of ASP.NET 2.0. With it, we managed to create a paginated report with sorting options without having had to write a single line of code! At most, we could modify the default number of rows in each page (10) by changing the control's PageSize property, but this is also a task that can be carried out in the Design mode of Visual Studio. |
| | | Figure 32: A reporting page created without writing a single line of code, thanks to the GridView control. | Figure 33: By clicking on column headers we can sort the data... | Figure 34: ...while at the bottom of the control, the pagination is nice and proper. | |
What is Grasshopper Without Linux? |
| |
| Figure 35: The Java build category of the project property allows us to change the deployment method from Incremental to Full deployment package (WAR). The default value in Debug_Java configuration is set incremental to shorten the development cycle and is set to WAR in Release_Java configuration to create a full deployment package. | Now that our application is ready, we can deploy it on a Linux machine. First, we need to create the deployment package, which is as easy as switching your configuration from Debug_Java to Release_Java. By default, the deployment method in the Release configuration is set to Full deployment package (WAR) (see Figure 35) and creates a production-ready WAR deployment package. Our WAR file is created under the bin directory JDExpenses\bin\JDExpenses.war and is 22 Mbytes big, since it contains all Grasshopper Framework libraries. When you deploy several applications on the same server, you would probably prefer to move the Framework libraries to the shared classpath. To run the application on Linux, Tomcat must be installed; in all likelihood we will also need to make some small changes. By default, almost all distributions of Linux use a version of Tomcat featuring JDK 1.4.2; however, we developed the JDTravel class using JDK 1.5.0, and we therefore need to install it and configure the application server to suit. From the Sun download site, download JDK 1.5.0 for Linux (the version with the .bin extension) and install it on the machine simply by launching the file. Let us say it was installed in /opt/jdk1.5.0_06; on most Linux installations it will be sufficient to change the JAVA_HOME variable to suit, before launching Tomcat, with: |
export JAVA_HOME=/opt/jdk1.5.0_06 |
| |
| Figure 36: The Tomcat version included in the Red Hat Linux distributions allow modification of the JAVA_HOME variable from the configuration file. | In order to avoid having to carry out this procedure before each run, we need simply to modify the Tomcat launching script. In the case of Red Hat Linux, the featured Tomcat version offers a simpler and faster method. All you have to do is open the /usr/share/tomcat5/conf/tomcat5.conf file and modify the JAVA_HOME row (Figure 36). Copy the WAR package on the Linux machine – for example, through a Samba share – and deploy it using the Tomcat Manager (Figure 37). Do not forget that in order to be able to access the Tomcat Manager, you need to create a user for the manager role in /usr/share/tomcat5/conf/tomcat-users.xml! Since we wish to use the SQL Server, copy the corresponding JDBC driver, sqljdbc.jar, from \java_refs\jdbc in Windows to /usr/share/tomcat5/common/lib in Linux, and restart Tomcat. Launching the application will result in JDExpenses finally running on Linux (Figure 38), with the report of the speakers' balances populated by SQL Server 2005! |
| |
| Figure 38: A report created with an ASP.NET 2.0 control, populated with data retrieved by SQL Server 2005 running on a Red Hat Enterprise Linux. Wasn't it worth it? :-) |
| |
| Figure 37: The Tomcat Manager allows us to easily deploy the WAR package created by the Deployment Packager. | Now that our application has been deployed on Linux, we can use the remote debugging capabilities offered by Grasshopper and track our Linux application runtime from the Visual Studio debugger windows. First, configure the Tomcat instance running on Linux for debugging. In order to do so, suspend Tomcat, if it is running, and type the following commands in a prompt: |
set JAVA_HOME= set JPDA_ADDRESS=8019 set JPDA_TRANSPORT=dt_socket cd \bin catalina.bat jpda start |
| Figure 39: Configurations required for remote debugging in the Debug tab of the Project Properties. | Back in Visual Studio, right-click the project name in Solution Explorer and choose Properties. A new page will open in the IDE, with several project configurations; click the Web tab and specify (Figure 39): - The IP address of the remote machine (select the check box in Start action).
- The debug port number that you specified in the JPDA_ADDRESS value.
The last thing to do is to start the SQL Server Browser service, which is used by the Grasshopper runtime to locate the SQL Server Express database by the IP address you specified in the connection string, and to connect to it. To make sure that the SQL Server Browser Service is running, launch the Computer Management again, expand Services and Applications, and then click Services. In the list of services, double-click SQL Server Browser, and in the SQL Server Browser Properties window, click Start. | Figure 40: For remote debugging, we have same options as for local one. | Now, we just need to add a breakpoint in the specified page and start the remote application, performing all steps needed to reach the page where a breakpoint has been set. The Grasshopper debugger will stop the execution at the row you designate (Figure 40). Conclusion We hope you enjoyed this tour of Grasshopper 2.0, which shows how to extend your ASP.NET 2.0 skills to open systems using cross-platform development. To recap, we operated entirely inside the Visual Studio 2005 IDE; we used advanced ASP.NET 2.0 controls; and we used an integrated debugger, both on the local and the production machines. Grasshopper 2.0 produced a deployment package that required little configuration and minimal knowledge to deploy on a non-Windows platform. There were only a few particulars (i.e., using SQL Server 2005 from a non-Windows platform), and just TWO lines of code that we wrote that were different from "traditional" ASP.NET 2.0 project development. The Derby pure Java database and Grasshopper Site Administration Tool offer a rich Visual Studio-like experience and also enable you to use advanced features such as role-based Membership. The output, native Java bytecode, offers ease of use, high portability and a level of performance that would be difficult to achieve using "traditional" Web Services. |