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