使用 Visual Studio 创建你的第一个纯 Java 应用程序





0/5 (0投票)
2007 年 8 月 1 日
30分钟阅读

32690
如何使用 Visual Studio 2005 开发环境、Grasshopper 2.0 和 ASP.NET 2.0 控件构建您的第一个纯 Java 应用程序并将其部署到 Linux 上
这是我们对 The Code Project 赞助商的展示性评测。这些评测旨在为您提供我们认为对开发人员有用且有价值的产品和服务信息。
本教程由 Paolo De Nictolis 撰写,他是一位意大利 Web 开发人员,专注于 .NET 和 PHP 技术以及 Windows/Linux 系统管理,是 MSDN 和 UGI.NET 的技术贡献者,自由 IT 记者,以及 Grasshopper 的爱好者。本文已翻译成英文,但一些屏幕截图仍保留其原始意大利语。
创建您的第一个纯 Java ASP.NET 应用程序
Grasshopper 2.0 与 Visual Studio 2005 完全集成,并提供了对新 ASP.NET 2.0 功能的实现。在本文中,我们将了解如何使用 Grasshopper 2.0 和 ASP.NET 2.0 控件开发一个具有成员资格和基于角色的安全性的 Web 应用程序,并将其部署到 Linux 上。我们将使用母版页和新控件,包括:Login,这是一项开箱即用的身份验证功能;Wizard,用于拆分大型表单并维护状态一致性;以及强大的 GridView,这是一个数据控件,提供 GUI 可管理的翻页和排序支持。
该示例应用程序最初是为一年一度的 罗马 JavaDay 开发的,它使用了 Grasshopper 的新 ASP.NET 2.0 提供程序。这些 Grasshopper 2.0 提供程序基于 Apache Derby™,一个嵌入式数据库,为 ASP.NET 2.0 成员资格和角色配置文件提供程序提供了纯 Java 运行时替代 MS SQL Express 的方案。此外,我们在本示例中展示了如何访问应用程序数据,这些数据仍然存储在 MS SQL Express 2005 中。最后,我们将向您展示如何利用基于开放标准 JPDA(Java 平台调试器体系结构)的集成调试器,以及如何创建一个可用于多平台应用程序服务器的部署包。
一切始于一个好数据库
不可否认:创建一个好的应用程序数据库可以大大加快应用程序的开发速度。
JDExpenses 是一个虚构的会计应用程序,为 JavaDay 开发,允许会议演讲者输入他们的费用,并由管理员查看每个人的余额。请注意,由于此 Grasshopper 版本具有 ASP.NET 2.0 的 Membership
和 Role
API,因此不需要身份验证和角色数据库表。余额定义为 BigInteger
,展示了 Grasshopper 如何处理 BigDecimal
等原生 Java 类型。此外,我更喜欢大的费用账户!
为了让我们能够从 Grasshopper 应用程序使用 SQL Server,我们需要配置数据库。显然,Windows 身份验证不适用于可能运行在任何有 JVM 的操作系统的应用程序。因此,我们将使用 SQL Server 身份验证登录,并在数据库属性中启用“混合模式身份验证”(参见图 2)。为此,您可以使用 Microsoft SQL Server Management Studio Express (SSMSE) 工具,该工具可从 Microsoft 免费下载。
启动 SSMSE,登录,选择数据库,然后右键单击。从上下文菜单中选择“属性”。您将看到 SQL Server 属性对话框。选择“安全性”,并确保选择了“SQL Server 和 Windows 身份验证模式”单选按钮。
接下来,我们需要启用预设的 sa 帐户以通过 SQL Server 身份验证进行访问,因为它在默认设置中被禁用(参见图 3)。使用相同的工具,展开“安全性”>“登录名”>“sa”。右键单击并选择“属性”。切换到“状态”页面,并将“登录”设置为“已启用”。本示例假定您为 sa 用户设置了空白密码。如果不是,请在 Web.Config 文件的 JavaDayConnectionString
属性中更新密码。
为了实现多平台可移植性,Grasshopper 的 SQL Server JDBC 驱动程序(与 Microsoft 提供的相同)仅支持通过 TCP/IP 进行连接,不支持通过命名管道。因此,我们需要从“开始”>“程序文件”>“Microsoft SQL Server 2005”>“配置工具”运行 **SQL Server 配置管理器**,并启用通过 TCP/IP 的连接(图 4)。进行这些更改后,请重新启动 SQL Server。
![]() |
![]() |
![]() |
图 2:自然,一个可能运行在任何操作系统上的应用程序不能依赖 Windows 身份验证;因此,有必要启用 SQL Server 的混合模式身份验证。 |
图 3:可能需要启用“sa”用户。 |
图 4:用于访问 SQL Server 的 JDBC 驱动程序仅支持 TCP/IP 连接;因此,我们需要在 SQL Server 配置管理器中启用该协议。 |
齐心协力,一个模板用于所有页面:母版页
ASP.NET 2.0 的 **母版页** 允许我们通过定义一个将用于每个页面的模板来创建应用程序一致的外观和感觉。首先,从“文件”>“新建”>“项目”>“Visual C# for Java EE”项目模板创建一个新应用程序(图 5)。确保 Grasshopper 2.0 附带的 Tomcat 应用程序服务器已启动。
在 Visual Studio 的“解决方案资源管理器”中,添加一个新的母版页。我们可以在设计模式下直接处理此图形模板,添加所有必要的元素(图 6)。ContentPlaceHolder
元素将接收应用于该模板的每个页面的个性化内容。在这种情况下,我相信您会毫不费力地识别左侧列中的两个字符;它们甚至可能在您公司的会计部门工作!
现在我们可以将模板用于所有页面。在 Default.aspx
的 @Page
指令中,添加新的 MasterPageFile
属性,其值将匹配刚刚创建的母版页(Visual Studio 会自动建议)。选择 Title
属性,这将覆盖母版页上的同一属性。在 Default.aspx 的“源”视图中,删除 @Page
指令下的所有代码,并将其替换为 <asp:Content>
控件。
<asp:Content
ContentPlaceHolderID="MainPage" runat="server">
</asp:Content>
当然,内容将与母版页中定义的同名 ContentPlaceHolder
相关联。接下来,开始向页面添加内容(图 7)。请注意,在母版页中定义的区域在使用它的页面中无法编辑。修改模板只需更新母版页即可。Default.aspx 的预览显示标题在运行时模式下也被替换。
![]() |
![]() |
图 6:将母版页添加到 Visual Studio 项目后,将能够完全在可视化模式下定义模板。请特别注意 ContentPlaceHolder 元素,它将接收每个页面的特定内容。 |
图 7:定义母版页后,可以编辑其内容,但母版页区域本身无法修改。 |
告诉我你是谁,我就会告诉你你能做什么:基于角色的访问控制
![]() 图 8:WSAT 的 Grasshopper 2.0 版本,从应用程序服务器内部执行。 |
![]() 图 9:Grasshopper 版本的成员资格、角色和配置文件 API 数据库管理工具:创建角色... |
现在是时候“亲眼见证”由 ASP.NET 2.0 控件提供的出色的基于角色的身份验证功能了,这得益于每个应用程序通过 Membership
和 Role
API 提供的基础架构,这些 API 可以通过网站管理工具 (WSAT) 以图形方式进行配置。ASP.NET 2.0 成员资格的运行是 MSDN 上一系列文章的主题,对于实现问题,我建议阅读本教程:演练:使用成员资格和用户登录创建网站 (Visual Studio)。
在 Microsoft .NET 上,WSAT 图形工具会在默认的 App_Data
应用程序文件夹中创建一个 ASPNETDB.MDF MS SQL Express 数据库,用户对此是透明的。Grasshopper 2.0 在同一文件夹中创建一个基于 Apache Derby 的 ASPNETDB
数据库,这是一个纯 Java 开源数据库,并提供其自己的 WSAT 工具版本。虽然 ASP.NET 2.0 提供了 AspNetSqlMembershipProvider
,使开发人员无需详细处理包含成员资格数据的 SQL Server 数据库,但 Grasshopper 2.0 在 Mainsoft.Web.Security
命名空间中提供了类似的 AspNetDerbyMembershipProvider
。
对于 JDExpenses,我们需要设置两个组件:ExpensesManagement
,它允许演讲者输入他们的费用;以及 BalancesReport
,它将由会计团队用于检索账户数据。第一个组件将对所有与 speaker 角色和会计团队关联的演讲者开放;第二个组件将仅对由 admin 角色定义的会计团队开放。在解决方案资源管理器中,创建两个同名的文件夹。这些文件夹将包含与这两个元素相关的页面。然后,编译应用程序并启动 Grasshopper 2.0 版本的 WSAT,可以从 Visual Studio IDE 启动,也可以通过 URL https://:8090/JDExpenses/aspnetconfig/Default.aspx 在应用程序运行时访问(图 8)。
为应用程序创建角色(图 9)和用户(图 10)。创建用户时,我们将通过在表单中勾选相应的框来分配特定角色。管理页面的最后一个链接“管理用户”允许在创建用户后修改这些设置(图 11)。
为了设置访问控制并确定哪些角色可以访问应用程序的哪些部分,我们将使用 ASP.NET 2.0 在 web.config 文件中定义适当的 allow
和 deny
规则。在这种情况下,我们的任务是:
- 默认情况下,拒绝所有用户访问应用程序子文件夹。
- 允许 speaker 和 admin 角色访问
ExpensesManagement
子文件夹。 - 仅允许 admin 角色访问
BalancesReport
子文件夹。
规则集将如下所示:
<location path="ExpensesManagement">
<system.web>
<authorization>
<allow roles="admin, speaker"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
<location path="BalancesReport">
<system.web>
<authorization>
<allow roles="admin"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
在运行时,基于 Derby 的 Grasshopper ASP.NET 2.0 提供程序将创建一个 aspnetdb
文件夹,其中包含与 Microsoft ASP.NET 2.0 使用的 SQL Server 数据库对应的 Derby 存储。默认情况下,使用 App_Data
文件夹,在开发过程中,将从项目文件夹中查找它,以便可以共享或复制项目及其成员资格数据。将应用程序部署为 WAR 文件时,将从部署目录中查找 App_Data
。在必要时,负责应用程序部署的系统管理员将能够更改此设置,这要归功于 web.xml 部署文件中的新上下文参数。
<context-param>
<param-name>DataDirectory</param-name>
<param-value>App_Data</param-value>
</context-param>
在运行时,应用程序开发人员将能够在程序本身中检索此参数的值。
(string)AppDomain.CurrentDomain.GetData("DataDirectory")
AppDomain.CurrentDomain.SetDat("DataDirectory","/usr/tmp");
访问 JDExpenses 的控件
现在我们准备利用 ASP.NET 2.0 提供的身份验证工具,这些工具可以从 Visual Studio **工具箱**的“登录”组中访问。在 Default.aspx 中,添加两个 HyperLink 控件,并通过 NavigateUrl
属性将它们连接到 JDExpenses 的两个元素 ExpensesManagement.aspx 和 BalancesReport.aspx(图 12)。由于这两个页面都需要身份验证(它们属于需要身份验证的两个文件夹),用户将被重定向到包含登录窗体的页面,该页面默认为 Login.aspx。可以在 web.config 中更改默认页面,但这没有必要,并且会使本示例复杂化。
![]() 图 12:在应用程序的主页中,添加两个 HyperLink 控件,它们通过分配 NavigateUrl 属性值来连接到两个 JDExpenses 元素。 |
![]() 图 13:ASP.NET 2.0 的 Login 控件处理基于窗体的身份验证。 |
让我们创建一个 Login.aspx 页面,我们将从 **工具箱**中将 Login 控件拖放到该页面上。一如 ASP.NET 2.0 控件的惯例,我们可以使用关联的 **智能标记**(通过控件边缘的黑色箭头访问)来个性化其外观。Login 控件通过集成的 Validator
控件以对开发人员透明的方式管理用户名和密码输入错误的显示,并且可以通过 **记住我** 复选框使用 cookie 来记住用户的凭据,以便将来登录。我们通常会为 DestinationPageUrl
和 MembershipProvider
赋值,但在本例中没有必要。目标页面将由调用 Login.aspx 的 HyperLink
处理,由 Grasshopper 2.0 自主处理。
启动应用程序,单击主页上的任一 HyperLinks
,然后在登录页面输入凭据。这应该会让您能够访问登录页面和应用程序本身(图 14)。您会注意到经典的“欢迎消息”和注销链接已使用用户名进行了个性化。这些是通过 **LoginView**、LoginName
和 LoginStatus
这三个控件 **开箱即用** 实现的。后两者只需要很少的工作:LoginName
是一个纯拖放控件,而 LoginStatus
需要您只需个性化用户登录前或已认证后出现的文本,分别使用 LoginText
和 LogoutText
属性。LoginView
需要通过关联的 **智能标记**(图 15)对匿名或注册用户的视图进行一些个性化。最终,我们可以通过输入以下简单代码在页面的“代码”视图中获得相同的结果。
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
Authenticated! Welcome,
</LoggedInTemplate>
<AnonymousTemplate>
You're not logged. Click over "Login"
</AnonymousTemplate>
</asp:LoginView>
如前所述,Login 控件还处理凭据输入失败和身份验证失败(图 16);通过选择“下次记住我”复选框(与 RememberMeSet
属性关联),用户下次启动应用程序时将不会被要求输入其凭据。
![]() |
![]() |
![]() |
图 14:展示了对需要身份验证的区域的访问。注意使用 LoginView、LoginName 和 LoginStatus 控件实现的欢迎消息和注销链接。 |
图 15:LoginView 控件只需要使用关联的智能标记对匿名或注册用户的模板进行最少的个性化设置。 |
图 16:通过 ValidatorControls,Login 控件处理用户名、密码或两者都输入失败的情况,并在身份验证失败时显示错误消息。 |
输入数据:Wizard 控件和数据源
为了模拟身份验证功能,我们使用了 ExpensesManagement.aspx 的简单“存根”页面。我们可以做得更好。我们将使用此页面来允许用户输入大量数据,这要归功于 Wizard 控件。这个新的 ASP.NET 2.0 控件可以在 Visual Studio **工具箱**的“标准”组中找到。它旨在允许创建大量字段,这些字段一次只向用户显示几个。此功能提供了 **开箱即用** 的菜单和一系列按钮来在各个页面之间导航,同时当然会维护其状态。
将 Wizard
控件拖到页面上,并查看生成的代码大纲。
<asp:Wizard ID="Wizard1" runat="server">
<WizardSteps>
<asp:WizardStep runat="server" Title="Step 1">
</asp:WizardStep> <asp:WizardStep runat="server" Title="Step 2">
</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
向导由一系列“步骤”组成,每个步骤都允许通过关联的智能标记提供的 GUI 进行一系列个性化。几乎没有必要说,从智能标记本身,我们可以通过 **自动格式化** 功能选择一系列图形模板(图 17)。
让我们开始填充向导的第一步。从工具箱中拖动 Label
和 Calendar
控件,演讲者将使用它们来选择他或她参加 JavaDay 的日期。没有特别的原因要使用 Calendar
控件选择日期;但是,哦,如果没有 Calendar
,Grasshopper 教程是什么呢?
让我们插入另一个 Label
和一个 DropDownList
,用于从 SQL Server 2005 Express 数据库的相应表中选择主办城市。在这里,Visual Studio 再次帮助了我们,允许我们通过向导将下拉框连接到数据源(图 18)。
选择 **选择数据源**。出现向导的第一步。选择创建一个新数据源(图 19),其类型将是 **SqlDataSource**(图 20)。
单击“确定”。现在可以通过 **新建连接** 创建新的数据库连接(图 21)。将出现“添加连接”对话框(图 22)。选择要使用的 SQL Server 实例、通过 SQL Server 身份验证进行身份验证以及包含数据的数据库。为了能够从 Java 应用程序服务器使用 SQL Server 2005 Express 数据源,我们还需要完成最后一步:启用 TCP/IP 而不是命名管道。
![]() |
![]() |
图 19:在数据源向导的第一步中,选择创建一个新的数据源…… |
图 20:……类型将是 SqlDataSource。 |
单击 **高级** 按钮。在连接属性中,选择在 **网络库**下使用 TCP/IP。另外,请注意 **集成安全性** 如何自动设置为 **False**。单击 **测试连接** 以确保一切正常。我们的连接字符串现在已准备就绪(图 24)。在部署到非 Windows 平台时,还需要进行最后一次更改——这涉及到用数据库服务器的 IP 地址替换 SQL Server 的命名实例(如图中的 STEFANENKO\MSSMLBIZ
)。为此,您需要打开 Web.Config 文件,并将 Data Source=.\SqlExpress
替换为 Data Source=<ip address>\SqlExpress
。
单击 **下一步** 将连接字符串持久化到 web.config,并可视化创建 SQL 查询(图 25),该查询将在数据源向导的最后一步进行测试。以同样的方式,将 DropDownList
关联到数据库 Accounts
表,以选择演讲者的姓名。最后,添加一个 TextBox
以便可以输入 JavaDay 参与者的总数。最终结果如图 26 所示。
差旅费,或:我如何学会热爱外部 Java 类
我们不打算用数据填充向导控件的第二页。相反,我们将使用一系列标签来显示演讲者在第一页上选择的差旅费。首先,请注意,无需从 DropDownList
中检索值,因为控件会以对开发人员透明的方式处理数据持久化。但是,在实际应用程序中,差旅费数据库很可能由另一个应用程序填充,或者更好的是,由几个应用程序填充:一个由旅行社托管,另一个由演讲者入住的酒店连锁店托管,还有一个由演讲者用餐的餐馆连锁店托管,依此类推。那么为什么不在 **JDExpenses** 中添加“现实主义的触感”呢?
差旅费数据实际上源自另一个应用程序 JDTravel,该应用程序由负责该活动后勤的机构提供。让我们看一下应用程序的源代码。
package JDTravel;
import java.lang.*;
import java.util.*;
public class TravelExpenses {
private String speaker;
private int expensesTravel;
private int expensesLodging;
private int expensesBoard;
/** Creates a new instance of TravelExpenses */
public TravelExpenses() {
}
public int[] calculateExpenses(String surnameToSearch) {
int[] speakerExpenses = {0,0,0};
HashMap<String,int[]> expenses = getExpensesByERP();
if (expenses.containsKey(surnameToSearch))
{
speakerExpenses = expenses.get(surnameToSearch);
}
return speakerExpenses;
}
protected HashMap getExpensesByERP() {
HashMap<String,int[]> 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;
}
}
列表 1:使用 NetBeans 开发的 Java 类。
图 27:“添加 Java 引用”对话框允许我们添加打包在 JAR 文件中的 Java 类库,该 JAR 文件将被视为 .NET 程序集。
不可否认:这不是一个典型的 .NET 应用程序!实际上,我们是用 NetBeans 5.0,BlueJ 的精简、教育版本创建的。公共方法 calculateExpenses
处理一个包含演讲者费用的整数数组,通过将传递的姓氏作为键从 HashMap
中检索它们。HashMap
由内部方法 getExpensesByERP
返回。虽然此方法仅使用硬编码的值填充 HashMap,但其名称暗示了一个更深层次的概念:如果 map 实际上是由 ERP 等复杂应用程序生成的,则包装器 calculateExpenses
方法将使结果在我们的 .NET 应用程序中可用。
要启用和使用应用程序类,请右键单击“解决方案资源管理器”中的项目名称,然后选择“添加 Java 引用”。单击“浏览”选项卡,然后选择 **BlueJ JAR** - JDTravel.jar - 执行 JDTravel 项目的构建(图 27)。为了使项目内部一致,我将 JAR 添加到了 Visual Studio JDExpenses 项目下的新 ext(外部)文件夹中。将 JAR 添加到项目引用中需要一些时间,如果我们查看完成后引用的属性(图 28),我们将意识到原因:Grasshopper 将 JAR 记录为原生 .NET 程序集,为“通用”程序集的许多属性分配了值。
为了能够使用 TravelExpenses
类,必须在过渡到向导的第二页时创建它的一个实例。实际上,我们必须处理 Wizard 控件的 NextButtonClick
事件。在 ExpensesManagement.aspx 的“代码”视图中,将指令 — OnNextButtonClick="LoadExpenses"
— 添加到 Wizard 控件的属性中(Visual Studio 提供 IntelliSense 的自动完成功能),并在页面代码隐藏文件 ExpensesManagement.aspx.cs 中编写处理代码。这是一个具有以下签名的事件处理程序。
protected void LoadExpenses (
object sender, WizardNavigationEventArgs e)
将其定义为受保护后,我们将不必在 Page_Load()
中添加 AddHandler
指令。在代码隐藏中,添加以下指令:
using JDTravel;
并创建一个类的实例,以及一个接收 calculateExpenses
方法返回的整数数组的private
变量。
private TravelExpenses ExpensesJD = new TravelExpenses();
private int[] expenses;
返回 ExpensesManagement.aspx,在 **设计** 模式下,通过选择控件菜单中定义的相应“各种费用”项,移动到 Wizard 控件的第二步。请记住,在编译阶段之前,您需要重新选择第一个 Wizard 步骤,然后执行将从编译期间突出显示的页面开始。然后,添加三个标签,它们将帮助我们显示用户的差旅费、住宿费和餐费。
<table cellspacing="20" style="width: 536px; height: 353px">
<tr>
<td style="width: 359px">
<asp:Label ID="lblTravelExpenses" runat="server"
Text="Travel expenses: " Font-Size="X-Large">
</asp:Label>
</td>
<td>
<img alt="Travel"
src="https://codeproject.org.cn/Portals/0/images/viaggio.png" />
</td>
</tr>
<tr>
<td style="width: 359px">
<asp:Label ID="lblLodgingExpenses" runat="server"
Text="Lodging expenses: " Font-Size="X-Large">
</asp:Label>
</td>
<td>
<img alt="Alloggio"
src="https://codeproject.org.cn/Portals/0/images/alloggio.png" />
</td>
</tr>
<tr>
<td style="width: 359px">
<asp:Label ID="lblBoardExpenses" runat="server"
Text="Board expenses: " Font-Size="X-Large">
</asp:Label>
</td>
<td>
<img alt="Vitto"
src="https://codeproject.org.cn/Portals/0/images/vitto.jpg" />
</td>
</tr>
</table>
使用以下代码填充它们。
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";
}
除了代码的极大简洁性之外,请注意当 expenses 变量被赋值时实际发生了什么:一个 Java 数组,它是 java.lang.reflect.Array
的实例,被转换为一个数组,即 .NET 实现的 ICollection
的 System.Array
的实例。如果我们使用 Web Services 作为集成技术,将很难如此高效地实现这种转换。
结果如图 29 所示。
跨平台调试器
要查看 Grasshopper 的集成调试器,请在 expenses
变量接收其值的行中插入一个断点,像往常一样单击行号旁边的灰色栏,然后按 **F5** 启动应用程序。代码流将在断点处暂停,正如预期的那样,我们将能够使用所有熟悉的调试工具:调用堆栈、局部变量窗口和 **单步跳过**,以及通过鼠标悬停来验证变量值的机会(图 30)。
作为 ExpensesManagement
组件的最后一项任务,我们需要处理 Wizard 控件中收集的数据。此控件不执行数据 POST,甚至不是 ASP.NET 2.0 跨页发布 的类型安全形式。相反,它会公开 FinishButtonClick
事件,我们将在其中执行数据持久化以及状态管理。对于自包含和跨平台应用程序,ASP.NET 2.0 提供的最合适的设备是 Session 对象。因此,我们将把此调用添加到事件处理程序 — OnFinishButtonClick="WizardDataManagement"
— 并在 ExpensesManagement.aspx.cs 中编写持久化代码,如下所示。
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.ConnectionString["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);
}
}
实际在 SQL Server 数据库中进行持久化的是 updateBalance
私有方法,该方法接收演讲者的总报酬。对于使用 ADO.NET 的开发人员来说,此方法没有什么新东西。更有趣的是查看应用程序如何计算此报酬——即总费用(通过对整数数组 expenses
进行简单 Iterator
检索,其元素被转换为 Decimal
)加上每位演讲者的固定金额(DollarsForPresent
)。实际上,参与者的数量有点不现实,因为它相当于地球人口的十万倍以上,但使用 BigDecimal
(即原生 Java 类型,需要使用 java.math
指令)是演示 Grasshopper 2.0 在代码级别集成潜力的绝佳方式。
为了将 generalExpenses
从 .NET Decimal
转换为 Java BigDecimal
,我们将使用 PrimitiveTypeUtils.DecimalToBigDecimal
,这是一个由 Grasshopper 提供并在 vmw.common
命名空间中可用的辅助方法(前提是使用)。为此,我们需要通过 **添加 Java 引用** 将 J2SE.Helpers.jar
类添加到项目引用中。如果选择使用 Web Services 来集成 .NET 和 Java 应用程序,则很难实现此类转换!
参与者人数和每人报酬分别通过 Wizard 控件的 TextBox
和内部变量检索,因为 BigDecimal
构建器接受 String
或 int
。此时,我们通过 BigDecimal
multiply
和 add
方法对非常大的数字进行“简单”算术运算,调用数据库中的持久化方法,并通过尽可能多的 Session
变量来记忆在 Wizard 控件中输入的值。
应用程序的最后一页 Review.aspx 允许我们简单地验证报酬在数据库中的持久化,以及验证 Session
变量是否已实际赋值。在执行与 FinishButtonClick
事件关联的方法后,单击 Wizard 控件的 **完成** 按钮,我们将自动重定向到此页面,只要它在 Wizard 控件的 FinishDestinationPageUrl
属性中被指定为值。现在,让我们在 Review.aspx 的 Page_Load
事件中检索演讲者的余额。
protected void Page_Load(object sender, EventArgs e)
{
string connectionString =
ConfigurationManager.ConnectionStrings["JavaDayConnectionString"]
.ConnectionString;
SqlConnection conn = new SqlConnection(connectionString);
try
{
conn.Open(); //you need till r68649 of Mono System.Data
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";
}
}
并在 Label
中将其显示给用户,并检索代码中的 Session
变量值。最终结果如图 31 所示。
演讲者账户报告和 GridView 控件
现在只剩下使用 GridView
控件来生成演讲者的当前账户报告,换句话说,就是 Accounts
表的内容,它将在 BalancesReport.aspx 页面中显示。在以通常方式定义母版页后,切换到 **设计** 模式,并将 GridView
从 **工具箱数据** 控件拖到工作区域。使用关联的 **智能标记** 中的 **自动格式化** 命令来个性化 GridView
的图形外观。此外,从 **智能标记** 中,选择 Accounts
数据库表作为控件的数据源,方法是使用数据源向导。向导完成后,选择 **智能标记** 中的 **启用分页** 和 **启用排序** 复选框以启用相应的功能。无需其他操作 — 报表页面已准备就绪(图 32)。单击列标题可以按照该列对值进行排序(图 33),同时我们可以在控件底部看到分页,一切都很整洁(图 34)。
GridView
控件是 ASP.NET 2.0 的最佳新功能之一。借助它,我们无需编写一行代码即可创建带有排序选项的分页报表!最多,我们可以修改每页的默认行数(10),方法是更改控件的 PageSize
属性,但这也可以在 Visual Studio 的 **设计** 模式下完成。
![]() 图 32:使用 GridView 控件创建的报表页面,无需编写任何代码。 |
![]() 图 33:通过单击列标题,我们可以对数据进行排序…… |
![]() 图 34:……而在控件底部,分页也很整洁。 |
没有 Linux 的 Grasshopper 是什么?

图 35:项目属性的 Java 构建类别允许我们将部署方法从 **增量** 更改为 **完整部署包 (WAR)**。Debug_Java 配置中的默认值设置为增量以缩短开发周期,而在 Release_Java 配置中设置为 WAR 以创建完整的部署包。
现在我们的应用程序已准备就绪,我们可以将其部署到 Linux 机器上。首先,我们需要创建部署包,这就像将配置从 Debug_Java 切换到 Release_Java 一样简单。默认情况下,Release 配置中的部署方法设置为 **完整部署包 (WAR)**(参见图 35),并创建一个生产就绪的 WAR 部署包。我们的 WAR 文件位于 bin 目录 JDExpenses\bin\JDExpenses.war 下,大小为 22MB,因为它包含了所有 Grasshopper 框架库。当您在同一服务器上部署多个应用程序时,您可能更倾向于将框架库移到共享类路径。
要在 Linux 上运行应用程序,必须安装 Tomcat;在大多数情况下,我们还需要进行一些小的更改。默认情况下,几乎所有 Linux 发行版都使用功能齐全的 JDK 1.4.2 的 Tomcat 版本;但是,我们使用 JDK 1.5.0 开发了 JDTravel
类,因此我们需要安装它并相应地配置应用程序服务器。从 Sun 下载站点,下载适用于 Linux 的 JDK 1.5.0(扩展名为 .bin 的版本)并简单地通过运行文件进行安装。假设它已安装在 /opt/jdk1.5.0_06;
上,在启动 Tomcat 之前,通常只需将 JAVA_HOME
变量设置为适合的值即可。
export JAVA_HOME=/opt/jdk1.5.0_06
为了避免在每次运行时都执行此过程,我们只需修改 Tomcat 启动脚本即可。在 Red Hat Linux 的情况下,附带的 Tomcat 版本提供了一种更简单、更快捷的方法。您只需打开 /usr/share/tomcat5/conf/tomcat5.conf 文件,然后修改 JAVA_HOME
行(图 36)。
将 WAR 包复制到 Linux 机器 — 例如,通过 Samba 共享 — 并使用 **Tomcat Manager** 进行部署(图 37)。不要忘记,为了能够访问 **Tomcat Manager**,您需要在 /usr/share/tomcat5/conf/tomcat-users.xml 中为 manager 角色创建一个用户!由于我们希望使用 SQL Server,请将相应的 JDBC 驱动程序 sqljdbc.jar 从 Windows 中的 <Mainsoft_install_dir>\java_refs\jdbc
复制到 Linux 中的 /usr/share/tomcat5/common/lib,然后重新启动 Tomcat。启动应用程序将最终在 Linux 上运行 **JDExpenses**(图 38),其演讲者余额报告由 SQL Server 2005 填充!
![]() 图 38:使用 ASP.NET 2.0 控件创建的报表,填充了在 Red Hat Enterprise Linux 上运行的 SQL Server 2005 检索到的数据。难道不值得吗? |
![]() 图 37:Tomcat Manager 允许我们轻松部署 Deployment Packager 创建的 WAR 包。 |
现在我们的应用程序已部署到 Linux 上,我们可以使用 Grasshopper 提供的远程调试功能,从 Visual Studio 调试器窗口跟踪我们的 Linux 应用程序运行时。首先,为在 Linux 上运行的 Tomcat 实例配置调试。为此,请在 Tomcat 正在运行时暂停它,然后在命令提示符下键入以下命令:
set JAVA_HOME=<jdk_install_dir>
set JPDA_ADDRESS=8019
set JPDA_TRANSPORT=dt_socket
cd <tomcat_install_dir>\bin
catalina.bat jpda start
回到 Visual Studio,右键单击“解决方案资源管理器”中的项目名称,然后选择“属性”。IDE 中将打开一个新页面,其中包含多个项目配置;单击“Web”选项卡并指定(图 39):
- 远程机器的 IP 地址(选择 **启动操作** 中的复选框)。
- 您在
JPDA_ADDRESS
值中指定的调试端口号。
最后要做的就是启动 **SQL Server Browser** 服务,Grasshopper 运行时使用该服务通过您在连接字符串中指定的 IP 地址定位 SQL Server Express 数据库,并连接到它。为了确保 SQL Server Browser 服务正在运行,请再次启动 **计算机管理**,展开“服务和应用程序”,然后单击“服务”。在服务列表中,双击 **SQL Server Browser**,然后在 SQL Server Browser 属性窗口中单击“启动”。
现在,我们只需要在指定页面添加一个断点并启动远程应用程序,执行所有必要的步骤以到达设置了断点的页面。Grasshopper 调试器将在您指定的行处停止执行(图 40)。
结论
我们希望您喜欢这次 Grasshopper 2.0 之旅,它展示了如何将您的 ASP.NET 2.0 技能扩展到使用跨平台开发来支持开放系统。总而言之,我们完全在 Visual Studio 2005 IDE 中进行操作;我们使用了高级 ASP.NET 2.0 控件;并且我们使用了集成调试器,包括本地和生产机器。Grasshopper 2.0 生成了一个部署包,该包只需少量配置和最少的知识即可部署到非 Windows 平台。只有少数细节(例如,从非 Windows 平台使用 SQL Server 2005)以及我们编写的仅两条与“传统”ASP.NET 2.0 项目开发不同的代码行。Derby 纯 Java 数据库和 Grasshopper 站点管理工具提供了丰富的类似 Visual Studio 的体验,并且还使您能够使用基于角色的成员资格等高级功能。输出的本地 Java 字节码提供了易用性、高可移植性和一种性能水平,这是使用“传统”Web Services 难以实现的。