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