Sun Java System Content Delivery Server 定制指南
|
|
本章介绍了 Sun Java System Content Delivery Server 用户管理 API。可以使用此 API 来创建订户适配器,以便添加、删除、检索、更新、启用和禁用用户。此 API 还提供了一些方法,以便从 Content Delivery Server 数据库中检索与设备有关的信息。
用户配置 API 包含以下类和接口:
- UserManager - 抽象类,您可以扩展该类来创建自己的订户适配器。该类用于控制个人用户帐户的创建和状态。
- User - 接口,您可以实现该接口来管理每个用户的特定属性。
- UserDeviceManager - 可以实现该接口,以提供订户所使用的设备的相关设备信息和安全信息。可以使用此接口来防止订户只付费一次而将内容下载到几个设备上的情况。
- UserDeviceModel - 此接口提供 Content Delivery Server 数据库中的设备相关信息。
有关类和方法的其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。
6.1 UserManager 类
UserManager 类用于定义创建、删除或访问用户信息的方法。可以根据需要扩展此类并实现所有抽象方法,以便与订户数据库进行交互。要获取 Content Delivery Server 已知的设备的相关信息,可以使用 getDeviceModel 和 getDeviceModelList 方法。
有关未介绍的方法的信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。
6.1.1 doFormatMobileId 方法
protected abstract String doFormatMobileId(String mobileId);
doFormatMobileId 方法用于设置移动 ID 的格式,以符合订户数据库的要求。例如,如果数据库无法处理移动 ID 中的连字符,并且订户在 Content Delivery Server 中注册时输入的移动 ID 包含连字符,则可以实现该方法以删除这些特殊字符。
6.1.2 doFormatLoginId 方法
protected abstract String doFormatLoginId(String loginId);
doFormatLoginID 方法用于设置登录 ID 的格式,以符合订户数据库的要求。例如,如果数据库要求所有登录 ID 使用小写字母,并且订户在 Content Delivery Server 中注册时输入的 ID 包含大写字母,则可以实现该方法以将 ID 转换成小写字母。
6.1.3 doGetUserDeviceModel 方法
protected long doGetUserDeviceModel(User user, UserDeviceModel model);
doGetUserDeviceModel 方法用于标识与订户关联的设备。当订户进行登录时,或者订户选择向未在 Content Delivery Server 中注册的用户赠送礼品或与其共享内容时,Content Delivery Server 将会调用该方法。
您必须返回以下值之一:
如果具有足够的信息来标识订户的设备,则会返回手机 ID。此 ID 是在系统中添加设备时 Content Delivery Server 指定的 ID。如果知道设备的用户代理模式,则可以调用 UserManager.getDeviceModel 方法并从返回的对象中获取手机 ID。要获取 Content Delivery Server 已知的设备列表,请调用 UserManager.getDeviceModelList 方法。可以按名称、型号或其他属性查找该列表,以确定所需的设备并获取手机 ID。如果返回的设备 ID 与传递的设备型号不同,则订户的用户配置会随新设备而更新。
如果无法确定设备,或者要使用现有的设备(如果有),则会返回该值。
如果无法确定设备,或者要删除设备与订户之间的关联,则会返回该值。
6.2 User 接口
User 接口用于定义获取、设置或删除用户属性的通用方法。实现此接口和所有方法。有关其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。
6.3 UserDeviceManager 接口
UserDeviceManager 接口定义了一种方法,以根据设备连接时使用的 IP 地址访问设备的唯一 ID,例如,电子序列号 (Electronic Serial Number, ESN)。实现此接口及其方法。有关其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。
6.4 UserDeviceModel 接口
UserDeviceModel 接口定义了一些方法,以访问 Content Delivery Server 已知的设备的相关信息。您可能调用的方法会返回这种类型的对象。有关其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。
6.5 使用用户配置 API
cdsapi.jar 中提供了用户配置 API 的类。在编译适配器时,此文件必须位于类路径中。为方便起见,$CDS_HOME/dist/cds/staging/jar 目录中提供了所有 Content Delivery Server JAR 文件的副本。在编译所创建的适配器时,请在类路径中使用此缓冲区。
使适配器可用于 Content Delivery Server 取决于所使用的应用服务器,以及是否已部署了该服务器。要使适配器可用,请执行以下操作:
1. 为适配器创建 JAR 文件。
2. 对于所有应用服务器,请将该 JAR 文件放在 $CDS_HOME/dist/cds/lib/external 目录中。
现在,该适配器将包含在所有以后的部署中。
3. 如果现有部署需要使用该适配器,请将该 JAR 文件放在每个部署的 $CDS_HOME/deployment/deployment-name/lib/external 目录中。
如果使用的是 WebLogic Server,则会为您处理类路径。
如果使用的是 Sun Java System Application Server,则会为每个部署更新类路径:
a. 在编辑 $CDS_HOME/deployment/deployment-name/sun/domains/cdsdomain/config/domain.xml 文件之前,先对其进行备份,以便从可能在编辑期间引入的任何错误中恢复。
b. 编辑 domain.xml 并修改 java-config 元素,将 JAR 文件的绝对路径添加到 classpath-suffix 属性中。
c. 保存所做的更改。
4. 编辑 $CDS_HOME/deployment/deployment-name/conf 目录中的 security.config 文件:
a. 将 module.security.subscriber.usermanager 属性设置为 User 接口实现的类名,例如:
module.security.subscriber.usermanager=com.sun.content.server.vending.security.user.SubscriberImpl
属性名称和值必须在同一行中。此样例语句占两行是为了适应页面大小。
b. 保存所做的更改。
5. 重新启动任何现有部署以识别新的 JAR 文件。
6.6 用户管理器 API 的样例实现
本节中的代码样例提供了一个使用用户配置 API 创建的订户适配器示例。在 Content Delivery Server 中创建订户帐户时,该实现将在本地文件系统中对用户信息进行编索并存储这些信息。
6.6.1 SampleUserImpl.java
以下代码是 User 接口的样例实现。此接口包括 Content Delivery Server 用于用户配置的字段。如果特定实现具有其他字段,请添加所需的方法以获取和设置这些值。
编码样例 6-1 样例 User 实现
package com.wireless.adapter;
import com.sun.content.server.service.security.User;
import java.util.Date;
import java.util.Hashtable;
import java.io.Serializable;
public class SampleUserImpl implements User, Serializable {
long uid = -1L;
private String loginId;
private String firstName;
private String lastName;
private String email;
private String middleName;
private String gender;
private String street1;
private String street2;
private String postalCode;
private String city;
private String state;
private String country;
private String phone;
private String salutation;
private boolean enabled;
String password;
private String uniqueDeviceId;
private String mobileId;
private boolean prepay;
public SampleUserImpl() {
}
public String getLoginId() { return loginId; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getEmail() { return email; }
public void setLoginId(String loginId) { this.loginId = loginId; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public void setEmail(String email) { this.email = email; }
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getMiddleName() { return middleName; }
public void setGender(String gender) { this.gender = gender; }
public String getGender() { return gender; }
public String getStreet1() { return street1; }
public void setStreet1(String street1) { this.street1 = street1; }
public String getStreet2() { return street2; }
public void setStreet2(String street2) { this.street2 = street2; }
public String getPostalCode() { return postalCode; }
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getCountry() { return country; }
public void setCountry(String country) { this.country = country; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getSalutation() { return salutation; }
public void setSalutation(String salutation) {
this.salutation = salutation;
}
public boolean isEnabled() { return enabled; }
public void setIsEnabled(boolean enabled) { this.enabled = enabled; }
public String getPassword() {
// we will not disclose password !
return null;
}
public void setPassword(String password) { this.password = password; }
public String getUniqueDeviceId() { return uniqueDeviceId; }
public void setUniqueDeviceId(String uniqueDeviceId) {
this.uniqueDeviceId = uniqueDeviceId;
}
public String getMobileId() { return mobileId; }
public void setMobileId(String mobileId) { this.mobileId = mobileId; }
public boolean isPrepay() { return prepay; }
public void setIsPrepay(boolean prepay) { this.prepay = prepay; }
// deprecated methods
public Date getCreateDate() { return null; }
public void setCreateDate(Date date) {}
public Date getLastLogin() { return null; }
public void setLastLogin(Date date) {}
public Object getAttribute(String name) { return null; }
public Object getAttribute(String name, Object def) { return null; }
public Hashtable getAttributes() { return null; }
public void setHasLoggedIn(boolean b) {}
public boolean hasLoggedIn() { return false; }
public void setAttribute(String name, Object val) {}
public void setAttributes(Hashtable h) {}
public boolean isConfirmed() { return false; }
public void updateLastLogin() {}
public Date getActivateDate() { return null; }
public void setActivateDate(Date date) {}
public Date getDeActivateDate() { return null; }
public void setDeActivateDate(Date date) {}
}
|
6.6.2 SampleUserManagerImpl.java
以下代码是 UserManager 类的样例扩展。
编码样例 6-2 样例 UserManager 实现
package com.wireless.adapter;
import com.sun.content.server.service.security.*;
import com.sun.content.server.foundation.log.LogCategory;
import com.sun.content.server.service.security.util.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.*;
/**
* A sample user manager for Content Delivery Server.
*
* <p>
* The sample user manager indexes and stores user information on a
* local file system. The implementation is rudimentary, and doesn't support
* concurrent file modification of the database, so only one server should
* be sharing the user data.
*
* <p>
* The user manager is provided strictly for educational and experimental
* purposes, it has not passed any testing.
*/
public class SampleUserManagerImpl extends UserManager {
// you can change these constants
private final static String FS_ROOT = "/var/tmp/cds_user_db";
private final static String FS_INDEX_LOGIN = "login.idx";
private final static String FS_INDEX_UNIQUE = "unique.idx";
private final static String FS_INDEX_MOBILE = "mobile.idx";
private final static String FS_INDEX = "master.idx";
private final static String DS_KEY = "____sum___timestamp";
private final static long LDS_KEY = -1L;
private final static String EXT = ".act";
private static Map<String,Long> indexLogin;
private static Map<String,Long> indexUnique;
private static Map<String,Long> indexMobile;
private static Map<Long,String[]> index;
private static Object idxLock = new Object();
private static AtomicLong seq = new AtomicLong();
private static boolean dirty;
private static Object dirtyLock = new Object();
private static LogCategory log = LogCategory.getLog();
static {
init();
}
private static void init() {
File fs = new File(FS_ROOT);
if (!fs.exists()) {
if (!fs.mkdirs()) {
throw new RuntimeException("Can not create directories"+
" for user database " + fs.getAbsolutePath());
}
}
File f = new File(fs, FS_INDEX_LOGIN);
indexLogin = readMap(f);
f = new File(fs, FS_INDEX_UNIQUE);
indexUnique = readMap(f);
f = new File(fs, FS_INDEX_MOBILE);
indexMobile = readMap(f);
f = new File(fs, FS_INDEX);
index = readMap(f);
if (index == null) {
rebuildMasterIndex();
}
long masterStamp = Long.parseLong(index.get(LDS_KEY)[0]);
if (indexLogin != null && indexLogin.get(DS_KEY) < masterStamp) {
indexLogin = null;
}
if (indexUnique != null && indexUnique.get(DS_KEY) < masterStamp) {
indexUnique = null;
}
if (indexMobile != null && indexMobile.get(DS_KEY) < masterStamp) {
indexMobile = null;
}
boolean needIL = false;
boolean needIU = false;
boolean needIM = false;
if (indexLogin == null) {
log.info("SUM: will rebuild login index");
needIL = true;
indexLogin = new HashMap<String,Long>();
}
if (indexUnique == null) {
log.info("SUM: will rebuild unique index");
needIU = true;
indexUnique = new HashMap<String,Long>();
}
if (indexMobile == null) {
log.info("SUM: will rebuild mobile index");
needIM = true;
indexMobile = new HashMap<String,Long>();
}
long maxVal = 0L;
for (Map.Entry<Long,String[]> entry : index.entrySet()) {
Long key = entry.getKey();
if (key.equals(LDS_KEY)) { continue; }
String [] data = entry.getValue();
if (needIL) {
indexLogin.put(data[1], key);
}
if (needIU) {
indexUnique.put(data[2], key);
}
if (needIM) {
indexMobile.put(data[3], key);
}
if (maxVal < key) { maxVal = key; }
}
seq.set(maxVal);
log.info("SUM: initialized, index is "+ index.size()+" entries");
dirty = true;
Runnable r = new Updater();
(new Thread(r)).start();
}
private static void rebuildMasterIndex() {
try {
log.info("SUM: rebuilding master index");
File fs = new File(FS_ROOT);
index = new HashMap<Long,String[]>();
File [] lst = fs.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(EXT);
} });
for (File file : lst) {
SampleUserImpl user = readUser(file, false);
if (user == null) { continue; }
if (seq.get() <= user.uid) { seq.set(user.uid + 1L); }
index.put(user.uid, new String[]{
file.getName(),
user.getLoginId(),
user.getUniqueDeviceId(),
user.getMobileId()});
}
index.put(LDS_KEY,
new String[]{String.valueOf(System.currentTimeMillis())});
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static SampleUserImpl readUser(File f,
boolean reThrow) throws Exception {
try {
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
SampleUserImpl user = (SampleUserImpl)ois.readObject();
ois.close();
fis.close();
return user;
} catch (Exception e) {
if (reThrow) { throw e; }
log.warn("error reading file "+f.getAbsolutePath(), e);
}
return null;
}
private static Map readMap(File f) {
try {
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
Map map = (Map)ois.readObject();
ois.close();
fis.close();
return map;
} catch (Exception e) {
log.warn("error reading file "+f.getAbsolutePath(), e);
}
return null;
}
public SampleUserManagerImpl() {
}
public boolean doAddUser(User _user) throws UserProfileResourceException {
SampleUserImpl user = (SampleUserImpl)_user;
synchronized (user) {
if (user.uid != -1L) {
throw new UserProfileResourceException("the user is already"+
" added");
}
user.uid = seq.incrementAndGet();
doUpdateUser(_user);
}
return true;
}
protected User doGetUserInstance() {
return new SampleUserImpl();
}
protected User doGetUserByUniqueDeviceId(String id)
throws UserProfileResourceException {
return getUserFromIndex(id, indexUnique);
}
protected User doGetUserByMobileId(String id)
throws UserProfileResourceException {
return getUserFromIndex(id, indexMobile);
}
protected User doGetUserByLoginId(String id)
throws UserProfileResourceException {
return getUserFromIndex(id, indexLogin);
}
protected boolean doAuthenticatePassword(User _user, String pwd) {
return pwd.equals(((SampleUserImpl)_user).password);
}
protected void doEnableUser(User _user)
throws UserProfileResourceException {
SampleUserImpl user = (SampleUserImpl)_user;
if (user.isEnabled()) { return; }
user.setIsEnabled(true);
doUpdateUser(_user);
}
protected void doDisableUser(User _user)
throws UserProfileResourceException {
SampleUserImpl user = (SampleUserImpl)_user;
if (!user.isEnabled()) { return; }
user.setIsEnabled(false);
doUpdateUser(_user);
}
protected boolean doRemoveUser(User _user)
throws UserProfileResourceException {
synchronized (idxLock) {
SampleUserImpl user = (SampleUserImpl)_user;
String [] data = index.get(user.uid);
if (data == null) { return false; }
index.remove(user.uid);
indexLogin.remove(data[1]);
indexUnique.remove(data[2]);
indexMobile.remove(data[3]);
File f = new File(FS_ROOT, data[0]);
f.delete();
user.uid = -1L;
}
synchronized (dirtyLock) {
dirty = true;
}
return true;
}
protected boolean doUpdateUser(User _user)
throws UserProfileResourceException {
SampleUserImpl user = (SampleUserImpl)_user;
if (user.uid == -1L) { return false; }
try {
File fs = new File(FS_ROOT);
String [] data = index.get(user.uid);
if (data == null) {
File tc = File.createTempFile("sum-", EXT, fs);
data = new String[]{
tc.getName(), user.getLoginId(),
user.getUniqueDeviceId(), user.getMobileId()
};
}
// dump out user data
File f = new File(fs, data[0]);
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
oos.close();
fos.close();
synchronized (idxLock) {
index.put(user.uid, data);
indexLogin.remove(data[1]);
indexLogin.put(user.getLoginId(), user.uid);
indexUnique.remove(data[2]);
indexUnique.put(user.getUniqueDeviceId(), user.uid);
indexMobile.remove(data[3]);
indexMobile.put(user.getMobileId(), user.uid);
}
synchronized (dirtyLock) {
dirty = true;
}
} catch (Exception e) {
e.printStackTrace();
throw new UserProfileResourceException(e);
}
return true;
}
private User getUserFromIndex(String id,
Map<String,Long> index) throws UserProfileResourceException {
try {
Long pk = index.get(id);
if (pk == null) { return null; }
return getUserByPK(pk);
} catch (Exception e) {
e.printStackTrace();
throw new UserProfileResourceException(e);
}
}
private User getUserByPK(Long pk) throws Exception {
String [] data = index.get(pk);
if (data == null) { return null; }
File f = new File(FS_ROOT, data[0]);
return readUser(f, true);
}
protected String doFormatMobileId(String id) {
return id.trim();
}
protected String doFormatLoginId(String id) {
return id.trim();
}
static class Updater implements Runnable {
public void run() {
Thread.currentThread().setName("SUM-Updater");
log.info("SUM: updater thread initialized");
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {}
synchronized (dirtyLock) {
if (!dirty) { continue; }
dirty = false;
}
// the maps are dirty, well...
try {
long stamp = System.currentTimeMillis();
Map<Long,String[]> _index = new HashMap<Long,String[]>();
Map<String,Long> _login = new HashMap<String,Long>();
Map<String,Long> _unique = new HashMap<String,Long>();
Map<String,Long> _mobile = new HashMap<String,Long>();
synchronized (idxLock) {
index.put(LDS_KEY, new String[]{String.valueOf(stamp)});
indexLogin.put(DS_KEY, stamp);
indexUnique.put(DS_KEY, stamp);
indexMobile.put(DS_KEY, stamp);
_index.putAll(index);
_login.putAll(indexLogin);
_unique.putAll(indexUnique);
_mobile.putAll(indexMobile);
}
File f = new File(FS_ROOT, FS_INDEX);
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(_index);
oos.close();
fos.close();
f = new File(FS_ROOT, FS_INDEX_LOGIN);
fos = new FileOutputStream(f);
oos = new ObjectOutputStream(fos);
oos.writeObject(_login);
oos.close();
fos.close();
f = new File(FS_ROOT, FS_INDEX_UNIQUE);
fos = new FileOutputStream(f);
oos = new ObjectOutputStream(fos);
oos.writeObject(_unique);
oos.close();
fos.close();
f = new File(FS_ROOT, FS_INDEX_MOBILE);
fos = new FileOutputStream(f);
oos = new ObjectOutputStream(fos);
oos.writeObject(_mobile);
oos.close();
fos.close();
log.debug("SUM: indices synced to fs");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
|
6.6.3 使用该样例适配器
如果要试验该代码,您可以在测试部署中运行该样例适配器。第一次部署该适配器时,Vending Manager 中不能有任何订户帐户。
要使用该样例适配器,请执行以下操作:
1. 如果 Vending Manager 正在运行,请将其停止。
必须在部署了 Vending Manager 的服务器上运行该订户适配器。
2. 在目录中创建 SampleUserImpl 和 SampleUserManagerImpl 类,例如 /tmp/sum。
3. 在包含刚创建的类文件的目录中,按顺序执行以下命令。
以单行代码的形式输入每个命令。
$ mkdir bin
$ javac -d bin -classpath $CDS_HOME/dist/cds/lib/cdslib/cdsapi.jar:$CDS_HOME/dist/cds/lib/cdslib/foundation.jar SampleUserImpl.java SampleUserManagerImpl.java && ( cd bin && jar cf ../sum.jar .)
$ cp sum.jar $CDS_HOME/deployment/deployment-name/lib/external/
4. 编辑 $CDS_HOME/deployment/deployment-name/conf/security.config 文件。
将 module.security.subscriber.usermanager 属性设置为 com.wireless.adapter.SampleUserManagerImpl。
5. 启动 Vending Manager。
Sun Java System Content Delivery Server 定制指南
|
820-5375-10
|
|
版权所有 © 2008, Sun Microsystems, Inc. 保留所有权利