From 146a0fb8b0e90f9196e569152f649baf60d6cc8f Mon Sep 17 00:00:00 2001
From: Joaquín Reñé <jrene@curisit.net>
Date: Tue, 07 Oct 2025 14:52:57 +0000
Subject: [PATCH] #4410 - Comments on classes

---
 securis/src/main/java/net/curisit/securis/db/License.java |  940 ++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 591 insertions(+), 349 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/db/License.java b/securis/src/main/java/net/curisit/securis/db/License.java
index 8b36055..d71759d 100644
--- a/securis/src/main/java/net/curisit/securis/db/License.java
+++ b/securis/src/main/java/net/curisit/securis/db/License.java
@@ -1,3 +1,6 @@
+/*
+* Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+*/
 package net.curisit.securis.db;
 
 import java.io.Serializable;
@@ -41,438 +44,677 @@
 import net.curisit.securis.services.exception.SeCurisServiceException;
 
 /**
- * Entity implementation class for Entity: license
- * 
- */
+* License
+* <p>
+* Main license entity. Contains identity, ownership, timestamps and payload fields.
+* Includes convenience JSON properties for related IDs/names.
+*
+* Mapping details:
+* - Table: license
+* - Listeners: CreationTimestampListener, ModificationTimestampListener
+* - Named queries: license-by-code, license-by-activation-code, last-code-suffix-used-in-pack, ...
+* - Status column uses custom Hibernate type: net.curisit.securis.db.common.LicenseStatusType
+* 
+* @author JRA
+* Last reviewed by JRA on Oct 5, 2025.
+*/
 @JsonAutoDetect
 @JsonInclude(Include.NON_NULL)
 @Entity
 @EntityListeners({ CreationTimestampListener.class, ModificationTimestampListener.class })
 @Table(name = "license")
 @JsonIgnoreProperties(ignoreUnknown = true)
-@NamedQueries({ @NamedQuery(name = "license-by-code", query = "SELECT l FROM License l where l.code = :code"),
-		@NamedQuery(name = "license-by-activation-code", query = "SELECT l FROM License l where l.activationCode = :activationCode"),
-		@NamedQuery(name = "last-code-suffix-used-in-pack", query = "SELECT max(l.codeSuffix) FROM License l where l.pack.id = :packId"),
-		@NamedQuery(name = "list-licenses-by-pack", query = "SELECT l FROM License l where l.pack.id = :packId"),
-		@NamedQuery(name = "list-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash"),
-		@NamedQuery(name = "list-active-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash and l.status in ('AC', 'PA')"),
-		@NamedQuery(name = "list-valid-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash and l.status in ('RE', 'AC', 'PA')")
-
+@NamedQueries({
+    @NamedQuery(name = "license-by-code", query = "SELECT l FROM License l where l.code = :code"),
+    @NamedQuery(name = "license-by-activation-code", query = "SELECT l FROM License l where l.activationCode = :activationCode"),
+    @NamedQuery(name = "last-code-suffix-used-in-pack", query = "SELECT max(l.codeSuffix) FROM License l where l.pack.id = :packId"),
+    @NamedQuery(name = "list-licenses-by-pack", query = "SELECT l FROM License l where l.pack.id = :packId"),
+    @NamedQuery(name = "list-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash"),
+    @NamedQuery(name = "list-active-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash and l.status in ('AC', 'PA')"),
+    @NamedQuery(name = "list-valid-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash and l.status in ('RE', 'AC', 'PA')")
 })
 public class License implements CreationTimestampEntity, ModificationTimestampEntity, Serializable {
 
-	private static final long serialVersionUID = 2700310404904877227L;
+    private static final long serialVersionUID = 2700310404904877227L;
 
-	private static final Logger LOG = LogManager.getLogger(License.class);
+    private static final Logger LOG = LogManager.getLogger(License.class);
 
-	@Id
-	@GeneratedValue
-	private Integer id;
+    // ------------------------------------------------------------------
+    // Columns & relations
+    // ------------------------------------------------------------------
 
-	private String code;
+    @Id
+    @GeneratedValue
+    private Integer id;
 
-	@Column(name = "metadata_obsolete")
-	@JsonProperty("metadata_obsolete")
-	private Boolean metadataObsolete;
+    private String code;
 
-	@Column(name = "activation_code")
-	@JsonProperty("activation_code")
-	private String activationCode;
+    @Column(name = "metadata_obsolete")
+    @JsonProperty("metadata_obsolete")
+    private Boolean metadataObsolete;
 
-	@Column(name = "code_suffix")
-	@JsonProperty("code_suffix")
-	private Integer codeSuffix;
+    @Column(name = "activation_code")
+    @JsonProperty("activation_code")
+    private String activationCode;
 
-	@JsonIgnore
-	@ManyToOne
-	@JoinColumn(name = "pack_id")
-	private Pack pack;
+    @Column(name = "code_suffix")
+    @JsonProperty("code_suffix")
+    private Integer codeSuffix;
 
-	@JsonIgnore
-	@ManyToOne
-	@JoinColumn(name = "created_by")
-	private User createdBy;
+    @JsonIgnore
+    @ManyToOne
+    @JoinColumn(name = "pack_id")
+    private Pack pack;
 
-	@JsonIgnore
-	@ManyToOne
-	@JoinColumn(name = "cancelled_by")
-	private User cancelledBy;
+    @JsonIgnore
+    @ManyToOne
+    @JoinColumn(name = "created_by")
+    private User createdBy;
 
-	@Type(type = "net.curisit.securis.db.common.LicenseStatusType")
-	private LicenseStatus status;
+    @JsonIgnore
+    @ManyToOne
+    @JoinColumn(name = "cancelled_by")
+    private User cancelledBy;
 
-	@Column(name = "full_name")
-	@JsonProperty("full_name")
-	private String fullName;
+    @Type(type = "net.curisit.securis.db.common.LicenseStatusType")
+    private LicenseStatus status;
 
-	private String email;
+    @Column(name = "full_name")
+    @JsonProperty("full_name")
+    private String fullName;
 
-	@Column(name = "request_data")
-	@JsonProperty("request_data")
-	private String requestData;
+    private String email;
 
-	/**
-	 * request data hash is automatically set when we use
-	 * {@link License#setRequestData(String)} method
-	 */
-	@Column(name = "request_data_hash")
-	@JsonIgnore
-	private String reqDataHash;
+    @Column(name = "request_data")
+    @JsonProperty("request_data")
+    private String requestData;
 
-	@Column(name = "license_data")
-	@JsonProperty("license_data")
-	@JsonIgnore
-	// The license data is sent to user as a separate file, It doesn't need to
-	// be included as License attribute on browser
-	private String licenseData;
+    /**
+    * Request data hash (not serialized). Automatically updated by setRequestData().
+    */
+    @Column(name = "request_data_hash")
+    @JsonIgnore
+    private String reqDataHash;
 
-	@Column(name = "creation_timestamp")
-	@JsonProperty("creation_timestamp")
-	private Date creationTimestamp;
+    @Column(name = "license_data")
+    @JsonProperty("license_data")
+    @JsonIgnore
+    // License data is delivered separately (e.g., file download). Not sent in list views.
+    private String licenseData;
 
-	@Column(name = "modification_timestamp")
-	@JsonProperty("modification_timestamp")
-	private Date modificationTimestamp;
+    @Column(name = "creation_timestamp")
+    @JsonProperty("creation_timestamp")
+    private Date creationTimestamp;
 
-	@Column(name = "last_access_timestamp")
-	@JsonProperty("last_access_timestamp")
-	private Date lastAccessTimestamp;
+    @Column(name = "modification_timestamp")
+    @JsonProperty("modification_timestamp")
+    private Date modificationTimestamp;
 
-	@Column(name = "expiration_date")
-	@JsonProperty("expiration_date")
-	private Date expirationDate;
+    @Column(name = "last_access_timestamp")
+    @JsonProperty("last_access_timestamp")
+    private Date lastAccessTimestamp;
 
-	private String comments;
+    @Column(name = "expiration_date")
+    @JsonProperty("expiration_date")
+    private Date expirationDate;
 
-	@OneToMany(fetch = FetchType.LAZY, mappedBy = "license")
-	@JsonIgnore
-	private List<LicenseHistory> history;
+    private String comments;
 
-	public Integer getId() {
-		return id;
-	}
+    @OneToMany(fetch = FetchType.LAZY, mappedBy = "license")
+    @JsonIgnore
+    private List<LicenseHistory> history;
 
-	public String getCode() {
-		return code;
-	}
+    // ------------------------------------------------------------------
+    // Basic accessors
+    // ------------------------------------------------------------------
 
-	public void setCode(String code) {
-		this.code = code;
-	}
+    /** 
+     * getId<p>
+     * Return primary key. 
+     * 
+     * @return id
+     */
+    public Integer getId() { return id; }
 
-	@Override
-	public Date getCreationTimestamp() {
-		return creationTimestamp;
-	}
+    /** 
+     * getCode<p>
+     * Return human-readable license code. 
+     * 
+     * @return code
+     */
+    public String getCode() { return code; }
 
-	@Override
-	public void setCreationTimestamp(Date creationTimestamp) {
-		this.creationTimestamp = creationTimestamp;
-	}
+    /** 
+     * setCode<p>
+     * Set human-readable license code. 
+     * 
+     * @param code
+     */
+    public void setCode(String code) { this.code = code; }
 
-	public User getCreatedBy() {
-		return createdBy;
-	}
+    /** 
+     * getCreationTimestamp<p>
+     * Required by CreationTimestampEntity. 
+     * 
+     * @return creationTimestamp
+     */
+    @Override
+    public Date getCreationTimestamp() { return creationTimestamp; }
 
-	public void setCreatedBy(User createdBy) {
-		this.createdBy = createdBy;
-	}
+    /** 
+     * setCreationTimestamp<p>
+     * Set creation timestamp. 
+     * 
+     * @param creationTimestamp
+     */
+    @Override
+    public void setCreationTimestamp(Date creationTimestamp) { this.creationTimestamp = creationTimestamp; }
 
-	public Pack getPack() {
-		return pack;
-	}
+    /** 
+     * getCreatedBy<p>
+     * Return creator user (entity). 
+     * 
+     * @return user
+     */
+    public User getCreatedBy() { return createdBy; }
 
-	public void setPack(Pack pack) {
-		this.pack = pack;
-	}
+    /** 
+     * setCreatedBy<p>
+     * Set creator user (entity). 
+     * 
+     * @param user
+     */
+    public void setCreatedBy(User createdBy) { this.createdBy = createdBy; }
 
-	@JsonProperty("created_by_id")
-	public String getCreatedById() {
-		return createdBy == null ? null : createdBy.getUsername();
-	}
+    /** 
+     * getPack<p>
+     * Return owning pack. 
+     * 
+     * @return pack
+     */
+    public Pack getPack() { return pack; }
 
-	@JsonProperty("created_by_id")
-	public void setCreatedById(String username) {
-		if (username == null) {
-			createdBy = null;
-		} else {
-			createdBy = new User();
-			createdBy.setUsername(username);
-		}
-	}
+    /** 
+     * setPack<p>
+     * Set owning pack.
+     * 
+     * @param pack
+     */
+    public void setPack(Pack pack) { this.pack = pack; }
 
-	@JsonProperty("cancelled_by_id")
-	public String getCancelledById() {
-		return cancelledBy == null ? null : cancelledBy.getUsername();
-	}
+    /** 
+     * getCreatedById<p>
+     * Expose creator username as JSON. 
+     * 
+     * @return username
+     */
+    @JsonProperty("created_by_id")
+    public String getCreatedById() { return createdBy == null ? null : createdBy.getUsername(); }
 
-	@JsonProperty("cancelled_by_id")
-	public void setCancelledById(String username) {
-		if (username == null) {
-			cancelledBy = null;
-		} else {
-			cancelledBy = new User();
-			cancelledBy.setUsername(username);
-		}
-	}
+    /** 
+     * setCreatedById<p>
+     * Setter by username for JSON binding. 
+     * 
+     * @param username
+     */
+    @JsonProperty("created_by_id")
+    public void setCreatedById(String username) {
+        if (username == null) {
+            createdBy = null;
+        } else {
+            createdBy = new User();
+            createdBy.setUsername(username);
+        }
+    }
 
-	@JsonProperty("pack_code")
-	public String getPackCode() {
-		return pack == null ? null : pack.getCode();
-	}
+    /** 
+     * getCancelledById<p>
+     * Expose cancelling user username as JSON. 
+     * 
+     * @return username
+     */
+    @JsonProperty("cancelled_by_id")
+    public String getCancelledById() { return cancelledBy == null ? null : cancelledBy.getUsername(); }
 
-	@JsonProperty("pack_id")
-	public Integer getPackId() {
-		return pack == null ? null : pack.getId();
-	}
+    /** 
+     * setCancelledById<p>
+     * Setter by username for JSON binding. 
+     * 
+     * @param username
+     */
+    @JsonProperty("cancelled_by_id")
+    public void setCancelledById(String username) {
+        if (username == null) {
+            cancelledBy = null;
+        } else {
+            cancelledBy = new User();
+            cancelledBy.setUsername(username);
+        }
+    }
 
-	@JsonProperty("pack_id")
-	public void setPackId(Integer idPack) {
-		if (idPack == null) {
-			pack = null;
-		} else {
-			pack = new Pack();
-			pack.setId(idPack);
-		}
-	}
+    /** 
+     * getPackCode<p>
+     * Expose pack code for convenience. 
+     * 
+     * @return packCode
+     */
+    @JsonProperty("pack_code")
+    public String getPackCode() { return pack == null ? null : pack.getCode(); }
 
-	public LicenseStatus getStatus() {
-		return status;
-	}
+    /** 
+     * getPackId<p>
+     * Expose pack id for convenience. 
+     * 
+     * @return packId
+     */
+    @JsonProperty("pack_id")
+    public Integer getPackId() { return pack == null ? null : pack.getId(); }
 
-	public void setStatus(LicenseStatus status) {
-		this.status = status;
-	}
+    /** 
+     * setPackId<p>
+     * Setter by id for JSON binding (creates a shallow Pack).
+     * 
+     * @param packId
+     */
+    @JsonProperty("pack_id")
+    public void setPackId(Integer idPack) {
+        if (idPack == null) {
+            pack = null;
+        } else {
+            pack = new Pack();
+            pack.setId(idPack);
+        }
+    }
 
-	@Override
-	public Date getModificationTimestamp() {
-		return modificationTimestamp;
-	}
+    /** 
+     * getStatus<p>
+     * Return license status. 
+     * 
+     * @return licenseStatus
+     */
+    public LicenseStatus getStatus() { return status; }
 
-	@Override
-	public void setModificationTimestamp(Date modificationTimestamp) {
-		this.modificationTimestamp = modificationTimestamp;
-	}
+    /** 
+     * setStatus<p>
+     * Set license status. 
+     * 
+     * @param status
+     */
+    public void setStatus(LicenseStatus status) { this.status = status; }
 
-	public String getFullName() {
-		return fullName;
-	}
+    /** 
+     * getModificationTimestamp<p>
+     * Required by ModificationTimestampEntity. 
+     * 
+     * @return modificationTimestamp
+     */
+    @Override
+    public Date getModificationTimestamp() { return modificationTimestamp; }
 
-	public void setFullName(String fullName) {
-		this.fullName = fullName;
-	}
+    /** 
+     * setModificationTimestamp<p>
+     * Set modification timestamp. 
+     * 
+     * @param modificationTimestamp
+     */
+    @Override
+    public void setModificationTimestamp(Date modificationTimestamp) { this.modificationTimestamp = modificationTimestamp; }
 
-	public String getEmail() {
-		return email;
-	}
+    /** 
+     * getFullName<p>
+     * Return license holder full name. 
+     * 
+     * @return name
+     */
+    public String getFullName() { return fullName; }
 
-	public void setEmail(String email) {
-		this.email = email;
-	}
+    /** 
+     * setFullName<p>
+     * Set license holder full name. 
+     * 
+     * @param name
+     */
+    public void setFullName(String fullName) { this.fullName = fullName; }
 
-	public void setId(Integer id) {
-		this.id = id;
-	}
+    /** 
+     * getEmail<p>
+     * Return email address. 
+     * 
+     * @return email
+     */
+    public String getEmail() { return email; }
 
-	public User getCancelledBy() {
-		return cancelledBy;
-	}
+    /** 
+     * setEmail<p>
+     * Set email address. 
+     * 
+     * @param email
+     */
+    public void setEmail(String email) { this.email = email; }
+
+    /** 
+     * setId<p>
+     * Set primary key (rarely used). 
+     * 
+     * @param id
+     */
+    public void setId(Integer id) { this.id = id; }
+
+    /** 
+     * getCancelledBy<p>
+     * Return cancelling user (entity). 
+     * 
+     * @param user
+     */
+    public User getCancelledBy() { return cancelledBy; }
+
+    /** 
+     * setCancelledBy<p>
+     * Set cancelling user (entity). 
+     * 
+     * @param cancelledBy
+     */
+    public void setCancelledBy(User cancelledBy) { this.cancelledBy = cancelledBy; }
 
-	public void setCancelledBy(User cancelledBy) {
-		this.cancelledBy = cancelledBy;
-	}
+    /** 
+     * getLastAccessTimestamp<p>
+     * Return last access timestamp. 
+     * 
+     * @return lastAccessTimestamp
+     */
+    public Date getLastAccessTimestamp() { return lastAccessTimestamp; }
 
-	public Date getLastAccessTimestamp() {
-		return lastAccessTimestamp;
-	}
+    /** 
+     * setLastAccessTimestamp<p>
+     * Set last access timestamp. 
+     * 
+     * @param lastAccessTimestamp
+     */
+    public void setLastAccessTimestamp(Date lastAccessTimestamp) { this.lastAccessTimestamp = lastAccessTimestamp; }
 
-	public void setLastAccessTimestamp(Date lastAccessTimestamp) {
-		this.lastAccessTimestamp = lastAccessTimestamp;
-	}
+    /** 
+     * getRequestData<p>
+     * Return raw request data. 
+     * 
+     * @return requestData
+     */
+    public String getRequestData() { return requestData; }
 
-	public String getRequestData() {
-		return requestData;
-	}
+    /**
+    * setRequestData<p>
+    * Set raw request data and recompute {@link #reqDataHash} immediately using
+    * the same hashing strategy as BlockedRequest (SHA-256).
+    *
+    * @param requestData
+    */
+    public void setRequestData(String requestData) {
+        this.requestData = requestData;
+        this.reqDataHash = BlockedRequest.generateHash(this.requestData);
+    }
 
-	public void setRequestData(String requestData) {
-		this.requestData = requestData;
-		this.reqDataHash = BlockedRequest.generateHash(this.requestData);
-	}
+    /** 
+     * getLicenseData<p>
+     * Return opaque license data (not serialized in lists). 
+     * 
+     * @return licenseData
+     */
+    public String getLicenseData() { return licenseData; }
 
-	public String getLicenseData() {
-		return licenseData;
-	}
+    /** 
+     * setLicenseData<p>
+     * Set opaque license data (large content kept server-side). 
+     * 
+     * @param licenseDate
+     */
+    public void setLicenseData(String licenseData) { this.licenseData = licenseData; }
 
-	public void setLicenseData(String licenseData) {
-		this.licenseData = licenseData;
-	}
+    /** 
+     * getComments<p>
+     * Return optional comments. 
+     * 
+     * @return comments
+     */
+    public String getComments() { return comments; }
 
-	public String getComments() {
-		return comments;
-	}
+    /** 
+     * setComments<p>
+     * Set optional comments. 
+     * 
+     * @param comments
+     */
+    public void setComments(String comments) { this.comments = comments; }
 
-	public void setComments(String comments) {
-		this.comments = comments;
-	}
+    /** 
+     * getHistory<p>
+     * Return change history entries (lazy). 
+     * 
+     * @return history
+     */
+    public List<LicenseHistory> getHistory() { return history; }
 
-	public List<LicenseHistory> getHistory() {
-		return history;
-	}
+    /** 
+     * setHistory<p>
+     * Set change history entries. 
+     * 
+     * @param history
+     */
+    public void setHistory(List<LicenseHistory> history) { this.history = history; }
 
-	public void setHistory(List<LicenseHistory> history) {
-		this.history = history;
-	}
+    /** 
+     * getExpirationDate<p>
+     * Return expiration date (nullable). 
+     * 
+     * @return expirationDate
+     */
+    public Date getExpirationDate() { return expirationDate; }
 
-	public Date getExpirationDate() {
-		return expirationDate;
-	}
+    /** 
+     * setExpirationDate<p>
+     * Set expiration date (nullable). 
+     * 
+     * @param expirationDate
+     */
+    public void setExpirationDate(Date expirationDate) { this.expirationDate = expirationDate; }
 
-	public void setExpirationDate(Date expirationDate) {
-		this.expirationDate = expirationDate;
-	}
+    /** 
+     * getReqDataHash<p>
+     * Return cached hash of request data (not exposed in JSON). 
+     * 
+     * @return reqDataHash
+     */
+    public String getReqDataHash() { return reqDataHash; }
 
-	public String getReqDataHash() {
-		return reqDataHash;
-	}
+    /** 
+     * getCodeSuffix<p>
+     * Return numeric suffix of the code. 
+     * 
+     * @return codeSuffix
+     */
+    public Integer getCodeSuffix() { return codeSuffix; }
 
-	public static class Action {
-		public static final int CREATE = 1;
-		public static final int REQUEST = 2;
-		public static final int ACTIVATION = 3;
-		public static final int SEND = 4;
-		public static final int DOWNLOAD = 5;
-		public static final int CANCEL = 6;
-		public static final int DELETE = 7;
-		public static final int BLOCK = 8;
-		public static final int UNBLOCK = 9;
-	}
+    /** 
+     * setCodeSuffix<p>
+     * Set numeric suffix of the code. 
+     * 
+     * @param codeSuffix
+     */
+    public void setCodeSuffix(Integer codeSuffix) { this.codeSuffix = codeSuffix; }
 
-	public static class Status {
+    /** 
+     * getActivationCode<p>
+     * Return activation code. 
+     * 
+     * @return activationCode
+     */
+    public String getActivationCode() { return activationCode; }
 
-		private static final Map<Integer, List<LicenseStatus>> transitions = Utils.createMap( //
-				Action.REQUEST, Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED), //
-				Action.ACTIVATION, Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED, LicenseStatus.PRE_ACTIVE, LicenseStatus.EXPIRED), //
-				Action.SEND, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
-				Action.DOWNLOAD, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
-				Action.CANCEL, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE, LicenseStatus.REQUESTED, LicenseStatus.EXPIRED), //
-				Action.DELETE, Arrays.asList(LicenseStatus.CANCELLED, LicenseStatus.CREATED, LicenseStatus.BLOCKED), //
-				Action.UNBLOCK, Arrays.asList(LicenseStatus.BLOCKED), //
-				Action.BLOCK, Arrays.asList(LicenseStatus.CANCELLED) //
-		);
+    /** 
+     * setActivationCode<p>
+     * Set activation code. 
+     * 
+     * @param activationCode
+     */
+    public void setActivationCode(String activationCode) { this.activationCode = activationCode; }
 
-		/**
-		 * 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, LicenseStatus currentStatus) {
-			List<LicenseStatus> validStatuses = transitions.get(action);
-			LOG.info("Action {} is valid ? => {} current: {} OK? {}", action, validStatuses, currentStatus, validStatuses.contains(currentStatus));
-			return validStatuses != null && validStatuses.contains(currentStatus);
-		}
-	}
+    /** 
+     * isMetadataObsolete<p>
+     * Convenience Boolean → primitive with null-safe false. 
+     * 
+     * @return isMetadataObsolete
+     */
+    public boolean isMetadataObsolete() { return metadataObsolete != null && metadataObsolete; }
 
-	/**
-	 * Return licenses with status: REquested, ACtive, Pre-Active for a given
-	 * request data
-	 * 
-	 * @param requestData
-	 * @param em
-	 * @return
-	 * @throws SeCurisServiceException
-	 */
-	public static License findValidLicenseByRequestData(String requestData, EntityManager em) throws SeCurisServiceException {
-		TypedQuery<License> query = em.createNamedQuery("list-valid-licenses-by-req-data", License.class);
-		query.setParameter("hash", BlockedRequest.generateHash(requestData));
-		try {
-			List<License> list = query.getResultList();
-			if (list.size() == 0) {
-				return null;
-			}
-			if (list.size() > 1) {
-				LOG.error("There are more than 1 active or requested license for request data: {}\nHash: {}", requestData, BlockedRequest.generateHash(requestData));
-			}
-			return list.get(0);
-		} catch (NoResultException e) {
-			// There is no license for request data
-			return null;
-		}
-	}
+    /** 
+     * setMetadataObsolete<p>
+     * Set metadata obsolete flag (nullable wrapper). 
+     * 
+     * @param obsolete
+     */
+    public void setMetadataObsolete(Boolean obsolete) { this.metadataObsolete = obsolete; }
 
-	/**
-	 * Return licenses with status: REquested, ACtive, Pre-Active for a given
-	 * request data
-	 * 
-	 * @param requestData
-	 * @param em
-	 * @return
-	 * @throws SeCurisServiceException
-	 */
-	public static License findLicenseByActivationCode(String activationCode, EntityManager em) throws SeCurisServiceException {
-		TypedQuery<License> query = em.createNamedQuery("license-by-activation-code", License.class);
-		query.setParameter("activationCode", activationCode);
-		try {
-			return query.getSingleResult();
-		} catch (NoResultException e) {
-			// There is no license for request data
-			return null;
-		}
-	}
+    // ------------------------------------------------------------------
+    // Status transitions helpers
+    // ------------------------------------------------------------------
 
-	public static License findActiveLicenseByRequestData(String requestData, EntityManager em) throws SeCurisServiceException {
-		TypedQuery<License> query = em.createNamedQuery("list-active-licenses-by-req-data", License.class);
-		query.setParameter("hash", BlockedRequest.generateHash(requestData));
-		try {
-			List<License> list = query.getResultList();
-			if (list.size() == 0) {
-				return null;
-			}
-			if (list.size() > 1) {
-				LOG.error("There are more than 1 active license for request data: {}\nHash: {}", requestData, BlockedRequest.generateHash(requestData));
-			}
-			return list.get(0);
-		} catch (NoResultException e) {
-			// There is no license for request data
-			return null;
-		}
-	}
+    /**
+     * Action<p>
+     * Actions to take with the license
+     */
+    public static class Action {
+        public static final int CREATE = 1;
+        public static final int REQUEST = 2;
+        public static final int ACTIVATION = 3;
+        public static final int SEND = 4;
+        public static final int DOWNLOAD = 5;
+        public static final int CANCEL = 6;
+        public static final int DELETE = 7;
+        public static final int BLOCK = 8;
+        public static final int UNBLOCK = 9;
+    }
 
-	public static License findLicenseByCode(String code, EntityManager em) throws SeCurisServiceException {
-		TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
-		query.setParameter("code", code);
-		try {
-			return query.getSingleResult();
-		} catch (NoResultException e) {
-			// There is no license for request data
-			return null;
-		}
-	}
+    /**
+     * Status<p>
+     * Status of the requested license
+     */
+    public static class Status {
 
-	public Integer getCodeSuffix() {
-		return codeSuffix;
-	}
+        private static final Map<Integer, List<LicenseStatus>> transitions = Utils.createMap(
+            Action.REQUEST,   Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED),
+            Action.ACTIVATION,Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED, LicenseStatus.PRE_ACTIVE, LicenseStatus.EXPIRED),
+            Action.SEND,      Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE),
+            Action.DOWNLOAD,  Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE),
+            Action.CANCEL,    Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE, LicenseStatus.REQUESTED, LicenseStatus.EXPIRED),
+            Action.DELETE,    Arrays.asList(LicenseStatus.CANCELLED, LicenseStatus.CREATED, LicenseStatus.BLOCKED),
+            Action.UNBLOCK,   Arrays.asList(LicenseStatus.BLOCKED),
+            Action.BLOCK,     Arrays.asList(LicenseStatus.CANCELLED)
+        );
 
-	public void setCodeSuffix(Integer codeSuffix) {
-		this.codeSuffix = codeSuffix;
-	}
+        /**
+        * isActionValid<p>
+        * Check whether an action is valid given the current license status.
+        *
+        * @param action action constant from {@link Action}
+        * @param currentStatus current license status
+        * @return true if allowed
+        */
+        public static boolean isActionValid(Integer action, LicenseStatus currentStatus) {
+            List<LicenseStatus> validStatuses = transitions.get(action);
+            LOG.info("Action {} is valid ? => {} current: {} OK? {}", action, validStatuses, currentStatus,
+                     validStatuses != null && validStatuses.contains(currentStatus));
+            return validStatuses != null && validStatuses.contains(currentStatus);
+        }
+    }
 
-	public String getActivationCode() {
-		return activationCode;
-	}
+    // ------------------------------------------------------------------
+    // Repository helpers (static queries)
+    // ------------------------------------------------------------------
 
-	public void setActivationCode(String activationCode) {
-		this.activationCode = activationCode;
-	}
+    /**
+    * findValidLicenseByRequestData<p>
+    * Return the first license in statuses RE/AC/PA for the given request data hash.
+    *
+    * @param requestData raw request data
+    * @param em entity manager
+    * @return matching license or null
+    * @throws SeCurisServiceException
+    */
+    public static License findValidLicenseByRequestData(String requestData, EntityManager em) throws SeCurisServiceException {
+        TypedQuery<License> query = em.createNamedQuery("list-valid-licenses-by-req-data", License.class);
+        query.setParameter("hash", BlockedRequest.generateHash(requestData));
+        try {
+            List<License> list = query.getResultList();
+            if (list.size() == 0) return null;
+            if (list.size() > 1) {
+                LOG.error("There are more than 1 active or requested license for request data: {}\nHash: {}",
+                          requestData, BlockedRequest.generateHash(requestData));
+            }
+            return list.get(0);
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
 
-	public boolean isMetadataObsolete() {
-		return metadataObsolete != null && metadataObsolete;
-	}
+    /**
+    * findLicenseByActivationCode<p>
+    * Retrieve a license by its activation code.
+    * 
+    * @param activationCode
+    * @param entityManager
+    * @return license
+    * @throws SeCurisServiceException
+    */
+    public static License findLicenseByActivationCode(String activationCode, EntityManager em) throws SeCurisServiceException {
+        TypedQuery<License> query = em.createNamedQuery("license-by-activation-code", License.class);
+        query.setParameter("activationCode", activationCode);
+        try {
+            return query.getSingleResult();
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
 
-	public void setMetadataObsolete(Boolean obsolete) {
-		this.metadataObsolete = obsolete;
-	}
+    /**
+    * findActiveLicenseByRequestData<p>
+    * Return the first AC/PA license for a given requestData.
+    * 
+    * @param requestData
+    * @param entityManager
+    * @return license
+    * @throws SeCurisServiceException
+    */
+    public static License findActiveLicenseByRequestData(String requestData, EntityManager em) throws SeCurisServiceException {
+        TypedQuery<License> query = em.createNamedQuery("list-active-licenses-by-req-data", License.class);
+        query.setParameter("hash", BlockedRequest.generateHash(requestData));
+        try {
+            List<License> list = query.getResultList();
+            if (list.size() == 0) return null;
+            if (list.size() > 1) {
+                LOG.error("There are more than 1 active license for request data: {}\nHash: {}",
+                          requestData, BlockedRequest.generateHash(requestData));
+            }
+            return list.get(0);
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
 
+    /**
+    * findLicenseByCode<p>
+    * Retrieve a license by human-readable code.
+    * 
+    * @param code
+    * @param entityManager
+    * @return license
+    * @throws SeCurisServiceException
+    */
+    public static License findLicenseByCode(String code, EntityManager em) throws SeCurisServiceException {
+        TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
+        query.setParameter("code", code);
+        try {
+            return query.getSingleResult();
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
 }
+

--
Gitblit v1.3.2