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

Java tutorial

Introduction

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

Source

/**
  * $Header: /home/master/nWave-DM-Common/src/com/npower/dm/processor/ProfileReAssignmentProcessor.java,v 1.29 2008/12/12 04:16:10 zhao Exp $
  * $Revision: 1.29 $
  * $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.29 $ $Date: 2008/12/12 04:16:10 $
 */
public class ProfileReAssignmentProcessor extends BaseProcessor implements ManagementProcessor, Serializable {

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

    /**
     * Hold the operations which are waited to return for getNextOperations().
     */
    private List<ManagementOperation> queuedOperations = null;

    /**
     * Hold the RootNode Path of ProfileAssign
     */
    private String profileRootNodePath = null;

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

    /**
     * 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;

    private int nameCounter = 0;

    /**
     * Indicate the operation mode: true is Replace, false is Add
     */
    private boolean deleteProfileRootNodePath = false;

    /**
     * Default constructor
     */
    public ProfileReAssignmentProcessor() {
        super();
    }

    /**
     * Compile the ProfileAssignment to a map<DDFNode, value).
     * @param factory
     *        ManagementBeanFactory
     * @return
     * @throws DMException
     * @throws ManagementException
     */
    private Map<DDFNode, Object> 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
        this.profileRootNodePath = assignment.getProfileRootNodePath();
        String rootNodeName = BaseProcessor.getNodeNameFromNodePath(this.profileRootNodePath);
        DDFNode rootNode = mapping.getRootDDFNode();
        if (this.profileRootNodePath == null || rootNodeName == null) {
            this.profileRootNodePath = rootNode.getNodePath();
        } else {
            this.nameMaps.put(rootNode, rootNodeName);
            this.nodePathMaps.put(this.profileRootNodePath, rootNode);
        }

        Map<DDFNode, Object> result = new TreeMap<DDFNode, Object>();
        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("Profile Assignment Mapping ... ");
            for (DDFNode node : result.keySet()) {
                Object v = result.get(node);
                log.debug("DDFNode: \"" + node.getNodePath() + "\" with value: " + v);
            }
        }
        return result;
    }

    /**
     * Get 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_RE_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;
    }

    /**
     * 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) {
            if (this.nameMaps.containsKey(node)) {
                name = this.nameMaps.get(node);
            } else {
                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) {
                /*
                if (!this.queuedOperations.isEmpty()) {
                   break;
                }
                */
                String nodePath = this.caculateNodePath(node);

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

                this.nodePathMaps.put(nodePath, node);
                //this.waitedNodeMaps.remove(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(ProfileReAssignmentProcessor.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 {
            factory = ManagementBeanFactory.newInstance(EngineConfig.getProperties());
            // Load Device boundled with this Session
            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
            setJobStatus4BeginSession();

            // Update DevInfo
            this.updateDevInfo(devInfo);

            this.queuedOperations = new ArrayList<ManagementOperation>();
            this.waitedNodeMaps = this.compileToNodeMap(factory);
            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 = this.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();
                profileAssignment.setProfileRootNodePath(profileRootNodePath);
                factory.beginTransaction();
                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("Could not load device: from DM inventory.", 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()) {
            this.appendDummyOperation(this.queuedOperations);
            return (ManagementOperation[]) this.queuedOperations.toArray(new ManagementOperation[0]);
        }

        if (this.waitedNodeMaps.isEmpty()) {
            // Profile has been push to terminal. all of operation is finished.
            return new ManagementOperation[0];
        }

        List<ManagementOperation> operatoins = new ArrayList<ManagementOperation>();
        if (this.deleteProfileRootNodePath) {
            // Root node exists! delete it.
            //DeleteManagementOperation oper = new DeleteManagementOperation();
            //TreeNode tNode = new TreeNode(this.profileRootNodePath, null);
            //oper.addTreeNode(tNode);
        }

        for (DDFNode node = this.getNextDDFNode(); node != null;) {
            // Retrieve value for this DDF Node
            Object value = this.waitedNodeMaps.get(node);
            TreeManagementOperation oper = null;
            String nodePath = this.getNodePath(node);
            if (this.deleteProfileRootNodePath) {
                // Update node
                if (!nodePath.equals(this.profileRootNodePath)) {
                    if (node.imply(DDFNode.ACCESS_TYPE_REPLACE)
                            && !node.getFormat().equals(DDFNode.DDF_FORMAT_NODE)) {
                        oper = new ReplaceManagementOperation();
                    }
                }
            } else {
                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();
                }
            }
            if (oper != null) {
                TreeNode tNode = null;
                if (node.getFormat().equals(DDFNode.DDF_FORMAT_NODE)) {
                    tNode = new TreeNode(nodePath);
                    tNode.setFormat(DDFNode.DDF_FORMAT_NODE);
                } else {
                    tNode = new TreeNode(nodePath, value);
                }
                oper.addTreeNode(tNode);
                operatoins.add(oper);
            }

            this.nodePathMaps.put(nodePath, node);
            this.waitedNodeMaps.remove(node);
            // 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 (this.deleteProfileRootNodePath) {
                    // Root node exists, and bypass other Result Get.
                    break;
                }
                if (status == 200) {
                    // Already exists
                    Map<String, Object> nodes = result.getNodes();
                    for (String oldPath : nodes.keySet()) {
                        if (oldPath.equals(this.profileRootNodePath)) {
                            // Set the flag , notify getNextOperations() adding a Delete Operation.
                            this.deleteProfileRootNodePath = true;
                            break;
                        }
                        DDFNode ddfNode = this.nodePathMaps.get(oldPath);

                        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;
                }
                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;

    }

}