summaryrefslogtreecommitdiffstats
path: root/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java
diff options
context:
space:
mode:
Diffstat (limited to 'dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java')
-rw-r--r--dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java
new file mode 100644
index 0000000..e08f3a6
--- /dev/null
+++ b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java
@@ -0,0 +1,1227 @@
+package org.onap.sdc.dcae.catalog.asdc;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathNotFoundException;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.onap.sdc.common.onaplog.Enums.LogLevel;
+import org.onap.sdc.common.onaplog.OnapLoggerDebug;
+import org.onap.sdc.dcae.catalog.Catalog;
+import org.onap.sdc.dcae.catalog.commons.*;
+import org.onap.sdc.dcae.checker.*;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+public class ASDCCatalog implements Catalog {
+
+ private
+ static final String JXPATH_NOT_FOUND_EXCEPTION = "JXPathNotFoundException {}";
+ private
+ static final String OCCURRENCES = "occurrences";
+ private
+ static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES = "/topology_template/node_templates";
+ private
+ static final String NODES_NAME = "/nodes[name='";
+ private
+ static final String ITEM_ID = "itemId";
+ private
+ static final String LABELS = "labels";
+ private
+ static final String ARTIFACT_URL = "artifactURL";
+ private
+ static final String CAPABILITY = "capability";
+ private
+ static final String DATABASE = "Database";
+ private
+ static final String COLLECTOR = "Collector";
+ private
+ static final String MICROSERVICE = "Microservice";
+ private
+ static final String ANALYTICS = "Analytics";
+ private
+ static final String POLICY = "Policy";
+ private
+ static final String SOURCE = "Source";
+ private
+ static final String UTILITY = "Utility";
+ private
+ static final String NAME = "name";
+ private
+ static final String ID = "id";
+ private
+ static final String ARTIFACT_NAME = "artifactName";
+ private
+ static final String DESCRIPTION = "description";
+ private
+ static final String MODELS = "models";
+ private
+ static final String ARTIFACTS = "artifacts";
+ private
+ static final String ITEMS = "items";
+ private
+ static final String PROPERTIES = "']/properties";
+ private
+ static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 = "/topology_template/node_templates/";
+ private
+ static final String PROPERTIES_NAME = "']/properties[name='";
+ private
+ static final String CAPABILITIES = "']/capabilities";
+ private
+ static final String CAPABILITIES_NAME = "']/capabilities[name='";
+
+ private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
+
+ private ASDC asdc;
+
+ private JSONObject folders = new JSONObject();
+ private String[] folderFields = new String[] {ID, ITEM_ID, NAME};
+
+ private ProxyBuilder proxies;
+ private Map<Target, JXPathContext> contexts = new HashMap<Target, JXPathContext>();
+
+ // resource and its catalog
+ private Map<UUID, org.onap.sdc.dcae.checker.Catalog> catalogs = new HashMap<UUID, org.onap.sdc.dcae.checker.Catalog>();
+
+ public ASDCCatalog(URI theURI) {
+
+ this.asdc = new ASDC();
+ this.asdc.setUri(theURI);
+
+ initFolders();
+
+ this.proxies = new ProxyBuilder().withConverter(v -> v == null ? null : UUID.fromString(v.toString()), UUID.class)
+ .withExtensions(
+ new ImmutableMap.Builder<String, BiFunction<Proxy, Object[], Object>>().put("data", (proxy, args) -> proxy.data())
+ .build()).withContext(new ImmutableMap.Builder<String, Object>().put("catalog", this).build());
+ }
+
+ private void initFolders() {
+
+ JSONArray labels = new JSONArray();
+ labels.put("Folder");
+ labels.put("DCAE");
+ labels.put("Superportfolio"); // for CCD compatibility
+
+ folders.put(DATABASE, new JSONObject().put(NAME, DATABASE).put(ID, "dcae_database")
+ .put(ITEM_ID, DATABASE).put(LABELS, labels));
+ folders.put(COLLECTOR, new JSONObject().put(NAME, COLLECTOR).put(ID, "dcae_collector")
+ .put(ITEM_ID, COLLECTOR).put(LABELS, labels));
+ folders.put(MICROSERVICE, new JSONObject().put(NAME, MICROSERVICE).put(ID, "dcae_microservice")
+ .put(ITEM_ID, MICROSERVICE).put(LABELS, labels));
+ folders.put(ANALYTICS, new JSONObject().put(NAME, ANALYTICS).put(ID, "dcae_analytics")
+ .put(ITEM_ID, ANALYTICS).put(LABELS, labels));
+ folders.put(POLICY, new JSONObject().put(NAME, POLICY).put(ID, "dcae_policy").put(ITEM_ID, POLICY)
+ .put(LABELS, labels));
+ folders.put(SOURCE, new JSONObject().put(NAME, SOURCE).put(ID, "dcae_source").put(ITEM_ID, SOURCE)
+ .put(LABELS, labels));
+ folders.put(UTILITY, new JSONObject().put(NAME, UTILITY).put(ID, "dcae_utility")
+ .put(ITEM_ID, UTILITY).put(LABELS, labels));
+ }
+
+ public URI getUri() {
+ return this.asdc.getUri();
+ }
+
+ public String namespace() {
+ return "asdc";
+ }
+
+ public boolean same(Catalog theCatalog) {
+ return true;
+ }
+
+ public <T> T proxy(JSONObject theData, Class<T> theType) {
+ return proxies.build(theData, theType);
+ }
+
+ /** */
+ public Future<Folders> roots() {
+
+ Folders roots = new Folders();
+ for (Iterator fi = folders.keys(); fi.hasNext();) {
+ roots.add(proxies.build(folders.getJSONObject((String) fi.next()), Folder.class));
+ }
+ return Futures.succeededFuture(roots);
+ }
+
+ /** */
+ public Future<Folders> rootsByLabel(String theLabel) {
+
+ Folders roots = new Folders();
+ for (Iterator fi = folders.keys(); fi.hasNext();) {
+ JSONObject folder = folders.getJSONObject((String) fi.next());
+ JSONArray labels = folder.getJSONArray(LABELS);
+
+ for (int i = 0; i < labels.length(); i++) {
+ if (labels.get(i).equals(theLabel)) {
+ roots.add(proxies.build(folder, Folder.class));
+ }
+ }
+ }
+ return Futures.succeededFuture(roots);
+ }
+
+ /** */
+ public Future<Mixels> lookup(JSONObject theSelector) {
+ return Futures.succeededFuture(new Mixels());
+ }
+
+ public Future<Mixels> lookup(String theAnnotation, JSONObject theSelector) {
+ return Futures.succeededFuture(new Mixels());
+ }
+
+ /** */
+ public ItemAction item(String theItemId) {
+ return new ResourceAction(UUID.fromString(theItemId));
+ }
+
+ /** */
+ public FolderAction folder(String theFolderId) {
+ return new FolderAction(theFolderId);
+ }
+
+ public TemplateAction template(String theId) {
+ return new TemplateAction(theId);
+ }
+
+ public TypeAction type(String theItemId, String theName) {
+ return new TypeAction(UUID.fromString(theItemId), theName);
+ }
+
+ protected static String resolveTargetName(Target theTarget) {
+ return (String) ((Map) ((Map) theTarget.getTarget()).get("metadata")).get("template_name");
+ }
+
+ protected Object resolve(Target theTarget, String thePath) {
+ try {
+ return contexts.get(theTarget).getValue(thePath);
+ } catch (JXPathNotFoundException pnfx) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "JXPathNotFoundException {}", pnfx);
+ return null;
+ }
+ }
+
+ // covers common TOSCA pattern of single entry maps
+ public Map.Entry<String, Map> toEntry(Object theValue) {
+ return (Map.Entry<String, Map>) ((Map) theValue).entrySet().iterator().next();
+ }
+
+ protected Map selectEntries(Map theOriginal, String... theKeys) {
+ Arrays.sort(theKeys);
+ Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream()
+ .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) >= 0)
+ .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+ return selection;
+ }
+
+ protected Map evictEntries(Map theOriginal, String... theKeys) {
+ Arrays.sort(theKeys);
+ Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream()
+ .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) < 0)
+ .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+ return selection;
+ }
+
+ protected MapBuilder renderEntry(Map.Entry theEntry, String... theKeys) {
+ MapBuilder out = new MapBuilder();
+ out.put(NAME, theEntry.getKey());
+
+ for (String key : theKeys) {
+ out.put(key, ((Map) theEntry.getValue()).get(key));
+ }
+ return out;
+ }
+
+ protected <T> Stream<T> stream(Iterator<T> theSource) {
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(theSource,
+ Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false);
+ }
+
+ private JSONArray selectModels(JSONArray theArtifacts) {
+ JSONArray models = new JSONArray();
+ if (theArtifacts == null) {
+ return models;
+ }
+
+ for (int i = 0; i < theArtifacts.length(); i++) {
+ JSONObject artifact = theArtifacts.getJSONObject(i);
+ String name = artifact.optString(ARTIFACT_NAME);
+ if (name != null && StringUtils.containsIgnoreCase(name, "template")) {
+ models.put(new JSONObject().putOpt(NAME, artifact.optString(ARTIFACT_NAME))
+ .putOpt("version", artifact.optString("artifactVersion"))
+ .putOpt(DESCRIPTION, artifact.optString("artifactType"))
+ .putOpt(ID, artifact.optString(ARTIFACT_URL))
+ .putOpt(ITEM_ID, artifact.optString(ARTIFACT_URL)));
+ }
+ }
+ return models;
+ }
+
+ private JSONObject patchResource(JSONObject theResource) {
+
+ theResource.remove("resources");
+ theResource.putOpt(ID, theResource.opt("uuid"));
+ theResource.putOpt(ITEM_ID, theResource.opt("uuid"));
+
+ return theResource;
+ }
+
+ private static void dumpTargets(String theDirName, Collection<Target> theTargets) {
+ try {
+ File targetDir = new File(theDirName);
+ if (!targetDir.exists() && !targetDir.mkdirs()) {
+ throw new IllegalStateException("Couldn't create dir: " + theDirName);
+ }
+ for (Target t : theTargets) {
+ FileWriter dump = new FileWriter(new File(theDirName, t.getName()));
+ IOUtils.copy(t.open(), dump);
+ dump.close();
+ }
+ } catch (IOException iox) {
+ debugLogger.log(LogLevel.DEBUG,"ASDCCatalog", "IOException {}", iox);
+ }
+ }
+
+ private static URI asURI(String theValue) {
+ try {
+ return new URI(theValue);
+ } catch (URISyntaxException urisx) {
+ throw new IllegalArgumentException("Invalid URI", urisx);
+ }
+ }
+
+ private static UUID asUUID(String theValue) {
+ return UUID.fromString(theValue);
+ }
+
+ private org.onap.sdc.dcae.checker.Catalog getCatalog(UUID theResourceId) {
+ return this.catalogs.get(theResourceId);
+ }
+
+ private String getArtifactVersion(JSONObject theData) {
+ return theData.getString("artifactVersion");
+ }
+
+ private String getArtifactName(JSONObject theData) {
+ return theData.getString(ARTIFACT_NAME);
+ }
+
+ private String getArtifactURL(JSONObject theData) {
+ return theData.getString(ARTIFACT_URL);
+ }
+
+ private URI getArtifactURI(JSONObject theData) {
+ return asURI(theData.getString(ARTIFACT_URL));
+ }
+
+ /** */
+ public class ResourceAction implements Catalog.ItemAction<Resource> {
+
+ private UUID iid;
+ private boolean doModels;
+
+ ResourceAction(UUID theItemId) {
+ this.iid = theItemId;
+ }
+
+ public ResourceAction withModels() {
+ this.doModels = true;
+ return this;
+ }
+
+ public ResourceAction withAnnotations() {
+ return this;
+ }
+
+ @Override
+ public Future<Resource> execute() {
+
+ return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> {
+ if (doModels) {
+ resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS)));
+ }
+ return proxies.build(patchResource(resourceData), Resource.class);
+ });
+ }
+
+ protected Future<JSONObject> executeRaw() {
+
+ return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> {
+ if (doModels) {
+ resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS)));
+ }
+ return resourceData;
+ }, resourceError -> new RuntimeException("Failed to retrieve item " + this.iid, resourceError));
+ }
+ }
+
+ public class FolderAction implements Catalog.FolderAction {
+
+ private boolean doItemModels;
+ private String folderName;
+
+ // use the id/UUID of the folder ??
+ private FolderAction(String theFolderName) {
+ this.folderName = theFolderName;
+ }
+
+ public FolderAction withAnnotations() {
+ return this;
+ }
+
+ public FolderAction withAnnotations(String theSelector) {
+ return this;
+ }
+
+ public FolderAction withItems() {
+ return this;
+ }
+
+ public FolderAction withItemAnnotations() {
+ return this;
+ }
+
+ public FolderAction withItemAnnotations(String theSelector) {
+ return this;
+ }
+
+ public FolderAction withItemModels() {
+ doItemModels = true;
+ return this;
+ }
+
+ public FolderAction withParts() {
+ return this;
+ }
+
+ public FolderAction withPartAnnotations() {
+ return this;
+ }
+
+ public FolderAction withPartAnnotations(String theSelector) {
+ return this;
+ }
+
+ @Override
+ public Future<Folder> execute() {
+
+ JSONObject folder = folders.optJSONObject(this.folderName);
+ if (folder == null) {
+ return Futures.failedFuture(new RuntimeException("No such folder " + this.folderName));
+ }
+
+ final JSONObject folderView = new JSONObject(folder, folderFields);
+
+ return Futures.advance(asdc.getResources(JSONArray.class, "DCAE Component", this.folderName),
+ resourcesData -> {
+
+ Actions.CompoundAction<Resource> itemsAction = new Actions.BasicCompoundAction<Resource>();
+ for (int i = 0; i < resourcesData.length(); i++) {
+ JSONObject resource = resourcesData.getJSONObject(i);
+
+ if (doItemModels) {
+ itemsAction
+ .addAction(new ResourceAction(asUUID(resource.getString("uuid"))).withModels());
+ } else {
+ folderView.append(ITEMS, patchResource(resource));
+ }
+ }
+
+ try {
+ List<Resource> items = itemsAction.execute().waitForResult();
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Number of DCAE item for : {} is {}", this.folderName, items.size());
+
+ for (Resource res : filterLatestVersion(items)) {
+ folderView.append(ITEMS, patchResource(res.data()));
+ }
+ } catch (Exception x) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Exception {}", x);
+ throw new RuntimeException("Failed to retrieve folder items", x);
+ }
+
+ return proxies.build(folderView, Folder.class);
+ }, resourcesError -> new RuntimeException("Failed to retrieve resources", resourcesError));
+ }
+
+ public Collection<Resource> filterLatestVersion(Collection<Resource> items) throws IllegalArgumentException {
+ if (items == null) {
+ throw new IllegalArgumentException("null is not acceptable as a list of items");
+ }
+ Map<UUID, Resource> itemsMap = new HashMap<UUID, Resource>(items.size());
+ for (Resource r : items) {
+ if (itemsMap.containsKey(r.invariantUUID()) && isNewerVersion(itemsMap, r)) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Avoiding adding item {} since it has a advanced version already", r.toString());
+ continue;
+ }
+ itemsMap.put(r.invariantUUID(), r);
+ }
+ return itemsMap.values();
+ }
+
+ private boolean isNewerVersion(Map<UUID, Resource> itemsMap, Resource r) {
+ return Float.valueOf(itemsMap.get(r.invariantUUID()).version()) > Float.valueOf(r.version());
+ }
+
+ }
+
+ /** */
+ public class TemplateAction implements Catalog.TemplateAction {
+
+ private String artifactId;
+ private Target target;
+ private org.onap.sdc.dcae.checker.Catalog catalog;
+ private JXPathContext ctx = JXPathContext.newContext(new HashMap());
+
+ private boolean doNodes, doNodeProperties, doNodePropertiesAssignments, doNodeRequirements, doNodeCapabilities,
+ doNodeCapabilityProperties, doNodeCapabilityPropertyAssignments;
+
+ protected TemplateAction(Target theTarget) {
+ this.target = theTarget;
+ }
+
+ /*
+ * expected to be the relative url provided by asdc for the template
+ * artifact
+ */
+ protected TemplateAction(String theArtifactId) {
+ this.artifactId = theArtifactId;
+ }
+
+ public TemplateAction withInputs() {
+ return this;
+ }
+
+ public TemplateAction withOutputs() {
+ return this;
+ }
+
+ public TemplateAction withNodes() {
+ this.doNodes = true;
+ return this;
+ }
+
+ protected TemplateAction doNodes() {
+ if (!this.doNodes) {
+ return this;
+ }
+
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ ctx.setValue("/nodes",
+ nodes.entrySet().stream()
+ .map(nodeEntry -> new MapBuilder().put(NAME, ((Map.Entry) nodeEntry).getKey())
+ .put(DESCRIPTION, this.artifactId)
+ .putAll(selectEntries((Map) ((Map.Entry) nodeEntry).getValue(), "type")).build())
+ .collect(Collectors.toList()));
+
+ return this;
+ }
+
+ // pre-requisite: a call to 'withNodes'
+ public TemplateAction withNodeProperties() {
+ this.doNodeProperties = true;
+ return this;
+ }
+
+ protected TemplateAction doNodeProperties() {
+ if (!this.doNodeProperties) {
+ return this;
+ }
+
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ nodes.entrySet().stream().forEach(node -> ctx.setValue(
+ NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES,
+ stream(catalog.facets(Construct.Node, Facet.properties,
+ ((Map) ((Map.Entry) node).getValue()).get("type").toString()))
+ .map(propEntry -> new MapBuilder().put(NAME, propEntry.getKey())
+ .putAll((Map) propEntry.getValue()).build())
+ .collect(Collectors.toList())));
+
+ return this;
+ }
+
+ // pre-requisite: a call to 'withNodesProperties'
+ public TemplateAction withNodePropertiesAssignments() {
+ this.doNodePropertiesAssignments = true;
+ return this;
+ }
+
+ protected TemplateAction doNodePropertiesAssignments() {
+ if (!this.doNodePropertiesAssignments) {
+ return this;
+ }
+
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ nodes.entrySet().stream().forEach(node -> {
+ List nodeProps = null;
+ try {
+ nodeProps = (List) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES);
+ } catch (JXPathNotFoundException pnfx) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
+ return;
+ }
+
+ nodeProps.stream().forEach(prop -> {
+ // pick from
+ String propPath = TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey()
+ + "/properties/" + ((Map) prop).get(NAME);
+ Object propValue = resolve(this.target, propPath);
+ // to conform with the db based api we should analyze the
+ // value for function calls
+ // dump at ..
+ propPath = NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES_NAME
+ + ((Map) prop).get(NAME) + "']";
+ if (propValue != null) {
+ ctx.setValue(propPath + "/assignment",
+ new ImmutableMap.Builder().put("value", propValue).build());
+ }
+ });
+ });
+
+ return this;
+ }
+
+ protected Map renderRequirementDefinition(Map.Entry theReq) {
+ Map def = (Map) theReq.getValue();
+ return new MapBuilder().put(NAME, theReq.getKey())
+ // capability must be present
+ .put(CAPABILITY,
+ new MapBuilder().put(NAME, def.get(CAPABILITY))
+ .put(ID, this.target.getName() + "/" + def.get(CAPABILITY)).build())
+ .putAll(evictEntries(def, CAPABILITY)).build();
+ }
+
+ // TODO: see how this comes out of neo and match it
+ protected Map renderRequirementAssignment(Map.Entry theReq) {
+ Map def = (Map) theReq.getValue();
+ return new MapBuilder().put(NAME, theReq.getKey())
+ // capability must be present
+ .put(CAPABILITY,
+ new MapBuilder().put(NAME, def.get(CAPABILITY))
+ // we provide an id only if the capability
+ // points to a type
+ .putOpt(ID,
+ catalog.hasType(Construct.Capability, (String) def.get(CAPABILITY))
+ ? (this.target.getName() + "/" + def.get(CAPABILITY)) : null)
+ .build())
+ .putAll(evictEntries(def, CAPABILITY)).build();
+ }
+
+ public TemplateAction withNodeRequirements() {
+ this.doNodeRequirements = true;
+ return this;
+ }
+
+ TemplateAction doNodeRequirements() {
+ if (!this.doNodeRequirements) {
+ return this;
+ }
+
+ // requirements come first from the type and then can be further
+ // refined by their assignment within the
+ // node template
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ // type
+ nodes.entrySet().stream()
+ .forEach(
+ node -> ctx
+ .setValue(
+ NODES_NAME
+ + ((Map.Entry) node)
+ .getKey()
+ + "']/requirements",
+ StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(
+ catalog.requirements(((Map) ((Map.Entry) node).getValue())
+ .get("type").toString()),
+ Spliterator.NONNULL | Spliterator.DISTINCT
+ | Spliterator.IMMUTABLE),
+ false)
+ .map((Map.Entry reqEntry) -> renderRequirementDefinition(reqEntry))
+ .collect(Collectors.toList())));
+
+ // merge assignments on top of definitions
+ nodes.entrySet().stream().forEach(node -> {
+ List nodeReqsAssigns = (List) resolve(this.target,
+ TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey() + "/requirements");
+ if (nodeReqsAssigns == null) {
+ return;
+ }
+ nodeReqsAssigns.stream().forEach(req -> {
+ Map.Entry reqAssign = toEntry(req);
+ catalog.mergeDefinitions((Map) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey()
+ + "']/requirements[name='" + reqAssign.getKey() + "']"),
+ renderRequirementAssignment(reqAssign));
+ });
+ });
+
+ return this;
+ }
+
+ public TemplateAction withNodeCapabilities() {
+ this.doNodeCapabilities = true;
+ return this;
+ }
+
+ protected Map renderCapabilityDefinition(Map.Entry theCap) {
+ Map def = (Map) theCap.getValue();
+ return new MapBuilder().put(NAME, theCap.getKey())
+ .put("type",
+ new MapBuilder().put(NAME, def.get("type"))
+ .put(ID, this.target.getName() + "/" + def.get("type")).build())
+ .putAll(evictEntries(def, "properties", "type")).build();
+ }
+
+ TemplateAction doNodeCapabilities() {
+ if (!this.doNodeCapabilities) {
+ return this;
+ }
+
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ // collect capabilities through the node type hierarchy
+
+ // we evict the properties from the node type capability declaration
+ // (when declaring a capability with the
+ // node type some re-definition of capability properties can take
+ // place).
+ nodes.entrySet().stream()
+ .forEach(node -> ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES,
+
+ stream(catalog.facets(Construct.Node, Facet.capabilities,
+ ((Map) ((Map.Entry) node).getValue()).get("type").toString()))
+ .map((Map.Entry capEntry) -> renderCapabilityDefinition(capEntry))
+ .collect(Collectors.toList())));
+
+ return this;
+ }
+
+ public TemplateAction withNodeCapabilityProperties() {
+ this.doNodeCapabilityProperties = true;
+ return this;
+ }
+
+ TemplateAction doNodeCapabilityProperties() {
+
+ if (!this.doNodeCapabilityProperties) {
+ return this;
+ }
+
+ Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
+ if (nodes == null) {
+ return this;
+ }
+
+ // pick up all the properties from the capability type hierarchy
+ // definition
+ nodes.entrySet().stream().forEach(node -> {
+ List nodeCapabilities = (List) ctx
+ .getValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES);
+ if (nodeCapabilities == null) {
+ return;
+ }
+
+ // collect properties from the capability type hierarchy
+ nodeCapabilities.stream().forEach(capability -> {
+ List capabilityProperties = StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(
+ catalog.facets(Construct.Capability, Facet.properties,
+ ((Map)((Map)capability).get("type")).get(NAME).toString()),
+ Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false)
+ .map((Map.Entry capEntry) -> new MapBuilder().put(NAME, capEntry.getKey())
+ .putAll((Map) capEntry.getValue()).build())
+ .collect(Collectors.toList());
+
+ if (!capabilityProperties.isEmpty()) {
+ ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES_NAME
+ + ((Map) capability).get(NAME) + PROPERTIES, capabilityProperties);
+ }
+ });
+
+ // and go over the node type (hierarchy) and pick up any
+ // re-definitions from there.
+ StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(
+ catalog.facets(Construct.Node, Facet.capabilities,
+ ((Map) ((Map.Entry) node).getValue()).get("type").toString()),
+ Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false)
+ .forEach((Map.Entry capability) -> {
+ // for each capability property that has some node
+ // type level re-definition
+ Map properties = (Map) ((Map) capability.getValue()).get("properties");
+ if (properties == null) {
+ return;
+ }
+
+ properties.entrySet().stream().forEach(property -> {
+ String propertyLoc = NODES_NAME + ((Map.Entry) node).getKey()
+ + CAPABILITIES_NAME + ((Map) capability).get(NAME)
+ + PROPERTIES_NAME + ((Map.Entry) property).getKey() + "']";
+ ctx.setValue(propertyLoc, catalog.mergeDefinitions((Map) ctx.getValue(propertyLoc),
+ (Map) ((Map.Entry) property).getValue()));
+ });
+ });
+ });
+
+ return this;
+ }
+
+ public TemplateAction withNodeCapabilityPropertyAssignments() {
+ this.doNodeCapabilityPropertyAssignments = true;
+ return this;
+ }
+
+ TemplateAction doNodeCapabilityPropertyAssignments() {
+ if (!this.doNodeCapabilityPropertyAssignments) {
+ return this;
+ }
+
+ // this is a wasteful: we go over all declared
+ // nodes/capabilities/properties and check if there is an assigned
+ // value in the actual template. It is optimal to approach the
+ // problem from the other direction: go over delared
+ // assignments and set them in the output structure ..
+
+ List nodes = null;
+ try {
+ nodes = (List) ctx.getValue("/nodes");
+ } catch (JXPathNotFoundException pnfx) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
+ return this;
+ }
+
+ nodes.stream().forEach(node -> {
+ List capabilities = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES);
+ if (capabilities == null) {
+ return;
+ }
+
+ capabilities.stream().forEach(capability -> {
+ List properties = null;
+ try {
+ properties = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME)
+ + CAPABILITIES_NAME + ((Map) capability).get(NAME) + PROPERTIES);
+ } catch (JXPathNotFoundException pnfx) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
+ return;
+ }
+
+ properties.stream().forEach(property -> {
+ String location = NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES_NAME
+ + ((Map) capability).get(NAME) + PROPERTIES_NAME + ((Map) property).get(NAME)
+ + "']/assignment";
+
+ // pick the value from the original
+ try {
+ Object assignment = resolve(this.target,
+ TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map) node).get(NAME) + "/capabilities/"
+ + ((Map) capability).get(NAME) + "/properties/"
+ + ((Map) property).get(NAME));
+ if (assignment != null) {
+ ctx.setValue(location, new ImmutableMap.Builder().put("value", assignment).build());
+ }
+ } catch (JXPathNotFoundException pnfx) {
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
+ // it's ok, no assignment
+ }
+ });
+ });
+ });
+
+ return this;
+ }
+
+ public TemplateAction withPolicies() {
+ return this;
+ }
+
+ public TemplateAction withPolicyProperties() {
+ return this;
+ }
+
+ public TemplateAction withPolicyPropertiesAssignments() {
+ return this;
+ }
+
+ public Future<Template> execute() {
+
+ if (this.target == null) {
+
+ String[] parts = this.artifactId.split("/");
+ if (parts.length != 8) {
+ return Futures
+ .failedFuture(new Exception("Unexpected artifact id for template " + this.artifactId));
+ }
+
+ UUID resourceId = asUUID(parts[5]);
+ this.catalog = ASDCCatalog.this.catalogs.get(resourceId);
+
+ // if we find a catalog for this resource we have to figure out
+ // if it contains the required target ..
+
+ try {
+ JSONObject resource = new ResourceAction(resourceId).executeRaw().waitForResult();
+
+ Checker checker = new Checker();
+ TargetLocator locator = new ASDCLocator(resource.getJSONArray(ARTIFACTS),
+ ASDCCatalog.this.catalogs.get(resourceId));
+ checker.setTargetLocator(locator);
+
+ Target template = locator.resolve("template");
+ if (template == null) {
+ return Futures.failedFuture(new Exception("Failed to locate template in " + resource));
+ }
+
+ checker.check(template);
+
+ for (Target t : checker.targets()) {
+ if (t.getReport().hasErrors()) {
+ dumpTargets(resourceId.toString(), checker.targets());
+ return Futures.failedFuture(new Exception("Failed template validation: " + t.getReport()));
+ }
+ }
+
+ this.target = template;
+ this.catalog = checker.catalog();
+ ASDCCatalog.this.catalogs.put(resourceId, this.catalog);
+ // we should only be doing this if we discovered an update
+ // (by checking timestampts). Actually, we should
+ // only do the artifact fetching if we detect an update
+ ASDCCatalog.this.contexts.put(template, JXPathContext.newContext(template.getTarget()));
+ } catch (Exception x) {
+ return Futures.failedFuture(x);
+ }
+ }
+
+ this.doNodes().doNodeProperties().doNodePropertiesAssignments().doNodeRequirements().doNodeCapabilities()
+ .doNodeCapabilityProperties().doNodeCapabilityPropertyAssignments();
+
+ JSONObject pack = new JSONObject((Map) ctx.getContextBean()).put(NAME, this.target.getName().toString())
+ .put(ID, this.target.getLocation().toString())
+ .put(ITEM_ID, this.target.getLocation().toString());
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2));
+
+ return Futures.succeededFuture(proxies.build(pack, Template.class));
+ }
+ }
+
+ public class TypeAction implements Catalog.TypeAction {
+
+ private String name;
+ private UUID resourceId;
+ private JXPathContext ctx;
+
+ private boolean doHierarchy = false, doRequirements = false, doCapabilities = false;
+
+ private TypeAction(UUID theResourceId, /* Construct theConstruct, */ String theName) {
+ this.resourceId = theResourceId;
+ this.name = theName;
+ }
+
+ public TypeAction withHierarchy() {
+ this.doHierarchy = true;
+ return this;
+ }
+
+ TypeAction doHierarchy(org.onap.sdc.dcae.checker.Catalog theCatalog) {
+ if (!this.doHierarchy) {
+ return this;
+ }
+
+ ctx.setValue("/hierarchy",
+ stream(theCatalog.hierarchy(Construct.Node, this.name)).skip(1) // skip
+ // self
+ .map((Map.Entry type) -> new MapBuilder()
+ .put(NAME, type.getKey()).put(ID, resourceId + "/" + type.getKey())
+ .putOpt(DESCRIPTION, ((Map) type.getValue()).get(DESCRIPTION)).build())
+ // renderEntry((Map.Entry)type,
+ // "description").build())
+ .collect(Collectors.toList()));
+ return this;
+ }
+
+ public TypeAction withRequirements() {
+ this.doRequirements = true;
+ return this;
+ }
+
+ TypeAction doRequirements(org.onap.sdc.dcae.checker.Catalog theCatalog) {
+ if (!this.doRequirements) {
+ return this;
+ }
+
+ ctx.setValue("requirements", stream(theCatalog.requirements(this.name)).map((Map.Entry req) -> {
+ String capability = (String) ((Map) req.getValue()).get(CAPABILITY),
+ node = (String) ((Map) req.getValue()).get(CAPABILITY);
+ return new MapBuilder().put(NAME, req.getKey()).put(ID, resourceId + "/" + req.getKey())
+ .put(OCCURRENCES, ((Map) req.getValue()).get(OCCURRENCES))
+ .put(CAPABILITY,
+ new MapBuilder().put(NAME, capability)
+ // if the capability points to a
+ // capability type then encode
+ // the type reference, else it is a name
+ // (within a node type)
+ .put(ID,
+ getCatalog(resourceId).hasType(Construct.Capability, capability)
+ ? (resourceId + "/" + capability) : capability.toString())
+ .build())
+ .put("node", new MapBuilder().putOpt(NAME, node).putOpt(ID, node == null ? null
+ : (resourceId + "/" + node)).buildOpt())
+ .put("relationship", ((Map) req.getValue()).get("relationship"))
+ // renderEntry((Map.Entry)requirement, "occurrences",
+ // "node", "capability", "relationship")
+ .build();
+ }).collect(Collectors.toList()));
+
+ return this;
+ }
+
+ public TypeAction withCapabilities() {
+ this.doCapabilities = true;
+ return this;
+ }
+
+ TypeAction doCapabilities(org.onap.sdc.dcae.checker.Catalog theCatalog) {
+ if (!this.doCapabilities) {
+ return this;
+ }
+
+ ctx.setValue("capabilities",
+ stream(theCatalog
+ .facets(Construct.Node, Facet.capabilities,
+ this.name))
+ .map((Map.Entry capability) -> new MapBuilder()
+ .put(NAME, capability.getKey()).put("type",
+ new MapBuilder()
+ .put(NAME, ((Map) capability.getValue())
+ .get("type"))
+ .put(ID,
+ resourceId + "/"
+ + ((Map) capability.getValue())
+ .get("type"))
+ .build())
+ .put(OCCURRENCES,
+ ((Map) capability.getValue()).get(OCCURRENCES))
+ .putOpt("validSourceTypes",
+ ((Map) capability.getValue()).get("validSourceTypes"))
+ .build()
+ // renderEntry((Map.Entry)capability,
+ // "occurrences",
+ // "validSourceTypes")
+ ).collect(Collectors.toList()));
+ return this;
+ }
+
+ public Future<Type> execute() {
+ org.onap.sdc.dcae.checker.Catalog catalog = ASDCCatalog.this.catalogs.get(this.resourceId);
+ if (catalog == null) {
+ return Futures.failedFuture(new Exception("No catalog available for resource " + this.resourceId
+ + ". You might want to fetch the model first."));
+ }
+
+ if (!catalog.hasType(Construct.Node, this.name)) {
+ return Futures.failedFuture(
+ new Exception("No " + this.name + " type in catalog for resource " + this.resourceId));
+ }
+
+ this.ctx = JXPathContext
+ .newContext(new MapBuilder().put(NAME, this.name).put(ID, this.resourceId + "/" + this.name)
+ .put(ITEM_ID, this.resourceId + "/" + this.name).build());
+
+ this.doHierarchy(catalog).doRequirements(catalog).doCapabilities(catalog);
+
+ JSONObject pack = new JSONObject((Map) this.ctx.getContextBean());
+ debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2));
+
+ return Futures.succeededFuture(proxies.build((Map) ctx.getContextBean(), Type.class));
+ }
+ }
+
+ public static interface Resource extends Catalog.Item<Resource> {
+
+ @Override
+ @Proxy.DataMap(map = "uuid")
+ public String id();
+
+ public UUID uuid();
+
+ public UUID invariantUUID();
+
+ public String category();
+
+ public String subCategory();
+
+ public String lastUpdaterFullName();
+
+ public String version();
+
+ @Proxy.DataMap(proxy = true, elementType = Artifact.class)
+ public Artifacts artifacts();
+
+ }
+
+ public static class Resources extends Elements<Resource> {
+ }
+
+ public static interface Artifact extends Catalog.Element<Artifact> {
+
+ @Proxy.DataMap(map = ARTIFACT_NAME)
+ public String name();
+
+ @Proxy.DataMap(map = "artifactType")
+ public String type();
+
+ @Proxy.DataMap(map = "artifactDescription")
+ public String description();
+
+ @Proxy.DataMap(map = "artifactUUID")
+ public UUID uuid();
+
+ @Proxy.DataMap(map = "artifactVersion")
+ public int version();
+
+ }
+
+ public static class Artifacts extends Elements<Artifact> {
+ }
+
+ public class ASDCLocator implements TargetLocator {
+
+ private JSONArray artifacts;
+ private org.onap.sdc.dcae.checker.Catalog catalog;
+
+ private ASDCLocator(JSONArray theArtifacts, org.onap.sdc.dcae.checker.Catalog theCatalog) {
+ this.artifacts = theArtifacts;
+ this.catalog = theCatalog;
+ }
+
+ public boolean addSearchPath(URI theURI) {
+ return false;
+ }
+
+ public boolean addSearchPath(String thePath) {
+ return false;
+ }
+
+ public Iterable<URI> searchPaths() {
+ return Collections.emptySet();
+ }
+
+ public Target resolve(String theName) {
+ JSONObject targetArtifact = null;
+
+ for (int i = 0; i < this.artifacts.length(); i++) {
+ JSONObject artifact = this.artifacts.getJSONObject(i);
+ String artifactName = artifact.getString(ARTIFACT_NAME);
+ if (StringUtils.containsIgnoreCase(artifactName, theName)) {
+ targetArtifact = artifact;
+ }
+ }
+
+ if (targetArtifact == null) {
+ return null;
+ }
+
+ ASDCTarget target = null;
+ if (this.catalog != null) {
+ // this is the caching!!
+ target = (ASDCTarget) this.catalog.getTarget(ASDCCatalog.this.getArtifactURI(targetArtifact));
+ if (target != null && target.getVersion().equals(ASDCCatalog.this.getArtifactVersion(targetArtifact))) {
+ return target;
+ }
+ }
+
+ return new ASDCTarget(targetArtifact);
+ }
+ }
+
+ public class ASDCTarget extends Target {
+
+ private String content;
+ private JSONObject artifact;
+
+ private ASDCTarget(JSONObject theArtifact) {
+ super(ASDCCatalog.this.getArtifactName(theArtifact), ASDCCatalog.this.getArtifactURI(theArtifact));
+ this.artifact = theArtifact;
+ }
+
+ // here is a chance for caching within the catalog! Do not go fetch the
+ // artifact if it has not been changed since the
+ // last fetch.
+
+ @Override
+ public Reader open() throws IOException {
+ if (this.content == null) {
+ try {
+ this.content = ASDCCatalog.this.asdc
+ .fetch(ASDCCatalog.this.getArtifactURL(this.artifact), String.class).waitForResult();
+ } catch (Exception x) {
+ throw new IOException("Failed to load " + ASDCCatalog.this.getArtifactURL(this.artifact), x);
+ }
+ }
+
+ // should return immediately a reader blocked until content
+ // available .. hard to handle errors
+ return new StringReader(this.content);
+ }
+
+ public String getVersion() {
+ return ASDCCatalog.this.getArtifactVersion(this.artifact);
+ }
+
+ }
+
+ public static void main(String[] theArgs) throws Exception {
+
+ ASDCCatalog catalog = new ASDCCatalog(new URI(theArgs[0]));
+
+ Folder f = catalog.folder(theArgs[1]).withItems().withItemModels().execute().waitForResult();
+
+ debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "folder: {}", f.data());
+
+ Resources items = f.elements(ITEMS, Resources.class);
+ if (items != null) {
+ for (Resource item : items) {
+ debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "\titem: {} : {}",item.name(), item.data());
+ Templates templates = item.elements(MODELS, Templates.class);
+ if (templates != null) {
+ for (Template t : templates) {
+ Template ft = catalog.template(t.id()).withNodes().withNodeProperties()
+ .withNodePropertiesAssignments().execute().waitForResult();
+
+ debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "template data: {}", ft.data());
+ }
+ }
+ }
+ }
+ }
+
+}