/**
   * Creates or fetches a JPA Node Entity for JSON Node Used for PUT and POST Requests for
   * NodeResource The services and commands are updated from that of the JSON Node
   *
   * @param nodeEntity
   * @return
   */
  public org.nikhil.webapp.nodeadmin.entity.Node getJPANodeFromJSONNode(Node jsonNode) {

    String nodeName = jsonNode.getName();
    org.nikhil.webapp.nodeadmin.entity.Node jpaNode = nodeOperation.getNode(nodeName);
    boolean addingANode = false;
    boolean prevServicesPresent = false;
    boolean prevCmmandsPresent = false;

    if (jpaNode == null) {
      // Create Mode
      jpaNode = new org.nikhil.webapp.nodeadmin.entity.Node();
      jpaNode.setName(nodeName);
      addingANode = true;
    }
    // create / update Mode
    jpaNode.setDescription(jsonNode.getDescription());
    jpaNode.setIpAddress(jsonNode.getIpAddress());
    jpaNode.setMaster(jsonNode.isMaster());
    jpaNode.setUserName(jsonNode.getUserName());
    jpaNode.setPassword(jsonNode.getPassword());
    jpaNode.setShutdownTimer(jsonNode.getShutdownTimer());
    jpaNode.setRestartTimer(jsonNode.getRestartTimer());
    jpaNode.setStatus(jsonNode.getStatus());

    /*		Add or Update the existing NodeService relationships
    	optimizing single fetch for node services
    */
    List<NodeAndService> nodeAssiginedServices = jpaNode.getNodeServices();

    if (jsonNode.getServices().size() > 0) {
      prevServicesPresent = true;
      for (Service jsonService : jsonNode.getServices()) {
        org.nikhil.webapp.nodeadmin.entity.Service jpaService = null;
        org.nikhil.webapp.nodeadmin.entity.NodeAndService jpaNodeAndService = null;
        boolean serviceFoundAlreadyLinked = false;

        if (nodeAssiginedServices.size() > 0) {
          for (org.nikhil.webapp.nodeadmin.entity.NodeAndService fetchedNodeAndService :
              nodeAssiginedServices) {
            if (fetchedNodeAndService
                .getCompositePK()
                .getService()
                .getName()
                .equals(jsonService.getName())) {
              jpaNodeAndService = fetchedNodeAndService;
              serviceFoundAlreadyLinked = true;
              break;
            }
          }
        }
        // Add service relationship for current json service
        if (!serviceFoundAlreadyLinked || nodeAssiginedServices.size() == 0) {
          jpaService = serviceOperation.getService(jsonService.getName());
          jpaNodeAndService = new NodeAndService();
          jpaNodeAndService.setCompositePK(new NodeAndServiceId(jpaNode, jpaService));
          // adding relationship
          jpaNode.getNodeServices().add(jpaNodeAndService);
        }

        // so here we have jpanodeandservice identified either new and added or retrieved from node
        // itself - update it now
        jpaNodeAndService.setAdditionalRunParamsStart(jsonService.getAdditionalRunParamsStart());
        jpaNodeAndService.setAdditionalRunParamsStop(jsonService.getAdditionalRunParamsStop());
        jpaNodeAndService.setStatusWildCardOverride(jsonService.getStatusWildCardOverride());
        jpaNodeAndService.setStatusCommandOverride(jsonService.getStatusCommandOverride());
        jpaNodeAndService.setRundir(jsonService.getRundir());
        jpaNodeAndService.setRunning(jsonService.isRunning());
      }

      /* delete the relationships that are not part of new assignment
       * if adding a node all service relationships are new so no need to delete anything from jpaNode
       */
      if (prevServicesPresent && !addingANode) {
        int index = 0;
        ArrayList<Integer> deleteIndices = new ArrayList<Integer>();
        for (org.nikhil.webapp.nodeadmin.entity.NodeAndService fetchedNodeAndService :
            nodeAssiginedServices) {

          boolean serviceMatched = false;
          for (Service jsonService : jsonNode.getServices()) {
            if (fetchedNodeAndService
                .getCompositePK()
                .getService()
                .getName()
                .equals(jsonService.getName())) {
              serviceMatched = true;
              break;
            }
          }

          if (!serviceMatched) {
            deleteIndices.add(index);
          }
          index++;
        }

        // delete unmatched services
        for (Integer indexToDelete : deleteIndices) {
          nodeAssiginedServices.remove(indexToDelete.intValue());
        }
      }
    }

    List<NodeAndCommand> nodeAssiginedCommands = jpaNode.getNodeCommands();

    if (jsonNode.getCommands().size() > 0) {
      prevCmmandsPresent = true;
      for (Command jsonCommand : jsonNode.getCommands()) {
        org.nikhil.webapp.nodeadmin.entity.Command jpaCommand = null;
        org.nikhil.webapp.nodeadmin.entity.NodeAndCommand jpaNodeAndCommand = null;
        boolean commandFoundAlreadyLinked = false;

        if (nodeAssiginedCommands.size() > 0) {
          for (org.nikhil.webapp.nodeadmin.entity.NodeAndCommand fetchedNodeAndCommand :
              nodeAssiginedCommands) {
            if (fetchedNodeAndCommand
                .getCompositePK()
                .getCommand()
                .getName()
                .equals(jsonCommand.getName())) {
              jpaNodeAndCommand = fetchedNodeAndCommand;
              commandFoundAlreadyLinked = true;
              break;
            }
          }
        }
        // Add command relationship for current json command
        if (!commandFoundAlreadyLinked || nodeAssiginedCommands.size() == 0) {
          jpaCommand = commandOperation.getCommand(jsonCommand.getName());
          jpaNodeAndCommand = new NodeAndCommand();
          jpaNodeAndCommand.setCompositePK(new NodeAndCommandId(jpaNode, jpaCommand));
          // adding relationship
          jpaNode.getNodeCommands().add(jpaNodeAndCommand);
        }

        // so here we have jpanodeandcommand identified either new and added or retrieved from node
        // itself - update it now
        jpaNodeAndCommand.setAdditionalRunParams(jsonCommand.getAdditionalRunParams());
        jpaNodeAndCommand.setAlwaysRunInBackground(jsonCommand.isAlwaysRunInBackground());
        jpaNodeAndCommand.setRundir(jsonCommand.getRundir());
      }

      /* delete the relationships that are not part of new assignment
       * if adding a node all command relationships are new so no need to delete anything from jpaNode
       */
      if (prevCmmandsPresent && !addingANode) {
        int index = 0;
        ArrayList<Integer> deleteIndices = new ArrayList<Integer>();
        for (org.nikhil.webapp.nodeadmin.entity.NodeAndCommand fetchedNodeAndCommand :
            nodeAssiginedCommands) {

          boolean commandMatched = false;
          for (Command jsonCommand : jsonNode.getCommands()) {
            if (fetchedNodeAndCommand
                .getCompositePK()
                .getCommand()
                .getName()
                .equals(jsonCommand.getName())) {
              commandMatched = true;
              break;
            }
          }

          if (!commandMatched) {
            deleteIndices.add(index);
          }
          index++;
        }

        // delete unmatched commands
        for (Integer indexToDelete : deleteIndices) {
          nodeAssiginedCommands.remove(indexToDelete.intValue());
        }
      }
    }

    return jpaNode;
  }