Liferay代码分析与扩展——用户服务
转载请保留作者信息:
作者:88250
Blog:http:/blog.csdn.net/DL88250
MSN & Gmail & QQ:DL88250@gmail.com
目录
摘要 1
环境 1
导入工程 1
用户服务分析 3
用户服务扩展 3
问题描述 4
解决方案分析 4
Solution 1 4
Solution 2 4
最终方案 4
步骤 4
总结 8
摘要
看了几天Liferay了,由于要和其他系统进行整合,所以分析了一下Liferay的用户服务代码。从其用户服务代码可以看出一些Liferay的主要设计思想,为将来分析他的架构设计做下准备。
环境
点此下载Liferay5.1.1源代码
导入工程
Liferay5.1.1是用NetBeans建立的Ant工程,源代码包解压后可以在NetBeans中直接打开。其工程视图如下:
portal-impl
包含了所有的服务实现代码,例如基本的用户管理、邮件服务、内容管理(CMS,JSR170)
portal-service
包含了portal-impl的所有服务接口
portal-kernel
包含了Portal/Portlet规范实现(JSR 168/268),以及基本的框架管理(Struts、Spring), 还有部署、配置、搜索、WebService、持久化的基本实现
support-glassfish/tomcat
对gsf/tomcat容器的支持实现
util-bridges
对一些框架/语言编写Portlet的支持。当前我们可以使用如下语言/框架来编写Portlet:
Bean Scripting Framework (BSF)
Groovy
Javascript
JSF
JSP
PHP
Python
Ruby
Struts
Web Accessibility Initiative (WAI)
util-java/taglib
Liferay使用的基本Utilities以及标签库实现
用户服务分析
目前,Liferay项目是用Struts1.2.x + Spring 2.0作为基本框架编写的。其架构设计思路清晰明了,是难得的学习设计的示例。下面,进入本文的主题——用户服务分析。
在目录portal-service下展开源包com.liferay.portal.service,可以看到一系列****Service的接口。我们找出与用户相关的,可以得出如下类依赖关系图:
从最上层的两个接口看来,Liferay在用户服务设计方面与EJB3.x的思路是一致的,分为了Local接口与Remote接口,从代码的注释也可以得到这一点。在liferay的当前版本中(5.1.1),在图右边的Remote接口中都将服务适当处理后委托给了图左边的Local实现。其他关键的地方请参考图中的说明部分。
用户服务扩展
这里与其说是“扩展”,不如说是“修改”。
在门户与其他系统进行整合的时候,第一个困难的问题就是用户管理的问题。这里说的用户管理可以简单分为两类管理:身份认证、帐号管理。关于身份认证的问题可以使用CAS(Central Authentication Service)来实现与可系统之间的SSO(Single Sign-On/Out),可以参考这里、还有这里。帐号管理就是当前最头疼的,具体问题描述如下:
问题描述
举个例子:如果Admin在门户Liferay中建立了一个帐号,其余系统也应该建立用户帐号;如果用户在门户中修改了密码,该用户在其余系统中的帐号密码也应该作相应修改,相反则不成立。
解决方案分析
结合先前的用户服务分析类关系图,可以得出如下两个方案:
Solution 1
继承UserLocalServiceImpl类,添加我们需要的功能。
优点:以后Liferay更新了代码,对我们需要的功能不会造成影响,基本不存在代码合并的代价。
缺点:要修改Spring的配置文件,将对UserLocalServiceImpl Bean的引用改为我们的实现Bean配置。
Solution 2
直接修改UserLocalServiceImpl类代码,将所需功能委托给我们的实现。
优点:不需要修改Spring配置文件。
缺点:以后Liferay更新了代码,有一定的代码合并代价。
最终方案
综上,权衡下来。我觉得使用Solution 2。因为我认为目前修改代码对以后升级的合并代价小于修改配置文件的代价。所以,与其说是“扩展”,不如说是“修改” : )
步骤
以添加用户为例:
打开com.liferay.portal.service.impl.UserLocalServiceImpl类,找到addUser方法,在此方法最后加入我们的实现委托:
- // unusing spring DI, now, just for simple.
- // by 88250, Aug 29, 2008
- new SubsystemUserServiceImpl().registerUser(firstName, lastName,
- emailAddress, screenName, password1);
这里没有使用Spring的DI,只是为了简便一点,不用修改配置文件。以后可能会使用Spring DI。
下面就是我们所需服务的实现:
- /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
- package com.jinfonet.developer.portal.service.impl;
- import com.liferay.portal.PortalException;
- import com.liferay.portal.security.pwd.PwdEncryptor;
- import java.io.IOException;
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import org.apache.commons.httpclient.HttpClient;
- import org.apache.commons.httpclient.HttpException;
- import org.apache.commons.httpclient.NameValuePair;
- import org.apache.commons.httpclient.methods.PostMethod;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /**
- * Subsystem user service utility. Currently, this class is mainly for
- * update coherence of user accounts existed in some subsystems(scarab, svn,
- * etc.).
- * @author 88250 <DL88250@gmail.com>
- * @version 1.0.0.2, Sep 2, 2008
- */
- public class SubsystemUserServiceImpl {
- // TODO places all properties in a configuration file, no hard-coding
- private final String driver = "com.mysql.jdbc.Driver";
- private final String dbURL = "jdbc:mysql://192.168.128.122:3306/scarab";
- private final String dbUserName = "root";
- private final String dbUserPwd = "sa";
- private Connection con = null;
- private static Log log = LogFactory.getLog(SubsystemUserServiceImpl.class);
- private final String REGISTER_URL = "http://daniel-desktop:9090/scarab/issues/template/AutoRegister";
- /**
- * Default constructor.
- * @throws com.liferay.portal.PortalException
- */
- public SubsystemUserServiceImpl() throws PortalException {
- try {
- Class.forName(driver);
- } catch (ClassNotFoundException ex) {
- log.error(ex);
- throw new PortalException(ex);
- }
- }
- /**
- * Changes user's password in all subsystems.
- * @param userName user name
- * @param newPwd the new password(clear text)
- * @throws PortalException
- */
- public void changePassword(String userName, String newPwd) throws PortalException {
- updatePassword(userName, newPwd);
- }
- /**
- * Registers a new user in all subsystems.
- * @param firstName first name
- * @param lastName last name
- * @param emailAddress email adress
- * @param screenName user name
- * @param password user password
- * @throws PortalException
- */
- public void registerUser(String firstName, String lastName, String emailAddress,
- String screenName, String password) throws PortalException {
- addUser(firstName, lastName, emailAddress, screenName, password);
- }
- private void addUser(String firstName, String lastName, String emailAddress,
- String screenName, String password) throws PortalException {
- HttpClient httpClient = new HttpClient();
- PostMethod registerMethod = new PostMethod(REGISTER_URL);
- NameValuePair[] data = {
- new NameValuePair("firstName", firstName),
- new NameValuePair("lastName", lastName),
- new NameValuePair("email", emailAddress),
- new NameValuePair("userName", screenName),
- new NameValuePair("password", password),
- };
- registerMethod.setRequestBody(data);
- try {
- int status = httpClient.executeMethod(registerMethod);
- log.info("Autoregister for subsystem Scarab successfully!");
- } catch (IOException ex) {
- Logger.getLogger(SubsystemUserServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
- log.error("Autoregister for subsystem Scarab faild!", ex);
- registerMethod.releaseConnection();
- }
- }
- private void updatePassword(String userName, String newPwd) throws PortalException {
- String newPwdCipherText = PwdEncryptor.encrypt(newPwd); // SHA crypt algorithm
- try {
- con = DriverManager.getConnection(dbURL, dbUserName, dbUserPwd);
- Statement smt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
- ResultSet.CONCUR_UPDATABLE);
- smt.executeUpdate("UPDATE TURBINE_USER SET PASSWORD_VALUE = '" + newPwdCipherText +
- "' WHERE LOGIN_NAME = '" + userName + "'");
- smt.close();
- con.close();
- } catch (SQLException ex) {
- log.error("Scarab Database modify failed: ", ex);
- throw new PortalException(ex);
- }
- }
- }
总结
Liferay是一个设计很优秀的Portal,他有很多设计思路、代码实现是我们值得学习、借鉴的地方。关于用户服务这一块我们就分析到此,在后续的篇章中我将给大家带来更精彩的Liferay代码分析,定制出适合我们自己的门户系统!请大家继续关注我的Blog哦:http://blog.csdn.net/DL88250
: )