com.npower.dm.processor.ProfileAssignmentProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.npower.dm.processor.ProfileAssignmentProcessor.java

Source

/**
 * $Header: /home/master/nWave-DM-Common/src/com/npower/dm/processor/ProfileAssignmentProcessor.java,v 1.37 2008/12/12 04:16:10 zhao Exp $
 * $Revision: 1.37 $
 * $Date: 2008/12/12 04:16:10 $
 *
 * ===============================================================================================
 * License, Version 1.1
 *
 * Copyright (c) 1994-2006 NPower Network Software Ltd.  All rights reserved.
 *
 * This SOURCE CODE FILE, which has been provided by NPower as part
 * of a NPower product for use ONLY by licensed users of the product,
 * includes CONFIDENTIAL and PROPRIETARY information of NPower.
 *
 * USE OF THIS SOFTWARE IS GOVERNED BY THE TERMS AND CONDITIONS
 * OF THE LICENSE STATEMENT AND LIMITED WARRANTY FURNISHED WITH
 * THE PRODUCT.
 *
 * IN PARTICULAR, YOU WILL INDEMNIFY AND HOLD NPower, ITS RELATED
 * COMPANIES AND ITS SUPPLIERS, HARMLESS FROM AND AGAINST ANY CLAIMS
 * OR LIABILITIES ARISING OUT OF THE USE, REPRODUCTION, OR DISTRIBUTION
 * OF YOUR PROGRAMS, INCLUDING ANY CLAIMS OR LIABILITIES ARISING OUT OF
 * OR RESULTING FROM THE USE, MODIFICATION, OR DISTRIBUTION OF PROGRAMS
 * OR FILES CREATED FROM, BASED ON, AND/OR DERIVED FROM THIS SOURCE
 * CODE FILE.
 * ===============================================================================================
 */
package com.npower.dm.processor;

import java.io.Serializable;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import sync4j.framework.core.dm.ddf.DevInfo;
import sync4j.framework.engine.dm.AddManagementOperation;
import sync4j.framework.engine.dm.DeviceDMState;
import sync4j.framework.engine.dm.GetManagementOperation;
import sync4j.framework.engine.dm.ManagementException;
import sync4j.framework.engine.dm.ManagementOperation;
import sync4j.framework.engine.dm.ManagementOperationResult;
import sync4j.framework.engine.dm.ManagementProcessor;
import sync4j.framework.engine.dm.ReplaceManagementOperation;
import sync4j.framework.engine.dm.SessionContext;
import sync4j.framework.engine.dm.TreeManagementOperation;
import sync4j.framework.engine.dm.TreeNode;

import com.npower.dm.core.APLinkValueNotFoundException;
import com.npower.dm.core.DDFNode;
import com.npower.dm.core.DMException;
import com.npower.dm.core.Device;
import com.npower.dm.core.Model;
import com.npower.dm.core.ProfileAssignment;
import com.npower.dm.core.ProfileConfig;
import com.npower.dm.core.ProfileMapping;
import com.npower.dm.core.ProfileNodeMapping;
import com.npower.dm.core.ProfileTemplate;
import com.npower.dm.core.ProvisionJob;
import com.npower.dm.management.ManagementBeanFactory;
import com.npower.dm.management.ProfileAssignmentBean;
import com.npower.dm.management.ProvisionJobBean;
import com.npower.dm.server.engine.EngineConfig;
import com.npower.dm.util.DMUtil;

/**
 * @author Zhao DongLu
 * @version $Revision: 1.37 $ $Date: 2008/12/12 04:16:10 $
 */
public class ProfileAssignmentProcessor extends BaseProcessor implements ManagementProcessor, Serializable {

    private static transient Log log = LogFactory.getLog(ProfileAssignmentProcessor.class);

    /**
     * Hold the ProfileAssignment related with this processor.
     */
    //private long profileAssignmentID  = 0;

    /**
     * Steps
     */
    protected int step = 0;

    /**
     * Hold the GET operation to detect node name.
     */
    private List<ManagementOperation> queuedOperations = null;

    /**
     * Hold the nodes<DDFNode, value> which wait to be processed.
     */
    private Map<DDFNode, String> waitedNodeMaps = new TreeMap<DDFNode, String>();

    /**
     * Hold the mapping nodePath and DDFNode
     */
    private Map<String, DDFNode> nodePathMaps = new TreeMap<String, DDFNode>();

    /**
     * Hold the no-name-node's name generated by this processor.
     */
    private Map<DDFNode, String> nameMaps = new TreeMap<DDFNode, String>();

    /**
     * 
     */
    private boolean success = true;

    /**
     * 
     */
    public ProfileAssignmentProcessor() {
        super();
    }

    /**
     * Compile the ProfileAssignment to a map<DDFNode, value).
     * @param factory
     *        ManagementBeanFactory
     * @return
     * @throws DMException
     * @throws ManagementException
     */
    private Map<DDFNode, String> compileToNodeMap(ManagementBeanFactory factory)
            throws DMException, ManagementException {

        ProfileAssignment assignment = getCurrentProfileAssignment(factory);

        ProfileConfig config = assignment.getProfileConfig();
        ProfileTemplate template = config.getProfileTemplate();
        Device device = assignment.getDevice();

        Model model = device.getModel();
        ProfileMapping mapping = model.getProfileMap(template);

        // Caculate the rootPath
        String rootPath = assignment.getProfileRootNodePath();
        String rootNodeName = BaseProcessor.getNodeNameFromNodePath(rootPath);
        DDFNode rootNode = mapping.getRootDDFNode();
        if (rootPath == null || rootNodeName == null) {
            rootPath = rootNode.getNodePath();
        } else {
            this.nameMaps.put(rootNode, rootNodeName);
            this.nodePathMaps.put(rootPath, rootNode);
        }

        Map<DDFNode, String> result = new TreeMap<DDFNode, String>();
        Set<ProfileNodeMapping> nodeMappings = mapping.getProfileNodeMappings();
        ProvisionJobBean jobBean = factory.createProvisionJobBean();
        for (ProfileNodeMapping nodeMapping : nodeMappings) {
            DDFNode node = nodeMapping.getDdfNode();

            // Caculate value
            String value = null;
            try {
                Object v = jobBean.caculateProfileAssignmentValue(assignment, nodeMapping);
                if (v != null) {
                    value = v.toString();
                }
            } catch (APLinkValueNotFoundException ex) {
                // Could not found ProfileAssignment linked by current profile assgingment
                continue;
            }

            // Put value into mapping tables.
            result.put(node, value);

            // NoNameNode, resultMap
            DDFNode parentNode = node.getParentDDFNode();
            while (parentNode != null) {
                if (parentNode.getName() == null && !result.containsKey(parentNode)) {
                    result.put(parentNode, null);
                }
                parentNode = parentNode.getParentDDFNode();
            }
        }
        // Debug: List DDFNode To Value Map
        if (log.isDebugEnabled()) {
            log.debug("Stage#1: Profile Node-Value Mapping ... ");
            for (DDFNode node : result.keySet()) {
                Object v = result.get(node);
                log.debug("DDFNode: \"" + node.getNodePath() + "\" with value: " + v);
            }
        }
        return result;
    }

    /**
     * Return the current profile assignment.
     * @param factory
     * @return
     * @throws DMException
     * @throws ManagementException
     */
    private ProfileAssignment getCurrentProfileAssignment(ManagementBeanFactory factory)
            throws DMException, ManagementException {
        ProvisionJobBean jobBean = factory.createProvisionJobBean();
        ProvisionJob job = jobBean.loadJobByID(this.sessionContext.getDmstate().mssid);
        if (job == null) {
            throw new DMException("Could not found the jobID: " + this.sessionContext.getDmstate().mssid);
        }
        if (!job.getJobType().equalsIgnoreCase(ProvisionJob.JOB_TYPE_ASSIGN_PROFILE)) {
            throw new DMException("The job's type wrong, must assign a assignment job to this processor. jobID: "
                    + this.sessionContext.getDmstate().mssid);
        }

        ProfileAssignment profileAssignment = jobBean.getProfileAssignment(job, this.getDevice(factory));
        return profileAssignment;
    }

    private int nameCounter = 0;

    /**
     * Generator a Node name: Name_Prefix + counter
     * 
     * @param node
     * @return
     */
    private String generateNodeName(DDFNode node) {
        // String newName = "" + System.currentTimeMillis();
        nameCounter++;
        String newName = DMUtil.NAME_PREFIX_FOR_NO_NAME_NODE + nameCounter;
        this.nameMaps.put(node, newName);
        return newName;
    }

    /**
     * caculate the nodePath. If the node's name is null, will instead with Map of
     * this.nameMaps. The path will start with "./"
     * 
     * @return
     */
    private String caculateNodePath(DDFNode node) {

        StringBuffer result = new StringBuffer();
        String name = node.getName();
        if (name == null) {
            name = this.generateNodeName(node);
        }
        result.append(name);
        DDFNode parent = node.getParentDDFNode();
        while (parent != null) {
            result.insert(0, "/");
            name = parent.getName();
            if (name == null) {
                if (this.nameMaps.containsKey(parent)) {
                    name = this.nameMaps.get(parent);
                } else {
                    throw new RuntimeException("Parent node haven't been specified name.");
                }
            }
            result.insert(0, name);
            parent = parent.getParentDDFNode();
        }
        String path = result.toString();
        if (!path.startsWith("./")) {
            return "./" + path;
        } else {
            return path;
        }
    }

    /**
     * caculate the nodePath. If the node's name is null, will instead with Map of
     * this.nameMaps. The path will start with "./"
     * 
     * @return
     */
    private String getNodePath(DDFNode node) {

        StringBuffer result = new StringBuffer();
        String name = node.getName();
        if (name == null) {
            if (this.nameMaps.containsKey(node)) {
                name = this.nameMaps.get(node);
            } else {
                throw new RuntimeException("node haven't been specified name.");
            }
        }
        result.append(name);
        DDFNode parent = node.getParentDDFNode();
        while (parent != null) {
            result.insert(0, "/");
            name = parent.getName();
            if (name == null) {
                if (this.nameMaps.containsKey(parent)) {
                    name = this.nameMaps.get(parent);
                } else {
                    throw new RuntimeException("Parent node haven't been specified name.");
                }
            }
            result.insert(0, name);
            parent = parent.getParentDDFNode();
        }
        String path = result.toString();
        if (!path.startsWith("./")) {
            return "./" + path;
        } else {
            return path;
        }
    }

    /**
     * Get next DDFNode from queue.
     * 
     * @return
     */
    private DDFNode getNextDDFNode() {
        Set<DDFNode> nodes = this.waitedNodeMaps.keySet();
        if (nodes.isEmpty()) {
            return null;
        } else {
            Iterator<DDFNode> iter = nodes.iterator();
            DDFNode node = iter.next();
            return node;
        }
    }

    /**
     * Get, Add.
     * 
     */
    private void fillGetOperations() {
        // The queued operations is empty and not finished.
        for (DDFNode node : this.waitedNodeMaps.keySet()) {
            String name = node.getName();
            if (name == null) {
                // Generate name
                String nodePath = this.caculateNodePath(node);

                // Generate a GET operation to detect the generated-name.
                GetManagementOperation oper = new GetManagementOperation();
                TreeNode tNode = new TreeNode(nodePath, null);
                oper.addTreeNode(tNode);

                this.nodePathMaps.put(nodePath, node);

                this.queuedOperations.add(oper);

            }
        }

        // Debug: List NodePath-To-DDF Map
        if (log.isDebugEnabled()) {
            log.debug("Stage#2: NodePath-To-DDFNode Mapping ... ");
            for (String nodePath : this.nodePathMaps.keySet()) {
                DDFNode node = this.nodePathMaps.get(nodePath);
                log.debug("NodePath: \"" + nodePath + "\" map to DDF: " + node.getNodePath());
            }
        }
    }

    /**
     * The readObject method is responsible for reading from the stream and
     * restoring the classes fields, and restore the transient fields.
     */
    protected void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
        super.readObject(in);
        log = LogFactory.getLog(ProfileAssignmentProcessor.class);
    }

    // Public methods
    // *****************************************************************************************

    // Implements ManagementProcessor interface
    // ***************************************************************

    /**
     * Called when a management session is started for the given principal.
     * sessionId is the content of the SessionID element of the OTA DM message.
     * 
     * @param sessionId
     *          the content of the SessionID element of the OTA DM message
     * @param principal
     *          the principal who started the management session
     * @param type
     *          the management session type (as specified in the message Alert)
     * @param devInfo
     *          device info of the device under management
     * @param dmstate
     *          device management state
     * 
     * @throws ManagementException
     *           in case of errors
     * 
     * @see com.npower.dm.processor.BaseProcessor#beginSession(sync4j.framework.engine.dm.SessionContext)
     */
    @Override
    public void beginSession(SessionContext context) throws ManagementException {
        this.sessionContext = context;

        String sessionId = context.getSessionId();
        Principal principal = context.getPrincipal();
        int type = context.getType();
        DevInfo devInfo = context.getDevInfo();

        if (log.isDebugEnabled()) {
            log.debug("Starting a new DM management session");
            log.debug("sessionId: " + sessionId);
            log.debug("principal: " + principal);
            log.debug("type: " + type);
            log.debug("deviceId: " + devInfo);
        }

        ManagementBeanFactory factory = null;
        try {
            // Load Device boundled with this Session
            factory = ManagementBeanFactory.newInstance(EngineConfig.getProperties());
            String deviceExternalID = this.sessionContext.getDmstate().deviceId;
            Device device = loadDeviceByExternalID(factory, deviceExternalID);
            if (device == null) {
                throw new ManagementException("Could not load device: from DM inventory.");
            }
            this.setDeviceID(device.getID());

            // Set the job status
            super.setJobStatus4BeginSession();

            // Update DevInfo
            super.updateDevInfo(devInfo);

            // Compile Profile to Map of <DDFNode, value>
            this.waitedNodeMaps = this.compileToNodeMap(factory);

            // Checking and generate DDFNode name, Generate GET operation to detect the node name.
            this.queuedOperations = new ArrayList<ManagementOperation>();
            this.fillGetOperations();

        } catch (Exception ex) {
            throw new ManagementException("Error in beginSession: ", ex);
        } finally {
            if (factory != null) {
                factory.release();
            }
        }
    }

    /**
     * Called when the management session is closed. CompletionCode can be one of:
     * <ul>
     * <li>DM_SESSION_SUCCESS</li>
     * <li>DM_SESSION_ABORT</li>
     * <li>DM_SESSION_FAILED</li>
     * 
     * CompletionCode defined by DeviceDMState.STATE_XXXX
     * 
     * @param completionCode
     *          the management session competion code
     * 
     * @throws ManagementException
     *           in case of errors
     * 
     * @see sync4j.framework.engine.dm.ManagementProcessor#endSession(int)
     */
    public void endSession(int completionCode) throws ManagementException {
        if (log.isDebugEnabled()) {
            log.debug("End a DM management session with sessionId: " + sessionContext.getSessionId());
        }
        ManagementBeanFactory factory = null;
        try {
            // update the jobStatus for end session
            setJobStatus4EndSession(completionCode);

            factory = ManagementBeanFactory.newInstance(EngineConfig.getProperties());
            if (this.success && completionCode == DeviceDMState.STATE_COMPLETED) {
                // ProfileAssignment
                ProfileAssignment profileAssignment = getCurrentProfileAssignment(factory);
                ProfileConfig config = profileAssignment.getProfileConfig();
                ProfileTemplate template = config.getProfileTemplate();

                Model model = this.getDevice(factory).getModel();
                ProfileMapping mapping = model.getProfileMap(template);
                DDFNode rootNode = mapping.getRootDDFNode();

                String profileRootNodePath = null;
                for (String path : this.nodePathMaps.keySet()) {
                    DDFNode node = this.nodePathMaps.get(path);
                    if (node.getID() == rootNode.getID()) {
                        profileRootNodePath = path;
                        break;
                    }
                }
                assert profileRootNodePath != null : "Could not find the root node path for ProfileAssignment.";
                ProfileAssignmentBean bean = factory.createProfileAssignmentBean();
                factory.beginTransaction();
                profileAssignment.setProfileRootNodePath(profileRootNodePath);
                profileAssignment.setLastSentToDevice(new Date());
                bean.update(profileAssignment);
                factory.commit();

                // Submit a discovery job to discover the device tree.
                ProvisionJobBean jobBean = factory.createProvisionJobBean();
                ProvisionJob job = jobBean.loadJobByID(this.sessionContext.getDmstate().mssid);
                this.submitDiscoveryJob(job.getID(), new String[] { profileRootNodePath });
            }
        } catch (Exception ex) {
            if (factory != null) {
                factory.rollback();
            }
            throw new ManagementException("End of DM ProfileAssignmentProcessor error", ex);
        } finally {
            if (factory != null) {
                factory.release();
            }
        }
    }

    /**
     * Called to retrieve the next management operations to be performed.
     * 
     * @returns an array of ManagementOperation representing the management
     *          operations to be performed.
     * 
     * @throws ManagementException
     *           in case of errors
     * 
     * @see sync4j.framework.engine.dm.ManagementProcessor#getNextOperations()
     */
    public ManagementOperation[] getNextOperations() throws ManagementException {

        if (!this.queuedOperations.isEmpty()) {
            // Name detection operations 
            this.appendDummyOperation(this.queuedOperations);
            return (ManagementOperation[]) this.queuedOperations.toArray(new ManagementOperation[0]);
        }

        if (this.waitedNodeMaps.isEmpty()) {
            // all of operation is finished.
            return new ManagementOperation[0];
        }

        DDFNode node = this.getNextDDFNode();
        List<ManagementOperation> operatoins = new ArrayList<ManagementOperation>();
        while (node != null) {
            TreeManagementOperation oper = null;
            if (node.imply(DDFNode.ACCESS_TYPE_ADD)) {
                oper = new AddManagementOperation();
            } else if (!node.getFormat().equals(DDFNode.DDF_FORMAT_NODE)) {
                // If the node do not permit to add operation.
                oper = new ReplaceManagementOperation();
            }
            // Retrieve node path for this DDF Node
            String nodePath = this.getNodePath(node);
            TreeNode tNode = null;
            if (node.getFormat().equals(DDFNode.DDF_FORMAT_NODE)) {
                tNode = new TreeNode(nodePath);
                tNode.setFormat(DDFNode.DDF_FORMAT_NODE);
            } else {
                // Retrieve value for this DDF Node
                Object value = this.waitedNodeMaps.get(node);
                tNode = new TreeNode(nodePath, value);
            }

            this.nodePathMaps.put(nodePath, node);
            this.waitedNodeMaps.remove(node);
            if (oper != null) {
                oper.addTreeNode(tNode);
                operatoins.add(oper);
            }

            // Get Next Node
            node = this.getNextDDFNode();
        }
        this.appendDummyOperation(operatoins);
        ManagementOperation[] operations = (ManagementOperation[]) operatoins.toArray(new ManagementOperation[0]);
        return operations;

    }

    /**
     * Called to set the results of a set of management operations.
     * 
     * @param results
     *          the results of a set of management operations.
     * 
     * @throws ManagementException
     *           in case of errors
     * 
     * @see sync4j.framework.engine.dm.ManagementProcessor#setOperationResults(sync4j.framework.engine.dm.ManagementOperationResult[])
     */
    public void setOperationResults(ManagementOperationResult[] results) throws ManagementException {

        step++;

        // Checking status of operation
        List<ManagementOperation> newQueuedOperations = new ArrayList<ManagementOperation>();
        for (ManagementOperationResult result : results) {
            String command = result.getCommand();
            int status = result.getStatusCode();

            if (command.equalsIgnoreCase("Get")) {
                if (status == 200) {
                    // Node Already exists, re-generate name
                    Map<String, Object> nodes = result.getNodes();
                    for (String oldPath : nodes.keySet()) {
                        DDFNode ddfNode = this.nodePathMaps.get(oldPath);

                        // Re-generate the name of node.
                        String newNodePath = this.caculateNodePath(ddfNode);

                        GetManagementOperation oper = new GetManagementOperation();
                        TreeNode tNode = new TreeNode(newNodePath, null);
                        oper.addTreeNode(tNode);

                        this.nodePathMaps.remove(oldPath);
                        this.nodePathMaps.put(newNodePath, ddfNode);
                        newQueuedOperations.add(oper);
                    }
                    continue;
                } else {
                    continue;
                }
            } else if (status == 200) {
                continue;
            }

            // Un-handle error
            this.success = false;
            log.error("Client response an error in ProfileAssignmentProcessor, Command: " + command + ", status: "
                    + status + ", Detail: ");
            Map<String, Object> nodes = result.getNodes();
            for (String oldPath : nodes.keySet()) {
                log.error("Target URI: " + oldPath);
                log.error("Value: " + nodes.get(oldPath));
            }
            // Throw exception to terminate this session.
            throw new ManagementException("Client response an error in ProfileAssignmentProcessor, Command: "
                    + command + ", status: " + status);

        }
        // re-create a new Queue.
        this.queuedOperations = newQueuedOperations;
    }

}