6


用户配置 API

本章介绍了 Sun Java System Content Delivery Server 用户管理 API。可以使用此 API 来创建订户适配器,以便添加、删除、检索、更新、启用和禁用用户。此 API 还提供了一些方法,以便从 Content Delivery Server 数据库中检索与设备有关的信息。

用户配置 API 包含以下类和接口:

有关类和方法的其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的用户配置 API 的 Javadoc 工具的 HTML 输出。


6.1 UserManager

UserManager 类用于定义创建、删除或访问用户信息的方法。可以根据需要扩展此类并实现所有抽象方法,以便与订户数据库进行交互。要获取 Content Delivery Server 已知的设备的相关信息,可以使用 getDeviceModelgetDeviceModelList 方法。

有关未介绍的方法的信息,请参见 $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. 在目录中创建 SampleUserImplSampleUserManagerImpl 类,例如 /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。