public class JSON {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    private ObjectMapper mapper = null;
    private JsonNode rootNode = null;

    /*
     * Class methods
     */

    /**
     * Constructs an empty JSON object.
     */
    public JSON() {

        mapper = new ObjectMapper();
        rootNode = mapper.createObjectNode();
    }

    /**
     * Constructs a JSON object from a string.
     *
     * @param string
     *            a string representation of a JSON object.
     *
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonParseException
     */
    public JSON(String string) throws JsonParseException, JsonMappingException, IOException {

        mapper = new ObjectMapper();
        rootNode = mapper.readValue(string, ObjectNode.class);
    }

    /**
     * Constructs a JSON object from a Jackson JSON node.
     *
     * @param node
     *            the Jackson JSON node.
     */
    public JSON(JsonNode node) {

        mapper = new ObjectMapper();
        rootNode = node;
    }

    /**
     * Answers the string value corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @return the string value.
     */
    public String getString(String key) {

        JsonNode node = rootNode.path(key);
        String value = null;

        if (!node.isMissingNode()) {
            value = node.asText();
        }

        return value;
    }

    /**
     * Answers the <code>Double</code> value corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @return the <code>Double</code> value.
     */
    public Double getDouble(String key) {

        JsonNode node = rootNode.path(key);
        Double value = 0.0;

        if (!node.isMissingNode()) {
            value = node.asDouble();
        }

        return value;
    }

    /**
     * Answers the <code>boolean</code> value corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @return the <code>boolean</code> value, or <code>false</code> if there is no value.
     */
    public boolean getBoolean(String key) {

        JsonNode node = rootNode.path(key);
        boolean value = false;

        if (!node.isMissingNode()) {
            value = node.asBoolean();
        }

        return value;
    }

    /**
     * Answers the JSON object corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @return the JSON object.
     */
    public JSON getJSON(String key) {

        JsonNode node = rootNode.path(key);
        JSON json = null;

        if (!node.isMissingNode()) {
            json = new JSON(node);
        } else {
            json = new JSON();
        }

        return json;
    }

    /**
     * Answers the JSON array corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @return the JSON array or <code>null</code> if the value does not exist or is not an array.
     */
    public JSONArray getJSONArray(String key) {

        JsonNode node = rootNode.path(key);
        JSONArray jsonArray = null;

        if (!node.isMissingNode() && node.isArray()) {
            jsonArray = new JSONArray((ArrayNode) node);
        }

        return jsonArray;
    }

    /**
     * Sets the string value corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @param value
     *            the new value.
     *
     * @return this instance.
     */
    public JSON putString(String key, String value) {

        if (rootNode != null && rootNode instanceof ObjectNode) {

            if (value != null && !value.equals("null")) {
                ((ObjectNode) rootNode).put(key, value);
            }

        } else {

            throw new IllegalStateException("Not an ObjectNode");

        }

        return this;
    }

    /**
     * Sets the <code>Double</code> value corresponding to the specified name.
     *
     * @param key
     *            the name of the value.
     *
     * @param value
     *            the new value.
     *
     * @return this instance.
     */
    public JSON putDouble(String key, Double value) {

        if (rootNode != null && rootNode instanceof ObjectNode) {

            if (value != null) {
                ((ObjectNode) rootNode).put(key, value);
            }

        } else {

            throw new IllegalStateException("Not an ObjectNode");

        }

        return this;
    }

    /**
     * Sets the JSON object corresponding to the specified name.
     *
     * @param key
     *            the name of the object.
     *
     * @param value
     *            the new value.
     *
     * @return this instance.
     */
    public JSON putJSON(String key, JSON value) {

        if (rootNode != null && rootNode instanceof ObjectNode) {

            if (value != null) {
                ((ObjectNode) rootNode).put(key, value.getRootNode());
            }

        } else {

            throw new IllegalStateException("Not an ObjectNode");

        }

        return this;
    }

    /**
     * Sets the JSON array corresponding to the specified name.
     *
     * @param key
     *            the name of the array.
     *
     * @param value
     *            the new value.
     *
     * @return this instance.
     */
    public JSON putJSONArray(String key, JSONArray value) {

        if (rootNode != null && rootNode instanceof ObjectNode) {

            if (value != null) {
                ((ObjectNode) rootNode).put(key, value.getRootNode());
            }

        } else {

            throw new IllegalStateException("Not an ObjectNode");

        }

        return this;
    }

    /**
     * Sets the JSON array corresponding to the specified name.
     *
     * @param key
     *            the name of the array.
     *
     * @param value
     *            the new value.
     *
     * @return this instance.
     */
    public JSON putArray(String key, List<JSON> list) {

        if (rootNode != null && rootNode instanceof ObjectNode) {

            List<JsonNode> nodes = new ArrayList<JsonNode>();

            for (JSON json : list) {
                nodes.add(json.getRootNode());
            }

            ((ObjectNode) rootNode).putArray(key).addAll(nodes);

        } else {

            throw new IllegalStateException("Not an ObjectNode");

        }

        return this;
    }

    /**
     * Answers the root node of this instance.
     *
     * @return the root node.
     */
    public JsonNode getRootNode() {

        return rootNode;
    }

    @Override
    public String toString() {

        String value = rootNode.toString();
        return value.replaceAll("^\"|\"$", "");
    }
}
 public class Bearers extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /*
     * Class methods
     */

    /**
     * Inserts a Bearer into the registry.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the JSON message with the status of the operation.
     */

    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String id = op.getString(AdapterConstants.FIELD_ID);
            String available = op.getString(AdapterConstants.FIELD_AVAILABILE).toLowerCase();

            if (id == null || id.length() == 0 || available == null
                    || (!available.equals("true") && !available.equals("false"))) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_BEARER, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                String description = op.getString(AdapterConstants.FIELD_DESCRIPTION);
                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);

                BearerFactory bearerFactory = FabricRegistry.getBearerFactory();
                Bearer bearer = bearerFactory.createBearer(id, available, description, (attr != null) ? attr.toString()
                        : null, null);
                boolean success = bearerFactory.save(bearer);

                if (!success) {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_BEARER, "Insert/update of bearer into the Registry failed",
                            correlId);
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_BEARER, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a Bearer from the registry.
     *
     * @param bearerID
     *            The ID of the node to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return the JSON message with the status of the operation.
     */
    public static JSON deregister(String bearerID, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (bearerID == null) {

            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_BEARER, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

        }

        BearerFactory bearerFactory = FabricRegistry.getBearerFactory(QueryScope.LOCAL);
        Bearer bearer = bearerFactory.getBearerById(bearerID);
        boolean success = bearerFactory.delete(bearer);

        if (success == false) {
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_BEARER, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
        }

        return status.toJsonObject();
    }

    /**
     * Queries the database for bearers.
     *
     * @param op
     * @param correlId
     * @return the JSON message with the query result.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();

        try {

            BearerFactory bearerFactory = FabricRegistry.getBearerFactory();
            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "BEARER_ID", QUERY_AVAILABLE
                    | QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_BEARER, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();

            } else {

                /* Lookup the bearer list in the Registry */
                Bearer[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = bearerFactory.getAllBearers();
                } else {
                    resultArray = bearerFactory.getBearers(querySQL);
                }

                /* Generate the response object */

                queryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_BEARERS);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each bearer... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON bearer = new JSON();
                    bearer.putString(AdapterConstants.FIELD_ID, resultArray[i].getId());
                    bearer.putString(AdapterConstants.FIELD_AVAILABILE, resultArray[i].getAvailable());
                    addOptionalFields(bearer, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(bearer);

                }
                queryResult = queryResult.putArray(AdapterConstants.FIELD_BEARERS, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_BEARER, message, correlId);
            queryResult = status.toJsonObject();

        }

        return queryResult;
    }
}
 public class NodeTypes extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /**
     * Inserts a Node Type into the registry.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String typeid = op.getString(AdapterConstants.FIELD_TYPE);

            if (typeid == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_NODE_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
                String description = op.getString(AdapterConstants.FIELD_DESCRIPTION);

                TypeFactory typeFactory = FabricRegistry.getTypeFactory();
                Type type = typeFactory.createNodeType(typeid, description, (attr != null) ? attr.toString() : null,
                        null);
                typeFactory.save(type);

            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_NODE_TYPE, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a Node from the registry.
     *
     * @param nodeTypeId
     *            The ID of the Node to be deleted.
     * @return A JSON status object.
     */
    public static JSON deregister(String nodeTypeId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);
        if (nodeTypeId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_NODE_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
            Type NodeType = typeFactory.getNodeType(nodeTypeId);
            boolean complete = typeFactory.delete(NodeType);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_NODE_TYPE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

    /**
     * Queries the database for Node types.
     *
     * @param op
     * @param correlId
     * @return The result of the query.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();

        try {

            TypeFactory typeFactory = FabricRegistry.getTypeFactory();

            String querySQL = generatePredicate(AdapterConstants.FIELD_TYPE, "TYPE_ID", QUERY_DESCRIPTION
                    | QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_NODE_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();

            } else {

                Type[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = typeFactory.getAllNodeTypes();
                } else {
                    resultArray = typeFactory.getNodeTypes(querySQL);
                }

                /* Generate the response object */

                queryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_NODE_TYPES);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each node type... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON type = new JSON();
                    type = type.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getId());
                    addOptionalFields(type, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(type);

                }
                queryResult = queryResult.putArray(AdapterConstants.FIELD_NODE_TYPES, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_NODE_TYPE, message, correlId);
            queryResult = status.toJsonObject();

        }
        return queryResult;
    }
}
 public class Nodes extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    private static String homeNode = null;

    /*
     * Class methods
     */

    /**
     * Inserts a Node into the registry.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the JSON message with the status of the operation.
     */

    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String id = op.getString(AdapterConstants.FIELD_ID);
            JSONArray interfaces = op.getJSONArray(AdapterConstants.FIELD_INTERFACES);
            String typeId = op.getString(AdapterConstants.FIELD_TYPE);

            if (id == null || interfaces == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_NODE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                if (typeId == null) {
                    typeId = AdapterConstants.FIELD_VALUE_UKNOWN;
                }

                String description = op.getString(AdapterConstants.FIELD_DESCRIPTION);
                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);

                NodeFactory nodeFactory = FabricRegistry.getNodeFactory();
                Node node = nodeFactory.createNode(id, typeId, // Node type
                        null, // affiliation
                        null, // Security Classification
                        null, // readiness
                        null, // availability,
                        0, // latitude,
                        0, // longitude,
                        0, // altitude,
                        0, // bearing,
                        0, // velocity,
                        description, (attr != null) ? attr.toString() : null, null);
                boolean success = nodeFactory.save(node);

                if (!success) {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_NODE, "Insert/update of node into the Registry failed", correlId);

                } else {

                    /* Map the Node to its network interfaces */

                    for (Iterator<JSON> interfacesIterator = interfaces.iterator(); interfacesIterator.hasNext()
                            && success;) {

                        JSON nextInterface = interfacesIterator.next();

                        String address = nextInterface.getString(AdapterConstants.FIELD_ADDRESS).substring(5);
                        String[] ipParts = address.split(":");
                        String ipAddress = ipParts[0];
                        int port = Integer.parseInt(ipParts[1]);

                        String nodeInterface = nextInterface.getString(AdapterConstants.FIELD_NODE_INTERFACE);

                        NodeIpMappingFactory ipFactory = FabricRegistry.getNodeIpMappingFactory();
                        NodeIpMapping ip = ipFactory.createNodeIpMapping(id, nodeInterface, ipAddress, port);
                        success = ipFactory.save(ip);

                        if (!success) {
                            status = new AdapterStatus(AdapterConstants.ERROR_ACTION,
                                    AdapterConstants.OP_CODE_REGISTER, AdapterConstants.ARTICLE_NODE,
                                    "Insert/update of node/IP address mapping into the Registry failed", correlId);
                        }
                    }
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_NODE, message, correlId);

        }

        return status.toJsonObject();
    }

    public static String getNode() {

        return homeNode;
    }

    public static void setNode(String homeNode) {

        Nodes.homeNode = homeNode;
    }

    /**
     * Deletes a Node from the registry.
     *
     * @param nodeId
     *            The ID of the node to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return the JSON message with the status of the operation.
     */
    public static JSON deregister(String nodeId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (nodeId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_NODE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        }

        /* Delete the node */
        NodeFactory nodeFactory = FabricRegistry.getNodeFactory(QueryScope.LOCAL);
        Node node = nodeFactory.getNodeById(nodeId);
        boolean success = nodeFactory.delete(node);

        if (success == false) {
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_NODE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
        } else {

            /* Delete the node's network interfaces */
            NodeIpMappingFactory ipMappingFactory = FabricRegistry.getNodeIpMappingFactory(QueryScope.LOCAL);
            NodeIpMapping[] ipMappingsForNode = ipMappingFactory.getAllMappingsForNode(nodeId);
            for (int ipm = 0; ipm < ipMappingsForNode.length && success; ipm++) {

                success = ipMappingFactory.delete(ipMappingsForNode[ipm]);

                if (success == false) {
                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                            AdapterConstants.ARTICLE_NODE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
                }
            }
        }

        return status.toJsonObject();
    }

    /**
     * Returns the local node ID
     *
     * @param correlId
     * @return the JSON message with the local node ID.
     */

    public static JSON queryLocalNode(final String correlId) {

        String nodeID = getNode();
        JSON nodeIDQueryResult = new JSON();
        nodeIDQueryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_LOCAL_NODE);
        nodeIDQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);
        nodeIDQueryResult.putString(AdapterConstants.FIELD_ID, nodeID);
        return nodeIDQueryResult;

    }

    /**
     * Queries the database for nodes.
     *
     * @param op
     * @param correlId
     * @return the JSON message with the query result.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();
        String address = op.getString(AdapterConstants.FIELD_ADDRESS);

        try {

            NodeFactory nodeFactory = FabricRegistry.getNodeFactory();
            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "NODE_ID", QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_NODE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();

            } else {

                /* Lookup the node list in the Registry */
                Node[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = nodeFactory.getAllNodes();
                } else {
                    resultArray = nodeFactory.getNodes(querySQL);
                }

                /* Generate the response object */

                queryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_NODES);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                NodeIpMapping[] nodeIPMappings = null;

                /* For each Node... */
                for (int i = 0; i < resultArray.length; i++) {

                    /* Build the JSON describing the node */

                    JSON node = new JSON();
                    node.putString(AdapterConstants.FIELD_ID, resultArray[i].getId());

                    /* Add the node's IP addresse(s) */

                    nodeIPMappings = resultArray[i].getAllIpMappings();
                    JSONArray nodeAddresses = new JSONArray();

                    for (int j = 0; j < nodeIPMappings.length; j++) {

                        JSON nextInterface = new JSON();

                        String interfaceName = nodeIPMappings[j].getNodeInterface();
                        nextInterface.putString(AdapterConstants.FIELD_NODE_INTERFACE, interfaceName);

                        /* Generate a URI of the form "ip://<address>:<port>" */
                        String ipMapping = String.format("ip://%s:%s", nodeIPMappings[j].getIpAddress(),
                                nodeIPMappings[j].getPort());
                        nextInterface.putString(AdapterConstants.FIELD_ADDRESS, ipMapping);

                        nodeAddresses.add(nextInterface);

                    }

                    /* Filter results on the specified address (if included in the request) */
                    if (address == null || nodeAddresses.toString().contains(address)) {
                        node.putJSONArray(AdapterConstants.FIELD_INTERFACES, nodeAddresses);
                        addOptionalFields(node, resultArray[i].getDescription(), resultArray[i].getAttributes());
                        jsonList.add(node);
                    }
                }

                queryResult = queryResult.putArray(AdapterConstants.FIELD_NODES, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_NODE, message, correlId);
            queryResult = status.toJsonObject();

        }

        return queryResult;
    }

    /**
     * Queries the database for node neighbours.
     *
     * @param op
     * @param correlId
     * @return the JSON message with the query result.
     */
    public static JSON queryNeighbours(final JSON op, final String correlId) {

        JSON neighbourQueryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String target = op.getString("target");
            QueryScope queryScope = (target != null && target.equals("local")) ? QueryScope.LOCAL
                    : QueryScope.DISTRIBUTED;
            NodeNeighbourFactory neighbourFactory = FabricRegistry.getNodeNeighbourFactory(queryScope);
            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "NODE_ID", QUERY_NONE, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_NEIGHBOURS, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                neighbourQueryResult = status.toJsonObject();

            } else {

                /* Lookup the neighbour list in the Registry */
                NodeNeighbour[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = neighbourFactory.getAllNeighbours();
                } else {
                    resultArray = neighbourFactory.getNeighbours(querySQL);
                }

                /* Generate the response object */

                neighbourQueryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_NEIGHBOURS);
                neighbourQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                HashMap<String, JSONArray> neighbourRecordsMap = new HashMap<String, JSONArray>();
                Fabric fabric = new Fabric();

                /* For each neighbour record... */
                for (int i = 0; i < resultArray.length; i++) {

                    /* Get the list of neighbour records for the node */
                    String nodeID = resultArray[i].getNodeId();
                    JSONArray neighbourRecordsList = neighbourRecordsMap.get(nodeID);
                    if (neighbourRecordsList == null) {
                        neighbourRecordsList = new JSONArray();
                        neighbourRecordsMap.put(nodeID, neighbourRecordsList);
                    }

                    /* Create a new neighbour record */
                    JSON neighbour = new JSON();
                    neighbour.putString(AdapterConstants.FIELD_NEIGHBOUR, resultArray[i].getNeighbourId());
                    neighbour.putString(AdapterConstants.FIELD_NEIGHBOUR_INTERFACE, resultArray[i]
                            .getNeighbourInterface());
                    neighbour.putString(AdapterConstants.FIELD_NODE_INTERFACE, resultArray[i].getNodeInterface());

                    neighbourRecordsList.add(neighbour);
                }

                JSONArray allNeighbourRecords = new JSONArray();

                /* For each node... */
                for (String nodeID : neighbourRecordsMap.keySet()) {

                    JSON nodeRecord = new JSON();
                    nodeRecord.putString(AdapterConstants.FIELD_ID, nodeID);
                    nodeRecord.putJSONArray(AdapterConstants.FIELD_NEIGHBOURS, neighbourRecordsMap.get(nodeID));
                    allNeighbourRecords.add(nodeRecord);

                }
                neighbourQueryResult.putJSONArray(AdapterConstants.FIELD_NODES, allNeighbourRecords);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_NODE, message, correlId);
            neighbourQueryResult = status.toJsonObject();

        }

        return neighbourQueryResult;
    }
}
 public class PlatformTypes extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /*
     * Class methods
     */

    /**
     * Inserts a Platform Type into the registry
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the operation.
     */
    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String typeId = op.getString(AdapterConstants.FIELD_TYPE);

            if (typeId == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_PLATFORM_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);

                TypeFactory typeFactory = FabricRegistry.getTypeFactory();
                Type type = typeFactory.createPlatformType(typeId, op.getString(AdapterConstants.FIELD_DESCRIPTION),
                        (attr != null) ? attr.toString() : null, null);
                boolean success = typeFactory.save(type);

                if (!success) {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_PLATFORM_TYPE,
                            "Insert/update of platform type into the Registry failed", correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_PLATFORM_TYPE, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a Platform Type from the registry.
     *
     * @param platformTypeId
     *            The ID of the platform type to be deleted.
     *
     * @param correlId
     *            The correlation ID for the status message.
     *
     * @return the status of the operation.
     */
    public static JSON deregister(final String platformTypeId, final String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (platformTypeId == null) {

            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_PLATFORM_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

        } else {

            TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
            Type platformType = typeFactory.getPlatformType(platformTypeId);
            boolean success = typeFactory.delete(platformType);

            if (success == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_PLATFORM_TYPE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }

        return status.toJsonObject();
    }

    /**
     * Queries the database for platform types.
     *
     * @param op
     * @param correlId
     * @return The result of the query.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<>();

        try {

            TypeFactory typeFactory = FabricRegistry.getTypeFactory();

            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "TYPE_ID", QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_PLATFORM_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();

            } else {

                Type[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = typeFactory.getAllPlatformTypes();
                } else {
                    resultArray = typeFactory.getPlatformTypes(querySQL);
                }

                queryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_PLATFORM_TYPES);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                for (int i = 0; i < resultArray.length; i++) {

                    JSON type = new JSON();
                    type.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getId());
                    addOptionalFields(type, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(type);

                }
                queryResult = queryResult.putArray(AdapterConstants.FIELD_PLATFORM_TYPES, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_PLATFORM_TYPE, message, correlId);
            queryResult = status.toJsonObject();

        }
        return queryResult;
    }
} public class Platforms extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    public static String homeNode = null;

    /*
     * Class methods
     */

    public static String getNode() {

        return homeNode;
    }

    public static void setNode(String homeNode) {

        Platforms.homeNode = homeNode;
    }

    /**
     * Inserts a Platform into the registry.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the operation.
     */
    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String platformId = op.getString(AdapterConstants.FIELD_ID);
            String platformTypeId = op.getString(AdapterConstants.FIELD_TYPE);

            if (platformId == null || platformTypeId == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_PLATFORM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                JSON location = op.getJSON(AdapterConstants.FIELD_LOCATION);

                PlatformFactory platformFactory = FabricRegistry.getPlatformFactory();

                Double lat = location.getDouble(AdapterConstants.FIELD_LATITUDE);
                Double lon = location.getDouble(AdapterConstants.FIELD_LONGITUDE);
                Double alt = location.getDouble(AdapterConstants.FIELD_ALTITUDE);
                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
                String description = op.getString(AdapterConstants.FIELD_DESCRIPTION);

                Platform platform = platformFactory.createPlatform(platformId, platformTypeId, getNode(), //
                        null, // affiliation
                        null, // Security classification,
                        "DEPLOYED", // Readiness,
                        "AVAILABLE", // Availability,
                        (lat.equals(Double.NaN)) ? 0.0 : lat, // Latitude
                        (lon.equals(Double.NaN)) ? 0.0 : lon, // Longitude
                        (alt.equals(Double.NaN)) ? 0.0 : alt, // Altitude
                        0.0, // Bearing
                        0.0, // Velocity
                        description, // Description
                        (attr != null) ? attr.toString() : null, // Attributes
                        null); // Attributes URI
                boolean success = platformFactory.save(platform);

                if (!success) {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_PLATFORM, "Insert/update of platform into the Registry failed",
                            correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_PLATFORM, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a Platform from the registry.
     *
     * @param platformId
     *            The ID of the platform to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return A status code.
     */
    public static JSON deregister(String platformId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (platformId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_PLATFORM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            PlatformFactory platformFactory = FabricRegistry.getPlatformFactory(QueryScope.LOCAL);
            Platform platform = platformFactory.getPlatformById(platformId);
            boolean complete = platformFactory.delete(platform);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_PLATFORM, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

    /**
     * Returns the result of the Systems query a JSON Object.
     *
     * @param correlId
     *            The correlation ID of the request.
     */
    public static JSON query(JSON op, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        JSONArray platformList = new JSONArray();

        try {

            PlatformFactory platformFactory = FabricRegistry.getPlatformFactory();
            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "PLATFORM_ID", QUERY_TYPE | QUERY_ATTRIBUTES
                    | QUERY_LOCATION, op);

            if (querySQL == null) {
                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_PLATFORM, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();
            } else {

                Platform[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = platformFactory.getAllPlatforms();
                } else {
                    resultArray = platformFactory.getPlatforms(querySQL);
                }

                queryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_PLATFORMS);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each platform... */
                for (int i = 0; status.isOK() && i < resultArray.length; i++) {

                    JSON platform = new JSON();
                    platform.putString(AdapterConstants.FIELD_NODE, resultArray[i].getNodeId());
                    platform.putString(AdapterConstants.FIELD_ID, resultArray[i].getId());
                    platform.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getTypeId());
                    platform.putJSON(AdapterConstants.FIELD_LOCATION, buildLocationObject(resultArray[i].getLatitude(),
                            resultArray[i].getLongitude(), resultArray[i].getAltitude()));
                    addOptionalFields(platform, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    platformList.add(platform);

                }
                queryResult.putJSONArray(AdapterConstants.FIELD_PLATFORMS, platformList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_PLATFORM, message, correlId);
            queryResult = status.toJsonObject();

        }

        return queryResult;
    }
}
 public class Registry extends FabricBus {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    private static String homeNode = null;

    /**
     * Method that handles where to direct the delete query and returns its result
     * 
     * @param jsonObject
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeDeleteQuery(JSON jsonObject) {
        String key = jsonObject.getString(AdapterConstants.FIELD_SQL_KEY);
        String value = jsonObject.getString(AdapterConstants.FIELD_SQL_VALUE);
        if (jsonObject.getString(AdapterConstants.FIELD_SQL_TABLE).equals(AdapterConstants.TABLE_DEFAULT_CONFIG)) {
            return executeDefaultConfigDeleteQuery(key, value);
        } else {
            String node = jsonObject.getString(AdapterConstants.FIELD_SQL_NODE);
            return executeNodeConfigDeleteQuery(node, key, value);
        }
    }

    /**
     * Method that interprets an incoming JSON object from HTTP and executes the update SQL query on the local node. It
     * directs update statements to the relevant methods.
     * 
     * @param jsonObject
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeUpdateQuery(JSON jsonObject) {
        String key = jsonObject.getString(AdapterConstants.FIELD_SQL_KEY);
        String value = jsonObject.getString(AdapterConstants.FIELD_SQL_VALUE);
        if (jsonObject.getString(AdapterConstants.FIELD_SQL_TABLE).equals(AdapterConstants.TABLE_DEFAULT_CONFIG)) {
            return executeDefaultConfigUpdateQuery(key, value);
        } else {
            String node = jsonObject.getString(AdapterConstants.FIELD_SQL_NODE);
            return executeNodeConfigUpdateQuery(node, key, value);
        }
    }

    /**
     * Method that interprets an incoming JSON object from HTTP and executes the select query on the local node. It
     * directs the select query to the relevant method
     * 
     * @param jsonObject
     * @return JSON result - JSON object with column:row as key:value pairs
     */
    public static JSON executeSelectQuery(JSON jsonObject) {
        if (jsonObject.getString(AdapterConstants.FIELD_SQL_TABLE).equals(AdapterConstants.TABLE_DEFAULT_CONFIG)) {
            return executeDefaultConfigSelectQuery();
        } else {
            return executeNodeConfigSelectQuery();
        }
    }

    /**
     * Responsible for executing a select query to get all elements on the default_config table. Less complex than
     * node_config due to lack of multiple entries per id.
     * 
     * @return JSON result - JSON object with column:row as key:value pairs
     */
    public static JSON executeDefaultConfigSelectQuery() {
        JSON result = new JSON();
        List<JSON> list = new ArrayList<JSON>();
        DefaultConfig[] dcs = FabricRegistry.getDefaultConfigFactory().getAllDefaultConfig();
        for (DefaultConfig row : dcs) {
            JSON value = new JSON();
            value.putString(row.getName(), row.getValue());
            list.add(value);
            result.putArray("default_config", list);
        }
        return result;
    }

    /**
     * Responsible for executing a select query to get all elements on the node_config table
     * 
     * @return JSON result - JSON object with column:row as key:value pairs
     */
    public static JSON executeNodeConfigSelectQuery() {
        JSON result = new JSON();
        JSON nodes = new JSON();
        HashMap<String, List<JSON>> nodeMap = new HashMap<String, List<JSON>>();
        NodeConfig[] nfs = FabricRegistry.getNodeConfigFactory().getAllNodeConfig();

        for (NodeConfig row : nfs) {
            JSON value = new JSON();
            value.putString(row.getName(), row.getValue());
            if (!nodeMap.containsKey(row.getNode())) {
                List<JSON> list = new ArrayList<JSON>();
                list.add(value);
                nodeMap.put(row.getNode(), list);
            } else {
                nodeMap.get(row.getNode()).add(value);
            }
        }

        Iterator<?> it = nodeMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, List<JSON>> pairs = (Map.Entry<String, List<JSON>>) it.next();
            nodes.putArray(pairs.getKey(), pairs.getValue());
        }
        result.putJSON("node_config", nodes);
        return result;
    }

    /**
     * Responsible for executing a delete query on the default_config table
     * 
     * @param key
     *            The column where the data lies
     * @param value
     *            The row value to delete
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeDefaultConfigDeleteQuery(String key, String value) {
        JSON result = new JSON();
        Boolean success = false;

        DefaultConfigFactory dfc = FabricRegistry.getDefaultConfigFactory(QueryScope.LOCAL);
        DefaultConfig dc = dfc.getDefaultConfigByName(key);
        if (dc == null) {
            result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());
        } else {
            success = dfc.delete(dc);
            result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());
        }

        return result;
    }

    /**
     * Responsible for executing a delete query on the default_config table
     * 
     * @param node
     *            The relevant node to delete the data from
     * @param key
     *            The column where the data lies
     * @param value
     *            The row value to delete
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeNodeConfigDeleteQuery(String node, String key, String value) {
        JSON result = new JSON();
        Boolean success = false;

        NodeConfigFactory nfc = FabricRegistry.getNodeConfigFactory(QueryScope.LOCAL);
        NodeConfig nc = nfc.getNodeConfigByName(node, key);
        if (nc == null) {
            result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());
        } else {
            success = nfc.delete(nc);
            result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());
        }

        return result;
    }

    /**
     * Responsible for executing an update query on the default_config table via the registry API
     * 
     * @param node
     *            Name of the fabric node
     * @param key
     *            The column name
     * @param value
     *            The row element for that column
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeDefaultConfigUpdateQuery(String key, String value) {
        JSON result = new JSON();
        Boolean success = false;

        DefaultConfigFactory dfc = FabricRegistry.getDefaultConfigFactory();
        DefaultConfig dc = dfc.getDefaultConfigByName(key);
        if (dc != null) {
            try {
                dc.setValue(value);
                success = dfc.update(dc);
            } catch (IncompleteObjectException e) {
                e.printStackTrace();
            } catch (PersistenceException e) {
                e.printStackTrace();
            }
        } else {
            DefaultConfig newdc = dfc.createDefaultConfig(key, value);
            try {
                success = dfc.save(newdc);
            } catch (IncompleteObjectException e) {
                e.printStackTrace();
            }
        }
        result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());

        return result;
    }

    /**
     * Responsible for executing an update query on the node_config table via the registry API
     * 
     * @param node
     *            Name of the fabric node
     * @param key
     *            The column name
     * @param value
     *            The row element for that column
     * @return JSON result - JSON object with success result in key "sql-update-result"
     */
    public static JSON executeNodeConfigUpdateQuery(String node, String key, String value) {
        JSON result = new JSON();
        Boolean success = false;

        NodeConfigFactory nfc = FabricRegistry.getNodeConfigFactory();
        NodeConfig nc = nfc.getNodeConfigByName(node, key);
        if (nc != null) {
            try {
                nc.setValue(value);
                success = nfc.update(nc);
            } catch (IncompleteObjectException e) {
                e.printStackTrace();
            } catch (PersistenceException e) {
                e.printStackTrace();
            }
        } else {
            NodeConfig newdc = nfc.createNodeConfig(node, key, value);
            try {
                success = nfc.save(newdc);
            } catch (IncompleteObjectException e) {
                e.printStackTrace();
            }
        }
        result.putString(AdapterConstants.FIELD_SQL_UPDATE_RESULT, success.toString());

        return result;
    }

    public static String getNode() {

        return homeNode;
    }

    public static void setNode(String homeNode) {

        Registry.homeNode = homeNode;
    }
}
 public class ServiceTypes {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /**
     * Inserts a Service Type into the registry.
     *
     * @param id
     *            the ID of the type being registered.
     *
     * @param jsonOperationObject
     *            The full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the operation.
     */
    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);
        String typeid = op.getString(AdapterConstants.FIELD_TYPE);

        try {

            if (typeid == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SERVICE_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                /* Get the mode of the service, indicating its semantics */
                String mode = op.getString(AdapterConstants.FIELD_MODE);

                /* Get the attributes of the service */
                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
                attr = (attr != null) ? attr : new JSON();

                /* Combine */
                attr.putString("mode", mode);

                switch ((mode != null) ? mode : "") {

                    case AdapterConstants.MODE_OUTPUT:
                    case AdapterConstants.MODE_INPUT:
                    case AdapterConstants.MODE_NOTIFY:
                    case AdapterConstants.MODE_LISTEN:
                    case AdapterConstants.MODE_SOLICIT:
                    case AdapterConstants.MODE_RESPONSE:
                    case "":

                        /* Create the Registry entry for the new service type */
                        TypeFactory typeFactory = FabricRegistry.getTypeFactory();
                        Type type = typeFactory.createServiceType(typeid, op
                                .getString(AdapterConstants.FIELD_DESCRIPTION), attr.toString(), null);
                        boolean success = typeFactory.save(type);

                        if (!success) {

                            status = new AdapterStatus(AdapterConstants.ERROR_ACTION,
                                    AdapterConstants.OP_CODE_REGISTER, AdapterConstants.ARTICLE_SERVICE_TYPE,
                                    "Insert/update of system type into the Registry failed", correlId);

                        }

                        break;

                    default:

                        status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                                AdapterConstants.ARTICLE_SERVICE_TYPE, AdapterConstants.STATUS_MSG_BAD_MODE, correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_SERVICE_TYPE, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a Service Type from the registry.
     *
     * @param serviceTypeId
     *            The ID of the Type to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return A status code.
     */
    public static JSON deregister(String serviceTypeId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (serviceTypeId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_SERVICE_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
            Type systemType = typeFactory.getServiceType(serviceTypeId);
            boolean complete = typeFactory.delete(systemType);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_SERVICE_TYPE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

}
 public class ServiceWiring extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014, 2016";

    /**
     * Returns the result of a Systems query in a JSON Object.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return the query result JSON Object.
     */
    public static JSON query(final JSON jsonOpObject, final String correlId) {

        JSON queryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> serviceWiringList = new ArrayList<JSON>();

        try {

            SystemWiringFactory systemWiringFactory = FabricRegistry.getSystemWiringFactory();
            String querySQL = generateWiringPredicate(jsonOpObject);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                queryResult = status.toJsonObject();

            } else {

                /* Lookup the system list in the Registry */

                SystemWiring[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = systemWiringFactory.getAll();
                }

                /* Generate the response object */

                queryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_SERVICE_WIRING);
                queryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each wired service... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON serviceWiring = new JSON();
                    serviceWiring.putString(AdapterConstants.FIELD_COMPOSITION, resultArray[i].getCompositeId());
                    serviceWiring.putString(AdapterConstants.FIELD_FROM_PLATFORM, resultArray[i]
                            .getFromSystemPlatformId());
                    serviceWiring.putString(AdapterConstants.FIELD_FROM_SYSTEM, resultArray[i].getFromSystemId());
                    serviceWiring.putString(AdapterConstants.FIELD_FROM_INTERFACE, resultArray[i].getFromInterfaceId());
                    serviceWiring.putString(AdapterConstants.FIELD_TO_PLATFORM, resultArray[i].getToSystemPlatformId());
                    serviceWiring.putString(AdapterConstants.FIELD_TO_SYSTEM, resultArray[i].getToSystemId());
                    serviceWiring.putString(AdapterConstants.FIELD_TO_INTERFACE, resultArray[i].getToInterfaceId());
                    addOptionalFields(serviceWiring, null, resultArray[i].getAttributes());
                    serviceWiringList.add(serviceWiring);

                }
                queryResult.putArray(AdapterConstants.FIELD_SERVICE_WIRING, serviceWiringList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);
            queryResult = status.toJsonObject();

        }

        return queryResult;
    }

    /**
     * Returns the SQL corresponding to the Systems query.
     *
     * @param op
     *            The JSON object that needs converting to SQL.
     *
     * @return The SQL for the query.
     */
    private static String generateWiringPredicate(JSON op) {

        String predicate = null;
        StringBuilder predicateBuilder = new StringBuilder();

        try {

            String compositeID = op.getString(AdapterConstants.FIELD_COMPOSITION);
            if (compositeID != null) {
                compositeID.replace("*", "%");
                predicateBuilder.append("COMPOSITE_ID LIKE '");
                predicateBuilder.append(compositeID);
                predicateBuilder.append("' AND ");
            }

            String fromPlatform = op.getString(AdapterConstants.FIELD_FROM_PLATFORM);
            if (fromPlatform != null) {
                fromPlatform.replace("*", "%");
                predicateBuilder.append("FROM_SERVICE_PLATFORM_ID LIKE '");
                predicateBuilder.append(fromPlatform);
                predicateBuilder.append("' AND ");
            }

            String toPlatform = op.getString(AdapterConstants.FIELD_TO_PLATFORM);
            if (toPlatform != null) {
                toPlatform.replace("*", "%");
                predicateBuilder.append("TO_SERVICE_PLATFORM_ID LIKE '");
                predicateBuilder.append(toPlatform);
                predicateBuilder.append("' AND ");
            }

            JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
            String attrString = (attr != null) ? attr.toString() : null;
            if (attrString != null && !attrString.equals("{}")) {
                int start = attrString.indexOf('{') + 1;
                int end = attrString.lastIndexOf('}');
                end = (end != -1) ? end : attrString.length();
                if (start <= end) {
                    attrString = attrString.substring(start, end);
                }
                predicateBuilder.append("ATTRIBUTES LIKE '%");
                predicateBuilder.append(attrString);
                predicateBuilder.append("%' AND ");
            }

            int trailingAndIndex = predicateBuilder.lastIndexOf(" AND ");
            if (trailingAndIndex == predicateBuilder.length() - 5) {
                predicateBuilder.delete(trailingAndIndex, predicateBuilder.length());
            }
            predicate = predicateBuilder.toString();

        } catch (Exception e) {

            predicate = null;

        }

        return predicate;
    }
}
 public class SystemTypes extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /**
     * Inserts a System Type into the registry.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the operation.
     */
    public static JSON register(final JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            /* The get the ID of the system type */
            String typeid = op.getString(AdapterConstants.FIELD_TYPE);

            if (typeid == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                /* Build a comma-separated list of services associated with this system type */

                JSONArray servicesArray = op.getJSONArray(AdapterConstants.FIELD_SERVICES);
                StringBuilder serviceTypeList = new StringBuilder();
                boolean isValidMessage = true;

                for (Iterator<JSON> servicesIterator = servicesArray.iterator(); isValidMessage
                        && servicesIterator.hasNext();) {

                    JSON nextServiceType = servicesIterator.next();
                    String type = nextServiceType.getString(AdapterConstants.FIELD_TYPE);
                    String mode = nextServiceType.getString(AdapterConstants.FIELD_MODE);
                    mode = (mode != null) ? mode : "";
                    String name = nextServiceType.getString(AdapterConstants.FIELD_ID);
                    name = (name != null) ? name : type;

                    switch (mode) {

                        case AdapterConstants.MODE_OUTPUT:
                        case AdapterConstants.MODE_INPUT:
                        case AdapterConstants.MODE_NOTIFY:
                        case AdapterConstants.MODE_LISTEN:
                        case AdapterConstants.MODE_SOLICIT:
                        case AdapterConstants.MODE_RESPONSE:
                        case "":

                            serviceTypeList.append(type);
                            serviceTypeList.append(':');
                            serviceTypeList.append(mode);
                            serviceTypeList.append(':');
                            serviceTypeList.append(name);
                            serviceTypeList.append(',');

                            break;

                        default:

                            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                                    AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_BAD_MODE,
                                    correlId);
                            isValidMessage = false;

                    }
                }

                if (isValidMessage) {

                    if (serviceTypeList.toString().endsWith(",")) {
                        serviceTypeList.setLength(serviceTypeList.length() - 1);
                    }

                    /* Get the attributes of the service */
                    JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
                    attr = (attr != null) ? attr : new JSON();

                    /*
                     * Add the system type to the Registry (a system's services are currently recorded in its attributes
                     * field)
                     */
                    TypeFactory typeFactory = FabricRegistry.getTypeFactory();
                    attr.putString("serviceTypes", serviceTypeList.toString());
                    Type type = typeFactory.createSystemType(typeid, op.getString(AdapterConstants.FIELD_DESCRIPTION),
                            attr.toString(), null);
                    boolean success = typeFactory.save(type);

                    if (!success) {

                        status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                                AdapterConstants.ARTICLE_SYSTEM_TYPE,
                                "Insert/update of system type into the Registry failed", correlId);

                    }
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_SYSTEM_TYPE, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a System Type from the registry.
     *
     * @param systemTypeId
     *            The ID of the Type to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return A JSON status object.
     */
    public static JSON deregister(String systemTypeId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (systemTypeId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
            Type systemType = typeFactory.getSystemType(systemTypeId);
            boolean complete = typeFactory.delete(systemType);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

    /**
     * Returns the result of the System Type query a JSON Object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return The query result JSON Object.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON systemTypesQueryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            TypeFactory typeFactory = FabricRegistry.getTypeFactory();

            String querySQL = generatePredicate(AdapterConstants.FIELD_TYPE, "TYPE_ID", QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                systemTypesQueryResult = status.toJsonObject();

            } else {

                /* Get all of the service types that match the query */

                Type[] matchingSystemTypes = null;

                if ("".equals(querySQL)) {
                    matchingSystemTypes = typeFactory.getAllSystemTypes();
                } else {
                    matchingSystemTypes = typeFactory.getSystemTypes(querySQL);
                }

                /* Get the list of specific service types that matching system types must include */

                JSONArray serviceTypesFilter = op.getJSONArray(AdapterConstants.FIELD_SERVICES);
                ArrayList<Type> matchingServiceTypes = new ArrayList<Type>();

                for (JSON nextFilter : serviceTypesFilter) {

                    Type[] serviceTypes = null;
                    // TODO querySQL = queryTypesSQL(nextFilter);

                    if ("".equals(querySQL)) {
                        serviceTypes = typeFactory.getAllServiceTypes();
                    } else {
                        serviceTypes = typeFactory.getServiceTypes(querySQL);
                    }

                    for (Type nextType : serviceTypes) {
                        if (!matchingServiceTypes.contains(nextType)) {
                            matchingServiceTypes.add(nextType);
                        }
                    }
                }

                /* Filter the list of system types by service types (if specified) */

                for (int i = 0; i < matchingSystemTypes.length; i++) {

                    String thisSystemsServices = matchingSystemTypes[i].getAttributes();

                    if (thisSystemsServices != null) {

                        boolean discard = true;

                        for (Type nextType : matchingServiceTypes) {
                            if (thisSystemsServices.matches(".*,*" + nextType.getId() + ":.*:.*,*.*")) {
                                discard = false;
                                break;
                            }
                        }

                        if (discard) {
                            matchingSystemTypes[i] = null;
                        }
                    }
                }

                /* Build a map of all possible service types */
                Type[] allServiceTypes = typeFactory.getAllServiceTypes();
                HashMap<String, Type> typeMap = new HashMap<String, Type>();
                for (Type nextType : allServiceTypes) {
                    typeMap.put(nextType.getId(), nextType);
                }

                /* Build the result */

                systemTypesQueryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_SYSTEM_TYPES);
                systemTypesQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                List<JSON> systemTypeJSONArray = new ArrayList<JSON>();

                for (Type nextType : matchingSystemTypes) {

                    if (nextType != null) {

                        /* Add basic system type information */

                        JSON nextTypeJSON = new JSON();

                        nextTypeJSON.putString(AdapterConstants.FIELD_TYPE, nextType.getId());
                        nextTypeJSON.putString(AdapterConstants.FIELD_DESCRIPTION, nextType.getDescription());

                        String attrString = nextType.getAttributes();
                        JSON attr = JsonUtils.stringTOJSON(attrString, "Attribute value is not valid JSON");
                        if (attr != null) {
                            nextTypeJSON.putJSON(AdapterConstants.FIELD_ATTRIBUTES, attr);
                        }

                        /* Get the list of services offered by this system type */

                        String serviceTypeString = attr.getString("serviceTypes");
                        String[] servicesOffered = serviceTypeString.split(",");
                        JSONArray servicesOfferedJSONArray = new JSONArray();

                        for (String service : servicesOffered) {

                            String[] serviceParts = service.split(":");
                            Type serviceToAdd = typeMap.get(serviceParts[0]);

                            String serviceTypeJSONString = String.format(
                                    "{\"type\":\"%s\",\"attr\":\"%s\",\"desc\":\"%s\"}", serviceToAdd.getId(),
                                    serviceToAdd.getAttributes(), serviceToAdd.getDescription());
                            JSON serviceTypeJSON = new JSON(serviceTypeJSONString);
                            servicesOfferedJSONArray.add(serviceTypeJSON);
                        }

                        nextTypeJSON.putJSONArray(AdapterConstants.FIELD_SERVICES, servicesOfferedJSONArray);
                        systemTypeJSONArray.add(nextTypeJSON);
                    }
                }

                systemTypesQueryResult.putArray(AdapterConstants.FIELD_SYSTEM_TYPES, systemTypeJSONArray);
            }

        } catch (Exception e) {

            e.printStackTrace();
            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_SYSTEM_TYPE, message, correlId);
            systemTypesQueryResult = status.toJsonObject();

        }

        return systemTypesQueryResult;
    }
} public class Systems extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /*
     * Class fields
     */

    /*
     * Class static methods
     */

    /**
     * Inserts a System into the Registry.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON register(final JSON op, String correlId, Object clientID) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String systemName = op.getString(AdapterConstants.FIELD_ID);
            String systemType = op.getString(AdapterConstants.FIELD_TYPE);

            String user = op.getString(AdapterConstants.FIELD_USER);

            if (systemName == null || systemType == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                SystemDescriptor systemDescriptor = new SystemDescriptor(systemName);

                if (!(user == null)) {
                    // TODO User logic
                }

                /* Lookup the list of service types associated with the system type */
                String[] serviceTypes = getServiceTypes(systemType);

                /* If there are any... */
                if (serviceTypes != null) {

                    /* Create the corresponding service instances in the Registry */
                    status = createServices(serviceTypes, systemDescriptor.platform(), systemDescriptor.system(),
                            correlId);

                    if (status != null && status.isOK()) {

                        JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);
                        attr = (attr != null) ? attr : new JSON();

                        /* Set the default client ID if required */
                        if ("true".equals(attr.getString("autoStart"))) {
                            if (attr.getString("clientID") == null) {
                                String id = (clientID != null) ? clientID.toString() : "$unknown";
                                attr.putString("clientID", id);
                            }
                        }

                        /* Insert the system into the Registry */
                        SystemFactory systemFactory = FabricRegistry.getSystemFactory();
                        System system = systemFactory.createSystem(systemDescriptor.platform(), // platform ID
                                systemDescriptor.system(), // service ID
                                systemType, // system type
                                null, // credentials
                                "DEPLOYED", // readiness
                                AdapterConstants.STATE_AVAILABLE, // availability,
                                0, // latitude,
                                0, // longitude,
                                0, // altitude,
                                0, // bearing,
                                0, // velocity,
                                op.getString(AdapterConstants.FIELD_DESCRIPTION), // system description
                                (attr != null) ? attr.toString() : null, null); // attributes, attributes URI;
                        boolean success = systemFactory.save(system);

                        if (!success) {

                            status = new AdapterStatus(AdapterConstants.ERROR_ACTION,
                                    AdapterConstants.OP_CODE_REGISTER, AdapterConstants.ARTICLE_SYSTEM,
                                    "Insert/update of system into the Registry failed", correlId);

                        }
                    }

                } else {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_UNRECOGNIZED_TYPE, correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Creates the Registry entries for the services corresponding to a system.
     *
     * @param serviceTypes
     *            the list of service types to register.
     *
     * @param platformId
     *            the system's platform ID.
     *
     * @param systemId
     *            the system's service ID.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the call.
     */
    private static AdapterStatus createServices(String[] serviceTypes, String platformId, String systemId,
            String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);
        TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
        Type serviceTypeRecord = null;

        /* For each service... */
        for (String nextServiceType : serviceTypes) {

            String[] serviceParts = nextServiceType.split(":");
            String name = null;
            String mode = "";
            String type = "";
            switch (serviceParts.length) {
                case 3:
                    name = serviceParts[2];
                case 2:
                    mode = serviceParts[1];
                case 1:
                    type = serviceParts[0];
                    name = (name == null) ? type : name;
                    break;
            }

            /* Lookup the service type in the Registry */
            serviceTypeRecord = typeFactory.getServiceType(type);

            /* If we didn't find it... */
            if (serviceTypeRecord == null) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_UNRECOGNIZED_TYPE, correlId);
                break;
            }

            /* Get the system's attributes */
            JSON attr = null;
            try {
                String attrString = serviceTypeRecord.getAttributes();
                attr = new JSON((attrString != null) ? attrString : "{}");
            } catch (Exception e) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_BAD_ATTRIBUTE_JSON, correlId);
                break;
            }

            /* If the mode was not specified with the system type... */
            if (mode.equals("")) {
                /* Get the mode of the service from the service type */
                mode = attr.getString("mode");
            }

            /* Used to hold the updated mode name (known as the "direction" in the research Fabric Registry schema) */
            String direction = null;

            switch ((mode != null) ? mode : "") {

                case AdapterConstants.MODE_INPUT:

                    direction = AdapterConstants.DIRECTION_INPUT;
                    break;

                case AdapterConstants.MODE_OUTPUT:

                    direction = AdapterConstants.DIRECTION_OUTPUT;
                    break;

                case AdapterConstants.MODE_NOTIFY:

                    direction = AdapterConstants.DIRECTION_NOTIFICATION;
                    break;

                case AdapterConstants.MODE_LISTEN:

                    direction = AdapterConstants.DIRECTION_ONE_WAY;
                    break;

                case AdapterConstants.MODE_SOLICIT:

                    direction = AdapterConstants.DIRECTION_SOLICIT_RESPONSE;
                    break;

                case AdapterConstants.MODE_RESPONSE:

                    direction = AdapterConstants.DIRECTION_REQUEST_RESPONSE;
                    break;

                default:

                    break;
            }

            status = createService(platformId, systemId, name, type, direction, attr, correlId);

            if (status.isOK()) {

                status = createTaskService(platformId, systemId, name, correlId);

            }
        }

        return status;
    }

    /**
     * Creates the Registry entries for a services.
     *
     * @param platformId
     *            the system's platform ID.
     *
     * @param systemId
     *            the system ID.
     *
     * @param serviceId
     *            the ID of the service.
     *
     * @param serviceTypeId
     *            the type of the service.
     *
     * @param mode
     *            the mode of the service.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the call.
     */
    private static AdapterStatus createService(String platformId, String systemId, String serviceId,
            String serviceTypeId, String mode, JSON attr, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            /* Create the Registry entry */
            ServiceFactory systemFactory = FabricRegistry.getServiceFactory();
            Service service = systemFactory.createService( //
                    platformId, //
                    systemId, //
                    serviceId, //
                    serviceTypeId, //
                    mode, //
                    null, // credentials
                    AdapterConstants.STATE_AVAILABLE, //
                    null, // description
                    (attr != null) ? attr.toString() : null, // attributes
                            null); // attributes URI
            boolean success = systemFactory.save(service);

            if (!success) {

                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, "Insert/update of system into the Registry failed", correlId);

            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_SYSTEM, message);

        }

        return status;
    }

    /**
     * Creates an entry for a service in the Registry's task services table.
     *
     * @param platformId
     *            the system's platform ID.
     *
     * @param systemId
     *            the system's service ID.
     *
     * @param serviceId
     *            the ID of the service.
     *
     * @param correlId
     *            the operation correlation ID.
     *
     * @return the status of the call.
     */
    private static AdapterStatus createTaskService(String platformId, String systemId, String serviceId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            /* Create the Registry entry */
            TaskServiceFactory tsf = FabricRegistry.getTaskServiceFactory();
            TaskService ts = tsf.createTaskService("$def", platformId, systemId, serviceId);
            boolean success = tsf.save(ts);

            if (!success) {

                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, "Insert/update of service into the Registry failed", correlId);

            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_SYSTEM, message);

        }

        return status;
    }

    /**
     * Gets the list of service types associated with a system type.
     *
     * @param systemType
     *            the system type.
     *
     * @return the corresponding list of service types.
     */
    private static String[] getServiceTypes(String systemType) {

        String[] serviceTypes = null;

        /* Lookup the system type in the Registry */
        TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
        Type type = typeFactory.getSystemType(systemType);

        /* If we found it... */
        if (type != null) {

            /* Unpack the list of service types */
            String attrString = type.getAttributes();
            JSON attr = null;
            try {
                attr = new JSON((attrString != null) ? attrString : "{}");
            } catch (Exception e) {
                attr = new JSON();
            }
            String serviceTypeList = attr.getString("serviceTypes");
            serviceTypes = serviceTypeList.split(",");

        }

        return serviceTypes;
    }

    /**
     * Deletes a System from the Registry.
     *
     * @param systemName
     *            The ID of the System to be deleted.
     *
     * @param correlId
     *            The correlation ID for the status message.
     *
     * @return The status of the call.
     */
    public static JSON deregister(String systemName, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        if (systemName == null) {

            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

        } else {

            SystemDescriptor systemDescriptor = new SystemDescriptor(systemName);

            /* Remove the system from the Registry */
            SystemFactory systemFactory = FabricRegistry.getSystemFactory(QueryScope.LOCAL);
            System system = systemFactory.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());
            boolean complete = systemFactory.delete(system);

            /* If it failed... */
            if (complete == false) {

                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);

            }
        }

        return status.toJsonObject();
    }

    /**
     * Returns the result of a Systems query in a JSON Object.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return the query result JSON Object.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON systemsQueryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();

        try {

            SystemFactory systemFactory = FabricRegistry.getSystemFactory();

            String querySQL = generatePredicate(AdapterConstants.FIELD_PLATFORM, "PLATFORM_ID", QUERY_TYPE
                    | QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_SYSTEM_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                systemsQueryResult = status.toJsonObject();

            } else {

                /* Lookup the system list in the Registry */

                System[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = systemFactory.getAll();
                } else {
                    resultArray = systemFactory.getSystems(querySQL);
                }

                /* Generate the response object */

                systemsQueryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_SYSTEMS);
                systemsQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each system... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON system = new JSON();
                    system.putString(AdapterConstants.FIELD_ID, resultArray[i].getId());
                    system.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getTypeId());
                    system.putString(AdapterConstants.FIELD_PLATFORM, resultArray[i].getPlatformId());
                    addOptionalFields(system, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(system);

                }
                systemsQueryResult.putArray(AdapterConstants.FIELD_SYSTEMS, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);
            systemsQueryResult = status.toJsonObject();

        }

        return systemsQueryResult;
    }

    /**
     * Changes the state of the system. Changes running to stopped, or stopped to running.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param client
     *            adapter-specific ID of the client, used to target messages sent to the client.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param adapterProxy
     *            the name of the class implementing the system adapter proxy for the JSON Fabric client.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON changeState(JSON op, Object client, RuntimeManager runtimeManager, String adapterProxy,
            String correlId) {

        JSON response = null;
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String id = op.getString(AdapterConstants.FIELD_ID);
            String state = op.getString(AdapterConstants.FIELD_STATE);

            if (id == null || state == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_STATE,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                SystemDescriptor systemDescriptor = new SystemDescriptor(id);

                switch (state) {

                    case AdapterConstants.STATE_RUNNING:

                        /* Start the system */
                        RuntimeStatus startStatus = runtimeManager.start(systemDescriptor, client, adapterProxy);

                        if (startStatus.getStatus() != RuntimeStatus.Status.OK) {
                            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                                    AdapterConstants.ARTICLE_SYSTEM, startStatus.getStatus() + ": "
                                            + startStatus.getMessage(), correlId);
                        }

                        break;

                    case AdapterConstants.STATE_STOPPED:

                        /* Stop the system */
                        RuntimeStatus stopStatus = runtimeManager.stop(systemDescriptor);

                        if (stopStatus.getStatus() != RuntimeStatus.Status.OK) {
                            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                                    AdapterConstants.ARTICLE_SYSTEM, stopStatus.getStatus() + ": "
                                            + stopStatus.getMessage(), correlId);
                        }

                        break;

                    default:

                        status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_STATE,
                                AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_BAD_STATE, correlId);
                        // response = status.getJson(correlId);
                        break;

                }
            }
        } catch (Exception e) {
            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);
        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Handles a request in a request/response operation.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON request(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = null;
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            JSONArray sendToList = op.getJSONArray(AdapterConstants.FIELD_REQUEST_RESPONSE);
            String respondTo = op.getString(AdapterConstants.FIELD_SOLICIT_RESPONSE);
            String msg = op.getJSON(AdapterConstants.FIELD_MESSAGE).toString();
            String encoding = op.getString(AdapterConstants.FIELD_ENCODING);

            if (sendToList == null || respondTo == null || msg == null || correlId == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REQUEST,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                if (encoding == null) {
                    encoding = AdapterConstants.FIELD_VALUE_ENCODING_ASCII;
                }

                ServiceDescriptor respondToDescriptor = new ServiceDescriptor(respondTo);

                /* For each target service... */
                for (Object sendTo : sendToList) {

                    ServiceDescriptor sendToDescriptor = new ServiceDescriptor(sendTo.toString());

                    /* Start the request */
                    RuntimeStatus requestStatus = runtimeManager.request(sendToDescriptor, respondToDescriptor, msg,
                            encoding, correlId);

                    if (requestStatus.getStatus() != RuntimeStatus.Status.OK) {
                        status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REQUEST,
                                AdapterConstants.ARTICLE_SYSTEM, requestStatus.getStatus() + ": "
                                        + requestStatus.getMessage(), correlId);
                        break;

                    }
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Handles a response in a request/response operation.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON response(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String producer = op.getString(AdapterConstants.FIELD_REQUEST_RESPONSE);
            String respondTo = op.getString(AdapterConstants.FIELD_SOLICIT_RESPONSE);
            String msg = op.getJSON(AdapterConstants.FIELD_MESSAGE).toString();
            String encoding = op.getString(AdapterConstants.FIELD_ENCODING);

            if (respondTo == null || msg == null || correlId == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_RESPONSE,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                /* Send the response */
                ServiceDescriptor respondToDescriptor = new ServiceDescriptor(respondTo);
                ServiceDescriptor producerDescriptor = new ServiceDescriptor(producer);
                RuntimeStatus responseStatus = runtimeManager.response(respondToDescriptor, producerDescriptor, msg,
                        encoding, correlId);

                if (responseStatus.getStatus() != RuntimeStatus.Status.OK) {
                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_RESPONSE,
                            AdapterConstants.ARTICLE_SYSTEM, responseStatus.getStatus() + ": "
                                    + responseStatus.getMessage(), correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Handles a notify message.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON notify(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            JSONArray sendToList = op.getJSONArray(AdapterConstants.FIELD_LISTENER);
            String msg = op.getJSON(AdapterConstants.FIELD_MESSAGE).toString();
            String producer = op.getString(AdapterConstants.FIELD_NOTIFICATION);
            String encoding = op.getString(AdapterConstants.FIELD_ENCODING);

            if (sendToList == null || producer == null || msg == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_NOTIFY,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                ServiceDescriptor producerDescriptor = new ServiceDescriptor(producer);

                /* For each target service... */
                for (Object sendTo : sendToList) {

                    ServiceDescriptor sendToDescriptor = new ServiceDescriptor(sendTo.toString());

                    /* Start the request */
                    RuntimeStatus notifyStatus = runtimeManager.notify(sendToDescriptor, producerDescriptor, msg,
                            encoding, correlId);

                    if (notifyStatus.getStatus() != RuntimeStatus.Status.OK) {
                        status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_NOTIFY,
                                AdapterConstants.ARTICLE_SYSTEM, notifyStatus.getStatus() + ": "
                                        + notifyStatus.getMessage(), correlId);
                        break;

                    }
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Publishes an output feed message.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON publish(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = null;
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String outputFeed = op.getString(AdapterConstants.FIELD_OUTPUT_FEED);
            String msg = op.getJSON(AdapterConstants.FIELD_MESSAGE).toString();
            String encoding = op.getString(AdapterConstants.FIELD_ENCODING);

            if (outputFeed == null || msg == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_PUBLISH,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                ServiceDescriptor outputFeedDescriptor = new ServiceDescriptor(outputFeed);

                /* Push the message onto the Fabric on-ramp */
                RuntimeStatus publishStatus = runtimeManager.publish(outputFeedDescriptor, msg, encoding);

                if (publishStatus.getStatus() != RuntimeStatus.Status.OK) {
                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_PUBLISH,
                            AdapterConstants.ARTICLE_SYSTEM, publishStatus.getStatus() + ": "
                                    + publishStatus.getMessage(), correlId);
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Subscribes to a list of output feeds.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON subscribe(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = null;
        AdapterStatus status = new AdapterStatus(correlId);
        List<ServiceDescriptor> subscribedList = new ArrayList<ServiceDescriptor>();
        String inputFeed = null;

        try {

            JSONArray outputFeedPatterns = op.getJSONArray(AdapterConstants.FIELD_OUTPUT_FEEDS);
            inputFeed = op.getString(AdapterConstants.FIELD_INPUT_FEED);

            if (outputFeedPatterns == null || inputFeed == null || correlId == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_SUBSCRIBE,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                ServiceDescriptor inputFeedDescriptor = new ServiceDescriptor(inputFeed);
                List<ServiceDescriptor> patternList = new ArrayList<ServiceDescriptor>();

                /* Extract the list of output feed patterns from the message */

                for (int sf = 0; sf < outputFeedPatterns.size(); sf++) {

                    String nextPattern = outputFeedPatterns.getString(sf);
                    ServiceDescriptor nextDescriptor = new ServiceDescriptor(nextPattern);
                    patternList.add(nextDescriptor);

                }

                ServiceDescriptor[] patternDescriptors = patternList.toArray(new ServiceDescriptor[patternList.size()]);

                /* Subscribe */

                RuntimeStatus subscribeStatus = runtimeManager.subscribe(patternDescriptors, inputFeedDescriptor,
                        subscribedList);

                if (!subscribeStatus.isOK()) {
                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_SUBSCRIBE,
                            AdapterConstants.ARTICLE_SYSTEM, subscribeStatus.getStatus() + ": "
                                    + subscribeStatus.getMessage(), correlId);
                }

                response = JSONAdapter.buildSubscriptionResponse(subscribedList, inputFeedDescriptor, correlId);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = (response == null) ? status.toJsonObject() : response;

        return response;
    }

    /**
     * Unsubscribe from an output feed.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON unsubscribe(JSON op, RuntimeManager runtimeManager, String correlId) {

        JSON response = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);

        try {

            JSONArray sourceFeedList = op.getJSONArray(AdapterConstants.FIELD_OUTPUT_FEEDS);
            String deliverTo = op.getString(AdapterConstants.FIELD_INPUT_FEED);

            if (deliverTo == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_UNSUBSCRIBE,
                        AdapterConstants.ARTICLE_SYSTEM, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                ServiceDescriptor[] sourceFeeds = null;

                /* If a list of output-feeds was specified... */
                if (sourceFeedList != null) {

                    sourceFeeds = new ServiceDescriptor[sourceFeedList.size()];

                    /* For each output-feed... */
                    for (int f = 0; f < sourceFeeds.length; f++) {
                        sourceFeeds[f] = new ServiceDescriptor(sourceFeedList.getString(f).toString());
                    }
                }

                ServiceDescriptor deliverToDescriptor = new ServiceDescriptor(deliverTo);

                /* Unsubscribe */
                RuntimeStatus unsubscribeStatus = runtimeManager.unsubscribe(sourceFeeds, deliverToDescriptor);

                if (!unsubscribeStatus.isOK()) {
                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_UNSUBSCRIBE,
                            AdapterConstants.ARTICLE_SYSTEM, unsubscribeStatus.getStatus() + ": "
                                    + unsubscribeStatus.getMessage(), correlId);
                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_STATE,
                    AdapterConstants.ARTICLE_SYSTEM, message, correlId);

        }

        response = status.toJsonObject();
        return response;
    }

    /**
     * Handles the disconnection of a client.
     *
     * @param op
     *            the full JSON operation object.
     *
     * @param runtimeManager
     *            the runtime manager instance responsible for managing this system instance.
     *
     * @return <code>null</code>.
     */
    public static JSON disconnect(JSON op, RuntimeManager runtimeManager) {

        String clientID = null;

        try {

            clientID = op.getString(AdapterConstants.FIELD_CLIENT);

            if (clientID == null) {

                Logger logger = Logger.getLogger("fabric.services.jsonclient.articles");
                logger.log(Level.INFO, "Disconnection message missing field [{0}]:\n{1}", new Object[] {
                        AdapterConstants.FIELD_CLIENT, op.toString()});

            } else {

                /* Clean-up anything left behind by the client */
                runtimeManager.cleanup(clientID);

            }

        } catch (Exception e) {

            Logger logger = Logger.getLogger("fabric.services.jsonclient.articles");
            logger.log(Level.WARNING, "Exception cleaning up after client [{0}]: {1}", new Object[] {clientID,
                    e.getMessage()});
            logger.log(Level.FINEST, "Full exception: ", e);

        }

        return null;
    }
}
 public class UserTypes extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /**
     * Inserts a User Type into the registry.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON register(JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String typeid = op.getString(AdapterConstants.FIELD_TYPE);

            if (typeid == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_USER_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);

                TypeFactory typeFactory = FabricRegistry.getTypeFactory();
                Type type = typeFactory.createActorType(typeid, //
                        op.getString(AdapterConstants.FIELD_DESCRIPTION), // Description
                        (attr != null) ? attr.toString() : null, // Attributes
                                null); // Attributes URI
                typeFactory.save(type);

            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_USER_TYPE, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a User Type from the registry.
     *
     * @param userTypeId
     *            The ID of the User to be deleted.
     * @return A JSON status object.
     */
    public static JSON deregister(String userTypeId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);
        if (userTypeId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_USER_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            TypeFactory typeFactory = FabricRegistry.getTypeFactory(QueryScope.LOCAL);
            Type actorType = typeFactory.getActorType(userTypeId);
            boolean complete = typeFactory.delete(actorType);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_USER_TYPE, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

    /**
     * Queries the database for user types.
     *
     * @param op
     * @param correlId
     * @return The result of the query.
     */
    public static JSON query(final JSON op, final String correlId) {

        JSON userTypesQueryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();

        try {

            TypeFactory typeFactory = FabricRegistry.getTypeFactory();

            String querySQL = generatePredicate(AdapterConstants.FIELD_TYPE, "TYPE_ID", QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_USER_TYPE, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                userTypesQueryResult = status.toJsonObject();

            } else {

                /* Lookup up the user types list in the Registry */

                Type[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = typeFactory.getAllActorTypes();
                } else {
                    resultArray = typeFactory.getActorTypes(querySQL);
                }

                /* Generate the response object */

                userTypesQueryResult.putString(AdapterConstants.FIELD_OPERATION,
                        AdapterConstants.OP_QUERY_RESPONSE_USER_TYPES);
                userTypesQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each user type... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON type = new JSON();
                    type = type.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getId());
                    addOptionalFields(type, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(type);

                }
                userTypesQueryResult = userTypesQueryResult.putArray(AdapterConstants.FIELD_USER_TYPES, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_USER_TYPE, message, correlId);
            userTypesQueryResult = status.toJsonObject();

        }

        return userTypesQueryResult;
    }
}
 public class Users extends Article {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /**
     * Inserts a User into the registry.
     *
     * @param op
     *            The full JSON operation object.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return A JSON status object.
     */
    public static JSON register(final JSON op, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);

        try {

            String id = op.getString(AdapterConstants.FIELD_ID);
            String type = op.getString(AdapterConstants.FIELD_TYPE);

            if (id == null || type == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_REGISTER,
                        AdapterConstants.ARTICLE_USER, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);

            } else {

                JSON attr = op.getJSON(AdapterConstants.FIELD_ATTRIBUTES);

                ActorFactory actorFactory = FabricRegistry.getActorFactory();
                Actor actor = actorFactory.createActor(id, //
                        type, //
                        null, // roles
                        null, // credentials
                        op.getString(AdapterConstants.FIELD_AFFIL), // Affiliation
                        op.getString(AdapterConstants.FIELD_DESCRIPTION), // Description
                        (attr != null) ? attr.toString() : null, // Attributes
                                null); // attributesURI
                boolean success = actorFactory.save(actor);

                if (!success) {

                    status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                            AdapterConstants.ARTICLE_USER, "Insert/update of platform type into the Registry failed",
                            correlId);

                }
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_REGISTER,
                    AdapterConstants.ARTICLE_USER, message, correlId);

        }

        return status.toJsonObject();
    }

    /**
     * Deletes a User from the registry.
     *
     * @param userId
     *            The ID of the User to be deleted.
     * @param correlId
     *            The correlation ID for the status message.
     * @return A JSON status object.
     */
    public static JSON deregister(String userId, String correlId) {

        AdapterStatus status = new AdapterStatus(correlId);
        if (userId == null) {
            status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_DEREGISTER,
                    AdapterConstants.ARTICLE_USER_TYPE, AdapterConstants.STATUS_MSG_FIELD_ERROR, correlId);
        } else {
            ActorFactory actorFactory = FabricRegistry.getActorFactory(QueryScope.LOCAL);
            Actor actor = actorFactory.getActorById(userId);
            boolean complete = actorFactory.delete(actor);
            if (complete == false) {
                status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_DEREGISTER,
                        AdapterConstants.ARTICLE_USER, AdapterConstants.STATUS_MSG_FAILED_DELETE, correlId);
            }
        }
        return status.toJsonObject();
    }

    /**
     * Queries the database for Users.
     *
     * @param op
     * @param correlId
     * @return The result of the query.
     */

    public static JSON query(JSON op, final String correlId) {

        JSON usersQueryResult = new JSON();
        AdapterStatus status = new AdapterStatus(correlId);
        List<JSON> jsonList = new ArrayList<JSON>();

        try {

            ActorFactory userFactory = FabricRegistry.getActorFactory();
            String querySQL = generatePredicate(AdapterConstants.FIELD_ID, "ACTOR_ID", QUERY_AFFILIATION
                    | QUERY_ATTRIBUTES, op);

            if (querySQL == null) {

                status = new AdapterStatus(AdapterConstants.ERROR_PARSE, AdapterConstants.OP_CODE_QUERY,
                        AdapterConstants.ARTICLE_USER, AdapterConstants.STATUS_MSG_BAD_SQL, correlId);
                usersQueryResult = status.toJsonObject();

            } else {

                /* Lookup the user list in the Registry */

                Actor[] resultArray = null;

                if ("".equals(querySQL)) {
                    resultArray = userFactory.getAllActors();
                } else {
                    resultArray = userFactory.getActors(querySQL);
                }

                /* Generate the response object */

                usersQueryResult.putString(AdapterConstants.FIELD_OPERATION, AdapterConstants.OP_QUERY_RESPONSE_USERS);
                usersQueryResult.putString(AdapterConstants.FIELD_CORRELATION_ID, correlId);

                /* For each user... */
                for (int i = 0; i < resultArray.length; i++) {

                    JSON user = new JSON();
                    user.putString(AdapterConstants.FIELD_ID, resultArray[i].getId());
                    user.putString(AdapterConstants.FIELD_TYPE, resultArray[i].getTypeId());
                    addOptionalFields(user, resultArray[i].getDescription(), resultArray[i].getAttributes());
                    jsonList.add(user);

                }
                usersQueryResult.putArray(AdapterConstants.FIELD_USERS, jsonList);
            }

        } catch (Exception e) {

            String message = e.getClass().getName() + ": " + e.getMessage();
            status = new AdapterStatus(AdapterConstants.ERROR_ACTION, AdapterConstants.OP_CODE_QUERY,
                    AdapterConstants.ARTICLE_USER, message, correlId);
            usersQueryResult = status.toJsonObject();

        }

        return usersQueryResult;
    }
}
 public class AdapterStatus extends AdapterConstants {

	/** Copyright notice. */
	public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

	/*
	 * Class constants
	 */

	private static final String STATUS_JSON = "{" + //
			"\"" + FIELD_OPERATION + "\":\"" + FIELD_STATUS + "\"," + // Operation
			"\"" + FIELD_CORRELATION_ID + "\":\"%s\"," + // Correlation ID
			"\"" + FIELD_STATUS + "\":\"%s\"," + // Status code
			"\"" + FIELD_MESSAGE + "\":\"%s\"" + // Status message
			"}";

	private static final String STATUS_JSON_WITH_SOURCE = "{" + //
			"\"" + FIELD_OPERATION + "\":\"" + FIELD_STATUS + "\"," + // Operation
			"\"" + FIELD_CORRELATION_ID + "\":\"%s\"," + // Correlation ID
			"\"" + FIELD_STATUS + "\":\"%s\"," + // Status code
			"\"" + FIELD_MESSAGE + "\":\"%s\"," + // Status message
			"\"" + FIELD_SOURCE_JSON + "\":\"%s\"" + // Source JSON
			"}";

	/*
	 * Class fields
	 */

	// public static final AdapterStatus STATUS_OK = new AdapterStatus();

	/**
	 * The code indicating the type of error that has occurred (one of the <code>ERROR_</code> constants in this class).
	 */
	private int errorCode;

	/** The code indicating the operation being performed (one of the <code>OP_CODE_</code> constants in this class). */
	private int opCode;

	/** The code indicating the article being referenced (one of the <code>ARTICLE_</code> constants in this class). */
	private int articleCode;

	/** Message containing additional error information. */
	private String message;

	/** The correlation ID associated with this instance. */
	private String correlationID;

	/** The string form of the object. */
	private String toString = null;

	/** The JSON form of the object. */
	private JSON toJson = null;

	/** The JSON string that caused this adapter status message to be generated. */
	private String sourceJSON = null;

	/*
	 * Class methods
	 */

	/**
	 * Constructor method for the <code>AdapterStatus</code> object. It sets the four variables such that the status is
	 * OK.
	 */
	private AdapterStatus() {

		this.errorCode = OK;
		this.opCode = OK;
		this.articleCode = OK;
		this.message = STATUS_MSG_OK;
		this.correlationID = null;
	}

	/**
	 * Constructor method for the <code>AdapterStatus</code> object.
	 * 
	 * @param statusCode
	 *            Code indicating the type of error that has occurred (one of the <code>STATUS_</code> constants in this
	 *            class).
	 * 
	 * @param opCode
	 *            Code indicating the operation being performed (one of the <code>OP_CODE_</code> constants in this
	 *            class).
	 * 
	 * @param articleCode
	 *            Code indicating the article being referenced (one of the <code>ARTICLE_</code> constants in this
	 *            class).
	 * 
	 * @param message
	 *            The message to be set.
	 */
	public AdapterStatus(int statusCode, int opCode, int articleCode, String message) {

		this.errorCode = statusCode;
		this.opCode = opCode;
		this.articleCode = articleCode;
		this.message = message;
		this.correlationID = null;
	}

	/**
	 * Constructor method for the <code>AdapterStatus</code> object.
	 * 
	 * @param statusCode
	 *            Code indicating the type of error that has occurred (one of the <code>STATUS_</code> constants in this
	 *            class).
	 * 
	 * @param opCode
	 *            Code indicating the operation being performed (one of the <code>OP_CODE_</code> constants in this
	 *            class).
	 * 
	 * @param articleCode
	 *            Code indicating the article being referenced (one of the <code>ARTICLE_</code> constants in this
	 *            class).
	 * 
	 * @param message
	 *            The message to be set.
	 * 
	 * @param correlationID
	 *            the message correlation ID.
	 */
	public AdapterStatus(int statusCode, int opCode, int articleCode, String message, String correlationID) {

		this.errorCode = statusCode;
		this.opCode = opCode;
		this.articleCode = articleCode;
		this.message = message;
		this.correlationID = correlationID;
	}

	/**
	 * Constructor method for the <code>AdapterStatus</code> object.
	 * 
	 * @param statusCode
	 *            Code indicating the type of error that has occurred (one of the <code>STATUS_</code> constants in this
	 *            class).
	 * 
	 * @param opCode
	 *            Code indicating the operation being performed (one of the <code>OP_CODE_</code> constants in this
	 *            class).
	 * 
	 * @param articleCode
	 *            Code indicating the article being referenced (one of the <code>ARTICLE_</code> constants in this
	 *            class).
	 * 
	 * @param message
	 *            The message to be set.
	 * 
	 * @param correlationID
	 *            the message correlation ID.
	 * 
	 * @param sourceJSON
	 *            the JSON string that led to this adapter status.
	 */
	public AdapterStatus(int statusCode, int opCode, int articleCode, String message, String correlationID,
			String sourceJSON) {

		this.errorCode = statusCode;
		this.opCode = opCode;
		this.articleCode = articleCode;
		this.message = message;
		this.correlationID = correlationID;
		this.sourceJSON = sourceJSON;
	}

	/**
	 * Constructor method for the <code>AdapterStatus</code> object.
	 * 
	 * @param status
	 *            Source status.
	 * 
	 * @param correlationID
	 *            the message correlation ID (overwrites the correlation ID in the source <code>AdapterStatus</code>
	 *            object).
	 */
	public AdapterStatus(String correlationID) {

		this();
		this.correlationID = correlationID;

	}

	/**
	 * Answers <code>true</code> if this is a non-error (i.e. OK) status.
	 * <p>
	 * The detail message and the correlation ID are not considered in this method.
	 * </p>
	 * 
	 * @return <code>true</code> if this is an OK state, <code>false</code> otherwise.
	 */
	public boolean isOK() {

		return (errorCode == OK) && (opCode == OK) && (articleCode == OK);
	}

	/**
	 * Constructs a JSON object representing this instance.
	 * 
	 * @return the JSON object.
	 */
	public JSON toJsonObject() {

		if (toJson == null) {
			try {
				toJson = new JSON(toString());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return toJson;
	}

	/**
	 * Formats the error code to be human readable.
	 * <p>
	 * The code is 5 characters long and consists of three parts (in hexadecimal):
	 * <ol>
	 * <li>First digit: error code.</li>
	 * <li>Second and third digits: operation code.</li>
	 * <li>Fourth and fifth digits: article code.</li>
	 * </ol>
	 * 
	 * @return The formatted error code.
	 */
	private String formatStatus() {

		String s = String.format("%01x%02x%02x", errorCode, opCode, articleCode);
		return s;
	}

	/**
	 * Getter method for the status code.
	 * 
	 * @return status code
	 */
	public int getErrorCode() {

		return errorCode;
	}

	/**
	 * Getter method for the operation code.
	 * 
	 * @return operation code
	 */
	public int getOpCode() {

		return opCode;
	}

	/**
	 * Getter method for the article code.
	 * 
	 * @return article code
	 */
	public int getArticleCode() {

		return articleCode;
	}

	/**
	 * Getter method for the correlation ID.
	 * 
	 * @return correlation ID.
	 */
	public String getCorrelationID() {

		return correlationID;
	}

	/**
	 * Getter method for the message.
	 * 
	 * @return the message.
	 */
	public String getMessage() {

		return message;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {

		if (toString == null) {

			String statusCode = formatStatus();

			JsonStringEncoder jse = JsonStringEncoder.getInstance();
			String escapedMessage = new String(jse.quoteAsString(message));

			if (sourceJSON == null) {

				toString = String.format(STATUS_JSON, correlationID, statusCode, escapedMessage);

			} else {

				String escapedSourceJSON = new String(jse.quoteAsString(sourceJSON));
				toString = String.format(STATUS_JSON_WITH_SOURCE, correlationID, statusCode, escapedMessage,
						escapedSourceJSON);

			}

		}

		return toString;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {

		boolean isEqual = false;

		if (obj != null && obj instanceof AdapterStatus) {

			AdapterStatus target = (AdapterStatus) obj;

			/* If it's not the same instance... */
			if (this != target) {

				/* If the fields are equal... */
				if (errorCode == target.errorCode
						&& opCode == target.opCode
						&& articleCode == target.articleCode
						&& ((message == null && target.message == null) || (message != null && message
								.equals(target.message)))
						&& ((correlationID == null && target.correlationID == null) || (correlationID != null && correlationID
								.equals(target.correlationID)))) {

					isEqual = true;

				}
			} else {

				isEqual = true;

			}
		}

		return isEqual;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {

		return toString().hashCode();
	}
} public class RuntimeManager extends FabricBus implements ISubscriptionCallback {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2010, 2014";

    /*
     * Class static fields
     */

    /** The descriptor for the Registry update notification service. */
    private static final TaskServiceDescriptor registryUpdatesDescriptor = new TaskServiceDescriptor("$def", "$fab",
            "$reg", "$updates");

    /** Fabric class instance used to access utility methods only. */
    private static final Fabric fabric = new Fabric();

    /*
     * Class fields
     */

    /**
     * To hold the list of active systems, including both those in the running and stopped states.
     */
    private final HashMap<SystemDescriptor, SystemRuntime> activeSystems = new HashMap<SystemDescriptor, SystemRuntime>();

    /** To hold the list of subscription requests for active systems. */
    private final HashMap<ServiceDescriptor, List<ServiceDescriptor>> systemSubscriptionRequests = new HashMap<ServiceDescriptor, List<ServiceDescriptor>>();

    /** Flag indicating if Registry queries should be local or distributed. */
    private QueryScope queryScope = QueryScope.DISTRIBUTED;

    /** The connection to the Fabric. */
    private FabricPlatform fabricClient = null;

    /** The subscription to Registry update notifications. */
    private ISubscription registryUpdates = null;

    /** Flag indicating if Registry updates should be actioned. */
    private boolean actionTopologyUpdates = true;

    /** Object used to protect access to wiring code. */
    private Object wiringLock = new Object();

    /** Object used to protect access to refresh thread control flag. */
    private Object refreshLock = new Object();

    /** Flag indicating if the refresh thread should exit. */
    private boolean doRefresh = true;

    /*
     * Inner classes
     */

    /**
     * Class implementing a periodic-refresh thread.
     */
    private class Refresh implements Runnable {

        /**
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {

            String action = "prune";
            String intervalConfig = null;
            int interval = 0;

            try {
                intervalConfig = fabric.config("fabric.runtimeManager.refreshInterval", "-1");
                interval = Integer.parseInt(intervalConfig);
            } catch (Exception e) {
                interval = 6;
                logger.log(
                        Level.WARNING,
                        "Invalid value configured for Runtime Manager refresh interval ([{0}]); using default value ({1})",
                        new Object[] {intervalConfig, interval});
            }

            logger.log(Level.INFO, "Runtime Manager refresh thread running");

            while (interval != -1) {

                try {
                    Thread.sleep(interval * 1000);
                } catch (InterruptedException e) {
                }

                synchronized (refreshLock) {

                    if (doRefresh) {

                        synchronized (wiringLock) {

                            switch (action) {

                                case "prune":

                                    logger.log(Level.FINE, "Pruning subscriptions for lost nodes");

                                    for (String n : missingRouteNodes()) {

                                        try {

                                            pruneSubscriptions(n);

                                        } catch (Exception e) {

                                            logger.log(
                                                    Level.WARNING,
                                                    "Exception cleaning up broken subscription using missing node [{0}]: {1}",
                                                    new Object[] {n, e.getMessage()});
                                            logger.log(Level.FINEST, "Full exception:", e);

                                        }
                                    }

                                    action = "match";
                                    break;

                                case "match":

                                    logger.log(Level.FINE, "Re-matching subscriptions");

                                    try {

                                        matchSubscriptions();

                                    } catch (Exception e) {

                                        logger.log(Level.WARNING, "Exception in subscription refresh: {0}", e
                                                .getMessage());
                                        logger.log(Level.FINEST, "Full exception:", e);

                                    }

                                    action = "prune";
                                    break;
                            }
                        }

                    } else {

                        break;

                    }
                }
            }
            logger.log(Level.INFO, "Runtime Manager refresh thread stopped");
        }

        /**
         * Determines the list of nodes that are used in an active subscription, but which are not currently visible (as
         * determined by a distributed node query).
         *
         * @return the list of missing nodes.
         */
        protected List<String> missingRouteNodes() {

            List<String> missingRouteNodes = new ArrayList<String>();

            if (!activeSystems.isEmpty()) {

                /* Get the list of nodes known to the Fabric */
                NodeFactory nf = FabricRegistry.getNodeFactory(QueryScope.DISTRIBUTED);
                Node[] visibleNodes = nf.getAllNodes();
                List<String> visibleNodeList = new ArrayList<String>();
                for (Node n : visibleNodes) {
                    visibleNodeList.add(n.getId());
                }

                /* For each active system... */
                for (SystemRuntime sys : activeSystems.values()) {

                    /* Get the list of feed subscriptions for this system */
                    HashMap<ServiceDescriptor, ISubscription> wif = sys.wiredInputFeeds();

                    /* For each subscription... */
                    for (ISubscription sub : wif.values()) {

                        /* For each node used in the route for this subscription... */
                        for (String routeNode : sub.route()) {

                            /* If the node is not visible and hasn't been recorded already... */
                            if (!visibleNodeList.contains(routeNode) && !missingRouteNodes.contains(routeNode)) {
                                missingRouteNodes.add(routeNode);
                            }
                        }
                    }
                }
            }
            return missingRouteNodes;
        }
    }

    /*
     * Class methods
     */

    public RuntimeManager() {

        this(Logger.getLogger("fabric.services.systems"));
    }

    public RuntimeManager(Logger logger) {

        this.logger = logger;
    }

    /**
     * Creates a new instance.
     *
     * @param fabricClient
     *            the connection to the Fabric.
     */
    public RuntimeManager(FabricPlatform fabricClient) {

        this.fabricClient = fabricClient;

        /* Determine if Registry queries should be local or distributed */
        boolean doQueryLocal = Boolean.parseBoolean(config("fabric.runtimeManager.queryLocal", "true"));
        this.queryScope = doQueryLocal ? QueryScope.LOCAL : QueryScope.DISTRIBUTED;

    }

    /**
     * Initialises this instance.
     */
    public void init() {

        try {

            /* Subscribe to Registry update notifications */
            registryUpdates = new Subscription(fabricClient);
            registryUpdates.subscribe(registryUpdatesDescriptor, this);

            /* Determine if topology updates should be actioned */
            String config = null;
            try {
                config = fabric.config("fabric.runtimeManager.actionTopologyUpdates", "true");
                actionTopologyUpdates = Boolean.parseBoolean(config);
            } catch (Exception e) {
                actionTopologyUpdates = true;
                logger.log(
                        Level.WARNING,
                        "Invalid value configured for Runtime Manager Registry update monitor ([{0}]); using default value ({1})",
                        new Object[] {config, actionTopologyUpdates});
            }

        } catch (Exception e) {

            logger.log(Level.WARNING,
                    "Cannot subscribe to service [{0}]; active subscription functions will not be available: {1}",
                    new Object[] {registryUpdatesDescriptor, e.getMessage()});
            logger.log(Level.FINEST, "Full exception: ", e);

        }

        /* Start periodic subscription refreshes */
        (new Thread(new Refresh(), "Runtime-Manager-Refresh")).start();
    }

    /**
     * Stops this instance.
     */
    public void stop() {

        try {

            /* Stop periodic subscription refreshes */
            synchronized (refreshLock) {
                doRefresh = false;
            }

            /* Unsubscribe from Registry update notifications */
            registryUpdates.unsubscribe();

        } catch (Exception e) {

            logger.log(Level.WARNING, "Cannot unsubscribe from feed [{0}]: {1}", new Object[] {
                    registryUpdatesDescriptor, e.getMessage()});
            logger.log(Level.FINEST, "Full exception: ", e);

        }
    }

    /**
     * Starts a system.
     *
     * @param systemDescriptor
     *            the system to start.
     *
     * @param client
     *            adapter-specific identifier for the client making the request.
     *
     * @param adapterProxy
     *            the name of the class implementing the system adapter proxy for the JSON Fabric client.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus start(SystemDescriptor systemDescriptor, Object client, String adapterProxy) {

        RuntimeStatus status = null;

        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is already a running service instance with this name... */
        if (systemRuntime != null && systemRuntime.isRunning()) {

            String message = format("System [%s]: already running", systemDescriptor);
            logger.log(Level.INFO, message);
            status = new RuntimeStatus(RuntimeStatus.Status.ALREADY_RUNNING, message);

        } else {

            /* Look up the system in the Registry */
            SystemFactory systemFactory = FabricRegistry.getSystemFactory(QueryScope.LOCAL);
            System system = systemFactory.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());

            /* If no valid system was found... */
            if (system == null || system.getTypeId() == null) {

                String message = format("System [%s] not found", systemDescriptor.toString());
                logger.log(Level.WARNING, message);
                status = new RuntimeStatus(RuntimeStatus.Status.NOT_FOUND, message);

            } else {

                try {

                    if (systemRuntime == null) {

                        /* Create it */
                        systemRuntime = new SystemRuntime(systemDescriptor, client, adapterProxy, this);

                    }

                    /* Instantiate the service */
                    systemRuntime.instantiate();

                    /* Initialize the new service instance */
                    systemRuntime.start();

                } catch (Exception e) {

                    String message = format("Failed to start system [%s]: %s", systemDescriptor.toString(), e
                            .getMessage());
                    logger.log(Level.SEVERE, message);
                    logger.log(Level.FINEST, "Full exception: ", e);
                    status = new RuntimeStatus(RuntimeStatus.Status.START_FAILED, message);

                }
            }
        }

        if (status == null) {
            /* The system was started OK */
            activeSystems.put(systemDescriptor, systemRuntime);
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;
    }

    /**
     * Stops a running instance of this service.
     *
     * @param systemDescriptor
     *            the service to stop.
     *
     * @param response
     *            the result of the operation.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus stop(SystemDescriptor systemDescriptor) {

        RuntimeStatus status = null;

        /* Get the service instance */
        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is not a running service instance with this name... */
        if (systemRuntime == null || !systemRuntime.isRunning()) {

            /* Make sure that this is reflected in the Registry */
            updateAvailability(systemDescriptor, "UNAVAILABLE");

            String message = format("System not running: %s", systemDescriptor);
            logger.log(Level.WARNING, message);
            status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

        } else {

            /* Attempt to stop the instance */
            if (systemRuntime.stop()) {

                /* Remove it from the manager */
                activeSystems.remove(systemDescriptor);

            } else {

                status = new RuntimeStatus(RuntimeStatus.Status.STILL_RUNNING,
                        "Cannot stop systems with autostart enabled");

            }
        }

        if (status == null) {
            /* The system was stopped OK */
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;

    }

    /**
     * Update the availability status of the service in the Registry.
     *
     * @param systemDescriptor
     *            the service for which status is being changed.
     *
     * @param availability
     *            the new availability status.
     */
    private void updateAvailability(SystemDescriptor systemDescriptor, String availability) {

        /* Lookup the service record in the Registry */
        SystemFactory systemFactory = FabricRegistry.getSystemFactory(queryScope);
        System system = systemFactory.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());

        try {

            if (system != null) {
                /* Update the availability and commit */
                system.setAvailability(availability);
                systemFactory.update(system);
            }

        } catch (Exception e) {

            logger.log(Level.SEVERE, "Cannot set availability to [{0}] on service [{1}/{2}]: {3}", new Object[] {
                    system.getAvailability(), system.getPlatformId(), system.getId(), e.getMessage()});
            logger.log(Level.FINEST, "Full exception: ", e);

        }
    }

    /**
     * Sends a request to a request-response service.
     *
     * @param requestResponseService
     *            the target for the request.
     *
     * @param solicitResponseService
     *            to reply-to service.
     *
     * @param msg
     *            the message payload.
     *
     * @param encoding
     *            the payload encoding.
     *
     * @param correlId
     *            the correlation ID for the request.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus request(ServiceDescriptor requestResponseService,
            ServiceDescriptor solicitResponseService, String msg, String encoding, String correlId) {

        RuntimeStatus status = null;

        SystemDescriptor systemDescriptor = solicitResponseService.toSystemDescriptor();
        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is a running service instance... */
        if (systemRuntime != null && systemRuntime.isRunning()) {

            try {

                /* Send the request message */
                systemRuntime.request(correlId, requestResponseService, solicitResponseService, msg.getBytes(),
                        encoding);

            } catch (Exception e) {

                String message = format("Error sending request message to request/response service [%s]:\n%s",
                        requestResponseService, e.getMessage());
                logger.log(Level.WARNING, message);
                logger.log(Level.FINEST, "Full exception: ", e);
                status = new RuntimeStatus(RuntimeStatus.Status.SEND_REQUEST_FAILED, message);

            }

        } else {

            String message = format("System is not running: %s", systemDescriptor);
            logger.log(Level.WARNING, message);
            status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

        }

        if (status == null) {
            /* The system was started OK */
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;
    }

    /**
     * Sends a response to a solicit-response service.
     *
     * @param sendTo
     *            the target for the request.
     *
     * @param producer
     *            to producer of the result.
     *
     * @param msg
     *            the message payload.
     *
     * @param encoding
     *            the payload encoding.
     *
     * @param correlId
     *            the correlation ID for the request.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus response(ServiceDescriptor sendTo, ServiceDescriptor producer, String msg,
            String encoding, String correlId) {

        RuntimeStatus status = null;

        SystemDescriptor systemDescriptor = producer.toSystemDescriptor();
        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is a running service instance... */
        if (systemRuntime != null && systemRuntime.isRunning()) {

            try {

                /* Send the response message */
                systemRuntime.respond(correlId, sendTo, producer, msg.getBytes());

            } catch (Exception e) {

                String message = format("Error sending response message to requesting service [%s]:\n%s", sendTo, e
                        .getMessage());
                logger.log(Level.WARNING, message);
                logger.log(Level.FINEST, "Full exception: ", e);
                status = new RuntimeStatus(RuntimeStatus.Status.SEND_REQUEST_FAILED, message);

            }

        } else {

            String message = format("System is not running: %s", (SystemDescriptor) producer);
            logger.log(Level.WARNING, message);
            status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

        }

        if (status == null) {
            /* The system was started OK */
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;
    }

    /**
     * Answers the Fabric client associated with this instance.
     *
     * @return the Fabric client instance.
     */
    public FabricPlatform fabricClient() {

        return fabricClient;

    }

    /**
     * Sends a notification to a listener service.
     *
     * @param listenerService
     *            the target for the notification.
     *
     * @param notificationService
     *            the notifying service.
     *
     * @param msg
     *            the message payload.
     *
     * @param encoding
     *            the payload encoding.
     *
     * @param correlId
     *            the correlation ID for the request.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus notify(ServiceDescriptor listenerService, ServiceDescriptor notificationService,
            String msg, String encoding, String correlId) {

        RuntimeStatus status = null;

        SystemDescriptor systemDescriptor = notificationService.toSystemDescriptor();
        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is a running service instance... */
        if (systemRuntime != null && systemRuntime.isRunning()) {

            try {

                /* Send the request message */
                systemRuntime.notify(listenerService, notificationService, msg.getBytes(), encoding, correlId);

            } catch (Exception e) {

                String message = format("Error sending notification message to listener service [%s]:\n%s",
                        listenerService, e.getMessage());
                logger.log(Level.WARNING, message);
                logger.log(Level.FINEST, "Full exception: ", e);
                status = new RuntimeStatus(RuntimeStatus.Status.SEND_NOTIFICATION_FAILED, message);

            }

        } else {

            String message = format("System is not running: %s", systemDescriptor);
            logger.log(Level.WARNING, message);
            status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

        }

        if (status == null) {
            /* The system was started OK */
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;
    }

    /**
     * Publishes an output feed message.
     *
     * @param outputFeedService
     *            the publishing service.
     *
     * @param msg
     *            the message payload.
     *
     * @param encoding
     *            the payload encoding.
     *
     * @return the status.
     */
    public synchronized RuntimeStatus publish(ServiceDescriptor outputFeedService, String msg, String encoding) {

        RuntimeStatus status = null;

        SystemDescriptor systemDescriptor = outputFeedService.toSystemDescriptor();
        SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

        /* If there is a running service instance... */
        if (systemRuntime != null && systemRuntime.isRunning()) {

            try {

                /* Publish the message */
                systemRuntime.publish(outputFeedService, msg.getBytes(), encoding);

            } catch (Exception e) {

                String message = format("Error publishing message to output-feed service [%s]:\n%s", outputFeedService,
                        e.getMessage());
                logger.log(Level.WARNING, message);
                logger.log(Level.FINEST, "Full exception: ", e);
                status = new RuntimeStatus(RuntimeStatus.Status.PUBLISH_FAILED, message);

            }

        } else {

            String message = format("System is not running: %s", systemDescriptor);
            logger.log(Level.WARNING, message);
            status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

        }

        if (status == null) {
            /* The system was started OK */
            status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
        }

        return status;
    }

    /**
     * Subscribes to a list of output feeds.
     *
     * @param outputFeedPatterns
     *            the list of output feeds (which may include wildcards) to which subscriptions are to be made.
     *
     * @param inputFeed
     *            the local service to which feed messages will be delivered.
     *
     * @param subscribedList
     *            to hold the list of actual subscriptions made.
     */
    public RuntimeStatus subscribe(ServiceDescriptor[] outputFeedPatterns, ServiceDescriptor inputFeed,
            List<ServiceDescriptor> subscribedList) throws Exception {

        RuntimeStatus subscribeStatus = RuntimeStatus.STATUS_OK;

        synchronized (wiringLock) {

            /* For each output feed pattern... */
            for (int sf = 0; sf < outputFeedPatterns.length; sf++) {

                /* Record this subscription request */
                @SuppressWarnings("unchecked")
                List<ServiceDescriptor> subscriptionList = fabric.lookupSublist(inputFeed, systemSubscriptionRequests);
                if (!subscriptionList.contains(outputFeedPatterns[sf])) {
                    subscriptionList.add(outputFeedPatterns[sf]);
                }

                /* Get the list of feeds that match the requested feed pattern (it may contain wildcards) */
                ServiceDescriptor[] feedsMatchingPattern = queryMatchingFeeds(outputFeedPatterns[sf]);

                /* While there are more feeds... */
                for (int f = 0; f < feedsMatchingPattern.length; f++) {

                    /* Subscribe to the feed */
                    subscribeStatus = subscribe(feedsMatchingPattern[f], inputFeed, outputFeedPatterns[sf]);

                    if (subscribeStatus.isOK()) {
                        subscribedList.add(feedsMatchingPattern[f]);
                    }
                }
            }
        }

        return subscribeStatus;
    }

    /**
     * Find the list of feeds in the Registry matching the specified pattern.
     *
     * @param feedPattern
     *            the pattern to match.
     *
     * @return the list of matching feeds.
     *
     * @throws RegistryQueryException
     */
    private static ServiceDescriptor[] queryMatchingFeeds(ServiceDescriptor feedPattern) throws RegistryQueryException {

        /* To hold the results */
        ServiceDescriptor[] matchingFeeds = null;

        /* To hold the predicate required to find matching feeds in the Registry */
        String queryPredicate = null;

        /* If the feed descriptor does not contain any wildcards... */
        if (!feedPattern.toString().contains("*")) {

            matchingFeeds = new ServiceDescriptor[] {feedPattern};

        } else {

            /* Generate the SQL predicate required to identify the matching feeds */

            String platform = feedPattern.platform();
            String platformPredicate = null;

            if (platform.contains("*")) {
                platformPredicate = String.format("platform_id like '%s'", platform.replace('*', '%'));
            } else {
                platformPredicate = String.format("platform_id = '%s'", platform);
            }

            String service = feedPattern.system();
            String servicePredicate = null;

            if (service.contains("*")) {
                servicePredicate = String.format("service_id like '%s'", service.replace('*', '%'));
            } else {
                servicePredicate = String.format("service_id = '%s'", service);
            }

            String feed = feedPattern.service();
            String feedPredicate = null;

            if (feed.contains("*")) {
                feedPredicate = String.format("id like '%s'", feed.replace('*', '%'));
            } else {
                feedPredicate = String.format("id = '%s'", feed);
            }

            queryPredicate = String.format("direction = 'output' and %s and %s and %s", platformPredicate,
                    servicePredicate, feedPredicate);

            /* Generate the list of matching feeds */
            ServiceFactory sf = FabricRegistry.getServiceFactory();
            Service[] registryFeedList = sf.getServices(queryPredicate);
            matchingFeeds = new ServiceDescriptor[registryFeedList.length];

            /* For each matching feed... */
            for (int f = 0; f < matchingFeeds.length; f++) {

                /* Create a feed descriptor */
                matchingFeeds[f] = new ServiceDescriptor(registryFeedList[f].getPlatformId(), registryFeedList[f]
                        .getSystemId(), registryFeedList[f].getId());

            }
        }

        return matchingFeeds;
    }

    /**
     * Subscribes to an output feed.
     *
     * @param outputFeedService
     *            the publishing service.
     *
     * @param inputFeedService
     *            the local service to which feed messages will be delivered.
     *
     * @param pattern
     *            the original subscription pattern.
     *
     * @return the status.
     */
    public RuntimeStatus subscribe(ServiceDescriptor outputFeedService, ServiceDescriptor inputFeedService,
            ServiceDescriptor pattern) {

        RuntimeStatus status = null;

        synchronized (wiringLock) {

            SystemDescriptor systemDescriptor = inputFeedService.toSystemDescriptor();
            SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

            /* If there is a running system instance... */
            if (systemRuntime != null && systemRuntime.isRunning()) {

                try {

                    /* Subscribe */
                    boolean isNewSubscription = systemRuntime.subscribe(outputFeedService, inputFeedService);

                    if (!isNewSubscription) {

                        String message = format("Already subscribed to output-feed service [%s] (input-feed [%s])",
                                outputFeedService, inputFeedService);
                        logger.log(Level.FINEST, message);
                        status = new RuntimeStatus(RuntimeStatus.Status.ALREADY_SUBSCRIBED, message);

                    } else {

                        recordSystemSubscription(systemDescriptor, pattern, inputFeedService);

                    }

                } catch (Exception e) {

                    String message = format("Error subscribing to output-feed service [%s] (input-feed [%s]): %s",
                            outputFeedService, inputFeedService, e.getMessage());
                    logger.log(Level.WARNING, message);
                    logger.log(Level.FINEST, "Full exception: ", e);
                    status = new RuntimeStatus(RuntimeStatus.Status.SUBSCRIBE_FAILED, message);

                }

            } else {

                String message = format("System is not running: %s", systemDescriptor);
                logger.log(Level.WARNING, message);
                status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

            }

            if (status == null) {
                status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
            }
        }

        return status;
    }

    /**
     * Records in the Registry an active subscription pattern/input-feed pair against a system.
     *
     * @param systemDescriptor
     *
     * @param pattern
     *
     * @param inputFeed
     *
     * @throws Exception
     */
    protected void recordSystemSubscription(SystemDescriptor systemDescriptor, ServiceDescriptor pattern,
            ServiceDescriptor inputFeed) throws Exception {

        String patternAndMatch = inputFeed.toString() + '=' + pattern.toString();
        boolean doAddSubscription = true;

        /* Lookup the system in the Registry */
        SystemFactory sf = FabricRegistry.getSystemFactory();
        System system = sf.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());

        /* Get the system's attributes */
        String attrString = system.getAttributes();
        JSON attr = new JSON((attrString != null) ? attrString : "{}");

        /* Get the list of active subscription patterns */
        JSONArray subscriptions = attr.getJSONArray("subscriptions");
        subscriptions = (subscriptions != null) ? subscriptions : new JSONArray();

        /* For each pattern... */
        for (int p = 0; doAddSubscription && p < subscriptions.size(); p++) {
            String next = subscriptions.getString(p);
            if (patternAndMatch.equals(next)) {
                doAddSubscription = false;
            }
        }

        if (doAddSubscription) {
            /* Add the new pattern and save */
            subscriptions.add(patternAndMatch);
            attr.putJSONArray("subscriptions", subscriptions);
            system.setAttributes(attr.toString());
            sf.save(system);
        }
    }

    /**
     * Unsubscribes from one or more output feeds.
     *
     * @param outputFeedServices
     *            the list of output feed services. If <code>null</code> then all feeds associated with the specified
     *            input feed service are unsubscribed.
     *
     * @param inputFeedService
     *            the local service to which feed messages are being delivered.
     *
     * @return the status.
     */
    public RuntimeStatus unsubscribe(ServiceDescriptor[] outputFeedServices, ServiceDescriptor inputFeedService) {

        RuntimeStatus status = null;

        synchronized (wiringLock) {

            SystemDescriptor systemDescriptor = inputFeedService.toSystemDescriptor();
            SystemRuntime systemRuntime = activeSystems.get(systemDescriptor);

            /* If there is a running system instance... */
            if (systemRuntime != null && systemRuntime.isRunning()) {

                try {

                    /* Get the list of subscription requests for this input feed */
                    @SuppressWarnings("unchecked")
                    List<ServiceDescriptor> subscriptionList = fabric.lookupSublist(inputFeedService,
                            systemSubscriptionRequests);

                    /* If a list of output feeds was provided... */
                    if (outputFeedServices != null && outputFeedServices.length > 0) {

                        /* Forget this subscription request */

                        for (ServiceDescriptor nextDescriptor : outputFeedServices) {
                            subscriptionList.remove(nextDescriptor);
                            removeSystemSubscription(systemDescriptor, nextDescriptor, inputFeedService);
                        }

                    } else {

                        /* Forget all subscriptions */
                        subscriptionList.clear();
                        removeAllSystemSubscription(systemDescriptor);

                    }

                    /* Unsubscribe */

                    systemRuntime.unsubscribe(outputFeedServices, inputFeedService);

                } catch (Exception e) {

                    String message = format("Error unsubscribing from output-feed services (input-feed [%s]):\n%s",
                            inputFeedService, e.getMessage());
                    logger.log(Level.WARNING, message);
                    logger.log(Level.FINEST, "Full exception: ", e);
                    status = new RuntimeStatus(RuntimeStatus.Status.UNSUBSCRIBE_FAILED, message);

                }

            } else {

                String message = format("System is not running: %s", systemDescriptor);
                logger.log(Level.WARNING, message);
                status = new RuntimeStatus(RuntimeStatus.Status.NOT_RUNNING, message);

            }

            if (status == null) {
                /* The unsubscribe completed OK */
                status = new RuntimeStatus(RuntimeStatus.Status.OK, RuntimeStatus.MESSAGE_OK);
            }
        }

        return status;
    }

    /**
     * Removes from the Registry an active subscription pattern/input-feed pair recorded against a system.
     *
     * @param pattern
     *
     * @param systemDescriptor
     *
     * @throws Exception
     */
    protected void removeSystemSubscription(SystemDescriptor systemDescriptor, ServiceDescriptor pattern,
            ServiceDescriptor inputFeed) throws Exception {

        String patternAndMatch = pattern.toString() + '=' + inputFeed.toString();

        /* Lookup the system in the Registry */
        SystemFactory sf = FabricRegistry.getSystemFactory();
        System system = sf.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());

        /* Get the system's attributes */
        String attrString = system.getAttributes();
        JSON attr = new JSON((attrString != null) ? attrString : "{}");

        /* Get the list of active subscription patterns */
        JSONArray subscriptions = attr.getJSONArray("subscriptions");

        JSONArray newSubscriptions = new JSONArray();

        /* For each subscription... */
        for (int p = 0; p < subscriptions.size(); p++) {
            String next = subscriptions.getString(p);
            if (!patternAndMatch.equals(next)) {
                newSubscriptions.add(next);
            }
        }

        if (newSubscriptions.size() != subscriptions.size()) {
            /* Add the new pattern and save */
            attr.putJSONArray("subscriptions", newSubscriptions);
            system.setAttributes(attr.toString());
            sf.save(system);
        }
    }

    /**
     * Removes from the Registry an active subscription pattern/input-feed pair recorded against a system.
     *
     * @param pattern
     *
     * @param systemDescriptor
     *
     * @throws Exception
     */
    protected void removeAllSystemSubscription(SystemDescriptor systemDescriptor) throws Exception {

        /* Lookup the system in the Registry */
        SystemFactory sf = FabricRegistry.getSystemFactory();
        System system = sf.getSystemsById(systemDescriptor.platform(), systemDescriptor.system());

        /* Remove any subscriptions from the system's attributes */
        String attrString = system.getAttributes();
        JSON attr = new JSON((attrString != null) ? attrString : "{}");
        attr.putJSONArray("subscriptions", new JSONArray());

        /* Update the system in the Registry */
        system.setAttributes(attr.toString());
        sf.save(system);

    }

    /**
     * Actions a notification of a new system on the bus, attempting to fulfill any outstanding subscriptions that might
     * now be satisfied.
     *
     * @param systemNotification
     *            a JSON message containing details of the new system.
     *
     * @throws Exception
     */
    private void matchSubscriptions(JSON systemNotification) throws Exception {

        /* Get the list of new services */

        JSONArray servicesJSON = systemNotification.getJSONArray("services");
        List<ServiceDescriptor> newServices = new ArrayList<ServiceDescriptor>();

        for (JSON serviceJSON : servicesJSON) {

            /* Build a descriptor for the next new service */
            ServiceDescriptor nextService = new ServiceDescriptor(serviceJSON.getString("id"));
            newServices.add(nextService);

        }

        /* For each input feed that has subscription requests associated with it... */
        for (ServiceDescriptor nextInputFeed : systemSubscriptionRequests.keySet()) {

            /* To hold the list of new subscriptions that we make (if any) */
            List<ServiceDescriptor> newSubscriptions = new ArrayList<ServiceDescriptor>();

            /* Get the current list of subscription requests for the input feed */
            List<ServiceDescriptor> subscriptionRequests = fabric.lookupSublist(nextInputFeed,
                    systemSubscriptionRequests);

            /* For each subscription request... */
            for (ServiceDescriptor nextRequest : subscriptionRequests) {

                /* Get the set of new services that match the request */
                List<ServiceDescriptor> matchingServices = matchDescriptors(nextRequest, newServices);

                /* For each matching service... */
                for (ServiceDescriptor matchingService : matchingServices) {

                    /* Subscribe */
                    RuntimeStatus subscribeStatus = subscribe(matchingService, nextInputFeed, nextRequest);

                    if (subscribeStatus.isOK()) {
                        newSubscriptions.add(matchingService);
                    }
                }
            }

            /* If any new subscriptions were made... */
            if (newSubscriptions.size() > 0) {

                /* Get the system instance that owns the input feed */
                SystemRuntime systemRuntime = activeSystems.get(nextInputFeed.toSystemDescriptor());

                /* Build the response */
                JSON response = JSONAdapter.buildSubscriptionResponse(newSubscriptions, nextInputFeed, null);

                /* Send to the client */
                systemRuntime.system().sendToClient(response.toString());

            }
        }
    }

    /**
     * Actions a notification of a lost system on the bus, cleaning up any existing subscriptions that are now broken.
     *
     * @param systemNotification
     *            a JSON message containing details of the new system.
     *
     * @throws Exception
     */
    private void pruneSubscriptions(JSON systemNotification) throws Exception {

        /* Get the list of services that have been lost */

        JSONArray servicesJSON = systemNotification.getJSONArray("services");
        ServiceDescriptor[] lostServices = new ServiceDescriptor[servicesJSON.size()];
        int s = 0;

        for (JSON serviceJSON : servicesJSON) {
            /* Build a descriptor for the next new service */
            lostServices[s++] = new ServiceDescriptor(serviceJSON.getString("id"));
        }

        synchronized (wiringLock) {

            /* For each active system... */
            for (SystemRuntime system : activeSystems.values()) {

                HashMap<ServiceDescriptor, List<ServiceDescriptor>> pruned = null;

                /* Prune subscriptions */
                pruned = system.unsubscribe(lostServices);

                /* If any subscriptions were pruned... */
                if (pruned.size() > 0) {

                    /* For each input feed... */
                    for (ServiceDescriptor inputFeed : pruned.keySet()) {

                        /* Build the response */
                        JSON response = JSONAdapter.buildLostSubscriptionMessage(pruned.get(inputFeed), inputFeed);

                        /* Send to the client */
                        system.system().sendToClient(response.toString());
                    }
                }
            }
        }
    }

    /**
     * Actions a notification of a new node on the bus, attempting to fulfill any outstanding subscriptions that might
     * now be satisfied.
     *
     * @throws Exception
     */
    public void matchSubscriptions() throws Exception {

        /* For each input feed that has subscription requests associated with it... */
        for (ServiceDescriptor nextInputFeed : systemSubscriptionRequests.keySet()) {

            /* To hold the list of new subscriptions that we make */
            List<ServiceDescriptor> subscribedList = new ArrayList<ServiceDescriptor>();

            /* Get the current list of subscription requests for the input feed */
            List<ServiceDescriptor> patternList = fabric.lookupSublist(nextInputFeed, systemSubscriptionRequests);

            if (patternList != null && patternList.size() > 0) {

                /* Attempt to subscribe */
                ServiceDescriptor[] patternArray = patternList.toArray(new ServiceDescriptor[0]);
                subscribe(patternArray, nextInputFeed, subscribedList);

                /* If any new subscriptions were made... */
                if (subscribedList.size() > 0) {

                    /* Get the system instance that owns the input feed */
                    SystemRuntime systemRuntime = activeSystems.get(nextInputFeed.toSystemDescriptor());

                    /* Build the response */
                    JSON response = JSONAdapter.buildSubscriptionResponse(subscribedList, nextInputFeed, null);

                    /* Send to the client */
                    systemRuntime.system().sendToClient(response.toString());

                }
            }
        }
    }

    /**
     * Actions a notification of a lost node on the bus, cleaning up any existing subscriptions that are now broken.
     *
     * @param node
     *            the ID of the lost node.
     *
     * @throws Exception
     */
    public void pruneSubscriptions(String node) throws Exception {

        synchronized (wiringLock) {

            /* For each active system... */
            for (SystemRuntime system : activeSystems.values()) {

                HashMap<ServiceDescriptor, List<ServiceDescriptor>> pruned = null;

                /* Prune subscriptions */
                pruned = system.unsubscribe(node);

                /* If any subscriptions were pruned... */
                if (pruned.size() > 0) {

                    /* For each input feed... */
                    for (ServiceDescriptor inputFeed : pruned.keySet()) {

                        /* Build the response */
                        JSON response = JSONAdapter.buildLostSubscriptionMessage(pruned.get(inputFeed), inputFeed);

                        /* Send to the client */
                        system.system().sendToClient(response.toString());
                    }
                }
            }
        }
    }

    /**
     * Restores the subscriptions recorded against the specified system.
     *
     * @param desc
     *            the system descriptor.
     */
    protected void restoreSystemSubscriptions(SystemDescriptor desc) {

        /* Get the system's active subscriptions */
        SystemFactory sf = FabricRegistry.getSystemFactory(QueryScope.LOCAL);
        System system = sf.getSystemsById(desc.platform(), desc.system());
        String attrString = system.getAttributes();
        JSON attr = null;
        try {
            attr = new JSON((attrString != null) ? attrString : "{}");
        } catch (IOException e) {
            attr = new JSON();
        }

        /* Get the client ID for the system */
        String clientID = attr.getString("clientID");

        JSONArray subscriptions = attr.getJSONArray("subscriptions");

        /* For each subscription... */
        for (int sub = 0; subscriptions != null && sub < subscriptions.size(); sub++) {

            String patternAndMatch = subscriptions.getString(sub);
            String[] parts = patternAndMatch.split("=");
            ServiceDescriptor inputFeed = new ServiceDescriptor(parts[0]);
            ServiceDescriptor pattern = new ServiceDescriptor(parts[1]);

            /* Attempt to subscribe */

            logger.log(Level.INFO, "System [{0}]: resubscribing to [{1}] (wired to [{2}])", new Object[] {desc,
                    pattern, inputFeed});

            ServiceDescriptor[] patternArray = new ServiceDescriptor[] {pattern};
            List<ServiceDescriptor> subscribedList = new ArrayList<ServiceDescriptor>();
            try {
                subscribe(patternArray, inputFeed, subscribedList);
            } catch (Exception e) {
                logger.log(Level.WARNING, "Cannot restore service [{0}] subscription [{1}], input feed [{2}]",
                        new Object[] {desc, pattern, inputFeed});
            }

            /* If any new subscriptions were made... */
            if (subscribedList.size() > 0) {

                /* Get the system instance that owns the input feed */
                SystemRuntime systemRuntime = activeSystems.get(inputFeed.toSystemDescriptor());

                /* Build the response */
                JSON response = JSONAdapter.buildSubscriptionResponse(subscribedList, inputFeed, null);

                /* Send to the client */
                systemRuntime.system().sendToClient(response.toString());

            }
        }
    }

    /**
     * Searches a list of service descriptors for those matching the specified pattern and returns the matching subset.
     *
     * @param descriptorPattern
     *            the pattern to match.
     *
     * @param descriptorList
     *            the list of descriptors to check.
     *
     * @return the list of matching descriptors.
     *
     * @throws RegistryQueryException
     */
    private List<ServiceDescriptor> matchDescriptors(ServiceDescriptor descriptorPattern,
            List<ServiceDescriptor> descriptorList) {

        /* To hold the results */
        List<ServiceDescriptor> matchingDescriptors = new ArrayList<ServiceDescriptor>();

        /* Convert the pattern to its equivalent regular expression */
        String descriptorPatternString = descriptorPattern.toString().replace("*", ".*");

        /* For each descriptor... */
        for (ServiceDescriptor nextDescriptor : descriptorList) {

            /* If we have a match... */
            if (nextDescriptor.toString().matches(descriptorPatternString)) {
                matchingDescriptors.add(nextDescriptor);
            }
        }

        return matchingDescriptors;
    }

    /**
     * Cleans up any running systems associated with the specified client.
     *
     * @param client
     *            adapter-specific identifier for the client making the request.
     *
     * @return the status.
     */
    public synchronized void cleanup(Object client) {

        HashMap<SystemDescriptor, SystemRuntime> activeSystemsCopy = (HashMap<SystemDescriptor, SystemRuntime>) activeSystems
                .clone();

        /* For each active system... */
        for (SystemDescriptor nextSystem : activeSystemsCopy.keySet()) {

            /* Get the record for the next system */
            SystemRuntime nextRuntime = activeSystemsCopy.get(nextSystem);
            Object nextClient = nextRuntime.getClient();

            /* If the record is associated with the specified client... */
            if (nextClient != null && nextClient.equals(client)) {

                /* Stop the system */
                stop(nextSystem);

            }
        }
    }

    /**
     * @see fabric.bus.feeds.ISubscriptionCallback#startSubscriptionCallback()
     */
    @Override
    public void startSubscriptionCallback() {

        FLog.enter(logger, Level.FINER, this, "startSubscriptionCallback", (Object[]) null);
        FLog.exit(logger, Level.FINER, this, "startSubscriptionCallback", null);
    }

    /**
     * @see fabric.bus.feeds.ISubscriptionCallback#handleSubscriptionMessage(fabric.bus.messages.IFeedMessage)
     */
    @Override
    public synchronized void handleSubscriptionMessage(IFeedMessage message) {

        FLog.enter(logger, Level.FINER, this, "handleSubscriptionMessage", message);

        /* Get the target service of this message */
        String serviceDescriptor = message.getProperty(IServiceMessage.PROPERTY_DELIVER_TO_SERVICE);

        if (serviceDescriptor != null) {

            String payload = null;

            try {

                /* Get the message payload */
                byte[] payloadBytes = message.getPayload().getPayload();
                payload = (payloadBytes != null) ? new String(payloadBytes) : null;

                if (serviceDescriptor.equals("$fab/$reg/$updates")) {

                    /* The payload is a JSON structure containing details of a Registry update */
                    JSON triggerJSON = new JSON(payload);
                    String table = triggerJSON.getString("table");
                    String availability = triggerJSON.getString("availability");

                    if (table != null) {

                        String action = triggerJSON.getString("action");

                        if (action != null) {

                            if (availability != null) {

                                table = table.toUpperCase();
                                action = action.toUpperCase();
                                availability = availability.toUpperCase();

                                switch (table) {

                                    case "SYSTEMS":

                                        if (actionTopologyUpdates) {
                                            switch (action) {
                                                case "INSERT":
                                                case "UPDATE":
                                                    if (availability.equals("AVAILABLE")) {
                                                        SystemDescriptor desc = getSystemDescriptor(triggerJSON);
                                                        if (isLocalRunningSystem(desc)) {
                                                            restoreSystemSubscriptions(desc);
                                                        } else {
                                                            matchSubscriptions(triggerJSON);
                                                        }
                                                    }
                                                    break;
                                                case "DELETE":
                                                    pruneSubscriptions(triggerJSON);
                                                    break;
                                            }
                                        }
                                        break;

                                    case "NODE_NEIGHBOURS":

                                        if (actionTopologyUpdates) {
                                            switch (action) {
                                                case "INSERT":
                                                case "UPDATE":
                                                    if (availability.equals("AVAILABLE")) {
                                                        matchSubscriptions();
                                                    }
                                                    break;
                                                case "DELETE":
                                                    String id = triggerJSON.getString("id");
                                                    String[] nodes = id.split("/");
                                                    pruneSubscriptions(nodes[1]);
                                                    // updateRouteCache(triggerJSON);
                                                    break;
                                            }
                                        }
                                        break;
                                }
                            } else {
                                logger.log(Level.WARNING,
                                        "Unrecognised 'availability' value in registry update message: \n{0}",
                                        triggerJSON.toString());
                            }
                        } else {
                            logger.log(Level.WARNING, "Unrecognised 'action' value in registry update message: \n{0}",
                                    triggerJSON.toString());
                        }
                    } else {
                        logger.log(Level.WARNING, "Unrecognised 'table' value in registry update message: \n{0}",
                                triggerJSON.toString());
                    }
                }

            } catch (Exception e) {

                logger.log(Level.WARNING, "Error handling Registry update notification: {0}", e.getMessage());
                logger.log(Level.FINEST, "Full exception: ", e);
                logger.log(Level.FINEST, "Payload:\n{0}", payload);

            }
        }

        FLog.exit(logger, Level.FINER, this, "handleSubscriptionMessage", null);
    }

    /**
     * Answers the system ID from a JSON Registry trigger.
     *
     * @param triggerJSON
     * @return
     */
    private SystemDescriptor getSystemDescriptor(JSON triggerJSON) {

        /* Get the system descriptor from the JSON */
        String id = triggerJSON.getString("id");
        String[] idParts = id.split(":");
        SystemDescriptor systemDescriptor = new SystemDescriptor(idParts[0]);

        return systemDescriptor;
    }

    /**
     * Determines if the system specified is a locally running one.
     *
     * @param desc
     *            the system descriptor.
     *
     * @return <code>true</code> if the system is running locally, <code>false</code> otherwise.
     */
    private boolean isLocalRunningSystem(SystemDescriptor desc) {

        boolean isLocalRunningSystem = false;

        SystemRuntime systemRuntime = activeSystems.get(desc);

        /* If the system is running locally... */
        if (systemRuntime != null && systemRuntime.isRunning()) {
            isLocalRunningSystem = true;
        }

        return isLocalRunningSystem;
    }

    /**
     * @see fabric.bus.feeds.ISubscriptionCallback#handleSubscriptionEvent(fabric.bus.feeds.ISubscription,
     *      java.lang.String, fabric.bus.messages.IServiceMessage)
     */
    @Override
    public void handleSubscriptionEvent(ISubscription subscription, String event, IServiceMessage message) {

        FLog.enter(logger, Level.FINER, this, "handleSubscriptionEvent", subscription, event, message);
        FLog.exit(logger, Level.FINER, this, "handleSubscriptionEvent", null);
    }

    /**
     * @see fabric.bus.feeds.ISubscriptionCallback#cancelSubscriptionCallback()
     */
    @Override
    public void cancelSubscriptionCallback() {

        FLog.enter(logger, Level.FINER, this, "cancelSubscriptionCallback", null);
        FLog.exit(logger, Level.FINER, this, "cancelSubscriptionCallback", null);
    }
}
import fabric.FabricBus;
import fabric.core.json.JSON;
import fabric.services.jsonclient.articles.Bearers;
import fabric.services.jsonclient.articles.NodeTypes;
import fabric.services.jsonclient.articles.Nodes;
import fabric.services.jsonclient.articles.PlatformTypes;
import fabric.services.jsonclient.articles.Platforms;
import fabric.services.jsonclient.articles.Registry;
import fabric.services.jsonclient.articles.ServiceTypes;
import fabric.services.jsonclient.articles.ServiceWiring;
import fabric.services.jsonclient.articles.SystemTypes;
import fabric.services.jsonclient.articles.Systems;
import fabric.services.jsonclient.articles.UserTypes;
import fabric.services.jsonclient.articles.Users;
import fabric.services.jsonclient.utilities.AdapterConstants;
import fabric.services.jsonclient.utilities.AdapterStatus;
import fabric.services.systems.RuntimeManager;

/*
 * (C) Copyright IBM Corp. 2014
 *
 * LICENSE: Eclipse Public License v1.0
 * http://www.eclipse.org/legal/epl-v10.html
 */

package fabric.services.jsonclient.handler;


/**
 * Class that dispatches all incoming operations to their respective handlers.
 */
public class OperationDispatcher extends FabricBus {

    /** Copyright notice. */
    public static final String copyrightNotice = "(C) Copyright IBM Corp. 2014";

    /*
     * Class methods
     */

    /**
     * Handles a SQL operation.
     *
     * @param operation
     *            The name of the requested operation.
     *
     * @param op
     *            The full operation message.
     *
     * @param correlId
     *            The correlation ID of the request.
     *
     * @return the operation response.
     */
    public static JSON sql(final String operation, final JSON op, String correlId) {

        JSON response = null;

        /* Determine what operation has been requested, and invokes the correct class and method. */

        switch (operation) {

            case AdapterConstants.OP_QUERY_LOCAL_NODE:


                response = Nodes.queryLocalNode(correlId);
