alter.vitro.vgw.service.ContinuationOfProvisionService.java Source code

Java tutorial

Introduction

Here is the source code for alter.vitro.vgw.service.ContinuationOfProvisionService.java

Source

/*
 * #--------------------------------------------------------------------------
 * # Copyright (c) 2013 VITRO FP7 Consortium.
 * # All rights reserved. This program and the accompanying materials
 * # are made available under the terms of the GNU Lesser Public License v3.0 which accompanies this distribution, and is available at
 * # http://www.gnu.org/licenses/lgpl-3.0.html
 * #
 * # Contributors:
 * #     Antoniou Thanasis (Research Academic Computer Technology Institute)
 * #     Paolo Medagliani (Thales Communications & Security)
 * #     D. Davide Lamanna (WLAB SRL)
 * #     Alessandro Leoni (WLAB SRL)
 * #     Francesco Ficarola (WLAB SRL)
 * #     Stefano Puglia (WLAB SRL)
 * #     Panos Trakadas (Technological Educational Institute of Chalkida)
 * #     Panagiotis Karkazis (Technological Educational Institute of Chalkida)
 * #     Andrea Kropp (Selex ES)
 * #     Kiriakos Georgouleas (Hellenic Aerospace Industry)
 * #     David Ferrer Figueroa (Telefonica Investigacin y Desarrollo S.A.)
 * #
 * #--------------------------------------------------------------------------
 */
package alter.vitro.vgw.service;

import alter.vitro.vgw.service.query.UserNodeQuery;
import alter.vitro.vgw.service.query.UserNodeResponse;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw.ConfirmedNodesListItemType;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw.ConfirmedNodesListType;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw.EquivListNodesRespType;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw.ObjectFactory;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvsp.EquivListNodesReqType;
import alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvsp.EquivNodesListItemType;
import alter.vitro.vgw.service.resourceRegistry.ResourceAvailabilityService;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBElement;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * This class deals with continuation of service issues
 * If a  gateway is aware of equivalent nodes, then it keeps them in a cache of stored sets of equivalent nodes
 * These sets need to be updated consistently (eg. when adding a new node to a set, then we should check if two sets have now become equivalent (if another set already contained that node).
 * Also, in a set, there are no duplicates for a node. And two sets cannot have common nodes (if they do they should be merged).
 * Updates for these sets are coming as commands from the VSP.
 * TODO: also VGW could auto-detect possible node equivalencies, but it should always require WSI Enabler or VSP confirmation to add them to sets.
 * The sets are also kept (and persisted) at VSP level.
 * When a VGW is launched it could update the sets locally and at VSP (exchanging the right messages) to allow for purging of non-existing nodes.
 * So when launched the VGW should at least require the list of these sets from the VSP.
 * Also when the VSP is re-launched, it should sent the appropriate such lists to the corresponding gateways.
 *
 * Finally the nodes in a set should have the same capabilities (VITRO-queri-able capabilities) (this check is only done in the UI though to allow for flexibility).
 */
public class ContinuationOfProvisionService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /* Singleton pattern*/
    private long lastUpdatedTimeStamp;
    // Key is the ID of the the EQUIV LIST in the VSP database, List<String> is a list of the sensor Ids in an equivalency set
    private HashMap<Integer, List<String>> cachedSetsOfEquivalency;
    // vsn-id to  array. Array[0] the replaced node id, array[1] the replacement.
    // (or we could skip the vsn-id entirely.
    // todo: String[] has [0] the source node, [1] the replacement node and (todo) [3] the capability resource
    // todo: so the entries will not be unique per node, but unique per source node+capability.
    private HashMap<String, List<String[]>> cachedReplacedResources; // TODO: do we need such a cache (Yes. probably to refer to upon subsequent queries)?

    private static ContinuationOfProvisionService instance = null;

    private ContinuationOfProvisionService() {
        lastUpdatedTimeStamp = 0;
        setCachedSetsOfEquivalency(new HashMap<Integer, List<String>>());
        setCachedReplacedResources(new HashMap<String, List<String[]>>());
    }

    public static ContinuationOfProvisionService getInstance() {
        if (instance == null) {
            instance = new ContinuationOfProvisionService();
        }
        return instance;
    }

    public HashMap<Integer, List<String>> getCachedSetsOfEquivalency() {
        return cachedSetsOfEquivalency;
    }

    public void setCachedSetsOfEquivalency(HashMap<Integer, List<String>> cachedSetsOfEquivalency) {
        this.cachedSetsOfEquivalency = cachedSetsOfEquivalency;
    }

    public void addToCachedSetsOfEquivalency() {

    }

    public void removeFromCachedSetsOfEquivalency() {

    }

    public HashMap<String, List<String[]>> getCachedReplacedResources() {
        return cachedReplacedResources;
    }

    public void setCachedReplacedResources(HashMap<String, List<String[]>> cachedReplacedResources) {
        this.cachedReplacedResources = cachedReplacedResources;
    }

    /**
     * Sends back to the VSP, which sensor is replaced by which replacement (and, for which VSN id and capability)
     */
    // public void sendtoVSPCachedReplacedResources() {
    ////
    //}

    /**
     * Processes synch updates to the sets of equivalency received from the VSP.
     */
    public void handleVSPUpdateForSetsOfEquivalency(EquivNodesListItemType reqItem) {
        if (reqItem != null && reqItem.getListId() != null && !reqItem.getListId().isEmpty()
                && reqItem.getNodeVec() != null && reqItem.getNodeVec().getNodeId() != null
                && !reqItem.getNodeVec().getNodeId().isEmpty()) {
            Integer listCandId = 0;
            try {
                listCandId = Integer.valueOf(reqItem.getListId());
            } catch (Exception ex) {
                listCandId = 0;
                logger.error("Could not convert list id to integer!");
                return;
            }
            List<Integer> listsToBeMerged = new ArrayList<Integer>();
            for (String nodeCandId : reqItem.getNodeVec().getNodeId()) {

                for (Integer cachedListId : getCachedSetsOfEquivalency().keySet()) {
                    if (getCachedSetsOfEquivalency().get(cachedListId) != null) {
                        for (String cachedNodeInList : getCachedSetsOfEquivalency().get(cachedListId)) {
                            if (cachedNodeInList.compareToIgnoreCase(nodeCandId) == 0) {
                                if (!listsToBeMerged.contains(cachedListId)) {

                                    listsToBeMerged.add(cachedListId);
                                }
                            }
                        }
                    }

                }
            }
            // merge all the listsToBeMerged. Be careful not to add duplicates of node names.
            List<String> resultMergedList = new ArrayList<String>();
            if (listsToBeMerged.size() > 0) {
                listCandId = listsToBeMerged.get(0); //as long as it's something
            } //else it's the id retrieved from the query/request (which is unique in the VSP, and VGW gets all ids from VSP anyway
            for (int i = 0; i < listsToBeMerged.size(); i++) {
                Integer idOfListToBeMerged = listsToBeMerged.get(i);
                // safe-double-check
                if (getCachedSetsOfEquivalency().containsKey(idOfListToBeMerged)
                        && getCachedSetsOfEquivalency().get(idOfListToBeMerged) != null) {
                    for (String nodeInAList : getCachedSetsOfEquivalency().get(idOfListToBeMerged)) {
                        if (!resultMergedList.contains(nodeInAList)) {
                            resultMergedList.add(nodeInAList);
                        }
                    }
                    getCachedSetsOfEquivalency().remove(idOfListToBeMerged);
                }
            }
            // and after the merge, merge also the nodes in the request list (still checking for duplicates)
            for (String nodeCandId : reqItem.getNodeVec().getNodeId()) {
                if (!resultMergedList.contains(nodeCandId)) {
                    resultMergedList.add(nodeCandId);
                }
            }
            getCachedSetsOfEquivalency().put(listCandId, resultMergedList);
        }
    }

    /**
     * Processes synch updates to the sets of equivalency received from the VSP.
     */
    public String createSynchConfirmationForVSP(EquivListNodesReqType forRequest) {
        String retStr = "";
        // todo check also the timestamp of the Request with the lastUpdatedTimeStamp
        // and update the lastUpdatedTimeStamp if needed.
        if (forRequest != null) {
            long receivedTimestamp = 0;
            try {
                receivedTimestamp = Long.valueOf(forRequest.getTimestamp());
            } catch (Exception enmfrmtx) {
                logger.error("Exception while converting timestamp of synch message");
            }
            if (receivedTimestamp < lastUpdatedTimeStamp) {
                //ignore the message
                logger.info("Ignoring synch message with old timestamp");
                return null;
            } else {
                lastUpdatedTimeStamp = receivedTimestamp;
                // clean / purge existing cache of equivLists   and replacements
                getCachedSetsOfEquivalency().clear();
                getCachedReplacedResources().clear();
            }
        }

        try {
            EquivListNodesRespType response;

            javax.xml.bind.JAXBContext jaxbContext = javax.xml.bind.JAXBContext
                    .newInstance("alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw");
            // create an object to marshal
            ObjectFactory theFactory = new ObjectFactory();
            response = theFactory.createEquivListNodesRespType();

            if (response != null && forRequest != null) {

                response.setVgwId(VitroGatewayService.getVitroGatewayService().getAssignedGatewayUniqueIdFromReg());
                response.setMessageType(UserNodeResponse.COMMAND_TYPE_EQUIV_LIST_SYNCH_RESP);

                response.setTimestamp(Long.toString(new Date().getTime()));
                if (forRequest.getEquivNodesList() != null) {

                    if (response.getConfirmedNodesList() == null) {
                        ConfirmedNodesListType theConfirmListType = new ConfirmedNodesListType();
                        response.setConfirmedNodesList(theConfirmListType);
                    }
                    //
                    if (forRequest.getEquivNodesList().getEquivNodesListItem() != null
                            && !forRequest.getEquivNodesList().getEquivNodesListItem().isEmpty()) {
                        // loop though items and confirm each one
                        for (EquivNodesListItemType reqItem : forRequest.getEquivNodesList()
                                .getEquivNodesListItem()) {
                            ConfirmedNodesListItemType confirmedItemTmp = new ConfirmedNodesListItemType();
                            confirmedItemTmp.setListId(reqItem.getListId());
                            confirmedItemTmp.setOfRemoteTimestamp(reqItem.getOfRemoteTimestamp());
                            // process this addition to local cache
                            handleVSPUpdateForSetsOfEquivalency(reqItem);

                            response.getConfirmedNodesList().getConfirmedNodesListItem().add(confirmedItemTmp);
                        }
                    }
                }

                //javax.xml.bind.JAXBContext jaxbContext = javax.xml.bind.JAXBContext.newInstance("alter.vitro.vgw.service.query.xmlmessages.equivlistsynch.fromvgw");
                //ObjectFactory theFactory = new ObjectFactory();
                javax.xml.bind.Marshaller marshaller = jaxbContext.createMarshaller();
                marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                JAXBElement<EquivListNodesRespType> myResponseMsgEl = theFactory.createEquivListNodesResp(response);

                ByteArrayOutputStream baos = new ByteArrayOutputStream();

                marshaller.marshal(myResponseMsgEl, baos);
                retStr = baos.toString(HTTP.UTF_8);
            }

        } catch (javax.xml.bind.JAXBException je) {
            je.printStackTrace();
        } catch (UnsupportedEncodingException e2) {
            e2.printStackTrace();
        }
        return retStr;
    }

    // should return the cached equivalent if it is enabled
    // it could also consider for which VSN and which capability this equivalency is requested for
    // TODO: also check again against the current version of the Cached Equiv Lists. Equivalencies that did not exist, might be there, and vice versa old equivalencies might have been reverted!
    // TODO: however for safety when we update an equivalency list, we remove all the cachedReplacement records, to re-calculate them anew!
    public String[] findNextEquivalentNode(String vsnId, List<String> nodeIdsInThisVSN, String sourceNode,
            String capabilityId) {
        String[] retNodeReplacingInfo = null;
        boolean foundCachedReplacement = false;
        boolean foundAReplacement = false;
        // first search in the replacement cache for this VSNid
        if (!cachedReplacedResources.isEmpty() && cachedReplacedResources.containsKey(vsnId)) {
            List<String[]> nodeReplaceEntries = cachedReplacedResources.get(vsnId);
            for (String[] entryTmp : nodeReplaceEntries) {
                if (entryTmp.length == 3 && entryTmp[0] != null && !entryTmp[0].isEmpty()
                        && entryTmp[0].compareToIgnoreCase(sourceNode) == 0 && nodeIdsInThisVSN != null
                        && !nodeIdsInThisVSN.contains(entryTmp[1]) && entryTmp[1] != null && !entryTmp[1].isEmpty()
                        && entryTmp[1].compareToIgnoreCase(sourceNode) != 0 && entryTmp[2] != null
                        && !entryTmp[2].isEmpty() && entryTmp[2].compareToIgnoreCase(capabilityId) == 0) {
                    // check if this node is enabled (or also disabled)
                    if (ResourceAvailabilityService.getInstance().isNodeResourceAvailable(vsnId, entryTmp[1],
                            capabilityId)
                            && !replacesOtherNodeInVSN(vsnId, sourceNode, entryTmp[1], capabilityId)) {
                        // DONE: check if this node is enabled (or also disabled)

                        retNodeReplacingInfo = entryTmp;
                        foundCachedReplacement = true;
                    }
                    if (!foundCachedReplacement) {
                        // TODO: if this entry is stale or the node is disabled we should also remove it!
                        removeReplacementVectorForNodeOfVSN(vsnId, sourceNode, capabilityId);
                    }
                    break;
                }
            }
        }
        // if not found a replacement in cache, then search based on stored equiv lists and also if found a match, then
        //                  add to the replacement cache and return it.
        if (!foundCachedReplacement && !cachedSetsOfEquivalency.isEmpty()) {
            for (Integer setIdTmp : cachedSetsOfEquivalency.keySet()) {
                if (foundAReplacement) {
                    break;
                }
                List<String> setOfEquivEntry = cachedSetsOfEquivalency.get(setIdTmp);
                if (setOfEquivEntry != null && !setOfEquivEntry.isEmpty()) {
                    for (String itemNode : setOfEquivEntry) {
                        if (itemNode.compareToIgnoreCase(sourceNode) == 0) {
                            // we iterate the set one more time
                            for (String itemReplaceCandidateNode : setOfEquivEntry) {
                                if (!foundAReplacement
                                        && itemReplaceCandidateNode.compareToIgnoreCase(sourceNode) != 0
                                        && nodeIdsInThisVSN != null
                                        && !nodeIdsInThisVSN.contains(itemReplaceCandidateNode)
                                        && ResourceAvailabilityService.getInstance().isNodeResourceAvailable(vsnId,
                                                itemReplaceCandidateNode, capabilityId)
                                        && !replacesOtherNodeInVSN(vsnId, sourceNode, itemReplaceCandidateNode,
                                                capabilityId)) {

                                    // DONE: check if this node is enabled (or also disabled)\
                                    // TODO: check if this node actually exists in the VGW currently (or is it stale)!
                                    // TODO: if capability is set as an argument, we should check that if the (alive) replacement has also this capability
                                    // DONE: add node in the cache of replacements!
                                    retNodeReplacingInfo = updateReplacementVectorForNodeOfVSN(vsnId, sourceNode,
                                            itemReplaceCandidateNode, capabilityId);

                                    foundAReplacement = true;
                                    break;
                                }

                            }
                            break;
                        }
                    } // end of loop over nodes of an equiv set
                }

            } // an of loop over equiv sets of the cache
        }
        return retNodeReplacingInfo;
    }

    /**
     * removes and entry from the replacements cache for this node and vsnId
     * @param sourceNode
     * @param vsnId
     */
    synchronized private void removeReplacementVectorForNodeOfVSN(String vsnId, String sourceNode,
            String pCapabilityId) {
        List<String[]> replacementsListForVSN = null;
        if (cachedReplacedResources.containsKey(vsnId)) {

            replacementsListForVSN = cachedReplacedResources.get(vsnId);
            int initSizeOfList = replacementsListForVSN.size();
            for (int idx = initSizeOfList; idx >= 0; idx--) {
                if (replacementsListForVSN.get(idx) != null && replacementsListForVSN.get(idx).length == 3
                        && replacementsListForVSN.get(idx)[0].compareToIgnoreCase(sourceNode) == 0
                        && replacementsListForVSN.get(idx)[2].compareToIgnoreCase(pCapabilityId) == 0) {
                    replacementsListForVSN.remove(idx);
                }
            }
        }

    }
    //
    //

    /**
     * This is the only method updating the cache of replacements for vsnId. (other than the purging one)
     * @param vsnId
     * @param sourceNode
     * @param targetNode
     * @param pCapabilityId
     * @return
     */
    synchronized String[] updateReplacementVectorForNodeOfVSN(String vsnId, String sourceNode, String targetNode,
            String pCapabilityId) {
        String[] replacementInfoItem = null;
        if (vsnId != null && !vsnId.isEmpty() && sourceNode != null && !sourceNode.isEmpty() && targetNode != null
                && !targetNode.isEmpty() && pCapabilityId != null && !pCapabilityId.isEmpty()) {
            replacementInfoItem = new String[] { sourceNode, targetNode, pCapabilityId };
            List<String[]> replacementsListForVSN = null;
            if (cachedReplacedResources.containsKey(vsnId) && cachedReplacedResources.get(vsnId) != null) {
                replacementsListForVSN = cachedReplacedResources.get(vsnId);
            } else {
                replacementsListForVSN = new ArrayList<String[]>();
            }
            replacementsListForVSN.add(replacementInfoItem);
            cachedReplacedResources.put(vsnId, replacementsListForVSN);
        }
        return replacementInfoItem;
    }

    public boolean replacesOtherNodeInVSN(String pVSNId, String origMoteId, String replcPMoteId,
            String pCapabilityId) {
        // A replacement replcPMoteId node should not replace other nodes for the same VSN;
        boolean replacesOtherNodeInVSNFlg = false;
        if (pVSNId != null && !pVSNId.isEmpty() && origMoteId != null && !origMoteId.isEmpty()
                && replcPMoteId != null && !replcPMoteId.isEmpty() && pCapabilityId != null
                && !pCapabilityId.isEmpty()) {
            if (getCachedReplacedResources().containsKey(pVSNId)) {
                List<String[]> tokenList = getCachedReplacedResources().get(pVSNId);
                for (String[] tokenIt : tokenList) {
                    if (tokenIt[0].compareToIgnoreCase(origMoteId) != 0
                            && tokenIt[1].compareToIgnoreCase(replcPMoteId) == 0
                            && tokenIt[2].compareToIgnoreCase(pCapabilityId) == 0) {
                        replacesOtherNodeInVSNFlg = true;
                        break;
                    }
                }

            }
        }
        return replacesOtherNodeInVSNFlg;

    }

    /**
     * for debugging (verbose list of lists)
     * @return
     */
    public String printEquivListsAndReplacementLists() {
        String retStr = "";
        StringBuilder retStrBld = new StringBuilder();
        retStrBld.append("Nodes Equivalency List: \n");
        for (Integer equivSetId : getCachedSetsOfEquivalency().keySet()) {
            retStrBld.append("Set id: ");
            retStrBld.append(equivSetId);
            List<String> equivSetNodesList = getCachedSetsOfEquivalency().get(equivSetId);

            if (equivSetNodesList != null && !equivSetNodesList.isEmpty()) {
                retStrBld.append("\nNodes:");
                for (int i = 0; i < equivSetNodesList.size(); i++) {
                    retStrBld.append(equivSetNodesList.get(i));
                    if (i + 1 < equivSetNodesList.size()) {
                        retStrBld.append(",");
                    }
                }
            }
            retStrBld.append("\n");
        }
        retStrBld.append("Replaced Resources List: \n");
        for (String vsnId : getCachedReplacedResources().keySet()) {
            retStrBld.append("VSN id: ");
            retStrBld.append(vsnId);

            List<String[]> smDevListsTmp = getCachedReplacedResources().get(vsnId);
            if (smDevListsTmp != null && !smDevListsTmp.isEmpty()) {
                retStrBld.append("Replacement lists: ");

                for (int i = 0; i < smDevListsTmp.size(); i++) {
                    String[] smDevReplcInfo = smDevListsTmp.get(i);
                    if (smDevReplcInfo != null && smDevReplcInfo.length == 3) {
                        retStrBld.append("Device: ");
                        retStrBld.append(smDevReplcInfo[0]);
                        retStrBld.append(" Replaced By: ");
                        retStrBld.append(smDevReplcInfo[1]);
                        retStrBld.append(" For Capability: ");
                        retStrBld.append(smDevReplcInfo[2]);
                        retStrBld.append("\n");
                    }
                }
            }
            retStrBld.append("\n");
        }
        retStr = retStrBld.toString();
        return retStr;
    }

}