From 4f5711b8ec555ab8307516ce178b454445d3833f Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Fri, 24 Mar 2017 10:03:47 +0000
Subject: [PATCH] #3535 - Apply metadata changes in cascade

---
 securis/src/main/java/net/curisit/securis/db/common/Metadata.java              |   16 
 securis/src/main/java/net/curisit/securis/db/Application.java                  |   31 
 securis/src/main/java/net/curisit/securis/services/helpers/MetadataHelper.java |  155 +++++
 securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java          |   23 
 securis/src/main/java/net/curisit/securis/services/BasicServices.java          |   15 
 securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java          |   20 
 securis/src/main/java/net/curisit/securis/db/Pack.java                         |  572 ++++++++++----------
 securis/src/main/java/net/curisit/securis/services/ApplicationResource.java    |  337 ++++++------
 securis/src/main/java/net/curisit/securis/services/PackResource.java           |    4 
 securis/src/main/java/net/curisit/securis/services/LicenseTypeResource.java    |  356 ++++++------
 securis/src/main/java/net/curisit/securis/services/UserResource.java           |    5 
 securis/src/main/java/net/curisit/securis/db/LicenseType.java                  |    8 
 securis/src/main/java/net/curisit/securis/db/PackMetadata.java                 |   23 
 13 files changed, 894 insertions(+), 671 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/db/Application.java b/securis/src/main/java/net/curisit/securis/db/Application.java
index 508175a..c994373 100644
--- a/securis/src/main/java/net/curisit/securis/db/Application.java
+++ b/securis/src/main/java/net/curisit/securis/db/Application.java
@@ -64,7 +64,7 @@
 	@OneToMany(fetch = FetchType.LAZY, mappedBy = "application")
 	private Set<LicenseType> licenseTypes;
 
-	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "application")
+	@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH }, mappedBy = "application")
 	@JsonManagedReference
 	private Set<ApplicationMetadata> metadata;
 
@@ -110,20 +110,6 @@
 		this.metadata = metadata;
 	}
 
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof Application))
-			return false;
-		Application other = (Application) obj;
-		return id.equals(other.id);
-	}
-
-	@Override
-	public int hashCode() {
-
-		return (id == null ? 0 : id.hashCode());
-	}
-
 	public String getLicenseFilename() {
 		return licenseFilename;
 	}
@@ -148,4 +134,19 @@
 	public void setCode(String code) {
 		this.code = code;
 	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof Application))
+			return false;
+		Application other = (Application) obj;
+		return id.equals(other.id);
+	}
+
+	@Override
+	public int hashCode() {
+
+		return (id == null ? 0 : id.hashCode());
+	}
+
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java b/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
index 78acf4c..8123dae 100644
--- a/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
@@ -2,6 +2,7 @@
 
 import java.io.Serializable;
 import java.util.Date;
+import java.util.Objects;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -22,6 +23,8 @@
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import net.curisit.securis.db.common.Metadata;
+
 /**
  * Entity implementation class for Entity: application_metadata
  * 
@@ -32,7 +35,7 @@
 @Table(name = "application_metadata")
 @JsonIgnoreProperties(ignoreUnknown = true)
 @NamedQueries({ @NamedQuery(name = "list-application-metadata", query = "SELECT a FROM ApplicationMetadata a where a.application.id = :applicationId") })
-public class ApplicationMetadata implements Serializable {
+public class ApplicationMetadata implements Serializable, Metadata {
 
 	private static final Logger LOG = LogManager.getLogger(ApplicationMetadata.class);
 
@@ -100,7 +103,20 @@
 	@Override
 	public String toString() {
 
-		return String.format("ApplicationMetadata (%s)", this.key);
+		return String.format("AppMd (%s: %s)", this.key, value);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof ApplicationMetadata))
+			return false;
+		ApplicationMetadata other = (ApplicationMetadata) obj;
+		return Objects.equals(key, other.key) && Objects.equals(application, other.application);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(key, application);
 	}
 
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseType.java b/securis/src/main/java/net/curisit/securis/db/LicenseType.java
index a34e3ef..e335a25 100644
--- a/securis/src/main/java/net/curisit/securis/db/LicenseType.java
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseType.java
@@ -62,7 +62,7 @@
 	@JoinColumn(name = "application_id")
 	private Application application;
 
-	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "licenseType")
+	@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH }, mappedBy = "licenseType")
 	@JsonManagedReference
 	private Set<LicenseTypeMetadata> metadata;
 
@@ -152,7 +152,11 @@
 
 	@Override
 	public int hashCode() {
-
 		return (id == null ? 0 : id.hashCode());
 	}
+
+	@Override
+	public String toString() {
+		return String.format("LT: ID: %d, code: %s", id, code);
+	}
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java b/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
index 08c5fc1..777801b 100644
--- a/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
@@ -1,6 +1,7 @@
 package net.curisit.securis.db;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -17,6 +18,8 @@
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 
+import net.curisit.securis.db.common.Metadata;
+
 /**
  * Entity implementation class for Entity: licensetype_metadata
  * 
@@ -27,7 +30,7 @@
 @Table(name = "licensetype_metadata")
 @JsonIgnoreProperties(ignoreUnknown = true)
 @NamedQueries({ @NamedQuery(name = "list-licensetype-metadata", query = "SELECT a FROM LicenseTypeMetadata a where a.licenseType.id = :licenseTypeId") })
-public class LicenseTypeMetadata implements Serializable {
+public class LicenseTypeMetadata implements Serializable, Metadata {
 
 	private static final long serialVersionUID = 1L;
 
@@ -76,4 +79,22 @@
 	public void setMandatory(boolean mandatory) {
 		this.mandatory = mandatory;
 	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof LicenseTypeMetadata))
+			return false;
+		LicenseTypeMetadata other = (LicenseTypeMetadata) obj;
+		return Objects.equals(key, other.key) && Objects.equals(licenseType, other.licenseType);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(key, licenseType);
+	}
+
+	@Override
+	public String toString() {
+		return String.format("LTMD (%s: %s)", key, value);
+	}
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/Pack.java b/securis/src/main/java/net/curisit/securis/db/Pack.java
index 2cb6da8..3885630 100644
--- a/securis/src/main/java/net/curisit/securis/db/Pack.java
+++ b/securis/src/main/java/net/curisit/securis/db/Pack.java
@@ -20,8 +20,6 @@
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 
-import net.curisit.integrity.commons.Utils;
-
 import org.hibernate.annotations.Type;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
@@ -30,6 +28,8 @@
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
+
+import net.curisit.integrity.commons.Utils;
 
 /**
  * Entity implementation class for Entity: pack
@@ -40,358 +40,360 @@
 @Entity
 @Table(name = "pack")
 @JsonIgnoreProperties(ignoreUnknown = true)
-@NamedQueries({
-        @NamedQuery(name = "list-packs", query = "SELECT pa FROM Pack pa"),//
-        @NamedQuery(name = "pack-by-code", query = "SELECT pa FROM Pack pa where pa.code = :code"),//
-        @NamedQuery(name = "list-packs-by-orgs", query = "SELECT pa FROM Pack pa where pa.organization.id in :list_ids")
-})
+@NamedQueries({ @NamedQuery(name = "list-packs", query = "SELECT pa FROM Pack pa"), //
+		@NamedQuery(name = "pack-by-code", query = "SELECT pa FROM Pack pa where pa.code = :code"), //
+		@NamedQuery(name = "list-packs-by-lic-type", query = "SELECT pa FROM Pack pa where pa.licenseType.id = :lt_id"), //
+		@NamedQuery(name = "list-packs-by-orgs", query = "SELECT pa FROM Pack pa where pa.organization.id in :list_ids") })
 public class Pack implements Serializable {
 
-    private static final long serialVersionUID = 1L;
+	private static final long serialVersionUID = 1L;
 
-    @Id
-    @GeneratedValue
-    private Integer id;
+	@Id
+	@GeneratedValue
+	private Integer id;
 
-    private String code;
+	private String code;
 
-    private String comments;
+	private String comments;
 
-    @Column(name = "creation_timestamp")
-    @JsonProperty("creation_timestamp")
-    private Date creationTimestamp;
+	@Column(name = "creation_timestamp")
+	@JsonProperty("creation_timestamp")
+	private Date creationTimestamp;
 
-    @JsonIgnore
-    @ManyToOne
-    @JoinColumn(name = "organization_id")
-    private Organization organization;
+	@JsonIgnore
+	@ManyToOne
+	@JoinColumn(name = "organization_id")
+	private Organization organization;
 
-    @JsonIgnore
-    @ManyToOne
-    @JoinColumn(name = "license_type_id")
-    private LicenseType licenseType;
+	@JsonIgnore
+	@ManyToOne
+	@JoinColumn(name = "license_type_id")
+	private LicenseType licenseType;
 
-    @JsonIgnore
-    @ManyToOne
-    @JoinColumn(name = "created_by")
-    private User createdBy;
+	@JsonIgnore
+	@ManyToOne
+	@JoinColumn(name = "created_by")
+	private User createdBy;
 
-    @JsonIgnore
-    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "pack")
-    private Set<License> licenses;
+	@JsonIgnore
+	@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH }, mappedBy = "pack")
+	private Set<License> licenses;
 
-    @Column(name = "num_licenses")
-    @JsonProperty("num_licenses")
-    private int numLicenses;
+	@Column(name = "num_licenses")
+	@JsonProperty("num_licenses")
+	private int numLicenses;
 
-    @Column(name = "init_valid_date")
-    @JsonProperty("init_valid_date")
-    private Date initValidDate;
+	@Column(name = "init_valid_date")
+	@JsonProperty("init_valid_date")
+	private Date initValidDate;
 
-    @Column(name = "end_valid_date")
-    @JsonProperty("end_valid_date")
-    private Date endValidDate;
+	@Column(name = "end_valid_date")
+	@JsonProperty("end_valid_date")
+	private Date endValidDate;
 
-    @Type(type = "net.curisit.securis.db.common.PackStatusType")
-    private PackStatus status;
+	@Type(type = "net.curisit.securis.db.common.PackStatusType")
+	private PackStatus status;
 
-    @Column(name = "license_preactivation")
-    @JsonProperty("license_preactivation")
-    private boolean licensePreactivation;
+	@Column(name = "license_preactivation")
+	@JsonProperty("license_preactivation")
+	private boolean licensePreactivation;
 
-    @Column(name = "preactivation_valid_period")
-    @JsonProperty("preactivation_valid_period")
-    private Integer preactivationValidPeriod;
+	@Column(name = "preactivation_valid_period")
+	@JsonProperty("preactivation_valid_period")
+	private Integer preactivationValidPeriod;
 
-    @Column(name = "renew_valid_period")
-    @JsonProperty("renew_valid_period")
-    private Integer renewValidPeriod;
+	@Column(name = "renew_valid_period")
+	@JsonProperty("renew_valid_period")
+	private Integer renewValidPeriod;
 
-    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "pack")
-    private Set<PackMetadata> metadata;
+	@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH }, mappedBy = "pack")
+	private Set<PackMetadata> metadata;
 
-    public Integer getId() {
-        return id;
-    }
+	public Integer getId() {
+		return id;
+	}
 
-    public void setId(Integer id) {
-        this.id = id;
-    }
+	public void setId(Integer id) {
+		this.id = id;
+	}
 
-    public String getCode() {
-        return code;
-    }
+	public String getCode() {
+		return code;
+	}
 
-    public void setCode(String code) {
-        this.code = code;
-    }
+	public void setCode(String code) {
+		this.code = code;
+	}
 
-    public Date getCreationTimestamp() {
-        return creationTimestamp;
-    }
+	public Date getCreationTimestamp() {
+		return creationTimestamp;
+	}
 
-    public void setCreationTimestamp(Date creationTimestamp) {
-        this.creationTimestamp = creationTimestamp;
-    }
+	public void setCreationTimestamp(Date creationTimestamp) {
+		this.creationTimestamp = creationTimestamp;
+	}
 
-    public Organization getOrganization() {
-        return organization;
-    }
+	public Organization getOrganization() {
+		return organization;
+	}
 
-    public void setOrganization(Organization organization) {
-        this.organization = organization;
-    }
+	public void setOrganization(Organization organization) {
+		this.organization = organization;
+	}
 
-    public LicenseType getLicenseType() {
-        return licenseType;
-    }
+	public LicenseType getLicenseType() {
+		return licenseType;
+	}
 
-    public void setLicenseType(LicenseType licenseType) {
-        this.licenseType = licenseType;
-    }
+	public void setLicenseType(LicenseType licenseType) {
+		this.licenseType = licenseType;
+	}
 
-    public User getCreatedBy() {
-        return createdBy;
-    }
+	public User getCreatedBy() {
+		return createdBy;
+	}
 
-    public void setCreatedBy(User createdBy) {
-        this.createdBy = createdBy;
-    }
+	public void setCreatedBy(User createdBy) {
+		this.createdBy = createdBy;
+	}
 
-    public int getNumLicenses() {
-        return numLicenses;
-    }
+	public int getNumLicenses() {
+		return numLicenses;
+	}
 
-    public void setNumLicenses(int numLicenses) {
-        this.numLicenses = numLicenses;
-    }
+	public void setNumLicenses(int numLicenses) {
+		this.numLicenses = numLicenses;
+	}
 
-    @JsonProperty("num_activations")
-    public int getNumActivations() {
-        if (licenses == null) {
-            return 0;
-        }
-        int num = 0;
-        for (License lic : licenses) {
-            if (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE) {
-                num++;
-            }
-        }
-        return num;
-    }
+	@JsonProperty("num_activations")
+	public int getNumActivations() {
+		if (licenses == null) {
+			return 0;
+		}
+		int num = 0;
+		for (License lic : licenses) {
+			if (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE) {
+				num++;
+			}
+		}
+		return num;
+	}
 
-    /**
-     * Counts all created licenses, It counts active licenses and licenses
-     * waiting for activation This number will be used to control the max number
-     * of licenses created. Ignore canceled licenses.
-     * 
-     * @return
-     */
-    @JsonProperty("num_creations")
-    public int getNumCreations() {
-        if (licenses == null) {
-            return 0;
-        }
-        int num = 0;
-        for (License lic : licenses) {
-            if (lic.getStatus() != LicenseStatus.CANCELLED) {
-                num++;
-            }
-        }
-        return num;
-    }
+	/**
+	 * Counts all created licenses, It counts active licenses and licenses
+	 * waiting for activation This number will be used to control the max number
+	 * of licenses created. Ignore canceled licenses.
+	 * 
+	 * @return
+	 */
+	@JsonProperty("num_creations")
+	public int getNumCreations() {
+		if (licenses == null) {
+			return 0;
+		}
+		int num = 0;
+		for (License lic : licenses) {
+			if (lic.getStatus() != LicenseStatus.CANCELLED) {
+				num++;
+			}
+		}
+		return num;
+	}
 
-    /**
-     * Number of available licenses in this pack
-     * 
-     * @return
-     */
-    @JsonProperty("num_available")
-    public int getNumAvailables() {
-        return numLicenses - getNumActivations();
-    }
+	/**
+	 * Number of available licenses in this pack
+	 * 
+	 * @return
+	 */
+	@JsonProperty("num_available")
+	public int getNumAvailables() {
+		return numLicenses - getNumActivations();
+	}
 
-    @JsonProperty("organization_name")
-    public String getOrgName() {
-        return organization == null ? null : organization.getName();
-    }
+	@JsonProperty("organization_name")
+	public String getOrgName() {
+		return organization == null ? null : organization.getName();
+	}
 
-    @JsonProperty("application_name")
-    public String getAppName() {
-        if (licenseType == null) {
-            return null;
-        }
-        Application app = licenseType.getApplication();
-        return app == null ? null : app.getName();
-    }
+	@JsonProperty("application_name")
+	public String getAppName() {
+		if (licenseType == null) {
+			return null;
+		}
+		Application app = licenseType.getApplication();
+		return app == null ? null : app.getName();
+	}
 
-    @JsonProperty("organization_id")
-    public Integer getOrgId() {
-        return organization == null ? null : organization.getId();
-    }
+	@JsonProperty("organization_id")
+	public Integer getOrgId() {
+		return organization == null ? null : organization.getId();
+	}
 
-    @JsonProperty("organization_id")
-    public void setOrgId(Integer idOrg) {
-        if (idOrg == null) {
-            organization = null;
-        } else {
-            organization = new Organization();
-            organization.setId(idOrg);
-        }
-    }
+	@JsonProperty("organization_id")
+	public void setOrgId(Integer idOrg) {
+		if (idOrg == null) {
+			organization = null;
+		} else {
+			organization = new Organization();
+			organization.setId(idOrg);
+		}
+	}
 
-    @JsonProperty("license_type_id")
-    public void setLicTypeId(Integer idLT) {
-        if (idLT == null) {
-            licenseType = null;
-        } else {
-            licenseType = new LicenseType();
-            licenseType.setId(idLT);
-        }
-    }
+	@JsonProperty("license_type_id")
+	public void setLicTypeId(Integer idLT) {
+		if (idLT == null) {
+			licenseType = null;
+		} else {
+			licenseType = new LicenseType();
+			licenseType.setId(idLT);
+		}
+	}
 
-    @JsonProperty("license_type_id")
-    public Integer getLicTypeId() {
-        return licenseType == null ? null : licenseType.getId();
-    }
+	@JsonProperty("license_type_id")
+	public Integer getLicTypeId() {
+		return licenseType == null ? null : licenseType.getId();
+	}
 
-    @JsonProperty("created_by_id")
-    public String getCreatedById() {
-        return createdBy == null ? null : createdBy.getUsername();
-    }
+	@JsonProperty("created_by_id")
+	public String getCreatedById() {
+		return createdBy == null ? null : createdBy.getUsername();
+	}
 
-    @JsonProperty("created_by_id")
-    public void setCreatedById(String username) {
-        createdBy = new User();
-        createdBy.setUsername(username);
-    }
+	@JsonProperty("created_by_id")
+	public void setCreatedById(String username) {
+		createdBy = new User();
+		createdBy.setUsername(username);
+	}
 
-    @JsonProperty("created_by_name")
-    public String getCreatedByname() {
-        return createdBy == null ? null : String.format("%s %s (%s)", createdBy.getFirstName(),
-                createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
-    }
+	@JsonProperty("created_by_name")
+	public String getCreatedByname() {
+		return createdBy == null ? null
+				: String.format("%s %s (%s)", createdBy.getFirstName(), createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
+	}
 
-    @JsonProperty("licensetype_code")
-    public String getLicenseTypeCode() {
-        return licenseType == null ? null : licenseType.getCode();
-    }
+	@JsonProperty("licensetype_code")
+	public String getLicenseTypeCode() {
+		return licenseType == null ? null : licenseType.getCode();
+	}
 
-    public String getComments() {
-        return comments;
-    }
+	public String getComments() {
+		return comments;
+	}
 
-    public void setComments(String comments) {
-        this.comments = comments;
-    }
+	public void setComments(String comments) {
+		this.comments = comments;
+	}
 
-    public boolean isLicensePreactivation() {
-        return licensePreactivation;
-    }
+	public boolean isLicensePreactivation() {
+		return licensePreactivation;
+	}
 
-    public void setLicensePreactivation(boolean licensePreactivation) {
-        this.licensePreactivation = licensePreactivation;
-    }
+	public void setLicensePreactivation(boolean licensePreactivation) {
+		this.licensePreactivation = licensePreactivation;
+	}
 
-    public Set<PackMetadata> getMetadata() {
-        return metadata;
-    }
+	public Set<PackMetadata> getMetadata() {
+		return metadata;
+	}
 
-    public void setMetadata(Set<PackMetadata> metadata) {
-        this.metadata = metadata;
-    }
+	public void setMetadata(Set<PackMetadata> metadata) {
+		this.metadata = metadata;
+	}
 
-    public PackStatus getStatus() {
-        return status;
-    }
+	public PackStatus getStatus() {
+		return status;
+	}
 
-    public void setStatus(PackStatus status) {
-        this.status = status;
-    }
+	public void setStatus(PackStatus status) {
+		this.status = status;
+	}
 
-    public Date getInitValidDate() {
-        return initValidDate;
-    }
+	public Date getInitValidDate() {
+		return initValidDate;
+	}
 
-    public void setInitValidDate(Date initValidDate) {
-        this.initValidDate = initValidDate;
-    }
+	public void setInitValidDate(Date initValidDate) {
+		this.initValidDate = initValidDate;
+	}
 
-    public Date getEndValidDate() {
-        return endValidDate;
-    }
+	public Date getEndValidDate() {
+		return endValidDate;
+	}
 
-    public void setEndValidDate(Date endValidDate) {
-        this.endValidDate = endValidDate;
-    }
+	public void setEndValidDate(Date endValidDate) {
+		this.endValidDate = endValidDate;
+	}
 
-    public Set<License> getLicenses() {
-        return licenses;
-    }
+	public Set<License> getLicenses() {
+		return licenses;
+	}
 
-    public void setLicenses(Set<License> licenses) {
-        this.licenses = licenses;
-    }
+	public void setLicenses(Set<License> licenses) {
+		this.licenses = licenses;
+	}
 
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Pack))
-            return false;
-        Pack other = (Pack) obj;
-        return id.equals(other.id);
-    }
+	public Integer getPreactivationValidPeriod() {
+		return preactivationValidPeriod;
+	}
 
-    @Override
-    public int hashCode() {
+	public void setPreactivationValidPeriod(Integer preactivationValidPeriod) {
+		this.preactivationValidPeriod = preactivationValidPeriod;
+	}
 
-        return (id == null ? 0 : id.hashCode());
-    }
+	public Integer getRenewValidPeriod() {
+		return renewValidPeriod;
+	}
 
-    public Integer getPreactivationValidPeriod() {
-        return preactivationValidPeriod;
-    }
+	public void setRenewValidPeriod(Integer renewValidPeriod) {
+		this.renewValidPeriod = renewValidPeriod;
+	}
 
-    public void setPreactivationValidPeriod(Integer preactivationValidPeriod) {
-        this.preactivationValidPeriod = preactivationValidPeriod;
-    }
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof Application))
+			return false;
+		return id.equals(Pack.class.cast(obj).id);
+	}
 
-    public Integer getRenewValidPeriod() {
-        return renewValidPeriod;
-    }
+	@Override
+	public int hashCode() {
+		return (id == null ? 0 : id.hashCode());
+	}
 
-    public void setRenewValidPeriod(Integer renewValidPeriod) {
-        this.renewValidPeriod = renewValidPeriod;
-    }
+	@Override
+	public String toString() {
+		return String.format("Pack: ID: %d, code: %s", id, code);
+	}
 
-    public static class Action {
-        public static final int CREATE = 1;
-        public static final int ACTIVATION = 2;
-        public static final int PUT_ONHOLD = 3;
-        public static final int CANCEL = 4;
-        public static final int DELETE = 5;
-    }
+	public static class Action {
+		public static final int CREATE = 1;
+		public static final int ACTIVATION = 2;
+		public static final int PUT_ONHOLD = 3;
+		public static final int CANCEL = 4;
+		public static final int DELETE = 5;
+	}
 
-    public static class Status {
+	public static class Status {
 
-        private static final Map<Integer, List<PackStatus>> transitions = Utils.createMap( //
-                Action.ACTIVATION, Arrays.asList(PackStatus.CREATED, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
-                Action.PUT_ONHOLD, Arrays.asList(PackStatus.ACTIVE), //
-                Action.CANCEL, Arrays.asList(PackStatus.ACTIVE, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
-                Action.DELETE, Arrays.asList(PackStatus.CANCELLED, PackStatus.CREATED) //
-                );
+		private static final Map<Integer, List<PackStatus>> transitions = Utils.createMap( //
+				Action.ACTIVATION, Arrays.asList(PackStatus.CREATED, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
+				Action.PUT_ONHOLD, Arrays.asList(PackStatus.ACTIVE), //
+				Action.CANCEL, Arrays.asList(PackStatus.ACTIVE, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
+				Action.DELETE, Arrays.asList(PackStatus.CANCELLED, PackStatus.CREATED) //
+		);
 
-        /**
-         * It checks if a given action is valid for the License, passing the
-         * action and the current license status
-         * 
-         * @param oldStatus
-         * @param newStatus
-         * @return
-         */
-        public static boolean isActionValid(Integer action, PackStatus currentStatus) {
-            List<PackStatus> validStatuses = transitions.get(action);
+		/**
+		 * It checks if a given action is valid for the License, passing the
+		 * action and the current license status
+		 * 
+		 * @param oldStatus
+		 * @param newStatus
+		 * @return
+		 */
+		public static boolean isActionValid(Integer action, PackStatus currentStatus) {
+			List<PackStatus> validStatuses = transitions.get(action);
 
-            return validStatuses != null && validStatuses.contains(currentStatus);
-        }
-    }
+			return validStatuses != null && validStatuses.contains(currentStatus);
+		}
+	}
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/PackMetadata.java b/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
index 5267635..c6f07c2 100644
--- a/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
@@ -1,6 +1,7 @@
 package net.curisit.securis.db;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -18,6 +19,8 @@
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import net.curisit.securis.db.common.Metadata;
+
 /**
  * Entity implementation class for Entity: pack_metadata
  * 
@@ -28,7 +31,7 @@
 @Table(name = "pack_metadata")
 @JsonIgnoreProperties(ignoreUnknown = true)
 @NamedQueries({ @NamedQuery(name = "list-pack-metadata", query = "SELECT a FROM PackMetadata a where a.pack.id = :packId") })
-public class PackMetadata implements Serializable {
+public class PackMetadata implements Serializable, Metadata {
 
 	private static final long serialVersionUID = 1L;
 
@@ -103,4 +106,22 @@
 		this.mandatory = mandatory;
 	}
 
+	@Override
+	public String toString() {
+		return String.format("PackMD (%s: %s)", key, value);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof PackMetadata))
+			return false;
+		PackMetadata other = (PackMetadata) obj;
+		return Objects.equals(key, other.key) && Objects.equals(pack, other.pack);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(key, pack);
+	}
+
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/common/Metadata.java b/securis/src/main/java/net/curisit/securis/db/common/Metadata.java
new file mode 100644
index 0000000..8706e36
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/db/common/Metadata.java
@@ -0,0 +1,16 @@
+package net.curisit.securis.db.common;
+
+public interface Metadata {
+	public String getKey();
+
+	public void setKey(String key);
+
+	public String getValue();
+
+	public void setValue(String value);
+
+	public boolean isMandatory();
+
+	public void setMandatory(boolean mandatory);
+
+}
diff --git a/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java b/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
index 5ce62d6..e3fcced 100644
--- a/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
@@ -25,6 +25,9 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import net.curisit.integrity.commons.Utils;
 import net.curisit.securis.DefaultExceptionHandler;
 import net.curisit.securis.db.Application;
@@ -34,10 +37,8 @@
 import net.curisit.securis.security.Securable;
 import net.curisit.securis.services.exception.SeCurisServiceException;
 import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
+import net.curisit.securis.services.helpers.MetadataHelper;
 import net.curisit.securis.utils.TokenHelper;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 /**
  * Application resource, this service will provide methods to create, modify and
@@ -48,191 +49,185 @@
 @Path("/application")
 public class ApplicationResource {
 
-    @Inject
-    TokenHelper tokenHelper;
+	@Inject
+	TokenHelper tokenHelper;
 
-    @Context
-    EntityManager em;
+	@Inject
+	MetadataHelper metadataHelper;
 
-    private static final Logger LOG = LogManager.getLogger(ApplicationResource.class);
+	@Context
+	EntityManager em;
 
-    public ApplicationResource() {
-    }
+	private static final Logger LOG = LogManager.getLogger(ApplicationResource.class);
 
-    /**
-     * 
-     * @return the server version in format majorVersion.minorVersion
-     */
-    @GET
-    @Path("/")
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    public Response index() {
-        LOG.info("Getting applications list ");
+	public ApplicationResource() {
+	}
 
-        // EntityManager em = emProvider.get();
-        em.clear();
-        TypedQuery<Application> q = em.createNamedQuery("list-applications", Application.class);
-        List<Application> list = q.getResultList();
+	/**
+	 * 
+	 * @return the server version in format majorVersion.minorVersion
+	 */
+	@GET
+	@Path("/")
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	public Response index() {
+		LOG.info("Getting applications list ");
 
-        return Response.ok(list).build();
-    }
+		// EntityManager em = emProvider.get();
+		em.clear();
+		TypedQuery<Application> q = em.createNamedQuery("list-applications", Application.class);
+		List<Application> list = q.getResultList();
 
-    /**
-     * 
-     * @return the server version in format majorVersion.minorVersion
-     * @throws SeCurisServiceException
-     */
-    @GET
-    @Path("/{appid}")
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    public Response get(@PathParam("appid") String appid) throws SeCurisServiceException {
-        LOG.info("Getting application data for id: {}: ", appid);
-        if (appid == null || "".equals(appid)) {
-            LOG.error("Application ID is mandatory");
-            return Response.status(Status.NOT_FOUND).build();
-        }
+		return Response.ok(list).build();
+	}
 
-        em.clear();
+	/**
+	 * 
+	 * @return the server version in format majorVersion.minorVersion
+	 * @throws SeCurisServiceException
+	 */
+	@GET
+	@Path("/{appid}")
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	public Response get(@PathParam("appid") String appid) throws SeCurisServiceException {
+		LOG.info("Getting application data for id: {}: ", appid);
+		if (appid == null || "".equals(appid)) {
+			LOG.error("Application ID is mandatory");
+			return Response.status(Status.NOT_FOUND).build();
+		}
 
-        Application app = null;
-        try {
-            LOG.info("READY to GET app: {}", appid);
-            app = em.find(Application.class, Integer.parseInt(appid));
-        } catch (Exception e) {
-            LOG.info("ERROR GETTING app: {}", e);
-        }
-        if (app == null) {
-            LOG.error("Application with id {} not found in DB", appid);
-            throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "Application not found with ID: " + appid);
-        }
+		em.clear();
 
-        return Response.ok(app).build();
-    }
+		Application app = null;
+		try {
+			LOG.info("READY to GET app: {}", appid);
+			app = em.find(Application.class, Integer.parseInt(appid));
+		} catch (Exception e) {
+			LOG.info("ERROR GETTING app: {}", e);
+		}
+		if (app == null) {
+			LOG.error("Application with id {} not found in DB", appid);
+			throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "Application not found with ID: " + appid);
+		}
 
-    @POST
-    @Path("/")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @EnsureTransaction
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response create(Application app, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
-        LOG.info("Creating new application");
-        // EntityManager em = emProvider.get();
-        app.setCreationTimestamp(new Date());
-        em.persist(app);
+		return Response.ok(app).build();
+	}
 
-        if (app.getApplicationMetadata() != null) {
-            for (ApplicationMetadata md : app.getApplicationMetadata()) {
-                md.setApplication(app);
-                md.setCreationTimestamp(new Date());
-                em.persist(md);
-            }
-        }
-        LOG.info("Creating application ({}) with date: {}", app.getId(), app.getCreationTimestamp());
+	@POST
+	@Path("/")
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces({ MediaType.APPLICATION_JSON })
+	@EnsureTransaction
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response create(Application app, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+		LOG.info("Creating new application");
+		// EntityManager em = emProvider.get();
+		app.setCreationTimestamp(new Date());
+		em.persist(app);
 
-        return Response.ok(app).build();
-    }
+		if (app.getApplicationMetadata() != null) {
+			for (ApplicationMetadata md : app.getApplicationMetadata()) {
+				md.setApplication(app);
+				md.setCreationTimestamp(new Date());
+				em.persist(md);
+			}
+		}
+		LOG.info("Creating application ({}) with date: {}", app.getId(), app.getCreationTimestamp());
 
-    @PUT
-    @POST
-    @Path("/{appid}")
-    @EnsureTransaction
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response modify(Application app, @PathParam("appid") String appid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
-        LOG.info("Modifying application with id: {}", appid);
-        // EntityManager em = emProvider.get();
-        Application currentapp = em.find(Application.class, Integer.parseInt(appid));
-        if (currentapp == null) {
-            LOG.error("Application with id {} not found in DB", appid);
-            return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application not found with ID: " + appid)
-                    .build();
-        }
-        currentapp.setCode(app.getCode());
-        currentapp.setName(app.getName());
-        currentapp.setLicenseFilename(app.getLicenseFilename());
-        currentapp.setDescription(app.getDescription());
+		return Response.ok(app).build();
+	}
 
-        Set<ApplicationMetadata> newMD = app.getApplicationMetadata();
-        Set<ApplicationMetadata> oldMD = currentapp.getApplicationMetadata();
-        Map<String, ApplicationMetadata> directOldMD = getMapMD(oldMD);
-        Map<String, ApplicationMetadata> directNewMD = getMapMD(newMD);
-        for (ApplicationMetadata currentMd : oldMD) {
-            if (newMD == null || !directNewMD.containsKey(currentMd.getKey())) {
-                em.remove(currentMd);
-            }
-        }
+	@PUT
+	@POST
+	@Path("/{appid}")
+	@EnsureTransaction
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response modify(Application app, @PathParam("appid") String appid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+		LOG.info("Modifying application with id: {}", appid);
+		// EntityManager em = emProvider.get();
+		Application currentapp = em.find(Application.class, Integer.parseInt(appid));
+		if (currentapp == null) {
+			LOG.error("Application with id {} not found in DB", appid);
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application not found with ID: " + appid).build();
+		}
+		currentapp.setCode(app.getCode());
+		currentapp.setName(app.getName());
+		currentapp.setLicenseFilename(app.getLicenseFilename());
+		currentapp.setDescription(app.getDescription());
 
-        if (newMD != null) {
-            for (ApplicationMetadata md : newMD) {
-                if (directOldMD.containsKey(md.getKey())) {
-                    ApplicationMetadata amd = directOldMD.get(md.getKey());
-                    amd.setValue(md.getValue());
-                    amd.setMandatory(md.isMandatory());
-                    em.merge(amd);
-                } else {
-                    md.setApplication(currentapp);
-                    if (md.getCreationTimestamp() == null) {
-                        md.setCreationTimestamp(app.getCreationTimestamp());
-                    }
-                    em.persist(md);
-                }
-            }
-        }
-        currentapp.setApplicationMetadata(app.getApplicationMetadata());
-        em.merge(currentapp);
-        return Response.ok(currentapp).build();
-    }
+		Set<ApplicationMetadata> newMD = app.getApplicationMetadata();
+		Set<ApplicationMetadata> oldMD = currentapp.getApplicationMetadata();
+		boolean metadataChanges = !metadataHelper.match(newMD, oldMD);
+		if (metadataChanges) {
+			Map<String, ApplicationMetadata> directOldMD = getMapMD(oldMD);
+			Map<String, ApplicationMetadata> directNewMD = getMapMD(newMD);
+			for (ApplicationMetadata currentMd : oldMD) {
+				if (newMD == null || !directNewMD.containsKey(currentMd.getKey())) {
+					em.remove(currentMd);
+				}
+			}
 
-    private Map<String, ApplicationMetadata> getMapMD(Set<ApplicationMetadata> amd) {
-        Map<String, ApplicationMetadata> map = new HashMap<String, ApplicationMetadata>();
-        if (amd != null) {
-            for (ApplicationMetadata applicationMetadata : amd) {
-                map.put(applicationMetadata.getKey(), applicationMetadata);
-            }
-        }
-        return map;
-    }
+			if (newMD != null) {
+				for (ApplicationMetadata md : newMD) {
+					if (directOldMD.containsKey(md.getKey())) {
+						em.merge(md);
+					} else {
+						md.setApplication(currentapp);
+						if (md.getCreationTimestamp() == null) {
+							md.setCreationTimestamp(app.getCreationTimestamp());
+						}
+						em.persist(md);
+					}
+				}
+			}
+			currentapp.setApplicationMetadata(app.getApplicationMetadata());
+		}
+		em.merge(currentapp);
+		if (metadataChanges) {
+			metadataHelper.propagateMetadata(em, currentapp);
+		}
+		return Response.ok(currentapp).build();
+	}
 
-    @DELETE
-    @Path("/{appid}")
-    @EnsureTransaction
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response delete(@PathParam("appid") String appid, @Context HttpServletRequest request) {
-        LOG.info("Deleting app with id: {}", appid);
-        // EntityManager em = emProvider.get();
-        Application app = em.find(Application.class, Integer.parseInt(appid));
-        if (app == null) {
-            LOG.error("Application with id {} can not be deleted, It was not found in DB", appid);
-            return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application not found with ID: " + appid)
-                    .build();
-        }
-        /*
-         * if (app.getLicenseTypes() != null &&
-         * !app.getLicenseTypes().isEmpty()) { throw new
-         * SeCurisServiceException(ErrorCodes.NOT_FOUND,
-         * "Application can not be deleted becasue has assigned one or more License types, ID: "
-         * + appid); }
-         */
-        em.remove(app);
-        return Response.ok(Utils.createMap("success", true, "id", appid)).build();
-    }
+	private Map<String, ApplicationMetadata> getMapMD(Set<ApplicationMetadata> amd) {
+		Map<String, ApplicationMetadata> map = new HashMap<String, ApplicationMetadata>();
+		if (amd != null) {
+			for (ApplicationMetadata applicationMetadata : amd) {
+				map.put(applicationMetadata.getKey(), applicationMetadata);
+			}
+		}
+		return map;
+	}
+
+	@DELETE
+	@Path("/{appid}")
+	@EnsureTransaction
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response delete(@PathParam("appid") String appid, @Context HttpServletRequest request) {
+		LOG.info("Deleting app with id: {}", appid);
+		// EntityManager em = emProvider.get();
+		Application app = em.find(Application.class, Integer.parseInt(appid));
+		if (app == null) {
+			LOG.error("Application with id {} can not be deleted, It was not found in DB", appid);
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application not found with ID: " + appid).build();
+		}
+		/*
+		 * if (app.getLicenseTypes() != null &&
+		 * !app.getLicenseTypes().isEmpty()) { throw new
+		 * SeCurisServiceException(ErrorCodes.NOT_FOUND,
+		 * "Application can not be deleted becasue has assigned one or more License types, ID: "
+		 * + appid); }
+		 */
+		em.remove(app);
+		return Response.ok(Utils.createMap("success", true, "id", appid)).build();
+	}
 
 }
diff --git a/securis/src/main/java/net/curisit/securis/services/BasicServices.java b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
index 43289df..86c5e94 100644
--- a/securis/src/main/java/net/curisit/securis/services/BasicServices.java
+++ b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
@@ -7,8 +7,8 @@
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
+import javax.persistence.EntityManager;
 import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
@@ -44,6 +44,9 @@
 	@Inject
 	TokenHelper tokenHelper;
 
+	@Context
+	EntityManager em;
+
 	@Inject
 	public BasicServices() {
 	}
@@ -74,16 +77,6 @@
 		String page = "/index.jsp";
 		URI uri = UriBuilder.fromUri(page).build();
 		return Response.seeOther(uri).build();
-	}
-
-	@POST
-	@Path("/login")
-	@Produces({ MediaType.APPLICATION_JSON })
-	public Response login(@FormParam("username") String user, @FormParam("password") String password, @Context HttpServletRequest request) {
-		LOG.info("is user in role: {} == {} ? ", "advance", request.isUserInRole("advance"));
-
-		String tokenAuth = tokenHelper.generateToken(user);
-		return Response.ok(Utils.createMap("success", true, "token", tokenAuth)).build();
 	}
 
 	/**
diff --git a/securis/src/main/java/net/curisit/securis/services/LicenseTypeResource.java b/securis/src/main/java/net/curisit/securis/services/LicenseTypeResource.java
index aea9199..ea63e1b 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseTypeResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseTypeResource.java
@@ -4,6 +4,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
@@ -24,6 +25,9 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import net.curisit.integrity.commons.Utils;
 import net.curisit.securis.DefaultExceptionHandler;
 import net.curisit.securis.SeCurisException;
@@ -35,10 +39,8 @@
 import net.curisit.securis.security.Securable;
 import net.curisit.securis.services.exception.SeCurisServiceException;
 import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
+import net.curisit.securis.services.helpers.MetadataHelper;
 import net.curisit.securis.utils.TokenHelper;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 /**
  * LicenseType resource, this service will provide methods to create, modify and
@@ -49,206 +51,204 @@
 @Path("/licensetype")
 public class LicenseTypeResource {
 
-    private static final Logger LOG = LogManager.getLogger(LicenseTypeResource.class);
+	private static final Logger LOG = LogManager.getLogger(LicenseTypeResource.class);
 
-    @Inject
-    TokenHelper tokenHelper;
+	@Inject
+	TokenHelper tokenHelper;
 
-    @Context
-    EntityManager em;
+	@Inject
+	MetadataHelper metadataHelper;
 
-    public LicenseTypeResource() {
-    }
+	@Context
+	EntityManager em;
 
-    /**
-     * 
-     * @return the server version in format majorVersion.minorVersion
-     */
-    @GET
-    @Path("/")
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    public Response index() {
-        LOG.info("Getting license types list ");
+	public LicenseTypeResource() {
+	}
 
-        // EntityManager em = emProvider.get();
-        em.clear();
-        TypedQuery<LicenseType> q = em.createNamedQuery("list-license_types", LicenseType.class);
-        List<LicenseType> list = q.getResultList();
+	/**
+	 * 
+	 * @return the server version in format majorVersion.minorVersion
+	 */
+	@GET
+	@Path("/")
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	public Response index() {
+		LOG.info("Getting license types list ");
 
-        return Response.ok(list).build();
-    }
+		// EntityManager em = emProvider.get();
+		em.clear();
+		TypedQuery<LicenseType> q = em.createNamedQuery("list-license_types", LicenseType.class);
+		List<LicenseType> list = q.getResultList();
 
-    /**
-     * 
-     * @return the server version in format majorVersion.minorVersion
-     * @throws SeCurisServiceException
-     */
-    @GET
-    @Path("/{ltid}")
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    public Response get(@PathParam("ltid") String ltid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) throws SeCurisServiceException {
-        LOG.info("Getting license type data for id: {}: ", ltid);
-        if (ltid == null || "".equals(ltid)) {
-            LOG.error("LicenseType ID is mandatory");
-            return Response.status(Status.NOT_FOUND).build();
-        }
+		return Response.ok(list).build();
+	}
 
-        // EntityManager em = emProvider.get();
-        em.clear();
-        LicenseType lt = em.find(LicenseType.class, Integer.parseInt(ltid));
-        if (lt == null) {
-            LOG.error("LicenseType with id {} not found in DB", ltid);
-            throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "LicenseType was not found in DB");
-        }
-        return Response.ok(lt).build();
-    }
+	/**
+	 * 
+	 * @return the server version in format majorVersion.minorVersion
+	 * @throws SeCurisServiceException
+	 */
+	@GET
+	@Path("/{ltid}")
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	public Response get(@PathParam("ltid") String ltid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) throws SeCurisServiceException {
+		LOG.info("Getting license type data for id: {}: ", ltid);
+		if (ltid == null || "".equals(ltid)) {
+			LOG.error("LicenseType ID is mandatory");
+			return Response.status(Status.NOT_FOUND).build();
+		}
 
-    @POST
-    @Path("/")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @EnsureTransaction
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response create(LicenseType lt, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
-        LOG.info("Creating new license type");
-        // EntityManager em = emProvider.get();
+		// EntityManager em = emProvider.get();
+		em.clear();
+		LicenseType lt = em.find(LicenseType.class, Integer.parseInt(ltid));
+		if (lt == null) {
+			LOG.error("LicenseType with id {} not found in DB", ltid);
+			throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "LicenseType was not found in DB");
+		}
+		return Response.ok(lt).build();
+	}
 
-        try {
-            setApplication(lt, lt.getApplicationId(), em);
-        } catch (SeCurisException e) {
-            return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
-        }
+	@POST
+	@Path("/")
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces({ MediaType.APPLICATION_JSON })
+	@EnsureTransaction
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response create(LicenseType lt, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+		LOG.info("Creating new license type");
+		// EntityManager em = emProvider.get();
 
-        if (lt.getApplicationId() == null) {
-            LOG.error("Application is missing for current license type data");
-            return Response.status(Status.NOT_FOUND)
-                    .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application is missing for current license type data").build();
-        }
+		try {
+			setApplication(lt, lt.getApplicationId(), em);
+		} catch (SeCurisException e) {
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+		}
 
-        lt.setCreationTimestamp(new Date());
-        em.persist(lt);
-        Set<LicenseTypeMetadata> newMD = lt.getMetadata();
+		if (lt.getApplicationId() == null) {
+			LOG.error("Application is missing for current license type data");
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application is missing for current license type data").build();
+		}
 
-        if (newMD != null) {
-            for (LicenseTypeMetadata md : newMD) {
-                md.setLicenseType(lt);
-                em.persist(md);
-            }
-        }
-        lt.setMetadata(newMD);
+		lt.setCreationTimestamp(new Date());
+		em.persist(lt);
+		Set<LicenseTypeMetadata> newMD = lt.getMetadata();
 
-        return Response.ok(lt).build();
-    }
+		if (newMD != null) {
+			for (LicenseTypeMetadata md : newMD) {
+				md.setLicenseType(lt);
+				em.persist(md);
+			}
+		}
+		lt.setMetadata(newMD);
 
-    private Set<String> getMdKeys(Set<LicenseTypeMetadata> mds) {
-        Set<String> ids = new HashSet<String>();
-        if (mds != null) {
-            for (LicenseTypeMetadata md : mds) {
-                ids.add(md.getKey());
-            }
-        }
-        return ids;
-    }
+		return Response.ok(lt).build();
+	}
 
-    @PUT
-    @POST
-    @Path("/{ltid}")
-    @EnsureTransaction
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response modify(LicenseType lt, @PathParam("ltid") String ltid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
-        LOG.info("Modifying license type with id: {}", ltid);
-        // EntityManager em = emProvider.get();
-        LicenseType currentlt = em.find(LicenseType.class, Integer.parseInt(ltid));
-        if (currentlt == null) {
-            LOG.error("LicenseType with id {} not found in DB", ltid);
-            return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License type not found with ID: " + ltid)
-                    .build();
-        }
+	private Set<String> getMdKeys(Set<LicenseTypeMetadata> mds) {
+		Set<String> ids = new HashSet<String>();
+		if (mds != null) {
+			for (LicenseTypeMetadata md : mds) {
+				ids.add(md.getKey());
+			}
+		}
+		return ids;
+	}
 
-        try {
-            setApplication(currentlt, lt.getApplicationId(), em);
-        } catch (SeCurisException e) {
-            return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
-        }
+	@PUT
+	@POST
+	@Path("/{ltid}")
+	@EnsureTransaction
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response modify(LicenseType lt, @PathParam("ltid") String ltid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+		LOG.info("Modifying license type with id: {}", ltid);
+		// EntityManager em = emProvider.get();
+		LicenseType currentlt = em.find(LicenseType.class, Integer.parseInt(ltid));
+		if (currentlt == null) {
+			LOG.error("LicenseType with id {} not found in DB", ltid);
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License type not found with ID: " + ltid).build();
+		}
 
-        currentlt.setCode(lt.getCode());
-        currentlt.setName(lt.getName());
-        currentlt.setDescription(lt.getDescription());
+		try {
+			setApplication(currentlt, lt.getApplicationId(), em);
+		} catch (SeCurisException e) {
+			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+		}
 
-        Set<LicenseTypeMetadata> newMD = lt.getMetadata();
-        Set<String> newMdKeys = getMdKeys(newMD);
-        for (LicenseTypeMetadata currentMd : currentlt.getMetadata()) {
-            if (!newMdKeys.contains(currentMd.getKey())) {
-                em.remove(currentMd);
-                LOG.info("Removing MD: {}", currentMd);
-            }
-        }
+		currentlt.setCode(lt.getCode());
+		currentlt.setName(lt.getName());
+		currentlt.setDescription(lt.getDescription());
 
-        if (newMD != null) {
-            Set<LicenseTypeMetadata> oldMD = currentlt.getMetadata();
-            Set<String> oldMdKeys = getMdKeys(oldMD);
+		Set<LicenseTypeMetadata> newMD = lt.getMetadata();
+		Set<LicenseTypeMetadata> oldMD = currentlt.getMetadata();
+		boolean metadataChanges = !metadataHelper.match(newMD, oldMD);
+		if (metadataChanges) {
+			Set<String> newMdKeys = getMdKeys(newMD);
+			for (LicenseTypeMetadata currentMd : oldMD) {
+				if (!newMdKeys.contains(currentMd.getKey())) {
+					em.remove(currentMd);
+					LOG.info("Removing MD: {}", currentMd);
+				}
+			}
 
-            for (LicenseTypeMetadata md : newMD) {
-                if (oldMdKeys.contains(md.getKey())) {
-                    em.merge(md);
-                } else {
-                    md.setLicenseType(currentlt);
-                    em.persist(md);
-                }
-            }
-        }
-        currentlt.setMetadata(newMD);
-        em.merge(currentlt);
+			if (newMD != null) {
+				Set<String> oldMdKeys = getMdKeys(oldMD);
 
-        return Response.ok(currentlt).build();
-    }
+				for (LicenseTypeMetadata md : newMD) {
+					if (oldMdKeys.contains(md.getKey())) {
+						em.merge(md);
+					} else {
+						md.setLicenseType(currentlt);
+						em.persist(md);
+					}
+				}
+			}
+			currentlt.setMetadata(newMD);
+		}
+		em.merge(currentlt);
+		if (metadataChanges) {
+			Set<String> keys = newMD.parallelStream().map(md -> md.getKey()).collect(Collectors.toSet());
+			metadataHelper.propagateMetadata(em, currentlt, keys);
+		}
 
-    private void setApplication(LicenseType licType, Integer applicationId, EntityManager em) throws SeCurisException {
-        Application app = null;
-        if (applicationId != null) {
-            app = em.find(Application.class, applicationId);
-            if (app == null) {
-                LOG.error("LicenseType application with id {} not found in DB", applicationId);
+		return Response.ok(currentlt).build();
+	}
 
-                throw new SecurityException("License type's app not found with ID: " + applicationId);
-            }
-        }
-        licType.setApplication(app);
-    }
+	private void setApplication(LicenseType licType, Integer applicationId, EntityManager em) throws SeCurisException {
+		Application app = null;
+		if (applicationId != null) {
+			app = em.find(Application.class, applicationId);
+			if (app == null) {
+				LOG.error("LicenseType application with id {} not found in DB", applicationId);
 
-    @DELETE
-    @Path("/{ltid}")
-    @EnsureTransaction
-    @Produces({
-        MediaType.APPLICATION_JSON
-    })
-    @Securable
-    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
-    public Response delete(@PathParam("ltid") String ltid, @Context HttpServletRequest request) {
-        LOG.info("Deleting app with id: {}", ltid);
-        // EntityManager em = emProvider.get();
-        LicenseType app = em.find(LicenseType.class, Integer.parseInt(ltid));
-        if (app == null) {
-            LOG.error("LicenseType with id {} can not be deleted, It was not found in DB", ltid);
-            return Response.status(Status.NOT_FOUND).build();
-        }
+				throw new SecurityException("License type's app not found with ID: " + applicationId);
+			}
+		}
+		licType.setApplication(app);
+	}
 
-        em.remove(app);
-        return Response.ok(Utils.createMap("success", true, "id", ltid)).build();
-    }
+	@DELETE
+	@Path("/{ltid}")
+	@EnsureTransaction
+	@Produces({ MediaType.APPLICATION_JSON })
+	@Securable
+	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+	public Response delete(@PathParam("ltid") String ltid, @Context HttpServletRequest request) {
+		LOG.info("Deleting app with id: {}", ltid);
+		// EntityManager em = emProvider.get();
+		LicenseType app = em.find(LicenseType.class, Integer.parseInt(ltid));
+		if (app == null) {
+			LOG.error("LicenseType with id {} can not be deleted, It was not found in DB", ltid);
+			return Response.status(Status.NOT_FOUND).build();
+		}
+
+		em.remove(app);
+		return Response.ok(Utils.createMap("success", true, "id", ltid)).build();
+	}
 
 }
diff --git a/securis/src/main/java/net/curisit/securis/services/PackResource.java b/securis/src/main/java/net/curisit/securis/services/PackResource.java
index 4bdf7f6..89bc311 100644
--- a/securis/src/main/java/net/curisit/securis/services/PackResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/PackResource.java
@@ -86,10 +86,10 @@
 			LOG.info("Getting all packs for user: " + bsc.getUserPrincipal());
 			q = em.createNamedQuery("list-packs", Pack.class);
 		} else {
-			q = em.createNamedQuery("list-packs-by-orgs", Pack.class);
 			if (bsc.getOrganizationsIds() == null) {
-				Response.ok().build();
+				return Response.ok().build();
 			}
+			q = em.createNamedQuery("list-packs-by-orgs", Pack.class);
 			q.setParameter("list_ids", bsc.getOrganizationsIds());
 		}
 
diff --git a/securis/src/main/java/net/curisit/securis/services/UserResource.java b/securis/src/main/java/net/curisit/securis/services/UserResource.java
index 9c86ead..0c3e233 100644
--- a/securis/src/main/java/net/curisit/securis/services/UserResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/UserResource.java
@@ -264,10 +264,9 @@
 			em.getTransaction().rollback();
 		}
 		clearUserCache(username);
-
+		String userFullName = String.format("%s %s", user.getFirstName(), user.getLastName() == null ? "" : user.getLastName()).trim();
 		String tokenAuth = tokenHelper.generateToken(username);
-		return Response.ok(Utils.createMap("success", true, "token", tokenAuth, "username", username, "full_name", String.format("%s %s", user.getFirstName(), user.getLastName())))
-				.build();
+		return Response.ok(Utils.createMap("success", true, "token", tokenAuth, "username", username, "full_name", userFullName)).build();
 	}
 
 	/**
diff --git a/securis/src/main/java/net/curisit/securis/services/helpers/MetadataHelper.java b/securis/src/main/java/net/curisit/securis/services/helpers/MetadataHelper.java
new file mode 100644
index 0000000..6934095
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/services/helpers/MetadataHelper.java
@@ -0,0 +1,155 @@
+package net.curisit.securis.services.helpers;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.curisit.securis.db.Application;
+import net.curisit.securis.db.ApplicationMetadata;
+import net.curisit.securis.db.LicenseType;
+import net.curisit.securis.db.LicenseTypeMetadata;
+import net.curisit.securis.db.Pack;
+import net.curisit.securis.db.PackMetadata;
+import net.curisit.securis.db.common.Metadata;
+
+@ApplicationScoped
+public class MetadataHelper {
+
+	private static final Logger log = LogManager.getLogger(MetadataHelper.class);
+
+	public <T extends Metadata> boolean match(T m1, T m2) {
+		if (m1 == null || m2 == null) {
+			return false;
+		}
+		return Objects.equals(m1.getKey(), m2.getKey()) && Objects.equals(m1.getValue(), m2.getValue()) && m1.isMandatory() == m2.isMandatory();
+	}
+
+	public <T extends Metadata> Metadata findByKey(String key, Collection<T> listMd) {
+		return listMd.parallelStream().filter(m -> Objects.equals(key, m.getKey())).findAny().orElse(null);
+	}
+
+	public <T extends Metadata> boolean match(Set<T> listMd1, Set<T> listMd2) {
+		if (listMd1.size() != listMd2.size()) {
+			return false;
+		}
+		return listMd1.parallelStream().allMatch(m -> this.match(m, findByKey(m.getKey(), listMd2)));
+	}
+
+	public <T extends Metadata, K extends Metadata> void mergeMetadata(EntityManager em, Set<T> srcListMd, Set<K> tgtListMd, Set<String> keys) {
+
+		Set<K> mdToRemove = tgtListMd.parallelStream() // 
+				.filter(md -> !keys.contains(md.getKey())) //
+				.collect(Collectors.toSet());
+		for (K tgtMd : mdToRemove) {
+			log.info("MD key to remove: {} - {}", tgtMd.getKey(), tgtMd);
+			if (tgtMd instanceof LicenseTypeMetadata) {
+				log.info("LT: {}, tx: {}, contans: {}", LicenseTypeMetadata.class.cast(tgtMd).getLicenseType(), em.isJoinedToTransaction(), em.contains(tgtMd));
+			}
+			em.remove(tgtMd);
+		}
+		Set<K> keysToUpdate = tgtListMd.parallelStream() // 
+				.filter(md -> keys.contains(md.getKey())) //
+				.collect(Collectors.toSet());
+		for (K tgtMd : keysToUpdate) {
+			Metadata md = this.findByKey(tgtMd.getKey(), srcListMd);
+			if (md.isMandatory() != tgtMd.isMandatory() || !Objects.equals(md.getValue(), tgtMd.getValue())) {
+				tgtMd.setMandatory(md.isMandatory());
+				tgtMd.setValue(md.getValue());
+				log.info("MD key to update: {}", tgtMd.getKey());
+				em.merge(tgtMd);
+			}
+		}
+	}
+
+	private Set<LicenseTypeMetadata> createNewMetadata(Set<ApplicationMetadata> appMd, Set<LicenseTypeMetadata> existingMd, LicenseType licenseType) {
+		Set<String> oldKeys = existingMd.stream().map(md -> md.getKey()).collect(Collectors.toSet());
+		return appMd.parallelStream() // 
+				.filter(md -> !oldKeys.contains(md.getKey())) //
+				.map(appmd -> {
+					LicenseTypeMetadata ltmd = new LicenseTypeMetadata();
+					ltmd.setLicenseType(licenseType);
+					ltmd.setKey(appmd.getKey());
+					ltmd.setValue(appmd.getValue());
+					ltmd.setMandatory(appmd.isMandatory());
+					return ltmd;
+				}).collect(Collectors.toSet());
+	}
+
+	private Set<PackMetadata> createNewMetadata(Set<LicenseTypeMetadata> ltMd, Set<PackMetadata> existingMd, Pack pack) {
+		Set<String> oldKeys = existingMd.stream().map(md -> md.getKey()).collect(Collectors.toSet());
+		return ltMd.parallelStream() // 
+				.filter(md -> !oldKeys.contains(md.getKey())) //
+				.map(md -> {
+					PackMetadata pmd = new PackMetadata();
+					pmd.setPack(pack);
+					pmd.setKey(md.getKey());
+					pmd.setValue(md.getValue());
+					pmd.setMandatory(md.isMandatory());
+					return pmd;
+				}).collect(Collectors.toSet());
+	}
+
+	/**
+	 * Copy the modified app metadata to LicenseTypes and Packs
+	 * 
+	 * @param em
+	 * @param app
+	 */
+	public void propagateMetadata(EntityManager em, Application app) {
+		Set<ApplicationMetadata> appMd = app.getApplicationMetadata();
+		Set<String> keys = appMd.parallelStream().map(md -> md.getKey()).collect(Collectors.toSet());
+		log.info("App metadata keys: {}", keys);
+		for (LicenseType lt : app.getLicenseTypes()) {
+			log.info("Lic type to update: {}", lt.getCode());
+			this.mergeMetadata(em, appMd, lt.getMetadata(), keys);
+			Set<LicenseTypeMetadata> newMdList = createNewMetadata(appMd, lt.getMetadata(), lt);
+			for (LicenseTypeMetadata newMetadata : newMdList) {
+				log.info("MD key to add to lt: {}", newMetadata.getKey());
+				em.persist(newMetadata);
+			}
+			em.detach(lt);
+			// Probably there is a better way to get the final metadata from JPA...
+			TypedQuery<LicenseTypeMetadata> updatedMdQuery = em.createNamedQuery("list-licensetype-metadata", LicenseTypeMetadata.class);
+			updatedMdQuery.setParameter("licenseTypeId", lt.getId());
+			Set<LicenseTypeMetadata> updatedMd = new HashSet<>(updatedMdQuery.getResultList());
+
+			lt.setMetadata(updatedMd);
+			propagateMetadata(em, lt, keys);
+		}
+	}
+
+	/**
+	 * Copy the modified licenseType metadata to Packs
+	 * 
+	 * @param em
+	 * @param lt
+	 * @param keys
+	 */
+	public void propagateMetadata(EntityManager em, LicenseType lt, Set<String> keys) {
+		Set<LicenseTypeMetadata> ltMd = lt.getMetadata();
+		TypedQuery<Pack> packsQuery = em.createNamedQuery("list-packs-by-lic-type", Pack.class);
+		packsQuery.setParameter("lt_id", lt.getId());
+		List<Pack> packs = packsQuery.getResultList();
+		log.info("Packs to update the metadata: {}", packs.size());
+		for (Pack pack : packs) {
+			this.mergeMetadata(em, ltMd, pack.getMetadata(), keys);
+			Set<PackMetadata> newMdList = createNewMetadata(ltMd, pack.getMetadata(), pack);
+			for (PackMetadata newMetadata : newMdList) {
+				log.info("MD key to add to pack: {}", newMetadata.getKey());
+				em.persist(newMetadata);
+			}
+			em.detach(pack);
+		}
+	}
+
+}

--
Gitblit v1.3.2