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