net.java.truelicense.core.License Maven / Gradle / Ivy
Show all versions of truelicense-core Show documentation
/*
* Copyright (C) 2005-2013 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package net.java.truelicense.core;
import java.beans.*;
import java.util.*;
import static java.util.Calendar.*;
import javax.annotation.*;
import javax.security.auth.x500.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import net.java.truelicense.core.codec.*;
import net.java.truelicense.core.util.Objects;
/**
* A Java Bean which defines and provides the common properties of any license.
* In general, all properties may be {@code null} to indicate that this
* property is not set.
* However, {@linkplain LicenseValidation#validate license validation} may fail
* if some properties are not set when
* {@linkplain LicenseVendorManager#create creating},
* {@linkplain LicenseConsumerManager#install installing} or
* {@linkplain LicenseConsumerManager#verify verifying}
* license keys.
*
* There are two options for extending a license with custom properties:
*
* - The easiest way is to wrap the custom properties in another Java Bean
* and store it in the {@linkplain #setExtra(Object) extra} property
* (composition).
*
- Another option is to subclass this class and add the custom properties
* (inheritance).
* However, you may then need to override {@link #equals(Object)}, too.
* If you do, you also need to override {@link #hashCode()} for consistency.
*
* Either way, the custom properties must support the {@link Codec} of the
* respective license key format - see {@link LicenseManagementContext#codec}.
* This is easiest to achieve if the respective class follows the JavaBean
* pattern just like this class does.
*
* Note that this class deviates from the JavaBeans specification in that it
* neither implements {@link java.io.Serializable} nor
* {@link java.io.Externalizable}.
* This is because serialization is not required with the used {@code Codec}s
* and creates a major obligation for any subclass.
*
* @see LicenseProvider
* @author Christian Schlichtherle
*/
// #TRUELICENSE-50: The XML element name MUST be explicitly defined!
// Otherwise, it would get derived from the class name, but this would break
// if the class name gets obfuscated, e.g. when using the ProGuard
// configuration from the TrueLicense Maven Archetype.
@XmlRootElement(name = "license")
@Nullable
public class License {
private int consumerAmount = 1;
private String consumerType;
private Object extra;
private X500Principal holder;
private String info;
private Date issued;
private X500Principal issuer;
private Date notAfter;
private Date notBefore;
private String subject;
/**
* Returns the amount of consumers which are allowed to use the licensing
* subject.
* The default value is one in order to retain interoperability with V1
* format license keys and to reduce the size of the XML encoded form
* (default values don't need to get encoded).
*/
@XmlElement(defaultValue = "1")
public final int getConsumerAmount() { return consumerAmount; }
/**
* Sets the amount of consumers which are allowed to use the licensing
* subject.
*/
public final void setConsumerAmount(final int consumerAmount) {
this.consumerAmount = consumerAmount;
}
/**
* Returns the description of the type of the entity or object which
* allocates (consumes) the license for using the licensing subject.
* This could describe a computer or a user or anything else, e.g.
* {@code "User"}.
*/
@XmlElement(required = true)
public final String getConsumerType() { return consumerType; }
/**
* Sets the description of the type of the entity or object which
* allocates (consumes) the license for using the licensing subject.
*/
public final void setConsumerType(final String consumerType) {
this.consumerType = consumerType;
}
/**
* Returns the private license extra data.
* This may be any object which supports serialization via
* {@link XMLEncoder}.
* It is typically used by applications to provide some extra data for
* custom validation.
*/
public final Object getExtra() { return extra; }
/**
* Sets the private license extra data.
*
* @param extra the private license extra data.
* This object must support serialization via {@link XMLEncoder}.
*/
public final void setExtra(final Object extra) {
this.extra = extra;
}
/**
* Returns the description of the legal entity to which the license is
* granted by the issuer/vendor.
* This could describe a person or an organization or be a placeholder for
* any of these, e.g. {@code new X500Principal("CN=Anonymous")}.
*/
@XmlElement(required = true)
@XmlJavaTypeAdapter(X500PrincipalXmlAdapter.class)
public final X500Principal getHolder() { return holder; }
/**
* Sets the description of the legal entity to which the license is
* granted by the issuer/vendor.
*/
public final void setHolder(final X500Principal holder) {
this.holder = holder;
}
/**
* Returns the license information.
* This could be any text which may be displayed to users for informational
* purposes, e.g. in a GUI dialog.
*/
public final String getInfo() { return info; }
/**
* Sets the license information.
*/
public final void setInfo(final String info) {
this.info = info;
}
/**
* Returns the date/time when the license has been issued.
* Note that a protective copy is made.
*/
@XmlElement(required = true)
public final Date getIssued() { return clone(issued); }
/**
* Sets the date/time when the license has been issued.
* Note that a protective copy is made.
*/
public final void setIssued(final Date issued) {
this.issued = clone(issued);
}
/**
* Returns the description of the legal entity which grants the license to
* the holder/consumer.
* This could describe a person or an organization or be a placeholder for
* any of these, but typically describes an organization e.g.
* {@code new X500Principal("CN=Christian Schlichtherle,O=Schlichtherle IT Services")}.
*/
@XmlElement(required = true)
@XmlJavaTypeAdapter(X500PrincipalXmlAdapter.class)
public final X500Principal getIssuer() { return issuer; }
/**
* Sets the description of the legal entity which grants the license to
* the holder/consumer.
*/
public final void setIssuer(final X500Principal issuer) {
this.issuer = issuer;
}
/**
* Returns the date/time when the license ends to be valid (expires).
* Note that a protective copy is made.
*/
public final Date getNotAfter() { return clone(notAfter); }
/**
* Sets the date/time when the license ends to be valid (expires).
* Note that a protective copy is made.
*/
public final void setNotAfter(final Date notAfter) {
this.notAfter = clone(notAfter);
}
/**
* Returns the date/time when the license begins to be valid.
* Note that a protective copy is made.
*/
public final Date getNotBefore() { return clone(notBefore); }
/**
* Sets the date/time when the license begins to be valid.
* Note that a protective copy is made.
*/
public final void setNotBefore(final Date notBefore) {
this.notBefore = clone(notBefore);
}
/**
* Conveniently computes the {@code issued} date/time, {@code notBefore}
* date/time and {@code notAfter} date/time properties from the given
* number of days.
*
* Note that this computation depends on the system clock unless the
* property {@code issued} is set.
* However, this is not a security issue because this method is neither
* used for license initialization nor validation by a license consumer
* manager.
*
* @param days the validity period in (24 hour) days from now.
*/
public final void setTerm(final int days) {
Date issued = getIssued();
if (null == issued) {
issued = new Date();
setIssued(issued);
}
setNotBefore(issued);
final Calendar cal = getInstance();
cal.setTime(issued);
cal.add(DATE, days);
setNotAfter(cal.getTime());
}
/**
* Returns the description of the product which requires licensing.
* This could be any string, but is recommended to contain the canonical
* name of the application and some information for which version number
* range this license is applicable, e.g. {@code "MyApp 1.X"}.
*/
@XmlElement(required = true)
public final String getSubject() { return subject; }
/** Sets the description of the product which requires licensing. */
public final void setSubject(final String subject) {
this.subject = subject;
}
private static Date clone(Date date) {
return null == date ? null : (Date) date.clone();
}
/**
* Returns {@code true} if and only if {@code obj} is an instance of the
* {@link License class} and its properties compare
* {@linkplain Object#equals(Object) equal} to the properties of this
* license.
*/
@Override
@SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
public boolean equals(final Object obj) {
if (this == obj) return true;
if (!(obj instanceof License)) return false;
final License that = (License) obj;
return this.consumerAmount == that.consumerAmount
&& Objects.equals(this.consumerType, that.consumerType)
&& Objects.equals(this.extra, that.extra)
&& Objects.equals(this.holder, that.holder)
&& Objects.equals(this.info, that.info)
&& Objects.equals(this.issued, that.issued)
&& Objects.equals(this.issuer, that.issuer)
&& Objects.equals(this.notAfter, that.notAfter)
&& Objects.equals(this.notBefore, that.notBefore)
&& Objects.equals(this.subject, that.subject);
}
/** Returns a hash code which is consistent with {@link #equals(Object)}. */
@Override
public int hashCode() {
int c = 17;
c = 31 * c + consumerAmount;
c = 31 * c + Objects.hashCode(consumerType);
c = 31 * c + Objects.hashCode(extra);
c = 31 * c + Objects.hashCode(holder);
c = 31 * c + Objects.hashCode(info);
c = 31 * c + Objects.hashCode(issued);
c = 31 * c + Objects.hashCode(issuer);
c = 31 * c + Objects.hashCode(notAfter);
c = 31 * c + Objects.hashCode(notBefore);
c = 31 * c + Objects.hashCode(subject);
return c;
}
/**
* Returns a string representation of this object for logging and debugging
* purposes.
*/
@Override
public String toString() {
return String.format("%s@%x[subject=%s, holder=%s, issuer=%s, issued=%tc, notBefore=%tc, notAfter=%tc, consumerType=%s, consumerAmount=%d, info=%s]",
getClass().getName(),
hashCode(),
literal(getSubject()),
literal(getHolder()),
literal(getIssuer()),
getIssued(),
getNotBefore(),
getNotAfter(),
literal(getConsumerType()),
getConsumerAmount(),
literal(getInfo()));
}
private static @Nullable String literal(final @CheckForNull Object obj) {
if (null == obj) return null;
final String s = obj.toString();
return '"' +
s .replace("\\", "\\\\")
.replace("\"", "\\\"") +
'"';
}
}