Add missing apps/naming directory for testing
[policy/xacml-pdp.git] / applications / common / src / main / java / org / onap / policy / pdp / xacml / application / common / std / StdMatchableTranslator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pdp.xacml.application.common.std;
24
25 import com.att.research.xacml.api.Identifier;
26 import com.att.research.xacml.api.Obligation;
27 import com.att.research.xacml.api.Request;
28 import com.att.research.xacml.api.XACML3;
29 import com.att.research.xacml.std.IdentifierImpl;
30 import com.att.research.xacml.util.XACMLPolicyWriter;
31 import java.io.ByteArrayOutputStream;
32 import java.io.IOException;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.HashMap;
42 import java.util.LinkedHashMap;
43 import java.util.LinkedList;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Map.Entry;
47 import lombok.Setter;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
54 import org.apache.commons.lang3.tuple.Pair;
55 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
56 import org.onap.policy.common.utils.coder.CoderException;
57 import org.onap.policy.common.utils.coder.StandardCoder;
58 import org.onap.policy.common.utils.coder.StandardYamlCoder;
59 import org.onap.policy.models.decisions.concepts.DecisionRequest;
60 import org.onap.policy.models.decisions.concepts.DecisionResponse;
61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
62 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
63 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
64 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
65 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
66 import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate;
67 import org.onap.policy.pdp.xacml.application.common.OnapObligation;
68 import org.onap.policy.pdp.xacml.application.common.PolicyApiCaller;
69 import org.onap.policy.pdp.xacml.application.common.PolicyApiException;
70 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
71 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
72 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
73 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 /**
78  * This standard matchable translator uses Policy Types that contain "matchable" field in order
79  * to translate policies.
80  *
81  * @author pameladragosh
82  *
83  */
84 public class StdMatchableTranslator  extends StdBaseTranslator {
85
86     private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class);
87     private static final StandardYamlCoder standardYamlCoder = new StandardYamlCoder();
88
89     private final Map<ToscaPolicyTypeIdentifier, ToscaPolicyType> matchablePolicyTypes = new HashMap<>();
90     @Setter
91     private RestServerParameters apiRestParameters;
92     @Setter
93     private Path pathForData;
94
95     public StdMatchableTranslator() {
96         super();
97     }
98
99     @Override
100     public Request convertRequest(DecisionRequest request) {
101         LOGGER.info("Converting Request {}", request);
102         try {
103             return StdMatchablePolicyRequest.createInstance(request);
104         } catch (XacmlApplicationException e) {
105             LOGGER.error("Failed to convert DecisionRequest: {}", e);
106         }
107         //
108         // TODO throw exception
109         //
110         return null;
111     }
112
113     /**
114      * scanObligations - scans the list of obligations and make appropriate method calls to process
115      * obligations.
116      *
117      * @param obligations Collection of obligation objects
118      * @param decisionResponse DecisionResponse object used to store any results from obligations.
119      */
120     @Override
121     protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
122         //
123         // Implementing a crude "closest match" on the results, which means we will strip out
124         // any policies that has the lower weight than any of the others.
125         //
126         // Most likely these are "default" policies with a weight of zero, but not always.
127         //
128         // It is possible to have multiple policies with an equal weight, that is desired.
129         //
130         // So we need to track each policy type separately and the weights for each policy.
131         //
132         // policy-type -> weight -> List({policy-id, policy-content}, {policy-id, policy-content})
133         //
134         Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches = new LinkedHashMap<>();
135         //
136         // Now scan the list of obligations
137         //
138         for (Obligation obligation : obligations) {
139             Identifier obligationId = obligation.getId();
140             LOGGER.info("Obligation: {}", obligationId);
141             if (ToscaDictionary.ID_OBLIGATION_REST_BODY.equals(obligationId)) {
142                 scanClosestMatchObligation(closestMatches, obligation);
143             } else {
144                 LOGGER.warn("Unsupported Obligation Id {}", obligation.getId());
145             }
146         }
147         //
148         // Now add all the policies to the DecisionResponse
149         //
150         closestMatches.forEach((thePolicyType, weightMap) ->
151             weightMap.forEach((weight, policies) ->
152                 policies.forEach(policy -> {
153                     LOGGER.info("Policy {}", policy);
154                     decisionResponse.getPolicies().put(policy.getLeft(), policy.getRight());
155                 })
156             )
157         );
158     }
159
160     /**
161      * scanClosestMatchObligation - scans for the obligation specifically holding policy
162      * contents and their details.
163      *
164      * @param closestMatches Map holding the current set of highest weight policy types
165      * @param Obligation Obligation object
166      */
167     protected void scanClosestMatchObligation(
168             Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches, Obligation obligation) {
169         //
170         // Create our OnapObligation object
171         //
172         OnapObligation onapObligation = new OnapObligation(obligation);
173         //
174         // All 4 *should* be there
175         //
176         if (onapObligation.getPolicyId() == null || onapObligation.getPolicyContent() == null
177                 || onapObligation.getPolicyType() == null || onapObligation.getWeight() == null) {
178             LOGGER.error("Missing an expected attribute in obligation.");
179             return;
180         }
181         //
182         // Save the values
183         //
184         String policyId = onapObligation.getPolicyId();
185         String policyType = onapObligation.getPolicyType();
186         Map<String, Object> policyContent = onapObligation.getPolicyContentAsMap();
187         int policyWeight = onapObligation.getWeight();
188         //
189         // If the Policy Type exists, get the weight map.
190         //
191         Map<Integer, List<Pair<String, Map<String, Object>>>> weightMap = closestMatches.get(policyType);
192         if (weightMap != null) {
193             //
194             // Only need to check first one - as we will ensure there is only one weight
195             //
196             Entry<Integer, List<Pair<String, Map<String, Object>>>> firstEntry =
197                     weightMap.entrySet().iterator().next();
198             if (policyWeight < firstEntry.getKey()) {
199                 //
200                 // Existing policies have a greater weight, so we will not add it
201                 //
202                 LOGGER.info("{} is lesser weight {} than current policies, will not return it", policyWeight,
203                         firstEntry.getKey());
204             } else if (firstEntry.getKey().equals(policyWeight)) {
205                 //
206                 // Same weight - we will add it
207                 //
208                 LOGGER.info("Same weight {}, adding policy", policyWeight);
209                 firstEntry.getValue().add(Pair.of(policyId, policyContent));
210             } else {
211                 //
212                 // The weight is greater, so we need to remove the other policies
213                 // and point to this one.
214                 //
215                 LOGGER.info("New policy has greater weight {}, replacing {}", policyWeight, firstEntry.getKey());
216                 List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
217                 listPolicies.add(Pair.of(policyId, policyContent));
218                 weightMap.clear();
219                 weightMap.put(policyWeight, listPolicies);
220             }
221         } else {
222             //
223             // Create a new entry
224             //
225             LOGGER.info("New entry {} weight {}", policyType, policyWeight);
226             List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
227             listPolicies.add(Pair.of(policyId, policyContent));
228             Map<Integer, List<Pair<String, Map<String, Object>>>> newWeightMap = new LinkedHashMap<>();
229             newWeightMap.put(policyWeight, listPolicies);
230             closestMatches.put(policyType, newWeightMap);
231         }
232     }
233
234     @Override
235     public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
236         //
237         // Get the TOSCA Policy Type for this policy
238         //
239         Collection<ToscaPolicyType> toscaPolicyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier());
240         //
241         // If we don't have any TOSCA policy types, then we cannot know
242         // which properties are matchable.
243         //
244         if (toscaPolicyTypes.isEmpty()) {
245             throw new ToscaPolicyConversionException(
246                     "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName());
247         }
248         //
249         // Policy name should be at the root
250         //
251         String policyName = toscaPolicy.getMetadata().get(POLICY_ID);
252         //
253         // Set it as the policy ID
254         //
255         PolicyType newPolicyType = new PolicyType();
256         newPolicyType.setPolicyId(policyName);
257         //
258         // Optional description
259         //
260         newPolicyType.setDescription(toscaPolicy.getDescription());
261         //
262         // There should be a metadata section
263         //
264         fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
265         //
266         // Set the combining rule
267         //
268         newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
269         //
270         // Generate the TargetType - the policy should not be evaluated
271         // unless all the matchable properties it cares about are matched.
272         //
273         Pair<TargetType, Integer> pairGenerated = generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes);
274         newPolicyType.setTarget(pairGenerated.getLeft());
275         //
276         // Now represent the policy as Json
277         //
278         StandardCoder coder = new StandardCoder();
279         String jsonPolicy;
280         try {
281             jsonPolicy = coder.encode(toscaPolicy);
282         } catch (CoderException e) {
283             throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
284         }
285         //
286         // Add it as an obligation
287         //
288         addObligation(newPolicyType, policyName, jsonPolicy, pairGenerated.getRight(), toscaPolicy.getType());
289         //
290         // Now create the Permit Rule.
291         //
292         RuleType rule = new RuleType();
293         rule.setDescription("Default is to PERMIT if the policy matches.");
294         rule.setRuleId(policyName + ":rule");
295         rule.setEffect(EffectType.PERMIT);
296         rule.setTarget(new TargetType());
297         //
298         // The rule contains the Condition which adds logic for
299         // optional policy-type filtering.
300         //
301         rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType()));
302         //
303         // Add the rule to the policy
304         //
305         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
306         //
307         // Log output of the policy
308         //
309         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
310             XACMLPolicyWriter.writePolicyFile(os, newPolicyType);
311             LOGGER.info("{}", os);
312         } catch (IOException e) {
313             LOGGER.error("Failed to create byte array stream", e);
314         }
315         //
316         // Done
317         //
318         return newPolicyType;
319     }
320
321     /**
322      * For generating target type, we scan for matchable properties
323      * and use those to build the policy.
324      *
325      * @param properties Properties section of policy
326      * @param policyTypes Collection of policy Type to find matchable metadata
327      * @return {@code Pair<TargetType, Integer>} Returns a TargetType and a Total Weight of matchables.
328      */
329     protected Pair<TargetType, Integer> generateTargetType(Map<String, Object> properties,
330             Collection<ToscaPolicyType> policyTypes) {
331         TargetType targetType = new TargetType();
332         //
333         // Iterate the properties
334         //
335         int totalWeight = findMatchableFromMap(properties, policyTypes, targetType);
336         LOGGER.info("Total weight is {}", totalWeight);
337         return Pair.of(targetType, totalWeight);
338     }
339
340     @SuppressWarnings("unchecked")
341     protected int findMatchableFromList(List<Object> listProperties, Collection<ToscaPolicyType> policyTypes,
342             TargetType targetType) {
343         LOGGER.info("findMatchableFromList {}", listProperties);
344         int totalWeight = 0;
345         for (Object property : listProperties) {
346             if (property instanceof List) {
347                 totalWeight += findMatchableFromList((List<Object>) property, policyTypes, targetType);
348             } else if (property instanceof Map) {
349                 totalWeight += findMatchableFromMap((Map<String, Object>) property, policyTypes, targetType);
350             }
351         }
352         return totalWeight;
353     }
354
355     protected int findMatchableFromMap(Map<String, Object> properties, Collection<ToscaPolicyType> policyTypes,
356             TargetType targetType) {
357         LOGGER.info("findMatchableFromMap {}", properties);
358         int totalWeight = 0;
359         for (Entry<String, Object> entrySet : properties.entrySet()) {
360             //
361             // Is this a matchable property?
362             //
363             if (isMatchable(entrySet.getKey(), policyTypes)) {
364                 LOGGER.info("Found matchable property {}", entrySet.getKey());
365                 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue());
366                 LOGGER.info("Weight is {}", weight);
367                 totalWeight += weight;
368             } else {
369                 //
370                 // Check if we need to search deeper
371                 //
372                 totalWeight += checkDeeperForMatchable(entrySet.getValue(), policyTypes, targetType);
373             }
374         }
375         return totalWeight;
376     }
377
378     @SuppressWarnings("unchecked")
379     protected int checkDeeperForMatchable(Object property, Collection<ToscaPolicyType> policyTypes,
380             TargetType targetType) {
381         if (property instanceof List) {
382             return findMatchableFromList((List<Object>) property, policyTypes, targetType);
383         } else if (property instanceof Map) {
384             return findMatchableFromMap((Map<String, Object>) property, policyTypes,
385                     targetType);
386         }
387         LOGGER.info("checkDeeperForMatchable not necessary for {}", property);
388         return 0;
389     }
390
391     /**
392      * isMatchable - Iterates through available TOSCA Policy Types to determine if a property
393      * should be treated as matchable.
394      *
395      * @param propertyName Name of property
396      * @param policyTypes Collection of TOSCA Policy Types to scan
397      * @return true if matchable
398      */
399     protected boolean isMatchable(String propertyName, Collection<ToscaPolicyType> policyTypes) {
400         for (ToscaPolicyType policyType : policyTypes) {
401             for (Entry<String, ToscaProperty> propertiesEntry : policyType.getProperties().entrySet()) {
402                 if (checkIsMatchableProperty(propertyName, propertiesEntry)) {
403                     return true;
404                 }
405                 //
406                 // Check if its a list or map
407                 //
408                 if (isListOrMap(propertiesEntry.getValue().getType())
409                         && ! isYamlType(propertiesEntry.getValue().getEntrySchema().getType())) {
410                     LOGGER.info("need to search list or map");
411                 }
412             }
413         }
414         LOGGER.info("isMatchable false for {}", propertyName);
415         return false;
416     }
417
418     private boolean isListOrMap(String type) {
419         return "list".equalsIgnoreCase(type) || "map".equalsIgnoreCase(type);
420     }
421
422     private boolean isYamlType(String type) {
423         return "string".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type) || "float".equalsIgnoreCase(type)
424                 || "boolean".equalsIgnoreCase(type) || "timestamp".equalsIgnoreCase(type);
425     }
426
427     /**
428      * checkIsMatchableProperty - checks the policy contents for matchable field. If the metadata doesn't exist,
429      * then definitely not. If the property doesn't exist, then definitely not. Otherwise need to have a metadata
430      * section with the matchable property set to true.
431      *
432      * @param propertyName String value of property
433      * @param propertiesEntry Section of the TOSCA Policy Type where properties and metadata sections are held
434      * @return true if matchable
435      */
436     protected boolean checkIsMatchableProperty(String propertyName, Entry<String, ToscaProperty> propertiesEntry) {
437         if (! propertiesEntry.getKey().equals(propertyName)
438                 || propertiesEntry.getValue().getMetadata() == null) {
439             return false;
440         }
441         for (Entry<String, String> entrySet : propertiesEntry.getValue().getMetadata().entrySet()) {
442             if ("matchable".equals(entrySet.getKey()) && "true".equals(entrySet.getValue())) {
443                 return true;
444             }
445         }
446         return false;
447     }
448
449     /**
450      * generateMatchable - Given the object, generates list of MatchType objects and add them
451      * to the TargetType object. Returns a weight which is the number of AnyOf's generated. The
452      * weight can be used to further filter the results for "closest match".
453      *
454      * @param targetType TargetType object to add matches to
455      * @param key Property key
456      * @param value Object is the value - which can be a Collection or single Object
457      * @return int Weight of the match.
458      */
459     @SuppressWarnings("unchecked")
460     protected int generateMatchable(TargetType targetType, String key, Object value) {
461         int weight = 0;
462         if (value instanceof Collection) {
463             AnyOfType anyOf = generateMatches((Collection<Object>) value,
464                     new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
465             if (! anyOf.getAllOf().isEmpty()) {
466                 targetType.getAnyOf().add(anyOf);
467                 weight = 1;
468             }
469         } else {
470             AnyOfType anyOf = generateMatches(Arrays.asList(value),
471                     new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
472             if (! anyOf.getAllOf().isEmpty()) {
473                 targetType.getAnyOf().add(anyOf);
474                 weight = 1;
475             }
476         }
477         return weight;
478     }
479
480     /**
481      * generateMatches - Goes through the collection of objects, creates a MatchType object
482      * for each object and associates it with the given attribute Id. Returns the AnyOfType
483      * object that contains all the generated MatchType objects.
484      *
485      * @param matchables Collection of object to generate MatchType from
486      * @param attributeId Given attribute Id for each MatchType
487      * @return AnyOfType object
488      */
489     protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
490         //
491         // This is our outer AnyOf - which is an OR
492         //
493         AnyOfType anyOf = new AnyOfType();
494         for (Object matchable : matchables) {
495             //
496             // Default to string
497             //
498             Identifier idFunction = XACML3.ID_FUNCTION_STRING_EQUAL;
499             Identifier idDatatype = XACML3.ID_DATATYPE_STRING;
500             //
501             // See if we are another datatype
502             //
503             // We should add datetime support. But to do that we need
504             // probably more metadata to describe how that would be translated.
505             //
506             if (matchable instanceof Integer) {
507                 idFunction = XACML3.ID_FUNCTION_INTEGER_EQUAL;
508                 idDatatype = XACML3.ID_DATATYPE_INTEGER;
509             } else if (matchable instanceof Double) {
510                 idFunction = XACML3.ID_FUNCTION_DOUBLE_EQUAL;
511                 idDatatype = XACML3.ID_DATATYPE_DOUBLE;
512             } else if (matchable instanceof Boolean) {
513                 idFunction = XACML3.ID_FUNCTION_BOOLEAN_EQUAL;
514                 idDatatype = XACML3.ID_DATATYPE_BOOLEAN;
515             }
516             //
517             // Create a match for this
518             //
519             MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
520                     idFunction,
521                     matchable.toString(),
522                     idDatatype,
523                     attributeId,
524                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
525             //
526             // Now create an anyOf (OR)
527             //
528             anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
529         }
530         return anyOf;
531     }
532
533     /**
534      * Get Policy Type definitions. This could be previously loaded, or could be
535      * stored in application path, or may need to be pulled from the API.
536      *
537      *
538      * @param policyTypeId Policy Type Id
539      * @return A list of PolicyTypes
540      */
541     private List<ToscaPolicyType> getPolicyTypes(ToscaPolicyTypeIdentifier policyTypeId) {
542         //
543         // Create identifier from the policy
544         //
545         ToscaPolicyTypeIdentifier typeId = new ToscaPolicyTypeIdentifier(policyTypeId);
546         //
547         // Find the Policy Type
548         //
549         ToscaPolicyType policyType = findPolicyType(typeId);
550         if (policyType == null)  {
551             return Collections.emptyList();
552         }
553         //
554         // Create our return object
555         //
556         List<ToscaPolicyType> listTypes = new ArrayList<>();
557         listTypes.add(policyType);
558         //
559         // Look for parent policy types that could also contain matchable properties
560         //
561         ToscaPolicyType childPolicyType = policyType;
562         while (! childPolicyType.getDerivedFrom().startsWith("tosca.policies.Root")) {
563             //
564             // Create parent policy type id.
565             //
566             // We will have to assume the same version between child and the
567             // parent policy type it derives from.
568             //
569             // Or do we assume 1.0.0?
570             //
571             ToscaPolicyTypeIdentifier parentId = new ToscaPolicyTypeIdentifier(childPolicyType.getDerivedFrom(),
572                     "1.0.0");
573             //
574             // Find the policy type
575             //
576             ToscaPolicyType parentPolicyType = findPolicyType(parentId);
577             if (parentPolicyType == null) {
578                 //
579                 // Probably would be best to throw an exception and
580                 // return nothing back.
581                 //
582                 // But instead we will log a warning
583                 //
584                 LOGGER.warn("Missing parent policy type - proceeding anyway {}", parentId);
585                 //
586                 // Break the loop
587                 //
588                 break;
589             }
590             //
591             // Great save it
592             //
593             listTypes.add(parentPolicyType);
594             //
595             // Move to the next parent
596             //
597             childPolicyType = parentPolicyType;
598         }
599         return listTypes;
600     }
601
602     /**
603      * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or
604      * then tries to find it either locally on disk or pull it from the Policy
605      * Lifecycle API the given TOSCA Policy Type.
606      *
607      * @param policyTypeId ToscaPolicyTypeIdentifier to find
608      * @return ToscaPolicyType object. Can be null if failure.
609      */
610     private ToscaPolicyType findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
611         //
612         // Is it loaded in memory?
613         //
614         ToscaPolicyType policyType = this.matchablePolicyTypes.get(policyTypeId);
615         if (policyType == null)  {
616             //
617             // Load the policy
618             //
619             policyType = this.loadPolicyType(policyTypeId);
620         }
621         //
622         // Yep return it
623         //
624         return policyType;
625     }
626
627     /**
628      * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local
629      * storage. If it does not exist, will then attempt to pull from Policy Lifecycle
630      * API.
631      *
632      * @param policyTypeId ToscaPolicyTypeIdentifier input
633      * @return ToscaPolicyType object. Null if failure.
634      */
635     private ToscaPolicyType loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
636         //
637         // Construct what the file name should be
638         //
639         Path policyTypePath = this.constructLocalFilePath(policyTypeId);
640         //
641         // See if it exists
642         //
643         byte[] bytes;
644         try {
645             //
646             // If it exists locally, read the bytes in
647             //
648             bytes = Files.readAllBytes(policyTypePath);
649         } catch (IOException e) {
650             //
651             // Does not exist locally, so let's GET it from the policy api
652             //
653             LOGGER.error("PolicyType not found in data area yet {}", policyTypePath, e);
654             //
655             // So let's pull it from API REST call and save it locally
656             //
657             return this.pullPolicyType(policyTypeId, policyTypePath);
658         }
659         //
660         // Success - we have read locally the policy type. Now bring it into our
661         // return object.
662         //
663         LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath());
664         try {
665             ToscaServiceTemplate serviceTemplate = standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8),
666                     ToscaServiceTemplate.class);
667             JpaToscaServiceTemplate jtst = new JpaToscaServiceTemplate();
668             jtst.fromAuthorative(serviceTemplate);
669             ToscaServiceTemplate completedJtst = jtst.toAuthorative();
670             //
671             // Search for our Policy Type, there really only should be one but
672             // this is returned as a map.
673             //
674             for ( Entry<String, ToscaPolicyType> entrySet : completedJtst.getPolicyTypes().entrySet()) {
675                 ToscaPolicyType entryPolicyType = entrySet.getValue();
676                 if (policyTypeId.getName().equals(entryPolicyType.getName())
677                         && policyTypeId.getVersion().equals(entryPolicyType.getVersion())) {
678                     LOGGER.info("Found existing local policy type {} {}", entryPolicyType.getName(),
679                             entryPolicyType.getVersion());
680                     //
681                     // Just simply return the policy type right here
682                     //
683                     return entryPolicyType;
684                 } else {
685                     LOGGER.warn("local policy type contains different name version {} {}", entryPolicyType.getName(),
686                             entryPolicyType.getVersion());
687                 }
688             }
689             //
690             // This would be an error, if the file stored does not match what its supposed to be
691             //
692             LOGGER.error("Existing policy type file does not contain right name and version");
693         } catch (CoderException e) {
694             LOGGER.error("Failed to decode tosca template for {}", policyTypePath, e);
695         }
696         //
697         // Hopefully we never get here
698         //
699         LOGGER.error("Failed to find/load policy type {}", policyTypeId);
700         return null;
701     }
702
703     /**
704      * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API.
705      * If successful, will store it locally given the policyTypePath.
706      *
707      * @param policyTypeId ToscaPolicyTypeIdentifier
708      * @param policyTypePath Path object to store locally
709      * @return ToscaPolicyType object. Null if failure.
710      */
711     private synchronized ToscaPolicyType pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId, Path policyTypePath) {
712         //
713         // This is what we return
714         //
715         ToscaPolicyType policyType = null;
716         try {
717             PolicyApiCaller api = new PolicyApiCaller(this.apiRestParameters);
718
719             policyType = api.getPolicyType(policyTypeId);
720         } catch (PolicyApiException e) {
721             LOGGER.error("Failed to make API call", e);
722             LOGGER.error("parameters: {} ", this.apiRestParameters);
723             return null;
724         }
725         LOGGER.info("Successfully pulled {}", policyTypeId);
726         //
727         // Store it locally
728         //
729         try {
730             standardYamlCoder.encode(policyTypePath.toFile(), policyType);
731         } catch (CoderException e) {
732             LOGGER.error("Failed to store {} locally to {}", policyTypeId, policyTypePath, e);
733         }
734         //
735         // Done return the policy type
736         //
737         return policyType;
738     }
739
740     /**
741      * constructLocalFilePath - common method to ensure the name of the local file for the
742      * policy type is the same.
743      *
744      * @param policyTypeId ToscaPolicyTypeIdentifier
745      * @return Path object
746      */
747     private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) {
748         return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-"
749                 + policyTypeId.getVersion() + ".yaml");
750     }
751 }