Sonar cleanup and remove duplicate code
[policy/engine.git] / ONAP-XACML / src / main / java / org / onap / policy / xacml / std / pap / StdEngine.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-XACML
4  * ================================================================================
5  * Copyright (C) 2017-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  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.xacml.std.pap;
22
23 import com.att.research.xacml.api.pap.PAPException;
24 import com.att.research.xacml.api.pap.PDP;
25 import com.att.research.xacml.api.pap.PDPGroup;
26 import com.att.research.xacml.api.pap.PDPPIPConfig;
27 import com.att.research.xacml.api.pap.PDPPolicy;
28 import com.att.research.xacml.api.pap.PDPStatus;
29 import com.att.research.xacml.util.XACMLProperties;
30 import com.google.common.base.Joiner;
31 import com.google.common.base.Splitter;
32 import com.google.common.collect.Sets;
33
34 import java.io.FileInputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.nio.file.FileVisitResult;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.nio.file.SimpleFileVisitor;
43 import java.nio.file.attribute.BasicFileAttributes;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Enumeration;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Properties;
50 import java.util.Set;
51 import java.util.TreeSet;
52
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55 import org.onap.policy.common.logging.eelf.MessageCodes;
56 import org.onap.policy.common.logging.eelf.PolicyLogger;
57 import org.onap.policy.xacml.api.XACMLErrorConstants;
58 import org.onap.policy.xacml.api.pap.OnapPDP;
59 import org.onap.policy.xacml.api.pap.OnapPDPGroup;
60 import org.onap.policy.xacml.api.pap.PAPPolicyEngine;
61
62 /**
63  * This is a simple PAP engine that uses some property files and a simple directory structure in the file system to
64  * manage a policy repository and set of PDP nodes.
65  *
66  *
67  */
68 public class StdEngine extends StdPDPItemSetChangeNotifier implements PAPPolicyEngine {
69     public static final String PIP_PROPERTY_FILE = "pip.properties";
70
71     private static final String STR_ADDGROUP = "addGroup ";
72     private static final String STR_CLASS = "StdEngine";
73     private static final String STR_APPEND_NAME = ".name";
74     private static final String STR_APPEND_DESCRIPTION = ".description";
75     private static final String STR_APPEND_PDPS = ".pdps";
76
77     private static Log logger = LogFactory.getLog(StdEngine.class);
78
79     public static final String PROP_PAP_REPO = "xacml.pap.pdps";
80     public static final String PROP_PAP_GROUPS = "xacml.pap.groups";
81     public static final String PROP_PAP_GROUPS_DEFAULT = "xacml.pap.groups.default";
82     public static final String PROP_PAP_GROUPS_DEFAULT_NAME = "default";
83     // this value will be accessed from XacmlPapServlet so that we know if a default group did not exist
84     // and was just added. This way, we can add the new group to the database.
85     public boolean wasDefaultGroupJustAdded = false;
86
87     protected final Path repository;
88     protected Set<StdPDPGroup> groups;
89
90     /**
91      * StdEngine constructor.
92      *
93      * @throws PAPException PAPException
94      * @throws IOException IOException
95      */
96     public StdEngine() throws PAPException, IOException {
97         //
98         // Get the location in the file system of our repository
99         //
100         this.repository = Paths.get(XACMLProperties.getProperty(PROP_PAP_REPO));
101         //
102         // Initialize
103         //
104         this.intialize();
105     }
106
107     /**
108      * StdEngine constructor.
109      *
110      * @param properties Properties
111      * @throws PAPException PAPException
112      * @throws IOException IOException
113      */
114     public StdEngine(Properties properties) throws PAPException, IOException {
115         //
116         // Get the location in the file system of our repository
117         //
118         this.repository = Paths.get(properties.getProperty(PROP_PAP_REPO));
119         //
120         // Initialize
121         //
122         this.intialize();
123     }
124
125     /**
126      * StdEngine constructor.
127      *
128      * @param repository Path
129      * @throws PAPException PAPException
130      * @throws IOException IOException
131      */
132     public StdEngine(Path repository) throws PAPException, IOException {
133         //
134         // Save our location
135         //
136         this.repository = repository;
137         //
138         // Initialize
139         //
140         this.intialize();
141     }
142
143     private void intialize() throws PAPException, IOException {
144         //
145         // Sanity check the repository path
146         //
147         if (this.repository == null) {
148             throw new PAPException("No repository specified.");
149         }
150         if (! this.repository.toFile().exists()) {
151             Files.createDirectory(repository);
152         }
153         if (! this.repository.toFile().isDirectory()) {
154             throw new PAPException("Repository is NOT a directory: " + this.repository.toAbsolutePath());
155         }
156         if (! this.repository.toFile().canWrite()) {
157             throw new PAPException("Repository is NOT writable: " + this.repository.toAbsolutePath());
158         }
159         //
160         // Load our groups
161         //
162         this.loadGroups();
163     }
164
165     private void loadGroups() throws PAPException {
166         //
167         // Create a properties object
168         //
169         Properties properties = new Properties();
170         Path file = Paths.get(this.repository.toString(), XACMLProperties.XACML_PROPERTIES_NAME);
171         try {
172             //
173             // Load the properties
174             //
175             try (InputStream is = new FileInputStream(file.toFile())) {
176                 properties.load(is);
177             }
178
179             //
180             // Parse it
181             //
182             this.groups = this.readProperties(this.repository, properties);
183         } catch (IOException e) {
184             PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS, "Failed to load properties file");
185             this.groups = new HashSet<>();
186         }
187         //
188         // Initialize the default group
189         //
190         PDPGroup defaultGroup = this.initializeDefaultGroup(file, properties);
191         logger.info("Default group is: " + defaultGroup.getId() + "=" + defaultGroup.getName());
192     }
193
194     private PDPGroup initializeDefaultGroup(Path file, Properties properties) throws PAPException {
195         wasDefaultGroupJustAdded = false;
196         //
197         // Make sure we have the default group
198         //
199         PDPGroup group = this.getDefaultGroup();
200         if (group != null) {
201             wasDefaultGroupJustAdded = true;
202             return group;
203         }
204         //
205         // We don't have the default group, create it
206         //
207         String defaultId = properties.getProperty(PROP_PAP_GROUPS_DEFAULT, PROP_PAP_GROUPS_DEFAULT_NAME);
208         if ("".equals(defaultId)) {
209             defaultId = PROP_PAP_GROUPS_DEFAULT_NAME;
210         }
211         logger.warn("Default group does NOT exist, creating " + defaultId);
212         Path defaultPath = Paths.get(this.repository.toString(), defaultId);
213         try {
214             //
215             // Does it exist?
216             //
217             if (! defaultPath.toFile().exists()) {
218                 //
219                 // Create its directory
220                 //
221                 Files.createDirectory(defaultPath);
222                 //
223                 // Create property files
224                 //
225                 {
226                     Properties props = new Properties();
227                     props.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
228                     props.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
229                     Path policyPath = Paths.get(defaultPath.toAbsolutePath().toString(), "xacml.policy.properties");
230                     Files.createFile(policyPath);
231                     try (OutputStream os = Files.newOutputStream(policyPath)) {
232                         props.store(os, "");
233                     } catch (IOException e) {
234                         PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS,
235                                 "Failed to write default policy properties");
236                     }
237                 }
238                 {
239                     Properties props = new Properties();
240                     props = setPipProperties(props);
241                     Path pipPath = Paths.get(defaultPath.toAbsolutePath().toString(), "xacml.pip.properties");
242                     Files.createFile(pipPath);
243                     try (OutputStream os = Files.newOutputStream(pipPath)) {
244                         props.store(os, "");
245                     } catch (IOException e) {
246                         PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS,
247                                 "Failed to write default pip properties");
248                     }
249                 }
250             }
251             //
252             // Create the default group
253             //
254             StdPDPGroup newDefault = new StdPDPGroup(defaultId, true, PROP_PAP_GROUPS_DEFAULT_NAME,
255                     "The default group where new PDP's are put.", defaultPath);
256             //
257             // Add it to our list
258             //
259             this.groups.add(newDefault);
260             //
261             // Save our properties out since we have
262             // a new default group.
263             //
264             StdEngine.setGroupProperties(newDefault, properties);
265             //
266             // Save it to disk
267             //
268             try {
269                 try (OutputStream os = Files.newOutputStream(file)) {
270                     properties.store(os, "");
271                 }
272             } catch (IOException e) {
273                 PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, STR_CLASS,
274                         "Failed to save properties with new default group information.");
275             }
276             //
277             // Return it
278             //
279             wasDefaultGroupJustAdded = true;
280             return newDefault;
281         } catch (IOException e) {
282             PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, STR_CLASS, "Failed to create default group");
283             throw new PAPException("Failed to create default group");
284         }
285     }
286
287     @Override
288     public OnapPDPGroup getDefaultGroup() throws PAPException {
289         for (OnapPDPGroup group : this.groups) {
290             if (group.isDefaultGroup()) {
291                 return group;
292             }
293         }
294         //
295         // Default group doesn't exist
296         //
297         return null;
298     }
299
300     @Override
301     public OnapPDPGroup getGroup(String id) throws PAPException {
302         for (OnapPDPGroup g : this.groups) {
303             if (g.getId().equals(id)) {
304                 return g;
305             }
306         }
307         return null;
308     }
309
310     @Override
311     public void newGroup(String name, String description) throws PAPException {
312         //
313         // Null check
314         //
315         if (name == null) {
316             throw new NullPointerException();
317         }
318         //
319         // Do we already have this group?
320         //
321         for (PDPGroup group : this.groups) {
322             if (group.getName().equals(name)) {
323                 throw new PAPException("Group with this name=" + name + " already exists.");
324             }
325         }
326
327         // create an Id that can be used as a file name and a properties file key.
328         // Ids must not contain \/:*?"<>|=,;
329         // The ID must also be unique within the current set of PDPGroups.
330         String id = createNewPdpGroupId(name);
331
332         //
333         // Construct the directory path
334         //
335         Path groupPath = Paths.get(this.repository.toString(), id);
336         //
337         // If it exists already
338         //
339         if (Files.exists(groupPath)) {
340             logger.warn(STR_ADDGROUP + id + " directory exists");
341         } else {
342             try {
343                 //
344                 // Create the directory
345                 //
346                 Files.createDirectory(groupPath);
347             } catch (IOException e) {
348                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS, "Failed to create " + groupPath);
349                 throw new PAPException("Failed to create " + id);
350             }
351         }
352         //
353         // Create the Policies
354         //
355
356         Path policyProperties = Paths.get(groupPath.toString(), "xacml.policy.properties");
357         if (policyProperties.toFile().exists()) {
358             logger.warn(STR_ADDGROUP + id + " file exists");
359         } else {
360             Properties props = new Properties();
361             props.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
362             props.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
363             try {
364                 Files.createFile(policyProperties);
365                 try (OutputStream os = Files.newOutputStream(policyProperties)) {
366                     props.store(os, "");
367                 }
368             } catch (IOException e) {
369                 PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, STR_CLASS, "Failed to create policyProperties");
370                 throw new PAPException("Failed to create " + id);
371             }
372         }
373         //
374         // Create the PIP config
375         //
376         Path pipProperties = Paths.get(groupPath.toString(), "xacml.pip.properties");
377         Properties props = new Properties();
378         if (pipProperties.toFile().exists()) {
379             logger.warn(STR_ADDGROUP + id + " file exists.");
380         } else {
381             try {
382                 props = setPipProperties(props);
383                 Files.createFile(pipProperties);
384                 try (OutputStream os = Files.newOutputStream(pipProperties)) {
385                     props.store(os, "");
386                 }
387             } catch (IOException e) {
388                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS, "Failed to create pipProperties");
389                 throw new PAPException("Failed to create " + id);
390             }
391
392         }
393         //
394         // Ok now add it
395         //
396         StdPDPGroup newGroup = new StdPDPGroup(id, name, description, groupPath);
397         // Add the default PIP configuration.
398         String list = props.getProperty(XACMLProperties.PROP_PIP_ENGINES);
399         if (list != null && list.length() > 0) {
400             Set<PDPPIPConfig> pipConfigs = new HashSet<>();
401             for (String pipID : list.split("[,]")) {
402                 StdPDPPIPConfig config = new StdPDPPIPConfig(pipID, props);
403                 if (config.isConfigured()) {
404                     pipConfigs.add(config);
405                 }
406             }
407             newGroup.setPipConfigs(pipConfigs);
408         }
409         if (this.groups.add(newGroup)) {
410             // save the new group in our properties and notify any listeners of the change
411             groupChanged(newGroup);
412         }
413
414     }
415
416     /**
417      * Helper to create a new Group ID. Use the Name field to create the Id. The Name is expected to not be null; if it
418      * is then this method throws an exception. The name is supposed to be unique within the current set of groups, so
419      * creating the ID based on the name will create a unique string.
420      *
421      * @param name String
422      * @return String
423      */
424     private String createNewPdpGroupId(String name) {
425         String id = name;
426         // replace "bad" characters with sequences that will be ok for file names and properties keys.
427         id = id.replace(" ", "_sp_");
428         id = id.replace("\t", "_tab_");
429         id = id.replace("\\", "_bksl_");
430         id = id.replace("/", "_sl_");
431         id = id.replace(":", "_col_");
432         id = id.replace("*", "_ast_");
433         id = id.replace("?", "_q_");
434         id = id.replace("\"", "_quo_");
435         id = id.replace("<", "_lt_");
436         id = id.replace(">", "_gt_");
437         id = id.replace("|", "_bar_");
438         id = id.replace("=", "_eq_");
439         id = id.replace(",", "_com_");
440         id = id.replace(";", "_scom_");
441
442         return id;
443     }
444
445     @Override
446     public OnapPDP getPDP(String pdpId) throws PAPException {
447         for (OnapPDPGroup group : this.groups) {
448             for (OnapPDP pdp : group.getOnapPdps()) {
449                 if (pdp.getId().equals(pdpId)) {
450                     return pdp;
451                 }
452             }
453         }
454         return null;
455     }
456
457     @Override
458     public void movePDP(OnapPDP pdp, OnapPDPGroup newGroup) throws PAPException {
459         if (newGroup == null) {
460             throw new NullPointerException("You must specify which group the PDP will belong to.");
461         }
462         PDPGroup currentGroup = this.getPDPGroup(pdp);
463         if (currentGroup == null) {
464             throw new PAPException("PDP must already belong to a group.");
465         }
466         if (currentGroup.equals(newGroup)) {
467             logger.warn("Already in that group.");
468             return;
469         }
470         if (currentGroup instanceof StdPDPGroup && newGroup instanceof StdPDPGroup) {
471             if (((StdPDPGroup) currentGroup).removePDP(pdp)) {
472                 boolean result = ((StdPDPGroup) newGroup).addPDP(pdp);
473                 if (result) {
474                     //
475                     // Save the configuration
476                     //
477                     this.doSave();
478                 } else {
479                     PolicyLogger.error("Failed to add to new group, putting back into original group.");
480                     if (!((StdPDPGroup) currentGroup).removePDP(pdp)) {
481                         PolicyLogger
482                                 .error(MessageCodes.ERROR_DATA_ISSUE + "Failed to put PDP back into original group.");
483                     }
484                 }
485             }
486         } else {
487             String message = "Unknown PDP group class: " + newGroup.getClass().getCanonicalName() + " and "
488                     + currentGroup.getClass().getCanonicalName();
489             logger.warn(message);
490             throw new PAPException(message);
491         }
492     }
493
494     @Override
495     public void updatePDP(OnapPDP pdp) throws PAPException {
496         PDP currentPdp = this.getPDP(pdp.getId());
497         if (currentPdp == null) {
498             String message = "Unknown PDP id '" + pdp.getId() + "'";
499             logger.warn(message);
500             throw new PAPException(message);
501         }
502
503         // the only things that the user can change are name and description
504         currentPdp.setDescription(pdp.getDescription());
505         currentPdp.setName(pdp.getName());
506         if (currentPdp instanceof OnapPDP) {
507             ((OnapPDP) currentPdp).setJmxPort(pdp.getJmxPort());
508         }
509         this.doSave();
510     }
511
512     @Override
513     public void removePDP(OnapPDP pdp) throws PAPException {
514         PDPGroup group = this.getPDPGroup(pdp);
515         if (group == null) {
516             throw new NullPointerException();
517         }
518         if (group instanceof StdPDPGroup) {
519             boolean result = ((StdPDPGroup) group).removePDP(pdp);
520             if (result) {
521                 this.doSave();
522             }
523             return;
524         }
525         String message = "Unknown PDP group class: " + group.getClass().getCanonicalName();
526         logger.warn(message);
527         throw new PAPException(message);
528     }
529
530     @Override
531     /**
532      * Should never be called - Detailed status is held on the PDP, not the PAP
533      */
534     public PDPStatus getStatus(OnapPDP pdp) throws PAPException {
535         return getPDP(pdp.getId()).getStatus();
536     }
537
538     @Override
539     public void publishPolicy(String id, String name, boolean isRoot, InputStream policy, OnapPDPGroup group)
540             throws PAPException {
541         if (group == null) {
542             throw new NullPointerException();
543         }
544         if (group instanceof StdPDPGroup && this.groups.contains(group)) {
545             ((StdPDPGroup) group).publishPolicy(id, name, isRoot, policy);
546             return;
547         }
548         logger.warn("unknown PDP Group: " + group);
549         throw new PAPException("Unknown PDP Group: " + group.getId());
550     }
551
552     @Override
553     public void copyPolicy(PDPPolicy policy, OnapPDPGroup group) throws PAPException {
554         //
555         // Currently not used on the PAP side. This is done by ((StdPDPGroup) group).copyPolicyToFile
556         //
557     }
558
559     @Override
560     public void removePolicy(PDPPolicy policy, OnapPDPGroup group) throws PAPException {
561         if (group == null) {
562             throw new NullPointerException();
563         }
564         if (group instanceof StdPDPGroup && this.groups.contains(group)) {
565             ((StdPDPGroup) group).removePolicy(policy);
566             return;
567         }
568         logger.warn("unknown PDP Group: " + group);
569         throw new PAPException("Unknown PDP Group: " + group.getId());
570     }
571
572     //
573     // HELPER methods
574     //
575
576     private Set<StdPDPGroup> readProperties(Path repository, Properties properties) throws PAPException {
577         Set<StdPDPGroup> pdpGroups = new HashSet<>();
578         //
579         // See if there is a groups property
580         //
581         String groupList = properties.getProperty(PROP_PAP_GROUPS, "");
582         if (groupList == null) {
583             logger.warn("null group list " + PROP_PAP_GROUPS);
584             groupList = "";
585         }
586         if (logger.isDebugEnabled()) {
587             logger.debug("group list: " + groupList);
588         }
589         //
590         // Iterate the groups, converting to a set ensures we have unique groups.
591         //
592         for (String id : Splitter.on(',').trimResults().omitEmptyStrings().split(groupList)) {
593             //
594             // Add our Group Object
595             //
596             StdPDPGroup newGroup = new StdPDPGroup(id.trim(),
597                     id.equals(properties.getProperty(PROP_PAP_GROUPS_DEFAULT, PROP_PAP_GROUPS_DEFAULT_NAME)),
598                     properties, Paths.get(repository.toString(), id));
599
600             //
601             // Add it in
602             //
603             pdpGroups.add(newGroup);
604         }
605         //
606         // Dump what we got
607         //
608         if (logger.isDebugEnabled()) {
609             logger.debug("PDP Group List: " + pdpGroups.toString());
610         }
611         return pdpGroups;
612     }
613
614     private void saveConfiguration() throws PAPException, IOException {
615         //
616         // Create our properties object
617         //
618         Properties properties = new Properties() {
619             private static final long serialVersionUID = 1L;
620
621             // For Debugging it is helpful for the file to be in a sorted order,
622             // any by returning the keys in the natural Alpha order for strings we get close enough.
623             // TreeSet is sorted, and this just overrides the normal Properties method to get the keys.
624             @Override
625             public synchronized Enumeration<Object> keys() {
626                 return Collections.enumeration(new TreeSet<Object>(super.keySet()));
627             }
628         };
629         //
630         // Iterate our groups
631         //
632         List<String> ids = new ArrayList<>();
633         for (PDPGroup group : this.groups) {
634             ids.add(group.getId());
635             properties.setProperty(group.getId() + STR_APPEND_NAME, group.getName() == null ? "" : group.getName());
636             properties.setProperty(group.getId() + STR_APPEND_DESCRIPTION,
637                     group.getDescription() == null ? "" : group.getDescription());
638             //
639             // Iterate its PDPs
640             //
641             List<String> pdps = new ArrayList<>();
642             for (PDP pdp : group.getPdps()) {
643                 pdps.add(pdp.getId());
644                 properties.setProperty(pdp.getId() + STR_APPEND_NAME, pdp.getName() == null ? "" : pdp.getName());
645                 properties.setProperty(pdp.getId() + STR_APPEND_DESCRIPTION,
646                         pdp.getDescription() == null ? "" : pdp.getDescription());
647                 if (pdp instanceof OnapPDP) {
648                     properties.setProperty(pdp.getId() + ".jmxport",
649                             (((OnapPDP) pdp).getJmxPort() == 0 ? "" : ((OnapPDP) pdp).getJmxPort()).toString());
650                 }
651             }
652             String pdpList = "";
653             if (pdps.size() == 1) {
654                 pdpList = pdps.get(0);
655             } else if (pdps.size() > 1) {
656                 pdpList = Joiner.on(',').skipNulls().join(pdps);
657             }
658             if (logger.isDebugEnabled()) {
659                 logger.debug("Group " + group.getId() + " PDPS: " + pdpList);
660             }
661             properties.setProperty(group.getId() + STR_APPEND_PDPS, pdpList);
662         }
663         if (ids.isEmpty()) {
664             throw new PAPException("Inconsistency - we have NO groups. We should have at least one.");
665         }
666         String groupList = "";
667         if (ids.size() == 1) {
668             groupList = ids.get(0);
669         } else if (ids.size() > 1) {
670             groupList = Joiner.on(',').skipNulls().join(ids);
671         }
672         logger.info("New Group List: " + groupList);
673
674         properties.setProperty(PROP_PAP_GROUPS, groupList);
675         //
676         // Get the default group
677         //
678         PDPGroup defaultGroup = this.getDefaultGroup();
679         if (defaultGroup == null) {
680             throw new PAPException("Invalid state - no default group.");
681         }
682         properties.setProperty(PROP_PAP_GROUPS_DEFAULT, defaultGroup.getId());
683         //
684         // Now we can save the file
685         //
686         Path file = Paths.get(this.repository.toString(), "xacml.properties");
687         try (OutputStream os = Files.newOutputStream(file)) {
688             properties.store(os, "");
689         }
690     }
691
692     /**
693      * setGroupProperties.
694      *
695      * @param group PDPGroup
696      * @param properties Properties
697      */
698     public static void setGroupProperties(PDPGroup group, Properties properties) {
699         //
700         // make sure its in the list of groups
701         //
702         Iterable<String> groups =
703                 Splitter.on(',').trimResults().omitEmptyStrings().split(properties.getProperty(PROP_PAP_GROUPS, ""));
704         boolean inList = false;
705         for (String g : groups) {
706             if (g.equals(group.getId())) {
707                 inList = true;
708             }
709         }
710         if (!inList) {
711             Set<String> grps = Sets.newHashSet(groups);
712             grps.add(group.getId());
713             String newGroupList;
714             if (grps.size() == 1) {
715                 newGroupList = grps.iterator().next();
716             } else if (grps.size() > 1) {
717                 newGroupList = Joiner.on(',').skipNulls().join(grps);
718             } else {
719                 newGroupList = "";
720             }
721             logger.info("New Group List: " + newGroupList);
722             properties.setProperty(PROP_PAP_GROUPS, newGroupList);
723         }
724         //
725         // Set its properties
726         //
727         properties.setProperty(group.getId() + STR_APPEND_NAME, group.getName());
728         properties.setProperty(group.getId() + STR_APPEND_DESCRIPTION, group.getDescription());
729         //
730         // Set its PDP list
731         //
732         if (!group.getPdps().isEmpty()) {
733             String pdpList = "";
734             if (group.getPdps().size() == 1) {
735                 pdpList = group.getPdps().iterator().next().getId();
736             } else if (group.getPdps().size() > 1) {
737                 Set<String> ids = new HashSet<>();
738                 for (PDP pdp : group.getPdps()) {
739                     ids.add(pdp.getId());
740                 }
741                 pdpList = Joiner.on(',').skipNulls().join(ids);
742             }
743             properties.setProperty(group.getId() + STR_APPEND_PDPS, pdpList);
744         } else {
745             properties.setProperty(group.getId() + STR_APPEND_PDPS, "");
746         }
747     }
748
749     /**
750      * changed.
751      */
752     public void changed() {
753         if (logger.isDebugEnabled()) {
754             logger.debug("changed");
755         }
756         this.doSave();
757         this.fireChanged();
758     }
759
760     /**
761      * groupChanged.
762      *
763      * @param group OnapPDPGroup
764      */
765     public void groupChanged(OnapPDPGroup group) {
766         if (logger.isDebugEnabled()) {
767             logger.debug("groupChanged: " + group);
768         }
769         this.doSave();
770         this.firePDPGroupChanged(group);
771     }
772
773     /**
774      * pdpChanged.
775      *
776      * @param pdp OnapPDP
777      */
778     public void pdpChanged(OnapPDP pdp) {
779         if (logger.isDebugEnabled()) {
780             logger.debug("pdpChanged: " + pdp);
781         }
782         this.doSave();
783         this.firePDPChanged(pdp);
784     }
785
786     private void doSave() {
787         try {
788             //
789             // Save the configuration
790             //
791             this.saveConfiguration();
792         } catch (IOException | PAPException e) {
793             PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, STR_CLASS, "Failed to save configuration");
794         }
795     }
796
797     private Properties setPipProperties(Properties props) {
798         props.setProperty(XACMLProperties.PROP_PIP_ENGINES, "AAF");
799         props.setProperty("AAF.name", "AAFEngine");
800         props.setProperty("AAF.description", "AAFEngine to communicate with AAF to take decisions");
801         props.setProperty("AAF.classname", "org.onap.policy.xacml.std.pip.engines.aaf.AAFEngine");
802         // read from PIP properties file.
803         Path file = Paths.get(PIP_PROPERTY_FILE);
804         if (file.toFile().exists()) {
805             InputStream in;
806             Properties prop = new Properties();
807             try {
808                 in = new FileInputStream(file.toFile());
809                 prop.load(in);
810             } catch (IOException e) {
811                 PolicyLogger.error(
812                         XACMLErrorConstants.ERROR_SYSTEM_ERROR + "can not load the pip properties from file" + e);
813             }
814             props = prop;
815         }
816         return props;
817     }
818
819     @Override
820     public Set<OnapPDPGroup> getOnapPDPGroups() throws PAPException {
821         final Set<OnapPDPGroup> grps = new HashSet<>();
822         for (OnapPDPGroup g : this.groups) {
823             grps.add(g);
824         }
825         return Collections.unmodifiableSet(grps);
826     }
827
828     @Override
829     public OnapPDPGroup getPDPGroup(OnapPDP pdp) throws PAPException {
830         for (OnapPDPGroup group : this.groups) {
831             if (group.getPdps().contains(pdp)) {
832                 return group;
833             }
834         }
835         return null;
836     }
837
838     @Override
839     public void setDefaultGroup(OnapPDPGroup group) throws PAPException {
840         boolean changesMade = false;
841         for (OnapPDPGroup theGroup : groups) {
842             if (theGroup.getId().equals(group.getId())) {
843                 if (!theGroup.isDefaultGroup()) {
844                     if (theGroup instanceof StdPDPGroup) {
845                         ((StdPDPGroup) theGroup).setDefault(true);
846                         changesMade = true;
847                     } else {
848                         throw new IllegalArgumentException(
849                                 "Group in groups of unknown type '" + theGroup.getClass().getName() + "'");
850                     }
851                 }
852             } else {
853                 // not the new default group
854                 if (theGroup.isDefaultGroup()) {
855                     if (theGroup instanceof StdPDPGroup) {
856                         ((StdPDPGroup) theGroup).setDefault(false);
857                         changesMade = true;
858                     } else {
859                         throw new IllegalArgumentException(
860                                 "Group in groups of unknown type '" + theGroup.getClass().getName() + "'");
861                     }
862                 }
863             }
864         }
865         if (changesMade) {
866             this.doSave();
867         }
868     }
869
870     @Override
871     public void newPDP(String id, OnapPDPGroup group, String name, String description, int jmxport)
872             throws PAPException {
873         if (group == null) {
874             throw new PAPException("You must specify which group the PDP will belong to.");
875         }
876         if (!this.groups.contains(group)) {
877             throw new PAPException("Unknown group, not in our list.");
878         }
879         for (OnapPDP p : group.getOnapPdps()) {
880             if (p.getId().equals(id)) {
881                 throw new PAPException("A PDP with this ID exists.");
882             }
883         }
884         if (group instanceof StdPDPGroup) {
885             StdPDP pdp = new StdPDP(id, name, description, jmxport);
886             if (((StdPDPGroup) group).addPDP(pdp)) {
887                 //
888                 // Save the properties and notify any listeners
889                 //
890                 pdpChanged(pdp);
891             }
892         }
893     }
894
895     @Override
896     public void updateGroup(OnapPDPGroup group, String userName) throws PAPException {
897         // To pass the userId for PDP Audit log maintenance.
898
899     }
900
901     @Override
902     public void updateGroup(OnapPDPGroup group) throws PAPException {
903         if (group == null || group.getId() == null) {
904             throw new PAPException("Group or id is null");
905         }
906         if (group.getName() == null || group.getName().trim().length() == 0) {
907             throw new PAPException("New name for group cannot be null or blank");
908         }
909         StdPDPGroup existingGroup = (StdPDPGroup) getGroup(group.getId());
910         if (existingGroup == null) {
911             throw new PAPException("Update found no existing group with id '" + group.getId() + "'");
912         }
913
914         // We do dramatically different things when the Name changes
915         // because the Name is essentially the identity of the group (as the User knows it) so when the Identity changes
916         // we have to change the group ID.
917         if (group.getName().equals(existingGroup.getName())) {
918
919             // update the disk
920             try {
921                 ((StdPDPGroup) group).saveGroupConfiguration();
922             } catch (IOException e) {
923                 throw new PAPException(
924                         "Unable to save new configuration for '" + group.getName() + "': " + e.getMessage(), e);
925             }
926             // update the group in the set by simply replacing the old instance with the new one
927             this.groups.remove(existingGroup);
928             this.groups.add((StdPDPGroup) group);
929
930         } else {
931             // the name/identity of the group has changed
932             // generate the new id
933             String newId = createNewPdpGroupId(group.getName());
934
935             // make sure no other group uses the new id
936             for (OnapPDPGroup g : groups) {
937                 if (g.getId().equals(newId)) {
938                     throw new PAPException("Replacement name maps to ID '" + newId + "' which is already in use");
939                 }
940             }
941             ((StdPDPGroup) group).setId(newId);
942
943             // rename the existing directory to the new id
944             Path oldPath = existingGroup.getDirectory();
945             Path newPath = Paths.get(oldPath.getParent().toString(), newId);
946             ((StdPDPGroup) group).setDirectory(newPath);
947
948             try {
949                 boolean success = oldPath.toFile().renameTo(newPath.toFile());
950                 if (!success) {
951                     throw new PAPException("Unable to rename directory; reason unknown");
952                 }
953             } catch (Exception e) {
954                 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, STR_CLASS, "Unable to rename directory");
955                 throw new PAPException(
956                         "Unable to move directory from '" + oldPath + "' to '" + newPath + "': " + e.getMessage(), e);
957             }
958             // update the disk
959             try {
960                 ((StdPDPGroup) group).saveGroupConfiguration();
961             } catch (IOException e) {
962                 throw new PAPException(
963                         "Unable to save new configuration for '" + group.getName() + "': " + e.getMessage(), e);
964             }
965
966             // save the new group into the Set
967             groups.remove(existingGroup);
968             groups.add((StdPDPGroup) group);
969
970         }
971
972         // perhaps only the group changed, but if the name/id changed it may look to a listener like more than one group
973         changed();
974
975     }
976
977     @Override
978     public void removeGroup(OnapPDPGroup group, OnapPDPGroup newGroup) throws PAPException {
979         if (group == null) {
980             throw new NullPointerException();
981         }
982         //
983         // Does this group exist?
984         //
985         if (!this.groups.contains(group)) {
986             PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "This group doesn't exist.");
987             throw new PAPException("The group '" + group.getId() + "' does not exist");
988         }
989         //
990         // Is it the default group?
991         //
992         if (group.isDefaultGroup()) {
993             throw new PAPException("You cannot delete the default group.");
994         }
995         Set<OnapPDP> pdps = group.getOnapPdps();
996         //
997         // Are there PDPs? If so, then we need a target group
998         //
999         if (!pdps.isEmpty() && newGroup == null) {
1000             throw new NullPointerException(
1001                     "Group targeted for deletion has PDPs, you must provide a new group for them.");
1002         }
1003         //
1004         // Move the PDPs
1005         //
1006         if (!pdps.isEmpty()) {
1007             if (!(newGroup instanceof StdPDPGroup)) {
1008                 throw new PAPException("Unexpected class for newGroup: " + newGroup.getClass().getCanonicalName());
1009             }
1010             // The movePDP function will modify the set of PDPs in the group.
1011             // To avoid concurrent modification exceptions we need to duplicate the list before calling that function.
1012             List<OnapPDP> pdpList = new ArrayList<>();
1013             for (OnapPDP pdp : pdps) {
1014                 pdpList.add(pdp);
1015             }
1016             // now we can use the PDPs from the list without having ConcurrentAccessExceptions
1017             for (OnapPDP pdp : pdpList) {
1018                 this.movePDP(pdp, newGroup);
1019             }
1020         }
1021         //
1022         // remove the directory for the group
1023         //
1024         String id = group.getId();
1025         Path groupPath = Paths.get(this.repository.toString(), id);
1026         //
1027         // If it exists already
1028         //
1029         if (! groupPath.toFile().exists()) {
1030             logger.warn("removeGroup " + id + " directory does not exist" + groupPath.toString());
1031         } else {
1032             try {
1033                 Files.walkFileTree(groupPath, new SimpleFileVisitor<Path>() {
1034
1035                     @Override
1036                     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1037                         Files.delete(file);
1038                         return super.visitFile(file, attrs);
1039                     }
1040
1041                 });
1042                 //
1043                 // delete the directory
1044                 //
1045                 Files.delete(groupPath);
1046             } catch (IOException e) {
1047                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, STR_CLASS, "Failed to delete " + groupPath);
1048                 throw new PAPException("Failed to delete " + id);
1049             }
1050         }
1051
1052         // remove the group from the set of all groups
1053         groups.remove(group);
1054
1055         //
1056         // Save changes
1057         //
1058         changed();
1059         this.doSave();
1060     }
1061
1062 }