asklxf

A java developer's notebook.

常用链接

统计

积分与排名

最新评论

使用WebLogic Platform构建音乐站点

Orienginal URL: http://dev2dev.bea.com.cn/bbs/yuanch/ArticleShow.jsp?Id=35

摘要

拥有自己的音乐站点是个不错的想法,相比传统的ASPPHP站点,我们将采用J2EE架构实现多层结构的,高度可扩展的站点。您将看到,采用J2EE技术,加上强大的WebLogic平台,我们能更容易的实现一个音乐站点应用,而非传统意义上的平面结构的网站。

简介

随着J2EE的快速普及,越来越多的开发人员都想编写基于J2EE架构的分布式的企业级应用程序。为了降低开发难度,J2EE提供的基于组件的,分层的分布式应用模式,使具有可伸缩,可扩展和易维护等优点。利用J2EE,可以快速开发、部署和管理多层结构、面向Web的,以服务器为中心的企业级应用。

在下面的这个示例中,我们将构建一个基于J2EE的音乐站点,暂定名为JetMusic,计划给用户提供浏览,下载等在线服务。事实上,Internet上已有很多这种类型的站点,但是它们大多是传统的基于两层结构的模型,使用了混合HTMLASPPHP脚本代码的页面。

尽管我们的JetMusic站点可能现在规模不大,仅仅使用JSP/JavaBeans/数据库就已经足够了,但是作为示例,我们还是打算用J2EE来实现它。借助J2EE的强大功能,我们能够轻易地快速创建这样一个音乐站点,并且使我们的编码量最小。更重要的是,它是完全的3层分布式结构,具有很强的扩展性。

系统设计

我们的站点需要对用户提供浏览,下载等服务,还要允许系统管理员能随时登陆并管理站点资源。对于我们的JetMusic站点,我们计划使用树型结构来组织分类、艺术家、专辑、歌曲,一个简单的示例图如下:

对用户而言,我们需要提供以下服务:

用户可以浏览分类、艺术家、专辑、歌曲、歌词。

用户可以按关键字模糊搜索歌曲,专辑,艺术家和歌词。

用户可以对某一首歌评分。

如果歌曲文件存在,用户可以下载歌曲。

如果歌词存在,用户可以浏览歌词。

为了管理站点资源,我们把使用者分为用户和管理员两类:

用户可以注册,更改自己的信息。

管理员可以创建或删除分类,艺术家,专辑,歌曲。

以上可以简单看作是我们分析的全部用例。我们将采用J2EE标准的三层结构模型,将系统分为表示层,逻辑层和持久层(EIS层就免了,因为我们并没有所谓的“遗产系统”),下面将详细讨论设计方案。

通常,设计总是从域模型开始的,我们通过对用例的分析,可以建立以下实体:

分类对象(Category),艺术家对象(Artist),专辑对象(Album),歌曲对象(Song),这些对象是简单的一对多关系。我们把这些对象都一一映射为实体Bean,由于这些对象最终映射为数据库表,这些表的结构相对简单,因此我们选择CMP实现。相比BMPCMP不仅大大简化了数据库访问的代码,而且由容器管理的特性通常使CMPBMP具有更好的缓冲性能和可移植性。

在逻辑层,毫无疑问,会话Bean将是最好的选择。如果不使用Session Bean,您可能需要自行处理多线程同步,安全,事务处理等问题,这将是一项复杂而艰巨的工程。由于容器自动为我们处理了大量的如事务,安全,多线程等底层服务操作,我们就只需关注于我们的业务逻辑。此外,对于异步调用,消息驱动Bean是不错的选择。

在表示层,我们用JSP页面将界面呈现给用户。有过网站开发经验的开发人员一定有过这样的深刻感受,HTML元素和JSP代码混合在一起是多么巨大的灾难,它将导致可维护性随着页面数量的增加呈指数增长。

开源的Struts模型通过MVC架构大大简化了JSP前端的开发。但是,使用Struts还要求开发人员对于Struts底层结构和配置文件有相当的了解。WebLogic提供的Java Page Flow技术完全基于Struts技术,并且隐藏了更多的底层细节,提供了图形化的页面导航功能,大大简化了Web层的开发。此外,大量的NetUI标签和数据绑定技术,使您的JSP页面立刻具备了可视化开发的功能。我们将采用各种实现方式来编写Web层。

开发环境

J2EE仅仅是一个标准,众多的厂商提供了相应的实现平台,其中,BEAWebLogic无疑是最优秀的J2EE平台之一。我们将把JetMusic站点建立在WebLogic Platform 8.1平台上,并采用WebLogic Workshop 8.1这个强大的IDE作为开发工具。你可以免费从BEA官方站点上下载非商业目的开发使用的5-IP限制版本的WebLogic Platform 8.1

http://commerce.bea.com/showproduct.jsp?family=WLP&major=8.1&minor=2

我们的站点资源除了歌曲文件外,将全部存放在关系数据库中。选用MS SQL Server 2000的原因是这个数据库对硬件资源的消耗要比Oracle少,适合于我们作开发使用。如果您计划采用Oracle或其他数据库,没有关系,您只需修改实体Bean的相关部署配置,即可立刻移植到其他厂商的数据库上。

要流畅地运行WebLogic Server 8.1WebLogic Workshop 8.1,您至少需要512M内存,如果仅有256M内存,您可以修改WebLogic的启动参数,指定较小的JVM内存(如128M)。

配置数据库:

启动MS SQL Server,打开企业管理器,新建数据库music,请注意我们并不需要建立任何表,稍候部署我们的应用时WebLogic会自动创建相应的表。

配置WebLogic

新建一个名为“music”的配置,并选择模板Basic Weblogic Workshop Domain,以便能使用Workshop进行开发和调试。

启动WebLogic,打开浏览器,输入http://localhost:7001/console,登陆,在左边找到musicServicesJDBCConnection Pools,新建一个连接池,选择数据库“MS SQL Server”,驱动程序“BEAs MS SQL Server Driver(Type 4) Versions:7.0, 2000”,填好数据库名,用户名,口令等,测试无误后部署。然后选择左边的musicServicesJDBCData Sources,新建一个名为“jdbc/MusicDataSource”的数据源,使用刚才建好的连接池,部署。

启动Workshop,选择NewApplication新建应用程序Music,然后选择Browse,找到服务器配置文件“<BEA安装目录>user_projectsdomainsmusicconfig.xml”,确定后创建一个应用程序。

为了有效组织和管理代码,我们把所有的实体Bean均放在包music.ejb.db中,所有的会话Bean放在music.ejb中,另外建一个music.shared包用于存放会话Bean和表示层JSP/Servlets共享的Java类,包括各种自定义异常,传递的值对象等。

我们将创建3个工程,分别为MusicEjb Project,实现持久层和逻辑层;MusicClient Project,用于在命令行测试和调试逻辑层和持久层;MusicWeb Project,用于实现表示层。创建好的结构在Workshop中显示如下:

设计持久层

我们首先开始设计持久层,通过前面的分析,我们抽象出以下实体Bean

Category实体Bean:代表一个分类;

Artist实体Bean:代表一个艺术家;

Album实体Bean:代表一个专辑;

Song实体Bean:代表一首歌曲;

Account实体Bean:代表一个用户。

对应的CMR关系为:

Category-Artist:一对多

Artist-Album:一对多

Album-Song:一对多

先建立一个简单的名为CategoryEntity Bean:展开左侧MusicEjbmusicejbdb,选择菜单FileNewEntity Bean,在设计视图中分别添加3CMP字段,一个Create方法,一个Finder方法,另外我们还添加了一个copy方法用于返回一个Value Object对象:

然后在右边的属性面板中为Entity Bean设置属性,在这里我们的主要设置如下:

data-source-name: jdbc/MusicDataSource

default-transaction: Required

table-name: category

Local EJB

  JNDI name: ejb/Category

  Bean class name: CategoryLocal

  Home class name: CategoryLocalHome

为了实现自增主键的功能,需要指定一个Ejb-Gen属性:点击右键弹出菜单,选择Insert EJB Gentagautomatic-key-generation,指定字段名id,数据库类型SQLServer2000即可。

为了提高CMP的性能,我们全部采用本地接口,如果某些CMP字段需要提供只读接口(如id,username字段),只需右键点击字段名,然后选择“Read Only”。

按相同方法建立Artist.ejbAlbum.ejbSong.ejbAccount.ejb,然后创建CMR关系:

打开Category.ejb的设计视图,点击右键,在弹出菜单里选择Add Relation,建立一对多的CMR关系:

类似的,创建Artist-AlbumAlbum-Song的一对多关系。

注意我们还设计了一个AccountUtil类,使用正则表达验证用户信息的有效性,比如用户名,口令是否符合要求。

设计逻辑层

首先我们设计一个JndiHelper类,用于封装查找实体BeanJNDI操作,同时还对实体BeanHome接口缓存。

我们把所有的逻辑封装到3Session Bean中:

MusicView.ejb:用于封装浏览,下载等页面操作。

AccountManage.ejb:用于封装注册,登陆等账号操作。

MusicManage.ejb:用于封装管理员的各项操作。

首先建立MusicView.ejb,我们一共设计了以下业务方法:

所有的方法均为远程方法,这样Web层和EJB就可分开部署。当然你也可以将会话Bean接口全部改成本地接口以进一步提高性能。方法看上去虽多,但逻辑相当简单,例如,getAlbums(int artistId)方法:

基本逻辑大致为:

查找相应的实体Bean,获得实体Bean对应CMR关系的集合,在将其包装成值对象返回给客户端。你可以看到,由于没有JDBC操作,代码被大大的简化了,并且由于容器处理了底层数据库连接,使我们的代码更加健壮。

在这里我们采用了值对象(Value Object)模式,用于在Web层和逻辑层之间传送对象。你可以很容易的写出每个实体Bean对应的值对象,比如Album.ejb对应的值对象AlbumVO

然后创建AccountManage.ejb,用于处理用户帐号相关的操作:

如果要提高系统的安全性,建议使用MD5码存储用户口令,MD5算法是一个单向函数,即使获得数据库表也无法得知用户口令。如果要进一步防止使用预先算好的MD5码攻击,还可以采用加盐处理。为了简单起见,我们直接将用户口令存储在数据库表的字段中,因此login(String username, String password)看起来像这样:

如果找到了相应的实体Bean(即数据库表中存在此记录),再判断password字段,如果相符,验证通过,否则,抛出一个自定义的UnauthorizedException异常。这里也不涉及JDBC操作,因此代码非常简单。

搜索功能是通过实体BeanFinder方法实现的,标准的EJB QL 2.0并不支持带参数的like关键字(在SUN J2EE SDK中编译就会失败),幸好WebLogic对其进行了扩充。为了实现模糊搜索,这里我们定义了Song.ejb的一个Finder方法findByTitle(String title),对应的EJB QL为“SELECT OBJECT(o) FROM Song AS o WHERE lower(o.title) LIKE concat(%, concat(?1, %))”。如果使用其他厂商的服务器,你需要查看厂商的EJB QL文档然后作相应的修改。

最后一个是MusicManage.ejb,用于管理员创建、删除信息:

创建和删除实体Bean的代码都非常简单,唯一需要的就是捕获相应的异常,我们在deleteSong(int songId)中将RemoveException包装成更一般的ApplicationException异常返回客户端:

需要特别注意的是,对存在一对多CMR关系的实体Bean,如果指定了级联删除,当删除“一”时,对应的“多”会被自动删除,因此执行删除前要异常小心,至少应该提示用户,或者,只允许删除“多”为空的实体Bean,比如删除Artist.ejb时:

home = JndiHelper.getArtistLocalHome();

artist = home.findByPrimaryKey(new Integer(artistId));

Collection c = artist.getAlbums();

if(c.size()>0) throw new Exception("Cannot delete unless it is empty.");

为了安全起见,我们在删除时不允许删除“多”一方不为空的实体。

编译,部署。OK,小功告成!现在我们可以测试一下我们的逻辑层。一个好的办法是使用JUnit测试,但是似乎Workshop尚未集成JUnit。没关系,我们自己写一个客户端Java程序来测试逻辑层。

MusicClientProject中建立Client.Java类,放在包music.client中。为了测试,我们首先在数据库中创建一些测试数据:

void initData () throws Exception { … }

然后,我们写一个testGetCategories()方法:

void testGetCategoris(int parentCategoryId) throws Exception { … }

类似的,我们对每个业务逻辑都写一个相应的测试方法,直到每一个业务逻辑都正确无误。在一个独立的Java客户端程序中测试EJB要比在JSP中方便得多,你可以方便地设置断点,跟踪以便查看变量,随时使用System.out.println()在控制台输出任何调试信息。

设计表示层

表示层用于向用户提供系统交互的接口。采用J2EE的系统表示层一般都是瘦客户端,使用JSP/Servlets技术,使用户能通过浏览器使用系统。在JetMusic站点中,我们将考虑以下几种实现方式:

1.在JSP中使用业务代表模式:

通常,在JSP/Servlets中直接调用EJB并不是一个好主意,这需要大量的查找JNDI的代码,业务代表(Business Delegate)是一个不错的模式,它封装了所有的EJB查找和调用,使得表示层和逻辑层的耦合度能降到最低,并且还可以进行一些缓存。

由于业务代表类将WebEJB隔开了,因此,对于JSP/Servlets来说,它们根本就不知道EJB的存在。这将带来另一个好处:只要在设计时仔细定义了业务代表的接口,Web层和EJB的开发人员立即可以同时开发各自的模块,Web层的开发人员可以首先对业务代表类进行硬编码,以便返回他们希望的结果。等到EJB开发完成后,再用实际的EJB调用替换即可。

为了封装对MusicView.ejb的所有调用,我们建立一个MusicViewBD的业务代表类:

index.jsp中即可使用MusicViewBD以便输出Categories

2.使用NetUI标签实现数据绑定

WebLogic提供了大量的NetUI标签,能够方便地在JSP中绑定数据。我们下一步将创建一个viewCa.jsp页面,此页面向用户展示分类。

我们首先创建一个EJB ControlWebLogic向我们提供的EJB Control完全封装了EJB调用。利用EJB Control,我们不用写一行代码,立刻就可以在JSP中调用EJB。这个EJB Control可以看作是一个用标签封装的业务代表模式的应用。

MusicWeb工程下新建文件夹music.control,用于存放EJB Control。新建EJB Control,命名为CallMusicView

选择“Browse application EJBs…”,直接找到“MusicView (remote jndi)”,Workshop会自动填好相应的接口,点击“create”即创建成功。

当我们获得了从EJB返回的Collection后,可以使用Repeater标签显示这个复杂的数据,在这里我们不打算进一步讨论Repeater标签的细节,关于如何使用Repeater标签,可以参考下面的文章:

http://dev2dev.bea.com/products/wlworkshop81/articles/repeater.jsp

以下JSP页面显示了如何使用EJB Control调用EJBgetCategories远程方法,并将返回的Collection结果集用Repeater标签以超链接的形式显示出来,这里还用到了一个netui:anchor标签:

第一个DeclareControl标签申明了一个Control,名为ctrlMusicView,第二个CallControl标签调用控件ctrlMusicView的一个方法getCategories并附带参数MethodParameter,参数值由URLid确定,然后将结果放在变量categories中。Repeater标签从categories取得数据源,然后循环显示集合中的每个元素。

对应的源代码看起来是这样:

运行结果在IE中显示如下:

类似地,您可以自己“画”出列艺术家,列专辑的页面,然后将它们组合起来。这些JSP页面仅仅是通过简单的鼠标拖放和属性设置完成的,真是太棒了!

3.使用Java Page Flow

Java Page Flow是完全基于开源的Struts模型,但提供了更强的易用性和自动化的状态管理,支持数据绑定,拥有更强大的动作和异常处理。下一步,我们将创建一个名为FileUploadController的页面流,完成一个文件上传的功能。

新建页面流fileUploadWorkshop自动为我们创建了一个FileUploadController.jpf,这个jpf作为控制器,我们再添加一个uploadFileAction,包含一个名为“UploadForm”的ActionForm3JSP文件,然后设置导航如下:

详细代码请下载源代码包。

使用Java Page Flow的最大的优点是完全的MVC架构,有清晰的导航模型,并且配合NetUI标签,实现起来更为简单。

以上讨论了基本的浏览页面,下面我们要实现用户登陆的功能和针对管理员的管理功能。

实现用户登陆

为了跟踪用户,我们创建一个AccountManageBD的业务代理类,并将它放在Session中,封装所有与用户帐号相关的操作。几个关键的方法如下:

String getName() // 返回用户登陆名

boolean isAdmin() // 是否是管理员

void login(String username, String password) // 登陆系统

我们在MusicSessionListener中监听Session事件,以便创建和销毁与Session关联的AccountManageBD对象。然后在/WEB-INF/web.xml中注册MusicSessionListener

这样,任何时候均可在JSP中调用

AccountManageBD account = (accountManageBD)

    session.getAttribute(AccountManageBD.ACCOUNT);

来访问用户当前登陆信息。

创建一个登陆页面流LoginController.jpf实现登陆页面:

类似的,一个registerUser页面流实现用户注册功能:

实现管理功能

为了能使管理员登陆后管理站点,我们把所有的管理页面放在目录{MusicWeb}/admin/下,为了防止未授权用户访问任何/admin/下的任何页面,我们准备一个过滤器AdminFilter,将所有未授权的操作重定向到/login/LoginController.jpf中。核心代码如下:

修改/WEB-INF/web.xml,添加过滤器申明:

最后,你只需要用Dreamwaver之类的网页制作软件强化界面,然后把这些JSP及页面流组织起来,即可发布您的JetMusic站点。

安全

正如你所看到的,我们的安全是通过Web层的登陆验证实现的,在逻辑层并未使用基于角色的验证逻辑,对于我们现在的站点来说,可能已经足够了。不过,如果您打算将来向用户提供付费下载,付费收听的业务,那么就需要切实保证系统的安全性。通常,不建议您自己开发安全逻辑,因为开发防攻击的安全逻辑本身就不是一个简单的任务,此外,自定义的安全逻辑可能会导致较低的可重用性。

Java 1.4J2EE 1.3开始,Java验证与授权服务JAASJava Authentication and Authorization Service)就被引入到核心包中。JAAS实现了验证和授权两类服务,验证服务能够可靠并安全地确定目前是谁在执行代码,在我们的这个应用中,只需要用到JAAS的验证服务。以下是JAAS验证的一般步骤:

1.创建一个LoginContext实例。

2.指定LoginContext的配置文件。

3.加载指定的LoginModule

4.客户端调用LoginContextlogin方法。

5.如果登陆成功,就会将一个通过身份验证的PrincipalSubject联系起来。

6LoginContext将通过身份验证的Subject返回客户机。

WebLogic中,缺省的用户,组和角色都是在XML配置文件中指定的,我们只需要提供一个登陆页面,包含一个名为j_security_checkForm,一个名为j_usernameTextBox和一个名为j_passwordPasswordBox,然后在/WEB-INF/web.xml中配置即可使用WebLogic默认的JAAS身份验证。但是,这些用户是静态的,无法满足我们使用数据库动态管理用户的需求。好在JAAS本身就是一个“可插拔”的模块,我们可以轻易地使用自定义的安全模块实现验证功能。

要在WebLogic中实现自定义的安全验证,需要提供一个Security Provider,包括自定义的Authentication ProviderLogin ModuleIdentity AssertionPrincipal Validation ProviderRole Mapping Provider,看上去有点复杂,不过,你可以从BEA站点下载一个示例代码:

http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp

然后修改相应的实现类,配置部署,即可实现自定义的JAAS验证。

使用JAAS验证的好处是,验证逻辑从页面中分离,对页面的限制访问是通过/WEB-INF/web.xml中的配置指定的,无需自定义过滤器;在调用EJB时,Web容器能隐含的把Principal传递给EJB容器,从而和EJB的安全限制联系起来。

在我们的JetMusic站点中,目前仅仅使用Session+过滤器的机制,也许可以考虑在下一个版本中加入JAAS验证,以便更清晰地划分表示层逻辑和安全逻辑。

部署

可以直接在Workshop中启动WebLogic Server,即可自动完成部署。如果部署成功,WebLogic不会输出提示信息,如果部署失败,会提示异常信息。

常见的异常有:

JDBC连接出错:没有启动数据库,或者没有找到对应JNDI名称的数据库。你需要检查JNDI设置和数据库是否已经启动。

INSERT语句出错:这是创建实体Bean时容器的INSERT语句出错。如果指定了automatic-key-generation,第一次部署时WebLogic自动创建的主键为INT类型,但并不是AUTOINCREASE,需要打开数据库,手动更改主键设置,或者,您可以直接下载示例数据库,然后导入到SQL Server2000即可。

总结

我们已经利用J2EE技术在WebLogic Server上基本建立起了一个健壮的,可扩展的JetMusic音乐站点,其中涉及到EJBJSPServletsNetUIJava Page FlowJAAS以及几个常用的EJB设计模式等。相信您能从这个小小的Web应用中看到J2EE体系强大的功能。由于时间仓促,水平有限,文中不免会有一些错误,恳请读者指正。最后希望本文能够给您带来一点收获。最后申明:如果您使用JetMusic系统在网上发布音乐,所引起的一切版权纠纷本文作者概不负责。

源代码下载

JetMusic站点的全部源代码(Workshop 8.1工程)和示例数据库:

http://javap2p.nease.net/src/jetmusic.zip

参考

Sun J2EE Tutorialhttp://java.sun.com/j2ee/tutorial/1_3-fcs/index.html

EJB设计模式,[]Floyd Marinescu

EJB 2.0企业级应用程序开发,[]Chuck CavanessBrian Keeton

BEA WebLogic Server宝典,[]Joe Zuffoletto

WebLogic Workshop Helphttp://e-docs.bea.com/workshop/docs81/doc/en/core/index.html

关于作者

廖雪峰(dev2dev论坛IDxuefengl

北京邮电大学在校本科生,信息工程专业,对J2EE/J2ME开发有浓厚兴趣。

posted on 2005-12-02 10:38 Xuefeng's Weblog 阅读(381) 评论(0)  编辑  收藏 所属分类: J2EE


只有注册用户登录后才能发表评论。


网站导航: