private void loadGroups() throws PAPException {
    //
    // Create a properties object
    //
    Properties properties = new Properties();
    Path file = Paths.get(this.repository.toString(), XACMLProperties.XACML_PROPERTIES_NAME);
    try {
      //
      // Load the properties
      //
      try (InputStream is = new FileInputStream(file.toFile())) {
        properties.load(is);
      }

      //
      // Parse it
      //
      this.groups = this.readProperties(this.repository, properties);
    } catch (IOException e) {
      logger.error("Failed to load " + file.toAbsolutePath().toString());
      this.groups = new HashSet<StdPDPGroup>();
    }
    //
    // Initialize the default group
    //
    PDPGroup defaultGroup = this.initializeDefaultGroup(file, properties);
    logger.info("Default group is: " + defaultGroup.getId() + "=" + defaultGroup.getName());
  }
 @Override
 public PDPGroup getPDPGroup(PDP pdp) throws PAPException {
   for (PDPGroup group : this.groups) {
     if (group.getPdps().contains(pdp)) {
       return group;
     }
   }
   return null;
 }
 @Override
 public PDPGroup getGroup(String id) throws PAPException {
   for (PDPGroup g : this.groups) {
     if (g.getId().equals(id)) {
       return g;
     }
   }
   return null;
 }
 @Override
 public PDP getPDP(String pdpId) throws PAPException {
   for (PDPGroup group : this.groups) {
     for (PDP pdp : group.getPdps()) {
       if (pdp.getId().equals(pdpId)) {
         return pdp;
       }
     }
   }
   return null;
 }
 @Override
 public PDPGroup getDefaultGroup() throws PAPException {
   for (PDPGroup group : this.groups) {
     if (group.isDefaultGroup()) {
       return group;
     }
   }
   //
   // Default group doesn't exist
   //
   return null;
 }
 @Override
 public void newPDP(String id, PDPGroup group, String name, String description)
     throws PAPException, NullPointerException {
   if (group == null) {
     throw new PAPException("You must specify which group the PDP will belong to.");
   }
   if (!this.groups.contains(group)) {
     throw new PAPException("Unknown group, not in our list.");
   }
   for (PDP p : group.getPdps()) {
     if (p.getId().equals(id)) {
       throw new PAPException("A PDP with this ID exists.");
     }
   }
   if (group instanceof StdPDPGroup) {
     StdPDP pdp = new StdPDP(id, name, description);
     if (((StdPDPGroup) group).addPDP(pdp)) {
       //
       // Save the properties and notify any listeners
       //
       pdpChanged(pdp);
       return;
     }
   }
 }
 @Override
 public void removePDP(PDP pdp) throws PAPException {
   PDPGroup group = this.getPDPGroup(pdp);
   if (group == null) {
     throw new NullPointerException();
   }
   if (group instanceof StdPDPGroup) {
     boolean result = ((StdPDPGroup) group).removePDP(pdp);
     if (result) {
       this.doSave();
     }
     return;
   }
   String message = "Unknown PDP group class: " + group.getClass().getCanonicalName();
   logger.warn(message);
   throw new PAPException(message);
 }
  @Override
  public void SetDefaultGroup(PDPGroup group) throws PAPException {

    boolean changesMade = false;
    for (PDPGroup aGroup : groups) {
      if (aGroup.getId().equals(group.getId())) {
        if (!aGroup.isDefaultGroup()) {
          // TODO - since the original code checked for type we do also.
          if (aGroup instanceof StdPDPGroup) {
            ((StdPDPGroup) aGroup).setDefault(true);
            changesMade = true;
          } else {
            throw new IllegalArgumentException(
                "Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
          }
        }
      } else {
        // not the new default group
        if (aGroup.isDefaultGroup()) {
          // TODO - since the original code checked for type we do also.
          if (aGroup instanceof StdPDPGroup) {
            ((StdPDPGroup) aGroup).setDefault(false);
            changesMade = true;
          } else {
            throw new IllegalArgumentException(
                "Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
          }
        }
      }
    }
    if (changesMade) {
      this.doSave();
    }
  }
 @Override
 public void removePolicy(PDPPolicy policy, PDPGroup group) throws PAPException {
   if (group == null) {
     throw new NullPointerException();
   }
   if (group instanceof StdPDPGroup && this.groups.contains(group)) {
     ((StdPDPGroup) group).removePolicy(policy);
     return;
   }
   logger.warn("unknown PDP Group: " + group);
   throw new PAPException("Unknown PDP Group: " + group.getId());
 }
 @Override
 public void movePDP(PDP pdp, PDPGroup newGroup) throws PAPException {
   if (newGroup == null) {
     throw new NullPointerException("You must specify which group the PDP will belong to.");
   }
   PDPGroup currentGroup = this.getPDPGroup(pdp);
   if (currentGroup == null) {
     throw new PAPException("PDP must already belong to a group.");
   }
   if (currentGroup.equals(newGroup)) {
     logger.warn("Already in that group.");
     return;
   }
   if (currentGroup instanceof StdPDPGroup && newGroup instanceof StdPDPGroup) {
     if (((StdPDPGroup) currentGroup).removePDP(pdp)) {
       boolean result = ((StdPDPGroup) newGroup).addPDP(pdp);
       if (result) {
         //
         // Save the configuration
         //
         this.doSave();
       } else {
         logger.error("Failed to add to new group, putting back into original group.");
         if (!((StdPDPGroup) currentGroup).removePDP(pdp)) {
           logger.error("Failed to put PDP back into original group.");
         }
       }
     }
   } else {
     String message =
         "Unknown PDP group class: "
             + newGroup.getClass().getCanonicalName()
             + " and "
             + currentGroup.getClass().getCanonicalName();
     logger.warn(message);
     throw new PAPException(message);
   }
 }
 @Override
 public void publishPolicy(
     String id, String name, boolean isRoot, InputStream policy, PDPGroup group)
     throws PAPException {
   if (group == null) {
     throw new NullPointerException();
   }
   if (group instanceof StdPDPGroup && this.groups.contains(group)) {
     ((StdPDPGroup) group).publishPolicy(id, name, isRoot, policy);
     return;
   }
   logger.warn("unknown PDP Group: " + group);
   throw new PAPException("Unknown PDP Group: " + group.getId());
 }
  public static void setGroupProperties(PDPGroup group, Properties properties) {
    //
    // make sure its in the list of groups
    //
    Iterable<String> groups =
        Splitter.on(',')
            .trimResults()
            .omitEmptyStrings()
            .split(properties.getProperty(PROP_PAP_GROUPS, ""));
    boolean inList = false;
    for (String g : groups) {
      if (g.equals(group.getId())) {
        inList = true;
      }
    }
    if (!inList) {
      Set<String> grps = Sets.newHashSet(groups);
      grps.add(group.getId());
      String newGroupList = "";

      if (grps.size() == 1) {
        newGroupList = grps.iterator().next();
      } else if (grps.size() > 1) {
        newGroupList = Joiner.on(',').skipNulls().join(grps);
      }
      logger.info("New Group List: " + newGroupList);
      properties.setProperty(PROP_PAP_GROUPS, newGroupList);
    }
    //
    // Set its properties
    //
    properties.setProperty(group.getId() + ".name", group.getName());
    properties.setProperty(group.getId() + ".description", group.getDescription());
    //
    // Set its PDP list
    //
    if (group.getPdps().size() > 0) {
      String pdpList = "";
      if (group.getPdps().size() == 1) {
        pdpList = group.getPdps().iterator().next().getId();
      } else if (group.getPdps().size() > 1) {
        Set<String> ids = new HashSet<String>();
        for (PDP pdp : group.getPdps()) {
          ids.add(pdp.getId());
        }
        pdpList = Joiner.on(',').skipNulls().join(ids);
      }
      properties.setProperty(group.getId() + ".pdps", pdpList);
    } else {
      properties.setProperty(group.getId() + ".pdps", "");
    }
  }
  private void saveConfiguration() throws PAPException, IOException {
    //
    // Create our properties object
    //
    Properties properties =
        new Properties() {
          private static final long serialVersionUID = 1L;

          // For Debugging it is helpful for the file to be in a sorted order,
          // any by returning the keys in the natural Alpha order for strings we get close enough.
          // TreeSet is sorted, and this just overrides the normal Properties method to get the
          // keys.
          @Override
          public synchronized Enumeration<Object> keys() {
            return Collections.enumeration(new TreeSet<Object>(super.keySet()));
          }
        };
    //
    // Iterate our groups
    //
    List<String> ids = new ArrayList<String>();
    for (PDPGroup group : this.groups) {
      ids.add(group.getId());
      properties.setProperty(
          group.getId() + ".name", (group.getName() == null ? "" : group.getName()));
      properties.setProperty(
          group.getId() + ".description",
          (group.getDescription() == null ? "" : group.getDescription()));
      //
      // Iterate its PDPs
      //
      List<String> pdps = new ArrayList<String>();
      for (PDP pdp : group.getPdps()) {
        pdps.add(pdp.getId());
        properties.setProperty(pdp.getId() + ".name", (pdp.getName() == null ? "" : pdp.getName()));
        properties.setProperty(
            pdp.getId() + ".description",
            (pdp.getDescription() == null ? "" : pdp.getDescription()));
      }
      String pdpList = "";
      if (pdps.size() == 1) {
        pdpList = pdps.get(0);
      } else if (pdps.size() > 1) {
        pdpList = Joiner.on(',').skipNulls().join(pdps);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Group " + group.getId() + " PDPS: " + pdpList);
      }
      properties.setProperty(group.getId() + ".pdps", pdpList);
    }
    if (ids.isEmpty()) {
      throw new PAPException("Inconsistency - we have NO groups. We should have at least one.");
    }
    String groupList = "";
    if (ids.size() == 1) {
      groupList = ids.get(0);
    } else if (ids.size() > 1) {
      groupList = Joiner.on(',').skipNulls().join(ids);
    }
    logger.info("New Group List: " + groupList);

    properties.setProperty(PROP_PAP_GROUPS, groupList);
    //
    // Get the default group
    //
    PDPGroup defaultGroup = this.getDefaultGroup();
    if (defaultGroup == null) {
      throw new PAPException("Invalid state - no default group.");
    }
    properties.setProperty(PROP_PAP_GROUPS_DEFAULT, defaultGroup.getId());
    //
    // Now we can save the file
    //
    Path file = Paths.get(this.repository.toString(), "xacml.properties");
    try (OutputStream os = Files.newOutputStream(file)) {
      properties.store(os, "");
    }
  }
  @Override
  public void removeGroup(PDPGroup group, PDPGroup newGroup)
      throws PAPException, NullPointerException {
    if (group == null) {
      throw new NullPointerException();
    }
    //
    // Does this group exist?
    //
    if (!this.groups.contains(group)) {
      logger.error("This group doesn't exist.");
      throw new PAPException("The group '" + group.getId() + "' does not exist");
    }
    //
    // Is it the default group?
    //
    if (group.isDefaultGroup()) {
      throw new PAPException("You cannot delete the default group.");
    }
    Set<PDP> pdps = group.getPdps();
    //
    // Are there PDPs? If so, then we need a target group
    //
    if (!pdps.isEmpty() && newGroup == null) {
      throw new NullPointerException(
          "Group targeted for deletion has PDPs, you must provide a new group for them.");
    }
    //
    // Move the PDPs
    //
    if (!pdps.isEmpty()) {
      if (!(newGroup instanceof StdPDPGroup)) {
        throw new PAPException(
            "Unexpected class for newGroup: " + newGroup.getClass().getCanonicalName());
      }
      // The movePDP function will modify the set of PDPs in the group.
      // To avoid concurrent modification exceptions we need to duplicate the list before calling
      // that
      // function.
      List<PDP> pdpList = new ArrayList<PDP>();
      for (PDP pdp : pdps) {
        pdpList.add(pdp);
      }
      // now we can use the PDPs from the list without having ConcurrentAccessExceptions
      for (PDP pdp : pdpList) {
        this.movePDP(pdp, newGroup);
      }
    }
    //
    // remove the directory for the group
    //
    String id = group.getId();
    Path groupPath = Paths.get(this.repository.toString(), id);
    //
    // If it exists already
    //
    if (!Files.exists(groupPath)) {
      logger.warn("removeGroup " + id + " directory does not exist" + groupPath.toString());
    } else {
      try {
        Files.walkFileTree(
            groupPath,
            new SimpleFileVisitor<Path>() {

              @Override
              public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                  throws IOException {
                Files.delete(file);
                return super.visitFile(file, attrs);
              }
            });
        //
        // delete the directory
        //
        Files.delete(groupPath);
      } catch (IOException e) {
        logger.error("Failed to delete " + groupPath + ": " + e);
        throw new PAPException("Failed to delete " + id);
      }
    }

    // remove the group from the set of all groups
    groups.remove(group);

    //
    // Save changes
    //
    changed();
    this.doSave();
  }
  @Override
  public void updateGroup(PDPGroup group) throws PAPException {
    if (group == null || group.getId() == null) {
      throw new PAPException("Group or id is null");
    }
    if (group.getName() == null || group.getName().trim().length() == 0) {
      throw new PAPException("New name for group cannot be null or blank");
    }
    StdPDPGroup existingGroup = (StdPDPGroup) getGroup(group.getId());
    if (existingGroup == null) {
      throw new PAPException("Update found no existing group with id '" + group.getId() + "'");
    }

    // We do dramatically different things when the Name changes
    // 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.
    if (group.getName().equals(existingGroup.getName())) {

      // update the disk
      try {
        ((StdPDPGroup) group).saveGroupConfiguration();
      } catch (IOException e) {
        throw new PAPException(
            "Unable to save new configuration for '" + group.getName() + "': " + e.getMessage());
      }
      // update the group in the set by simply replacing the old instance with the new one
      this.groups.remove(existingGroup);
      this.groups.add((StdPDPGroup) group);

    } else {
      // the name/identity of the group has changed
      // generate the new id
      String newId = createNewPDPGroupId(group.getName());

      // make sure no other group uses the new id
      for (PDPGroup g : groups) {
        if (g.getId().equals(newId)) {
          throw new PAPException(
              "Replacement name maps to ID '" + newId + "' which is already in use");
        }
      }
      ((StdPDPGroup) group).setId(newId);

      // rename the existing directory to the new id
      Path oldPath = existingGroup.getDirectory();
      Path newPath = Paths.get(oldPath.getParent().toString(), newId);
      ((StdPDPGroup) group).setDirectory(newPath);

      try {
        boolean success = oldPath.toFile().renameTo(newPath.toFile());
        if (!success) {
          throw new PAPException("Unable to rename directory; reason unknown");
        }
      } catch (Exception e) {
        logger.error("Move '" + oldPath + "' to '" + newPath + "': " + e.getMessage(), e);
        throw new PAPException(
            "Unable to move directory from '"
                + oldPath
                + "' to '"
                + newPath
                + "': "
                + e.getMessage());
      }
      // update the disk
      try {
        ((StdPDPGroup) group).saveGroupConfiguration();
      } catch (IOException e) {
        throw new PAPException(
            "Unable to save new configuration for '" + group.getName() + "': " + e.getMessage());
      }

      // save the new group into the Set
      groups.remove(existingGroup);
      groups.add((StdPDPGroup) group);
    }

    // perhaps only the group changed, but if the name/id changed it may look to a listener like
    // more than
    // one group
    changed();
  }
  @Override
  public void newGroup(String name, String description) throws PAPException, NullPointerException {
    //
    // Null check
    //
    if (name == null) {
      throw new NullPointerException();
    }
    //
    // Do we already have this group?
    //
    for (PDPGroup group : this.groups) {
      if (group.getName().equals(name)) {
        throw new PAPException("Group with this name=" + name + " already exists.");
      }
    }

    // create an Id that can be used as a file name and a properties file key.
    // Ids must not contain \/:*?"<>|=,;
    // The ID must also be unique within the current set of PDPGroups.
    String id = createNewPDPGroupId(name);

    //
    // Construct the directory path
    //
    Path groupPath = Paths.get(this.repository.toString(), id);
    //
    // If it exists already
    //
    if (Files.exists(groupPath)) {
      logger.warn("addGroup " + id + " directory exists" + groupPath.toString());
    } else {
      try {
        //
        // Create the directory
        //
        Files.createDirectory(groupPath);
      } catch (IOException e) {
        logger.error("Failed to create " + groupPath);
        throw new PAPException("Failed to create " + id);
      }
    }
    //
    // Create the Policies
    //

    Path policyProperties = Paths.get(groupPath.toString(), "xacml.policy.properties");
    if (Files.exists(policyProperties)) {
      logger.warn("addGroup " + id + " file exists: " + policyProperties.toString());
    } else {
      Properties props = new Properties();
      props.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
      props.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
      try {
        Files.createFile(policyProperties);
        try (OutputStream os = Files.newOutputStream(policyProperties)) {
          props.store(os, "");
        }
      } catch (IOException e) {
        logger.error("Failed to create " + policyProperties);
        throw new PAPException("Failed to create " + id);
      }
    }
    //
    // Create the PIP config
    //
    Path pipProperties = Paths.get(groupPath.toString(), "xacml.pip.properties");
    if (Files.exists(pipProperties)) {
      logger.warn("addGroup " + id + " file exists: " + pipProperties.toString());
    } else {
      try {
        Properties props = new Properties();
        props.setProperty(XACMLProperties.PROP_PIP_ENGINES, "");
        Files.createFile(pipProperties);
        try (OutputStream os = Files.newOutputStream(pipProperties)) {
          props.store(os, "");
        }
      } catch (IOException e) {
        logger.error("Failed to create " + pipProperties);
        throw new PAPException("Failed to create " + id);
      }
    }
    //
    // Ok now add it
    //
    StdPDPGroup newGroup = new StdPDPGroup(id, name, description, groupPath);
    if (this.groups.add(newGroup)) {
      // save the new group in our properties and notify any listeners of the change
      groupChanged(newGroup);
    }
  }