Add persistence for PDP concepts

This review adds the JPA annotations to PDP group/subgroup and PDP for
persisting to the database.

It also updates the Provider API as requested by other team members.

Issue-ID: POLICY-1095
Change-Id: I8188afb763849ede9680f3751b464d9d76c27196
Signed-off-by: liamfallon <liam.fallon@est.tech>
diff --git a/models-pdp/pom.xml b/models-pdp/pom.xml
index 3f423b8..aaf9ee4 100644
--- a/models-pdp/pom.xml
+++ b/models-pdp/pom.xml
@@ -44,5 +44,15 @@
             <artifactId>policy-models-tosca</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
index c696794..a59d1af 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
@@ -27,6 +27,8 @@
 
 import lombok.Data;
 import lombok.NoArgsConstructor;
+
+import org.onap.policy.models.base.PfNameVersion;
 import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.pdp.enums.PdpState;
 
@@ -38,7 +40,7 @@
  */
 @Data
 @NoArgsConstructor
-public class PdpGroup {
+public class PdpGroup implements PfNameVersion {
     private String name;
     private String version;
     private String description;
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
index ea1c8ff..32e0b1a 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
@@ -20,7 +20,11 @@
 
 package org.onap.policy.models.pdp.concepts;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -34,4 +38,17 @@
 @ToString
 public class PdpGroups {
     private List<PdpGroup> groups;
+
+    /**
+     * Get the contents of this class as a list of PDP group maps.
+     * @return the PDP groups in a list of maps
+     */
+    public List<Map<String, PdpGroup>> toMapList() {
+        final Map<String, PdpGroup> pdpGroupMap = new LinkedHashMap<>();
+        for (PdpGroup pdpGroup : groups) {
+            pdpGroupMap.put(pdpGroup.getName() + ':' + pdpGroup.getVersion() , pdpGroup);
+        }
+
+        return Collections.singletonList(pdpGroupMap);
+    }
 }
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdp.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdp.java
new file mode 100644
index 0000000..0a9aa58
--- /dev/null
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdp.java
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Model
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.persistence.concepts;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.onap.policy.models.base.PfAuthorative;
+import org.onap.policy.models.base.PfConcept;
+import org.onap.policy.models.base.PfKey;
+import org.onap.policy.models.base.PfReferenceKey;
+import org.onap.policy.models.base.PfValidationMessage;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.PfValidationResult.ValidationResult;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.enums.PdpHealthStatus;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Class to represent a PDP in the database.
+ *
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+@Entity
+@Table(name = "Pdp")
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class JpaPdp extends PfConcept implements PfAuthorative<Pdp>, Serializable {
+    private static final long serialVersionUID = -357224425637789775L;
+
+    @EmbeddedId
+    private PfReferenceKey key;
+
+    @Column
+    private PdpState pdpState;
+
+    @Column
+    private PdpHealthStatus healthy;
+
+    @Column
+    private String message;
+
+    /**
+     * The Default Constructor creates a {@link JpaPdp} object with a null key.
+     */
+    public JpaPdp() {
+        this(new PfReferenceKey());
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdp} object with the given concept key.
+     *
+     * @param key the key
+     */
+    public JpaPdp(@NonNull final PfReferenceKey key) {
+        this(key, PdpState.PASSIVE, PdpHealthStatus.UNKNOWN);
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdp} object with all mandatory fields.
+     *
+     * @param key the key
+     * @param pdpState the state of the PDP
+     * @param healthy the health state of the PDP
+     */
+    public JpaPdp(@NonNull final PfReferenceKey key, @NonNull final PdpState pdpState,
+            @NonNull PdpHealthStatus healthy) {
+        this.key = key;
+        this.pdpState = pdpState;
+        this.healthy = healthy;
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param copyConcept the concept to copy from
+     */
+    public JpaPdp(final JpaPdp copyConcept) {
+        super(copyConcept);
+    }
+
+    /**
+     * Authorative constructor.
+     *
+     * @param authorativeConcept the authorative concept to copy from
+     */
+    public JpaPdp(final Pdp authorativeConcept) {
+        this.fromAuthorative(authorativeConcept);
+    }
+
+    @Override
+    public Pdp toAuthorative() {
+        Pdp pdp = new Pdp();
+
+        pdp.setInstanceId(key.getLocalName());
+        pdp.setPdpState(pdpState);
+        pdp.setHealthy(healthy);
+        pdp.setMessage(message);
+
+        return pdp;
+    }
+
+    @Override
+    public void fromAuthorative(final Pdp pdp) {
+        if (this.getKey().isNullKey()) {
+            this.setKey(new PfReferenceKey());
+            getKey().setLocalName(pdp.getInstanceId());
+        }
+
+        this.setPdpState(pdp.getPdpState());
+        this.setHealthy(pdp.getHealthy());
+        this.setMessage(pdp.getMessage());
+    }
+
+    @Override
+    public List<PfKey> getKeys() {
+        return getKey().getKeys();
+    }
+
+    @Override
+    public void clean() {
+        key.clean();
+
+        if (message != null) {
+            message = message.trim();
+        }
+    }
+
+    @Override
+    public PfValidationResult validate(final PfValidationResult resultIn) {
+        PfValidationResult result = resultIn;
+
+        if (key.isNullKey()) {
+            result.addValidationMessage(
+                    new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+        }
+
+        result = key.validate(result);
+
+        if (key.getParentConceptKey().isNullKey()) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "parent of key is a null key"));
+        }
+
+        if (PfKey.NULL_KEY_NAME.equals(key.getParentLocalName())) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "local name of parent of key is null"));
+        }
+
+        if (pdpState == null) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "PDP state may not be null"));
+        }
+
+        if (healthy == null) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "PDP health status may not be null"));
+        }
+
+        if (StringUtils.isBlank(message)) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "message may not be blank"));
+        }
+
+        return result;
+    }
+
+    @Override
+    public int compareTo(final PfConcept otherConcept) {
+        if (otherConcept == null) {
+            return -1;
+        }
+        if (this == otherConcept) {
+            return 0;
+        }
+        if (getClass() != otherConcept.getClass()) {
+            return this.hashCode() - otherConcept.hashCode();
+        }
+
+        final JpaPdp other = (JpaPdp) otherConcept;
+        if (!key.equals(other.key)) {
+            return key.compareTo(other.key);
+        }
+
+        int result = ObjectUtils.compare(pdpState, other.pdpState);
+        if (result != 0) {
+            return result;
+        }
+
+        result = ObjectUtils.compare(healthy, other.healthy);
+        if (result != 0) {
+            return result;
+        }
+
+        return ObjectUtils.compare(message, other.message);
+    }
+
+    @Override
+    public PfConcept copyTo(@NonNull final PfConcept target) {
+        Assertions.instanceOf(target, JpaPdp.class);
+
+        final JpaPdp copy = ((JpaPdp) target);
+        copy.setKey(new PfReferenceKey(key));
+        copy.setPdpState(pdpState);
+        copy.setHealthy(healthy);
+        copy.setMessage(message);
+
+        return copy;
+    }
+}
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpGroup.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpGroup.java
new file mode 100644
index 0000000..9c896a3
--- /dev/null
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpGroup.java
@@ -0,0 +1,307 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Model
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.persistence.concepts;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.onap.policy.common.utils.validation.ParameterValidationUtils;
+import org.onap.policy.models.base.PfAuthorative;
+import org.onap.policy.models.base.PfConcept;
+import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.base.PfKey;
+import org.onap.policy.models.base.PfReferenceKey;
+import org.onap.policy.models.base.PfUtils;
+import org.onap.policy.models.base.PfValidationMessage;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.PfValidationResult.ValidationResult;
+import org.onap.policy.models.pdp.concepts.PdpGroup;
+import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Class to represent a PDP group in the database.
+ *
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+@Entity
+@Table(name = "PdpGroup")
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class JpaPdpGroup extends PfConcept implements PfAuthorative<PdpGroup> {
+    private static final long serialVersionUID = -357224425637789775L;
+
+    @EmbeddedId
+    private PfConceptKey key;
+
+    @Column
+    private String description;
+
+    @Column
+    private PdpState pdpGroupState;
+
+    @ElementCollection
+    private Map<String, String> properties;
+
+    // @formatter:off
+    @OneToMany
+    @CollectionTable(joinColumns = {
+            @JoinColumn(name = "pdpGroupParentKeyName",    referencedColumnName = "parentKeyName"),
+            @JoinColumn(name = "pdpGroupParentKeyVersion", referencedColumnName = "parentKeyVersion"),
+            @JoinColumn(name = "pdpGroupParentLocalName",  referencedColumnName = "parentLocalName"),
+            @JoinColumn(name = "pdpGroupLocalName",        referencedColumnName = "localName")
+        })
+    // @formatter:on
+    private List<JpaPdpSubGroup> pdpSubGroups;
+
+    /**
+     * The Default Constructor creates a {@link JpaPdpGroup} object with a null key.
+     */
+    public JpaPdpGroup() {
+        this(new PfConceptKey());
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdpGroup} object with the given concept key.
+     *
+     * @param key the key
+     */
+    public JpaPdpGroup(@NonNull final PfConceptKey key) {
+        this(key, PdpState.PASSIVE, new ArrayList<>());
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdpGroup} object with all mandatory fields.
+     *
+     * @param key the key
+     * @param pdpGroupState State of the PDP group
+     */
+    public JpaPdpGroup(@NonNull final PfConceptKey key, @NonNull final PdpState pdpGroupState,
+            @NonNull final List<JpaPdpSubGroup> pdpSubGroups) {
+        this.key = key;
+        this.pdpGroupState = pdpGroupState;
+        this.pdpSubGroups = pdpSubGroups;
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param copyConcept the concept to copy from
+     */
+    public JpaPdpGroup(final JpaPdpGroup copyConcept) {
+        super(copyConcept);
+    }
+
+    /**
+     * Authorative constructor.
+     *
+     * @param authorativeConcept the authorative concept to copy from
+     */
+    public JpaPdpGroup(final PdpGroup authorativeConcept) {
+        this.fromAuthorative(authorativeConcept);
+    }
+
+    @Override
+    public PdpGroup toAuthorative() {
+        PdpGroup pdpGroup = new PdpGroup();
+
+        pdpGroup.setName(getKey().getName());
+        pdpGroup.setVersion(getKey().getVersion());
+        pdpGroup.setDescription(description);
+        pdpGroup.setPdpGroupState(pdpGroupState);
+
+        pdpGroup.setProperties(properties == null ? null : new LinkedHashMap<>(properties));
+
+        pdpGroup.setPdpSubgroups(new ArrayList<>(pdpSubGroups.size()));
+        for (JpaPdpSubGroup jpaPdpSubgroup : pdpSubGroups) {
+            pdpGroup.getPdpSubgroups().add(jpaPdpSubgroup.toAuthorative());
+        }
+
+        return pdpGroup;
+    }
+
+    @Override
+    public void fromAuthorative(final PdpGroup pdpGroup) {
+        this.setKey(new PfConceptKey(pdpGroup.getName(), pdpGroup.getVersion()));
+
+        this.description = pdpGroup.getDescription();
+        this.pdpGroupState = pdpGroup.getPdpGroupState();
+
+        this.properties =
+                (pdpGroup.getProperties() == null ? null : new LinkedHashMap<>(pdpGroup.getProperties()));
+
+        this.pdpSubGroups = new ArrayList<>();
+        for (PdpSubGroup pdpSubgroup : pdpGroup.getPdpSubgroups()) {
+            JpaPdpSubGroup jpaPdpSubGroup = new JpaPdpSubGroup();
+            jpaPdpSubGroup.setKey(new PfReferenceKey(getKey(), pdpSubgroup.getPdpType()));
+            jpaPdpSubGroup.fromAuthorative(pdpSubgroup);
+            this.pdpSubGroups.add(jpaPdpSubGroup);
+        }
+    }
+
+    @Override
+    public List<PfKey> getKeys() {
+        List<PfKey> keyList = getKey().getKeys();
+
+        for (JpaPdpSubGroup jpaPdpSubgroup : pdpSubGroups) {
+            keyList.addAll(jpaPdpSubgroup.getKeys());
+        }
+
+
+        return keyList;
+    }
+
+    @Override
+    public void clean() {
+        key.clean();
+
+        description = (description == null ? null : description.trim());
+
+        if (properties != null) {
+            Map<String, String> cleanedPropertyMap = new LinkedHashMap<>();
+            for (Entry<String, String> propertyEntry : properties.entrySet()) {
+                cleanedPropertyMap.put(propertyEntry.getKey().trim(), propertyEntry.getValue().trim());
+            }
+            properties = cleanedPropertyMap;
+        }
+
+        for (JpaPdpSubGroup jpaPdpSubgroup : pdpSubGroups) {
+            jpaPdpSubgroup.clean();
+        }
+    }
+
+    @Override
+    public PfValidationResult validate(final PfValidationResult resultIn) {
+        PfValidationResult result = resultIn;
+
+        if (key.isNullKey()) {
+            result.addValidationMessage(
+                    new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+        }
+
+        result = key.validate(result);
+
+        if (StringUtils.isBlank(description)) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "description may not be blank"));
+        }
+
+        if (properties != null) {
+            for (Entry<String, String> propertyEntry : properties.entrySet()) {
+                if (!ParameterValidationUtils.validateStringParameter(propertyEntry.getKey())) {
+                    result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                            "a property key may not be null or blank"));
+                }
+                if (!ParameterValidationUtils.validateStringParameter(propertyEntry.getValue())) {
+                    result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                            "a property value may not be null or blank"));
+                }
+            }
+        }
+
+
+        if (pdpSubGroups == null) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "a PDP group must have a list of PDP subgroups"));
+        } else {
+            for (JpaPdpSubGroup jpaPdpSubgroup : pdpSubGroups) {
+                result = jpaPdpSubgroup.validate(result);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public int compareTo(final PfConcept otherConcept) {
+        if (otherConcept == null) {
+            return -1;
+        }
+        if (this == otherConcept) {
+            return 0;
+        }
+        if (getClass() != otherConcept.getClass()) {
+            return this.getClass().getCanonicalName().compareTo(otherConcept.getClass().getCanonicalName());
+        }
+
+        final JpaPdpGroup other = (JpaPdpGroup) otherConcept;
+        if (!key.equals(other.key)) {
+            return key.compareTo(other.key);
+        }
+
+        int result = ObjectUtils.compare(description, other.description);
+        if (result != 0) {
+            return result;
+        }
+
+        result = ObjectUtils.compare(pdpGroupState, other.pdpGroupState);
+        if (result != 0) {
+            return result;
+        }
+
+        result = PfUtils.compareObjects(properties, other.properties);
+        if (result != 0) {
+            return result;
+        }
+
+        return PfUtils.compareObjects(pdpSubGroups, other.pdpSubGroups);
+    }
+
+    @Override
+    public PfConcept copyTo(@NonNull final PfConcept target) {
+        Assertions.instanceOf(target, JpaPdpGroup.class);
+
+        final JpaPdpGroup copy = ((JpaPdpGroup) target);
+        copy.setKey(new PfConceptKey(key));
+
+        copy.setDescription(description);
+        copy.setPdpGroupState(pdpGroupState);
+        copy.setProperties(properties == null ? null : new LinkedHashMap<>(properties));
+        copy.setPdpSubGroups(PfUtils.mapList(pdpSubGroups, JpaPdpSubGroup::new));
+
+        return copy;
+    }
+}
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpSubGroup.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpSubGroup.java
new file mode 100644
index 0000000..5164d34
--- /dev/null
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/concepts/JpaPdpSubGroup.java
@@ -0,0 +1,406 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Model
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.persistence.concepts;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+
+import org.onap.policy.common.utils.validation.Assertions;
+import org.onap.policy.common.utils.validation.ParameterValidationUtils;
+import org.onap.policy.models.base.PfAuthorative;
+import org.onap.policy.models.base.PfConcept;
+import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.base.PfKey;
+import org.onap.policy.models.base.PfKeyUse;
+import org.onap.policy.models.base.PfReferenceKey;
+import org.onap.policy.models.base.PfUtils;
+import org.onap.policy.models.base.PfValidationMessage;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.PfValidationResult.ValidationResult;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.pdp.concepts.PolicyTypeIdent;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+/**
+ * Class to represent a PDP subgroup in the database.
+ *
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+@Entity
+@Table(name = "PdpSubGroup")
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class JpaPdpSubGroup extends PfConcept implements PfAuthorative<PdpSubGroup> {
+    private static final long serialVersionUID = -357224425637789775L;
+
+    @EmbeddedId
+    private PfReferenceKey key;
+
+    @ElementCollection
+    private List<PfConceptKey> supportedPolicyTypes;
+
+    @ElementCollection
+    private List<PfConceptKey> policies;
+
+    @Column
+    private int currentInstanceCount;
+
+    @Column
+    private int desiredInstanceCount;
+
+    @ElementCollection
+    private Map<String, String> properties;
+
+    // @formatter:ofF
+    @OneToMany
+    @CollectionTable(joinColumns = {
+            @JoinColumn(name = "pdpSubGroupParentKeyName",    referencedColumnName = "parentKeyName"),
+            @JoinColumn(name = "pdpSubGroupParentKeyVersion", referencedColumnName = "parentKeyVersion"),
+            @JoinColumn(name = "pdpSubGroupParentLocalName",  referencedColumnName = "parentLocalName"),
+            @JoinColumn(name = "pdpSubGroupLocalName",        referencedColumnName = "localName")
+        })
+    // formatter:on
+    private List<JpaPdp> pdpInstances;
+
+    /**
+     * The Default Constructor creates a {@link JpaPdpSubGroup} object with a null key.
+     */
+    public JpaPdpSubGroup() {
+        this(new PfReferenceKey());
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdpSubGroup} object with the given concept key.
+     *
+     * @param key the key
+     */
+    public JpaPdpSubGroup(@NonNull final PfReferenceKey key) {
+        this(key, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+    }
+
+    /**
+     * The Key Constructor creates a {@link JpaPdpSubGroup} object with all mandatory fields.
+     *
+     * @param key the key
+     * @param supportedPolicyTypes Supported policy types
+     * @param policies policies deployed on this PDP subgroups
+     * @param pdpInstances the PDP instances on this PDP subgroups
+     */
+    public JpaPdpSubGroup(@NonNull final PfReferenceKey key, @NonNull final List<PfConceptKey> supportedPolicyTypes,
+            @NonNull List<PfConceptKey> policies, @NonNull final List<JpaPdp> pdpInstances) {
+        this.key = key;
+        this.supportedPolicyTypes = supportedPolicyTypes;
+        this.policies = policies;
+        this.pdpInstances = pdpInstances;
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param copyConcept the concept to copy from
+     */
+    public JpaPdpSubGroup(final JpaPdpSubGroup copyConcept) {
+        super(copyConcept);
+    }
+
+    /**
+     * Authorative constructor.
+     *
+     * @param authorativeConcept the authorative concept to copy from
+     */
+    public JpaPdpSubGroup(final PdpSubGroup authorativeConcept) {
+        this.fromAuthorative(authorativeConcept);
+    }
+
+    @Override
+    public PdpSubGroup toAuthorative() {
+        PdpSubGroup pdpSubgroup = new PdpSubGroup();
+
+        pdpSubgroup.setPdpType(getKey().getLocalName());
+
+        pdpSubgroup.setSupportedPolicyTypes(new ArrayList<>());
+        for (PfConceptKey supportedPolicyTypeKey : supportedPolicyTypes) {
+            PolicyTypeIdent supportedPolicyTypeIdent =
+                    new PolicyTypeIdent(supportedPolicyTypeKey.getName(), supportedPolicyTypeKey.getVersion());
+            pdpSubgroup.getSupportedPolicyTypes().add(supportedPolicyTypeIdent);
+        }
+
+        pdpSubgroup.setPolicies(new ArrayList<>());
+        for (PfConceptKey policyKey : policies) {
+            ToscaPolicy toscaPolicy = new ToscaPolicy();
+            toscaPolicy.setName(policyKey.getName());
+            toscaPolicy.setVersion(policyKey.getVersion());
+            pdpSubgroup.getPolicies().add(toscaPolicy);
+        }
+
+        pdpSubgroup.setCurrentInstanceCount(currentInstanceCount);
+        pdpSubgroup.setDesiredInstanceCount(desiredInstanceCount);
+        pdpSubgroup.setProperties(properties == null ? null : new LinkedHashMap<>(properties));
+
+        pdpSubgroup.setPdpInstances(new ArrayList<>());
+        for (JpaPdp jpaPdp : pdpInstances) {
+            pdpSubgroup.getPdpInstances().add(jpaPdp.toAuthorative());
+        }
+
+        return pdpSubgroup;
+    }
+
+    @Override
+    public void fromAuthorative(final PdpSubGroup pdpSubgroup) {
+        if (this.getKey().isNullKey()) {
+            this.setKey(new PfReferenceKey());
+            getKey().setLocalName(pdpSubgroup.getPdpType());
+        }
+
+        this.supportedPolicyTypes = new ArrayList<>();
+        for (PolicyTypeIdent supportedPolicyType : pdpSubgroup.getSupportedPolicyTypes()) {
+            this.supportedPolicyTypes
+                    .add(new PfConceptKey(supportedPolicyType.getName(), supportedPolicyType.getVersion()));
+        }
+
+
+        this.policies = new ArrayList<>();
+        for (ToscaPolicy toscaPolicy : pdpSubgroup.getPolicies()) {
+            this.policies.add(new PfConceptKey(toscaPolicy.getName(), toscaPolicy.getVersion()));
+        }
+
+        this.currentInstanceCount = pdpSubgroup.getCurrentInstanceCount();
+        this.desiredInstanceCount = pdpSubgroup.getDesiredInstanceCount();
+        this.properties =
+                (pdpSubgroup.getProperties() == null ? null : new LinkedHashMap<>(pdpSubgroup.getProperties()));
+
+        this.pdpInstances = new ArrayList<>();
+        for (Pdp pdp : pdpSubgroup.getPdpInstances()) {
+            JpaPdp jpaPdp = new JpaPdp();
+            jpaPdp.setKey(new PfReferenceKey(getKey(), pdp.getInstanceId()));
+            jpaPdp.fromAuthorative(pdp);
+            this.pdpInstances.add(jpaPdp);
+        }
+    }
+
+    @Override
+    public List<PfKey> getKeys() {
+        List<PfKey> keyList = getKey().getKeys();
+
+        for (PfConceptKey ptkey : supportedPolicyTypes) {
+            keyList.add(new PfKeyUse(ptkey));
+        }
+
+        for (PfConceptKey pkey : policies) {
+            keyList.add(new PfKeyUse(pkey));
+        }
+
+        for (JpaPdp jpaPdp : pdpInstances) {
+            keyList.addAll(jpaPdp.getKeys());
+        }
+
+
+        return keyList;
+    }
+
+    @Override
+    public void clean() {
+        key.clean();
+
+        for (PfConceptKey ptkey : supportedPolicyTypes) {
+            ptkey.clean();
+        }
+
+        for (PfConceptKey pkey : policies) {
+            pkey.clean();
+        }
+
+        if (properties != null) {
+            Map<String, String> cleanedPropertyMap = new LinkedHashMap<>();
+            for (Entry<String, String> propertyEntry : properties.entrySet()) {
+                cleanedPropertyMap.put(propertyEntry.getKey().trim(), propertyEntry.getValue().trim());
+            }
+            properties = cleanedPropertyMap;
+        }
+
+        for (JpaPdp jpaPdp : pdpInstances) {
+            jpaPdp.clean();
+        }
+    }
+
+    @Override
+    public PfValidationResult validate(final PfValidationResult resultIn) {
+        PfValidationResult result = resultIn;
+
+        if (key.isNullKey()) {
+            result.addValidationMessage(
+                    new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+        }
+
+        result = key.validate(result);
+
+        if (key.getParentConceptKey().isNullKey()) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "parent of key is a null key"));
+        }
+
+        if (currentInstanceCount < 0) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "the current instance count of a PDP group may not be negative"));
+        }
+
+        if (desiredInstanceCount < 0) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "the desired instance count of a PDP group may not be negative"));
+        }
+
+        if (properties != null) {
+            for (Entry<String, String> propertyEntry : properties.entrySet()) {
+                if (!ParameterValidationUtils.validateStringParameter(propertyEntry.getKey())) {
+                    result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                            "a property key may not be null or blank"));
+                }
+                if (!ParameterValidationUtils.validateStringParameter(propertyEntry.getValue())) {
+                    result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                            "a property value may not be null or blank"));
+                }
+            }
+        }
+
+
+        return validateSubConcepts(result);
+    }
+
+    /**
+     * Validate collections of sub concepts.
+     *
+     * @param result the result in which to store the validation result
+     * @return the validation result including the results of this method
+     */
+    private PfValidationResult validateSubConcepts(PfValidationResult result) {
+        if (supportedPolicyTypes == null || supportedPolicyTypes.isEmpty()) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "a PDP subgroup must support at least one policy type"));
+        } else {
+            for (PfConceptKey supportedPolicyType : supportedPolicyTypes) {
+                result = supportedPolicyType.validate(result);
+            }
+        }
+
+        if (policies == null) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "a PDP subgroup must have a list of policies"));
+        } else {
+            for (PfConceptKey policyKey : policies) {
+                result = policyKey.validate(result);
+            }
+        }
+
+        if (pdpInstances == null) {
+            result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+                    "a PDP subgroup must have a list of PDPs"));
+        } else {
+            for (JpaPdp jpaPdp : pdpInstances) {
+                result = jpaPdp.validate(result);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public int compareTo(final PfConcept otherConcept) {
+        if (otherConcept == null) {
+            return -1;
+        }
+        if (this == otherConcept) {
+            return 0;
+        }
+        if (getClass() != otherConcept.getClass()) {
+            return this.hashCode() - otherConcept.hashCode();
+        }
+
+        final JpaPdpSubGroup other = (JpaPdpSubGroup) otherConcept;
+        if (!key.equals(other.key)) {
+            return key.compareTo(other.key);
+        }
+
+        int result = PfUtils.compareObjects(supportedPolicyTypes, other.supportedPolicyTypes);
+        if (result != 0) {
+            return result;
+        }
+
+        result = PfUtils.compareObjects(policies, other.policies);
+        if (result != 0) {
+            return result;
+        }
+
+        if (currentInstanceCount != other.currentInstanceCount) {
+            return currentInstanceCount - other.currentInstanceCount;
+        }
+
+        if (desiredInstanceCount != other.desiredInstanceCount) {
+            return desiredInstanceCount - other.desiredInstanceCount;
+        }
+
+        result = PfUtils.compareObjects(properties, other.properties);
+        if (result != 0) {
+            return result;
+        }
+
+        return PfUtils.compareObjects(pdpInstances, other.pdpInstances);
+    }
+
+    @Override
+    public PfConcept copyTo(@NonNull final PfConcept target) {
+        Assertions.instanceOf(target, JpaPdpSubGroup.class);
+
+        final JpaPdpSubGroup copy = ((JpaPdpSubGroup) target);
+        copy.setKey(new PfReferenceKey(key));
+
+        copy.setSupportedPolicyTypes(PfUtils.mapList(supportedPolicyTypes, PfConceptKey::new));
+        copy.setPolicies(PfUtils.mapList(policies, PfConceptKey::new));
+        copy.setCurrentInstanceCount(currentInstanceCount);
+        copy.setDesiredInstanceCount(desiredInstanceCount);
+        copy.setProperties(properties == null ? null : new LinkedHashMap<>(properties));
+        copy.setPdpInstances(PfUtils.mapList(pdpInstances, JpaPdp::new));
+
+        return copy;
+    }
+}
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/provider/PdpProvider.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/provider/PdpProvider.java
index 6af2d21..a32f5a4 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/provider/PdpProvider.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/persistence/provider/PdpProvider.java
@@ -21,20 +21,28 @@
 package org.onap.policy.models.pdp.persistence.provider;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.core.Response;
+
 import lombok.NonNull;
 
 import org.apache.commons.lang3.tuple.Pair;
+import org.onap.policy.models.base.PfConceptKey;
 import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.base.PfValidationResult;
 import org.onap.policy.models.dao.PfDao;
 import org.onap.policy.models.pdp.concepts.PdpGroup;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.pdp.concepts.PdpStatistics;
 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This class provides the provision of information on PAP concepts in the database to callers.
@@ -42,6 +50,8 @@
  * @author Liam Fallon (liam.fallon@est.tech)
  */
 public class PdpProvider {
+    private static final Logger LOGGER = LoggerFactory.getLogger(PdpProvider.class);
+
     /**
      * Get PDP groups.
      *
@@ -51,9 +61,19 @@
      * @return the PDP groups found
      * @throws PfModelException on errors getting PDP groups
      */
-    public PdpGroups getPdpGroups(@NonNull final PfDao dao, final String name, final String version)
+    public List<PdpGroup> getPdpGroups(@NonNull final PfDao dao, final String name, final String version)
             throws PfModelException {
-        return new PdpGroups();
+
+        PfConceptKey jpaPdpGroupKey = new PfConceptKey(name, version);
+        JpaPdpGroup jpaPdpGroup = dao.get(JpaPdpGroup.class, jpaPdpGroupKey);
+
+        if (jpaPdpGroup != null) {
+            return Collections.singletonList(jpaPdpGroup.toAuthorative());
+        } else {
+            String errorMessage = "PDP group not found: " + jpaPdpGroupKey.getId();
+            LOGGER.warn(errorMessage);
+            throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, errorMessage);
+        }
     }
 
     /**
@@ -64,21 +84,21 @@
      * @return the PDP groups found
      * @throws PfModelException on errors getting policies
      */
-    public PdpGroups getLatestPdpGroups(@NonNull final PfDao dao, final String name) throws PfModelException {
-        return new PdpGroups();
+    public List<PdpGroup> getLatestPdpGroups(@NonNull final PfDao dao, final String name) throws PfModelException {
+        return new ArrayList<>();
     }
 
     /**
      * Get a filtered list of PDP groups.
      *
      * @param dao the DAO to use to access the database
-     * @param pdpType The PDP type filter for the returned PDP groups
+     * @param pdpType The PDP type filter for the returned PDP groups, null to get policy types across PDP subgroups
      * @param supportedPolicyTypes a list of policy type name/version pairs that the PDP groups must support.
      * @return the PDP groups found
      */
-    public PdpGroups getFilteredPdpGroups(@NonNull final PfDao dao, @NonNull final String pdpType,
+    public List<PdpGroup> getFilteredPdpGroups(@NonNull final PfDao dao, final String pdpType,
             @NonNull final List<Pair<String, String>> supportedPolicyTypes) {
-        return new PdpGroups();
+        return new ArrayList<>();
     }
 
     /**
@@ -89,9 +109,33 @@
      * @return the PDP groups created
      * @throws PfModelException on errors creating PDP groups
      */
-    public PdpGroups createPdpGroups(@NonNull final PfDao dao, @NonNull final PdpGroups pdpGroups)
+    public List<PdpGroup> createPdpGroups(@NonNull final PfDao dao, @NonNull final List<PdpGroup> pdpGroups)
             throws PfModelException {
-        return new PdpGroups();
+
+        for (PdpGroup pdpGroup : pdpGroups) {
+            JpaPdpGroup jpaPdpGroup = new JpaPdpGroup();;
+            jpaPdpGroup.fromAuthorative(pdpGroup);
+
+            PfValidationResult validationResult = jpaPdpGroup.validate(new PfValidationResult());
+            if (!validationResult.isOk()) {
+                String errorMessage = "pdp group \"" + jpaPdpGroup.getId() + "\" is not valid \n" + validationResult;
+                LOGGER.warn(errorMessage);
+                throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, errorMessage);
+            }
+
+            dao.create(jpaPdpGroup);
+        }
+
+        // Return the created PDP groups
+        List<PdpGroup> returnPdpGroups = new ArrayList<>();
+
+        for (PdpGroup pdpGroup : pdpGroups) {
+            JpaPdpGroup jpaPdpGroup =
+                    dao.get(JpaPdpGroup.class, new PfConceptKey(pdpGroup.getName(), pdpGroup.getVersion()));
+            returnPdpGroups.add(jpaPdpGroup.toAuthorative());
+        }
+
+        return returnPdpGroups;
     }
 
     /**
@@ -102,9 +146,9 @@
      * @return the PDP groups updated
      * @throws PfModelException on errors updating PDP groups
      */
-    public PdpGroups updatePdpGroups(@NonNull final PfDao dao, @NonNull final PdpGroups pdpGroups)
+    public List<PdpGroup> updatePdpGroups(@NonNull final PfDao dao, @NonNull final List<PdpGroup> pdpGroups)
             throws PfModelException {
-        return new PdpGroups();
+        return new ArrayList<>();
     }
 
 
@@ -163,7 +207,7 @@
      */
     public void updatePdpStatistics(@NonNull final PfDao dao, @NonNull final String pdpGroupName,
             @NonNull final String pdpGroupVersion, @NonNull final String pdpType, @NonNull final String pdpInstanceId,
-            @NonNull final PdpStatistics pdppStatistics)  throws PfModelException {
+            @NonNull final PdpStatistics pdppStatistics) throws PfModelException {
         // Not implemented yet
     }
 
@@ -171,12 +215,12 @@
      * Get deployed policies.
      *
      * @param dao the DAO to use to access the database
-     * @param name the name of the policy to get, null to get all policies
-     * @return the policies deployed as a map of policy lists keyed by PDP group
+     * @param name the name of the policy to get deployed policies for, null to get all deployed policies
+     * @return the policies deployed as a map of policy lists keyed by PDP group name and version
      * @throws PfModelException on errors getting policies
      */
-    public Map<PdpGroup, List<ToscaPolicy>> getDeployedPolicyList(@NonNull final PfDao dao, final String name)
-            throws PfModelException {
+    public Map<Pair<String, String>, List<ToscaPolicy>> getDeployedPolicyList(@NonNull final PfDao dao,
+            final String name) throws PfModelException {
         return new LinkedHashMap<>();
     }
 }
diff --git a/models-pdp/src/test/java/org/onap/policy/models/persistence/provider/PdpProviderTest.java b/models-pdp/src/test/java/org/onap/policy/models/persistence/provider/PdpProviderTest.java
new file mode 100644
index 0000000..14990f9
--- /dev/null
+++ b/models-pdp/src/test/java/org/onap/policy/models/persistence/provider/PdpProviderTest.java
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.persistence.provider;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.models.dao.DaoParameters;
+import org.onap.policy.models.dao.PfDao;
+import org.onap.policy.models.dao.PfDaoFactory;
+import org.onap.policy.models.dao.impl.DefaultPfDao;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.concepts.PdpGroup;
+import org.onap.policy.models.pdp.concepts.PdpGroups;
+import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.pdp.concepts.PolicyTypeIdent;
+import org.onap.policy.models.pdp.enums.PdpHealthStatus;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.onap.policy.models.pdp.persistence.provider.PdpProvider;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+/**
+ * Test the {@link SimpleToscaProvider} class.
+ *
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+public class PdpProviderTest {
+    private Connection connection;
+    private PfDao pfDao;
+    private StandardCoder standardCoder;
+
+
+    /**
+     * Set up the DAO towards the database.
+     *
+     * @throws Exception on database errors
+     */
+    @Before
+    public void setupDao() throws Exception {
+        // Use the JDBC UI "jdbc:h2:mem:testdb" to test towards the h2 database
+        // Use the JDBC UI "jdbc:mariadb://localhost:3306/policy" to test towards a locally installed mariadb instance
+        connection = DriverManager.getConnection("jdbc:h2:mem:testdb", "policy", "P01icY");
+
+        final DaoParameters daoParameters = new DaoParameters();
+        daoParameters.setPluginClass(DefaultPfDao.class.getCanonicalName());
+
+        // Use the persistence unit ToscaConceptTest to test towards the h2 database
+        // Use the persistence unit ToscaConceptMariaDBTest to test towards a locally installed mariadb instance
+        daoParameters.setPersistenceUnit("ToscaConceptTest");
+
+        pfDao = new PfDaoFactory().createPfDao(daoParameters);
+        pfDao.init(daoParameters);
+    }
+
+    /**
+     * Set up GSON.
+     */
+    @Before
+    public void setupGson() {
+        standardCoder = new StandardCoder();
+    }
+
+    @After
+    public void teardown() throws Exception {
+        pfDao.close();
+        connection.close();
+    }
+
+    @Test
+    public void testPoliciesGet() throws Exception {
+        /*
+         * try { new PdpProvider().gePdpGroupst(null, null); fail("test should throw an exception here"); } catch
+         * (Exception exc) { assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+         *
+         * try { new SimpleToscaProvider().getPolicies(null, new PfConceptKey());
+         * fail("test should throw an exception here"); } catch (Exception exc) {
+         * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+         *
+         * try { new SimpleToscaProvider().getPolicies(pfDao, null); fail("test should throw an exception here"); }
+         * catch (Exception exc) { assertEquals("policyKey is marked @NonNull but is null", exc.getMessage()); }
+         */
+
+        String originalJson = ResourceUtils.getResourceAsString("testdata/PdpGroups0.json");
+
+        @SuppressWarnings("unchecked")
+        PdpGroups pdpGroups0 = standardCoder.decode(originalJson, PdpGroups.class);
+
+        PdpGroups createdPdpGroups0 = new PdpGroups();
+        createdPdpGroups0.setGroups(new PdpProvider().createPdpGroups(pfDao, pdpGroups0.getGroups()));
+        String createdJson = standardCoder.encode(createdPdpGroups0);
+        assertEquals(originalJson.replaceAll("\\s+", ""), createdJson.replaceAll("\\s+", ""));
+
+        PdpGroups gotPdpGroups0 = new PdpGroups();
+        gotPdpGroups0.setGroups(new PdpProvider().getPdpGroups(pfDao, "PdpGroup0", "1.2.3"));
+
+        String gotJson = standardCoder.encode(gotPdpGroups0);
+
+        assertEquals(originalJson.replaceAll("\\s+", ""), gotJson.replaceAll("\\s+", ""));
+
+    }
+    /*
+     * @Test public void testPolicyCreate() throws Exception { try { new SimpleToscaProvider().createPolicies(null,
+     * null); fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().createPolicies(null, new JpaToscaServiceTemplate());
+     * fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().createPolicies(pfDao, null); fail("test should throw an exception here"); } catch
+     * (Exception exc) { assertEquals("serviceTemplate is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * ToscaServiceTemplate toscaServiceTemplate = standardCoder.decode(
+     * ResourceUtils.getResourceAsString("policies/vCPE.policy.monitoring.input.tosca.json"),
+     * ToscaServiceTemplate.class);
+     *
+     * JpaToscaServiceTemplate originalServiceTemplate = new JpaToscaServiceTemplate();
+     * originalServiceTemplate.fromAuthorative(toscaServiceTemplate);
+     *
+     * assertNotNull(originalServiceTemplate); JpaToscaServiceTemplate createdServiceTemplate = new
+     * SimpleToscaProvider().createPolicies(pfDao, originalServiceTemplate);
+     *
+     * assertEquals(originalServiceTemplate, createdServiceTemplate); }
+     *
+     * @Test public void testPolicyUpdate() throws Exception { try { new SimpleToscaProvider().updatePolicies(null,
+     * null); fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().updatePolicies(null, new JpaToscaServiceTemplate());
+     * fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().updatePolicies(pfDao, null); fail("test should throw an exception here"); } catch
+     * (Exception exc) { assertEquals("serviceTemplate is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * ToscaServiceTemplate toscaServiceTemplate = standardCoder.decode(
+     * ResourceUtils.getResourceAsString("policies/vCPE.policy.monitoring.input.tosca.json"),
+     * ToscaServiceTemplate.class);
+     *
+     * JpaToscaServiceTemplate originalServiceTemplate = new JpaToscaServiceTemplate();
+     * originalServiceTemplate.fromAuthorative(toscaServiceTemplate);
+     *
+     * assertNotNull(originalServiceTemplate); JpaToscaServiceTemplate updatedServiceTemplate = new
+     * SimpleToscaProvider().updatePolicies(pfDao, originalServiceTemplate);
+     *
+     * assertEquals(originalServiceTemplate, updatedServiceTemplate); }
+     *
+     * @Test public void testPoliciesDelete() throws Exception { try { new SimpleToscaProvider().deletePolicy(null,
+     * null); fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().deletePolicy(null, new PfConceptKey());
+     * fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("dao is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * try { new SimpleToscaProvider().deletePolicy(pfDao, null); fail("test should throw an exception here"); } catch
+     * (Exception exc) { assertEquals("policyKey is marked @NonNull but is null", exc.getMessage()); }
+     *
+     * ToscaServiceTemplate toscaServiceTemplate = standardCoder.decode(
+     * ResourceUtils.getResourceAsString("policies/vCPE.policy.monitoring.input.tosca.json"),
+     * ToscaServiceTemplate.class);
+     *
+     * JpaToscaServiceTemplate originalServiceTemplate = new JpaToscaServiceTemplate();
+     * originalServiceTemplate.fromAuthorative(toscaServiceTemplate);
+     *
+     * assertNotNull(originalServiceTemplate); JpaToscaServiceTemplate createdServiceTemplate = new
+     * SimpleToscaProvider().createPolicies(pfDao, originalServiceTemplate);
+     *
+     * assertEquals(originalServiceTemplate, createdServiceTemplate);
+     *
+     * PfConceptKey policyKey = new PfConceptKey("onap.restart.tca:1.0.0");
+     *
+     * JpaToscaServiceTemplate deletedServiceTemplate = new SimpleToscaProvider().deletePolicy(pfDao, new
+     * PfConceptKey(policyKey));
+     *
+     * assertEquals(originalServiceTemplate.getTopologyTemplate().getPolicies().get(policyKey),
+     * deletedServiceTemplate.getTopologyTemplate().getPolicies().get(policyKey));
+     *
+     * try { new SimpleToscaProvider().getPolicies(pfDao, new PfConceptKey(policyKey));
+     * fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("policy not found: onap.restart.tca:1.0.0", exc.getMessage()); } }
+     *
+     * @Test public void testAssertPoliciesExist() throws PfModelException { JpaToscaServiceTemplate testServiceTemplate
+     * = new JpaToscaServiceTemplate();
+     *
+     * try { new SimpleToscaProvider().createPolicies(pfDao, testServiceTemplate);
+     * fail("test should throw an exception here"); } catch (Exception exc) {
+     * assertEquals("topology template not specified on service template", exc.getMessage()); }
+     *
+     * testServiceTemplate.setTopologyTemplate(new JpaToscaTopologyTemplate()); try { new
+     * SimpleToscaProvider().createPolicies(pfDao, testServiceTemplate); fail("test should throw an exception here"); }
+     * catch (Exception exc) { assertEquals("no policies specified on topology template of service template",
+     * exc.getMessage()); }
+     *
+     * testServiceTemplate.getTopologyTemplate().setPolicies(new JpaToscaPolicies()); try { new
+     * SimpleToscaProvider().createPolicies(pfDao, testServiceTemplate); fail("test should throw an exception here"); }
+     * catch (Exception exc) {
+     * assertEquals("list of policies specified on topology template of service template is empty", exc.getMessage()); }
+     *
+     * }
+     */
+}
diff --git a/models-pdp/src/test/resources/META-INF/persistence.xml b/models-pdp/src/test/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..079ba49
--- /dev/null
+++ b/models-pdp/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2019 Nordix Foundation.
+  ================================================================================
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+       http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+  SPDX-License-Identifier: Apache-2.0
+  ============LICENSE_END=========================================================
+-->
+
+<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
+    <persistence-unit name="ToscaConceptTest" transaction-type="RESOURCE_LOCAL">
+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+
+        <class>org.onap.policy.models.dao.converters.CDataConditioner</class>
+        <class>org.onap.policy.models.dao.converters.Uuid2String</class>
+        <class>org.onap.policy.models.base.PfConceptKey</class>
+        <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType</class>
+        <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
+
+        <properties>
+            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
+            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:testdb" />
+            <property name="javax.persistence.jdbc.user" value="policy" />
+            <property name="javax.persistence.jdbc.password" value="P01icY" />
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
+            <property name="eclipselink.ddl-generation.output-mode" value="database" />
+            <property name="eclipselink.logging.level" value="INFO" />
+        </properties>
+    </persistence-unit>
+
+    <persistence-unit name="ToscaConceptMariaDBTest" transaction-type="RESOURCE_LOCAL">
+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+
+        <class>org.onap.policy.models.dao.converters.CDataConditioner</class>
+        <class>org.onap.policy.models.dao.converters.Uuid2String</class>
+        <class>org.onap.policy.models.base.PfConceptKey</class>
+        <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType</class>
+        <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
+
+        <properties>
+            <property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver" />
+            <property name="javax.persistence.jdbc.url" value="jdbc:mariadb://localhost:3306/policy" />
+            <property name="javax.persistence.jdbc.user" value="policy" />
+            <property name="javax.persistence.jdbc.password" value="P01icY" />
+            <property name="javax.persistence.schema-generation.database.action" value="create" />
+
+            <property name="eclipselink.logging.level" value="ALL" />
+            <property name="eclipselink.logging.level.jpa" value="ALL" />
+            <property name="eclipselink.logging.level.ddl" value="ALL" />
+            <property name="eclipselink.logging.level.connection" value="ALL" />
+            <property name="eclipselink.logging.level.sql" value="ALL" />
+            <property name="eclipselink.logging.level.transaction" value="ALL" />
+            <property name="eclipselink.logging.level.sequencing" value="ALL" />
+            <property name="eclipselink.logging.level.server" value="ALL" />
+            <property name="eclipselink.logging.level.query" value="ALL" />
+            <property name="eclipselink.logging.level.properties" value="ALL" />
+
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
+            <property name="eclipselink.ddl-generation.output-mode" value="database" />
+            <property name="eclipselink.logging.level" value="INFO" />
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/models-pdp/src/test/resources/testdata/PdpGroups0.json b/models-pdp/src/test/resources/testdata/PdpGroups0.json
new file mode 100644
index 0000000..6ebdd6c
--- /dev/null
+++ b/models-pdp/src/test/resources/testdata/PdpGroups0.json
@@ -0,0 +1,43 @@
+{
+    "groups": [
+        {
+            "name": "PdpGroup0",
+            "version": "1.2.3",
+            "description": "group description",
+            "pdpGroupState": "PASSIVE",
+            "properties": {
+                "groupProperty0": "Value of Group Property 0"
+            },
+            "pdpSubgroups": [
+                {
+                    "pdpType": "APEX",
+                    "supportedPolicyTypes": [
+                        {
+                            "name": "policy.type.0",
+                            "version": "1.2.3"
+                        }
+                    ],
+                    "policies": [
+                        {
+                            "name": "Policy0",
+                            "version": "4.5.6"
+                        }
+                    ],
+                    "currentInstanceCount": 123,
+                    "desiredInstanceCount": 456,
+                    "properties": {
+                        "subgroupProperty0": "Value of sub Group Property 0"
+                    },
+                    "pdpInstances": [
+                        {
+                            "instanceId": "apex-0",
+                            "pdpState": "ACTIVE",
+                            "healthy": "NOT_HEALTHY",
+                            "message": "message from PDP"
+                        }
+                    ]
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProvider.java b/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProvider.java
index b0494ff..af1c88f 100644
--- a/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProvider.java
+++ b/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProvider.java
@@ -28,7 +28,6 @@
 import org.apache.commons.lang3.tuple.Pair;
 import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.pdp.concepts.PdpGroup;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.pdp.concepts.PdpStatistics;
 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
@@ -52,6 +51,9 @@
      */
     public void init() throws PfModelException;
 
+    @Override
+    public void close() throws PfModelException;
+
     /**
      * Get policy types.
      *
@@ -145,10 +147,13 @@
      * Get policies for a policy type name.
      *
      * @param policyTypeName the name of the policy type for which to get policies
+     * @param policyTypeVersion the version of the policy type, null returns all versions of deployed policies for
+     *        policy types
      * @return the policies found
      * @throws PfModelException on errors getting policies
      */
-    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName) throws PfModelException;
+    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName,
+            final String policyTypeVersion) throws PfModelException;
 
     /**
      * Get latest policies.
@@ -284,25 +289,25 @@
      * @return the PDP groups found
      * @throws PfModelException on errors getting PDP groups
      */
-    public PdpGroups getPdpGroups(final String name, final String version) throws PfModelException;
+    public List<PdpGroup> getPdpGroups(final String name, final String version) throws PfModelException;
 
     /**
-     * Get latest PDP Groups.
+     * Get latest PDP Groups, returns PDP groups in all states.
      *
      * @param name the name of the PDP group to get, null to get all PDP groups
      * @return the PDP groups found
      * @throws PfModelException on errors getting policies
      */
-    public PdpGroups getLatestPdpGroups(final String name) throws PfModelException;
+    public List<PdpGroup> getLatestPdpGroups(final String name) throws PfModelException;
 
     /**
-     * Get a filtered list of PDP groups.
+     * Get a filtered list of PDP groups, returns only active PDP groups.
      *
-     * @param pdpType The PDP type filter for the returned PDP groups
+     * @param pdpType The PDP type filter for the returned PDP groups, null to get policy types across PDP subgroups
      * @param supportedPolicyTypes a list of policy type name/version pairs that the PDP groups must support.
      * @return the PDP groups found
      */
-    public PdpGroups getFilteredPdpGroups(@NonNull final String pdpType,
+    public List<PdpGroup> getFilteredPdpGroups(final String pdpType,
             @NonNull final List<Pair<String, String>> supportedPolicyTypes);
 
     /**
@@ -312,7 +317,7 @@
      * @return the PDP groups created
      * @throws PfModelException on errors creating PDP groups
      */
-    public PdpGroups createPdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException;
+    public List<PdpGroup> createPdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException;
 
     /**
      * Updates PDP groups.
@@ -321,7 +326,7 @@
      * @return the PDP groups updated
      * @throws PfModelException on errors updating PDP groups
      */
-    public PdpGroups updatePdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException;
+    public List<PdpGroup> updatePdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException;
 
 
     /**
@@ -372,8 +377,9 @@
      * Get deployed policies.
      *
      * @param name the name of the policy to get, null to get all policies
-     * @return the policies deployed as a map of policy lists keyed by PDP group
+     * @return the policies deployed as a map of policy lists keyed by PDP group name and version
      * @throws PfModelException on errors getting policies
      */
-    public Map<PdpGroup, List<ToscaPolicy>> getDeployedPolicyList(final String name) throws PfModelException;
+    public Map<Pair<String, String>, List<ToscaPolicy>> getDeployedPolicyList(final String name)
+            throws PfModelException;
 }
diff --git a/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProviderFactory.java b/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProviderFactory.java
index 718668b..86551b3 100644
--- a/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProviderFactory.java
+++ b/models-provider/src/main/java/org/onap/policy/models/provider/PolicyModelsProviderFactory.java
@@ -66,8 +66,12 @@
         }
 
         try {
-            return (PolicyModelsProvider) implementationClass.getConstructor(PolicyModelsProviderParameters.class)
-                    .newInstance(parameters);
+            PolicyModelsProvider provider = (PolicyModelsProvider) implementationClass
+                    .getConstructor(PolicyModelsProviderParameters.class).newInstance(parameters);
+
+            provider.init();
+
+            return provider;
         } catch (Exception exc) {
             String errorMessage =
                     "could not create an instance of PolicyModelsProvider \"" + parameters.getImplementation() + "\"";
diff --git a/models-provider/src/main/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderImpl.java b/models-provider/src/main/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderImpl.java
index ee8ed73..96185b6 100644
--- a/models-provider/src/main/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderImpl.java
+++ b/models-provider/src/main/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderImpl.java
@@ -38,7 +38,6 @@
 import org.onap.policy.models.dao.PfDaoFactory;
 import org.onap.policy.models.dao.impl.DefaultPfDao;
 import org.onap.policy.models.pdp.concepts.PdpGroup;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.pdp.concepts.PdpStatistics;
 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
 import org.onap.policy.models.pdp.persistence.provider.PdpProvider;
@@ -84,6 +83,12 @@
         LOGGER.debug("opening the database connection to {} using persistence unit {}", parameters.getDatabaseUrl(),
                 parameters.getPersistenceUnit());
 
+        if (connection != null || pfDao != null) {
+            String errorMessage = "provider is already initialized";
+            LOGGER.warn(errorMessage);
+            throw new PfModelException(Response.Status.NOT_ACCEPTABLE, errorMessage);
+        }
+
         // Decode the password using Base64
         String decodedPassword = new String(Base64.getDecoder().decode(parameters.getDatabasePassword()));
 
@@ -201,9 +206,10 @@
     }
 
     @Override
-    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName) throws PfModelException {
+    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName,
+            final String policyTypeVersion) throws PfModelException {
         assertInitilized();
-        return new AuthorativeToscaProvider().getPolicyList4PolicyType(pfDao, policyTypeName);
+        return new AuthorativeToscaProvider().getPolicyList4PolicyType(pfDao, policyTypeName, policyTypeVersion);
     }
 
     @Override
@@ -293,32 +299,32 @@
     }
 
     @Override
-    public PdpGroups getPdpGroups(final String name, final String version) throws PfModelException {
+    public List<PdpGroup> getPdpGroups(final String name, final String version) throws PfModelException {
         assertInitilized();
         return new PdpProvider().getPdpGroups(pfDao, name, version);
     }
 
     @Override
-    public PdpGroups getLatestPdpGroups(final String name) throws PfModelException {
+    public List<PdpGroup> getLatestPdpGroups(final String name) throws PfModelException {
         assertInitilized();
         return new PdpProvider().getLatestPdpGroups(pfDao, name);
     }
 
     @Override
-    public PdpGroups getFilteredPdpGroups(@NonNull final String pdpType,
+    public List<PdpGroup> getFilteredPdpGroups(final String pdpType,
             @NonNull final List<Pair<String, String>> supportedPolicyTypes) {
         assertInitilized();
         return new PdpProvider().getFilteredPdpGroups(pfDao, pdpType, supportedPolicyTypes);
     }
 
     @Override
-    public PdpGroups createPdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException {
+    public List<PdpGroup> createPdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException {
         assertInitilized();
         return new PdpProvider().createPdpGroups(pfDao, pdpGroups);
     }
 
     @Override
-    public PdpGroups updatePdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException {
+    public List<PdpGroup> updatePdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException {
         assertInitilized();
         return new PdpProvider().updatePdpGroups(pfDao, pdpGroups);
     }
@@ -345,14 +351,15 @@
     @Override
     public void updatePdpStatistics(@NonNull final String pdpGroupName, @NonNull final String pdpGroupVersion,
             @NonNull final String pdpType, @NonNull final String pdpInstanceId,
-            @NonNull final PdpStatistics pdppStatistics)  throws PfModelException {
+            @NonNull final PdpStatistics pdppStatistics) throws PfModelException {
         assertInitilized();
         new PdpProvider().updatePdpStatistics(pfDao, pdpGroupName, pdpGroupVersion, pdpType, pdpInstanceId,
                 pdppStatistics);
     }
 
     @Override
-    public Map<PdpGroup, List<ToscaPolicy>> getDeployedPolicyList(final String name) throws PfModelException {
+    public Map<Pair<String, String>, List<ToscaPolicy>> getDeployedPolicyList(final String name)
+            throws PfModelException {
         assertInitilized();
         return new PdpProvider().getDeployedPolicyList(pfDao, name);
     }
diff --git a/models-provider/src/main/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderImpl.java b/models-provider/src/main/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderImpl.java
index 3db8e5e..bf707ef 100644
--- a/models-provider/src/main/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderImpl.java
+++ b/models-provider/src/main/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderImpl.java
@@ -35,7 +35,6 @@
 import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.onap.policy.models.pdp.concepts.PdpGroup;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.pdp.concepts.PdpStatistics;
 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
 import org.onap.policy.models.provider.PolicyModelsProvider;
@@ -120,7 +119,8 @@
     }
 
     @Override
-    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName) throws PfModelException {
+    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final String policyTypeName,
+            final String policyTypeVersion) throws PfModelException {
         return new ArrayList<>();
     }
 
@@ -199,29 +199,29 @@
     }
 
     @Override
-    public PdpGroups getPdpGroups(final String name, final String version) throws PfModelException {
-        return null;
+    public List<PdpGroup> getPdpGroups(final String name, final String version) throws PfModelException {
+        return new ArrayList<>();
     }
 
     @Override
-    public PdpGroups getLatestPdpGroups(final String name) throws PfModelException {
-        return null;
+    public List<PdpGroup> getLatestPdpGroups(final String name) throws PfModelException {
+        return new ArrayList<>();
     }
 
     @Override
-    public PdpGroups getFilteredPdpGroups(@NonNull final String pdpType,
+    public List<PdpGroup> getFilteredPdpGroups(final String pdpType,
             @NonNull final List<Pair<String, String>> supportedPolicyTypes) {
-        return null;
+        return new ArrayList<>();
     }
 
     @Override
-    public PdpGroups createPdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException {
-        return null;
+    public List<PdpGroup> createPdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException {
+        return new ArrayList<>();
     }
 
     @Override
-    public PdpGroups updatePdpGroups(@NonNull final PdpGroups pdpGroups) throws PfModelException {
-        return null;
+    public List<PdpGroup> updatePdpGroups(@NonNull final List<PdpGroup> pdpGroups) throws PfModelException {
+        return new ArrayList<>();
     }
 
     @Override
@@ -243,12 +243,13 @@
     @Override
     public void updatePdpStatistics(@NonNull final String pdpGroupName, @NonNull final String pdpGroupVersion,
             @NonNull final String pdpType, @NonNull final String pdpInstanceId,
-            @NonNull final PdpStatistics pdppStatistics)  throws PfModelException {
+            @NonNull final PdpStatistics pdppStatistics) throws PfModelException {
         // Not implemented
     }
 
     @Override
-    public Map<PdpGroup, List<ToscaPolicy>> getDeployedPolicyList(final String name) throws PfModelException {
+    public Map<Pair<String, String>, List<ToscaPolicy>> getDeployedPolicyList(final String name)
+            throws PfModelException {
         return null;
     }
 
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderTest.java
index d925333..7ad2217 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DatabasePolicyModelsProviderTest.java
@@ -24,11 +24,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
+import java.util.ArrayList;
 import java.util.Base64;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.provider.PolicyModelsProvider;
 import org.onap.policy.models.provider.PolicyModelsProviderFactory;
 import org.onap.policy.models.provider.PolicyModelsProviderParameters;
@@ -69,6 +69,7 @@
         parameters.setDatabaseUrl("jdbc://www.acmecorp.nonexist");
 
         assertThatThrownBy(() -> {
+            databaseProvider.close();
             databaseProvider.init();
         }).hasMessage("could not connect to database with URL \"jdbc://www.acmecorp.nonexist\"");
 
@@ -116,7 +117,6 @@
     public void testProviderMethodsNull() throws Exception {
         PolicyModelsProvider databaseProvider =
                 new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
-        databaseProvider.init();
 
         assertThatThrownBy(() -> {
             databaseProvider.getPolicyTypes(null, null);
@@ -235,6 +235,8 @@
         PolicyModelsProvider databaseProvider =
                 new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
 
+        databaseProvider.close();
+
         assertThatThrownBy(() -> {
             databaseProvider.getPolicyTypes("name", "version");
         }).hasMessage("policy models provider is not initilaized");
@@ -244,7 +246,6 @@
     public void testProviderMethods() {
         try (PolicyModelsProvider databaseProvider =
                 new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters)) {
-            databaseProvider.init();
 
             assertThatThrownBy(() -> {
                 databaseProvider.getPolicyTypes("name", "version");
@@ -310,9 +311,12 @@
                 databaseProvider.deleteGuardPolicy("policy_id");
             }).hasMessage("no policy found for policy ID: policy_id");
 
-            assertNotNull(databaseProvider.getPdpGroups("name", "version"));
-            assertNotNull(databaseProvider.createPdpGroups(new PdpGroups()));
-            assertNotNull(databaseProvider.updatePdpGroups(new PdpGroups()));
+            assertThatThrownBy(() -> {
+                databaseProvider.getPdpGroups("name", "version");
+            }).hasMessage("PDP group not found: name:version");
+
+            assertNotNull(databaseProvider.createPdpGroups(new ArrayList<>()));
+            assertNotNull(databaseProvider.updatePdpGroups(new ArrayList<>()));
             assertNotNull(databaseProvider.deletePdpGroup("name", "version"));
 
         } catch (Exception exc) {
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyBadProviderImpl.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyBadProviderImpl.java
index 9320df5..c64603d 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyBadProviderImpl.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyBadProviderImpl.java
@@ -31,7 +31,6 @@
 import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.onap.policy.models.pdp.concepts.PdpGroup;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.pdp.concepts.PdpStatistics;
 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
 import org.onap.policy.models.provider.PolicyModelsProvider;
@@ -53,7 +52,7 @@
     }
 
     @Override
-    public void close() throws Exception {}
+    public void close() throws PfModelException {}
 
     @Override
     public void init() throws PfModelException {}
@@ -86,14 +85,12 @@
     }
 
     @Override
-    public ToscaServiceTemplate createPolicies(@NonNull ToscaServiceTemplate serviceTemplate)
-            throws PfModelException {
+    public ToscaServiceTemplate createPolicies(@NonNull ToscaServiceTemplate serviceTemplate) throws PfModelException {
         return null;
     }
 
     @Override
-    public ToscaServiceTemplate updatePolicies(@NonNull ToscaServiceTemplate serviceTemplate)
-            throws PfModelException {
+    public ToscaServiceTemplate updatePolicies(@NonNull ToscaServiceTemplate serviceTemplate) throws PfModelException {
         return null;
     }
 
@@ -147,17 +144,17 @@
     }
 
     @Override
-    public PdpGroups getPdpGroups(String name, String version) throws PfModelException {
+    public List<PdpGroup> getPdpGroups(String name, String version) throws PfModelException {
         return null;
     }
 
     @Override
-    public PdpGroups createPdpGroups(@NonNull PdpGroups pdpGroups) throws PfModelException {
+    public List<PdpGroup> createPdpGroups(@NonNull List<PdpGroup> pdpGroups) throws PfModelException {
         return null;
     }
 
     @Override
-    public PdpGroups updatePdpGroups(@NonNull PdpGroups pdpGroups) throws PfModelException {
+    public List<PdpGroup> updatePdpGroups(@NonNull List<PdpGroup> pdpGroups) throws PfModelException {
         return null;
     }
 
@@ -187,7 +184,8 @@
     }
 
     @Override
-    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull String policyTypeName) throws PfModelException {
+    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull String policyTypeName, final String policyTypeVersion)
+            throws PfModelException {
         return null;
     }
 
@@ -202,20 +200,19 @@
     }
 
     @Override
-    public PdpGroups getLatestPdpGroups(String name) throws PfModelException {
+    public List<PdpGroup> getLatestPdpGroups(String name) throws PfModelException {
         return null;
     }
 
     @Override
-    public PdpGroups getFilteredPdpGroups(@NonNull String pdpType,
+    public List<PdpGroup> getFilteredPdpGroups(@NonNull String pdpType,
             @NonNull List<Pair<String, String>> supportedPolicyTypes) {
         return null;
     }
 
     @Override
     public void updatePdpSubGroup(@NonNull String pdpGroupName, @NonNull String pdpGroupVersion,
-            @NonNull PdpSubGroup pdpSubGroup) throws PfModelException {
-    }
+            @NonNull PdpSubGroup pdpSubGroup) throws PfModelException {}
 
     @Override
     public List<PdpStatistics> getPdpStatistics(String name, String version) throws PfModelException {
@@ -224,11 +221,10 @@
 
     @Override
     public void updatePdpStatistics(@NonNull String pdpGroupName, @NonNull String pdpGroupVersion,
-            @NonNull String pdpType, @NonNull String pdpInstanceId, @NonNull PdpStatistics pdppStatistics) {
-    }
+            @NonNull String pdpType, @NonNull String pdpInstanceId, @NonNull PdpStatistics pdppStatistics) {}
 
     @Override
-    public Map<PdpGroup, List<ToscaPolicy>> getDeployedPolicyList(String name) throws PfModelException {
+    public Map<Pair<String, String>, List<ToscaPolicy>> getDeployedPolicyList(String name) throws PfModelException {
         return null;
     }
 }
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderTest.java
index a0b4857..01da449 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/DummyPolicyModelsProviderTest.java
@@ -24,10 +24,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.ArrayList;
+
 import org.junit.Test;
-import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.provider.PolicyModelsProvider;
 import org.onap.policy.models.provider.PolicyModelsProviderFactory;
 import org.onap.policy.models.provider.PolicyModelsProviderParameters;
@@ -91,9 +93,9 @@
         assertNotNull(dummyProvider.updateGuardPolicy(new LegacyGuardPolicyInput()));
         assertNotNull(dummyProvider.deleteGuardPolicy("policy_id"));
 
-        assertNull(dummyProvider.getPdpGroups("name", "version"));
-        assertNull(dummyProvider.createPdpGroups(new PdpGroups()));
-        assertNull(dummyProvider.updatePdpGroups(new PdpGroups()));
+        assertTrue(dummyProvider.getPdpGroups("name", "version").isEmpty());
+        assertTrue(dummyProvider.createPdpGroups(new ArrayList<>()).isEmpty());
+        assertTrue(dummyProvider.updatePdpGroups(new ArrayList<>()).isEmpty());
         assertNull(dummyProvider.deletePdpGroup("name", "version"));
 
 
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyGuardPersistenceTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyGuardPersistenceTest.java
index 2cb787d..47f099a 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyGuardPersistenceTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyGuardPersistenceTest.java
@@ -82,7 +82,6 @@
         parameters.setPersistenceUnit("ToscaConceptTest");
 
         databaseProvider = new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
-        databaseProvider.init();
     }
 
     /**
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyOperationalPersistenceTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyOperationalPersistenceTest.java
index 60d577c..65e6a2f 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyOperationalPersistenceTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyLegacyOperationalPersistenceTest.java
@@ -82,7 +82,6 @@
         parameters.setPersistenceUnit("ToscaConceptTest");
 
         databaseProvider = new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
-        databaseProvider.init();
     }
 
     /**
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyPersistenceTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyPersistenceTest.java
index b29e1d6..468d4d7 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyPersistenceTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyPersistenceTest.java
@@ -90,7 +90,6 @@
         parameters.setPersistenceUnit("ToscaConceptTest");
 
         databaseProvider = new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
-        databaseProvider.init();
     }
 
     /**
diff --git a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyToscaPersistenceTest.java b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyToscaPersistenceTest.java
index f2b8676..bf48292 100644
--- a/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyToscaPersistenceTest.java
+++ b/models-provider/src/test/java/org/onap/policy/models/provider/impl/PolicyToscaPersistenceTest.java
@@ -89,7 +89,6 @@
         parameters.setPersistenceUnit("ToscaConceptTest");
 
         databaseProvider = new PolicyModelsProviderFactory().createPolicyModelsProvider(parameters);
-        databaseProvider.init();
     }
 
     /**
diff --git a/models-provider/src/test/resources/META-INF/persistence.xml b/models-provider/src/test/resources/META-INF/persistence.xml
index 23e8567..c7d6d1e 100644
--- a/models-provider/src/test/resources/META-INF/persistence.xml
+++ b/models-provider/src/test/resources/META-INF/persistence.xml
@@ -28,6 +28,9 @@
         <class>org.onap.policy.models.base.PfConceptKey</class>
         <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType</class>
         <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
 
         <properties>
             <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
@@ -46,7 +49,11 @@
         <class>org.onap.policy.models.dao.converters.CDataConditioner</class>
         <class>org.onap.policy.models.dao.converters.Uuid2String</class>
         <class>org.onap.policy.models.base.PfConceptKey</class>
+        <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType</class>
         <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
+        <class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
 
         <properties>
             <property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver" />
diff --git a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/provider/AuthorativeToscaProvider.java b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/provider/AuthorativeToscaProvider.java
index e9e92d3..d0127d6 100644
--- a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/provider/AuthorativeToscaProvider.java
+++ b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/provider/AuthorativeToscaProvider.java
@@ -174,11 +174,13 @@
      *
      * @param dao the DAO to use to access the database
      * @param policyTypeName the name of the policy type for which to get policies
+     * @param policyTypeVersion the version of the policy type, null returns all versions of deployed policies for
+     *        policy types
      * @return the policies found
      * @throws PfModelException on errors getting policies
      */
-    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final PfDao dao, @NonNull final String policyTypeName)
-            throws PfModelException {
+    public List<ToscaPolicy> getPolicyList4PolicyType(@NonNull final PfDao dao, @NonNull final String policyTypeName,
+            final String policyTypeVersion) throws PfModelException {
         return new ArrayList<>();
     }
 
@@ -202,7 +204,7 @@
      * @return the policies found
      * @throws PfModelException on errors getting policies
      */
-    public List<ToscaPolicy> getLatestPolicyList(@NonNull final PfDao dao, final String name) throws PfModelException  {
+    public List<ToscaPolicy> getLatestPolicyList(@NonNull final PfDao dao, final String name) throws PfModelException {
         return new ArrayList<>();
     }