net.padlocksoftware.padlock.license.LicenseImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.padlocksoftware.padlock.license.LicenseImpl.java

Source

/*
 * Copyright (c) 2009-2012 Jason Nichols
    
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal 
 * in the Software without restriction, including without limitation the rights to 
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
 * of the Software, and to permit persons to whom the Software is furnished to do 
 * so, subject to the following conditions:
    
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
    
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 * SOFTWARE.
 */

package net.padlocksoftware.padlock.license;

import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

/**
 * @author Jason Nichols (jason@padlocksoftware.net)
 */
public final class LicenseImpl implements License {
    protected static Set<String> propertyNames(Properties p) {
        Set<String> set = new HashSet<String>();

        Enumeration<Object> e = p.keys();
        while (e.hasMoreElements()) {
            set.add(e.nextElement().toString());
        }

        return set;
    }

    private static final String prependString = "property_";

    private final Logger logger;

    Properties props;

    protected LicenseImpl() {
        this(new Date());
    }

    protected LicenseImpl(Date startDate) {
        logger = Logger.getLogger(getClass().getName());
        props = new Properties();
        setCreationDate(new Date());
        setStartDate(startDate);
        setLicenseVersion(2);
    }

    protected LicenseImpl(Properties properties) {
        logger = Logger.getLogger(getClass().getName());
        props = new Properties();
        for (String key : propertyNames(properties)) {
            props.put(key, properties.get(key));
        }

        // Check for mandatory fields

        // Creation Date must exist
        Date creationDate = getCreationDate();
        if (creationDate == null) {
            throw new ImportException("Creation date is invalid");
        }

        // Start date must exist
        Date startDate = getStartDate();
        if (startDate == null) {
            throw new ImportException("Start date is invalid");
        }

        // Manually get the expiration date, since the public method may
        // return null. We need to differentiate a missing date from an
        // invalid date.
        String expString = props.getProperty("expirationDate");
        if (expString != null) {
            try {
                new Date(Long.parseLong(expString));
            } catch (NumberFormatException e) {
                throw new ImportException("Expiration date is invalid");
            }
        }

        // If the license has a version entry, it must be valid
        String version = props.getProperty("version");
        if (version != null) {
            try {
                Integer.parseInt(version);
            } catch (Exception e) {
                throw new ImportException("License version is invalid");
            }
        }
    }

    /**
     * Concatenate all keys and values into a single String
     */
    public String concatenate() {
        StringBuffer buffer = new StringBuffer();

        TreeSet<String> keys = new TreeSet<String>();
        keys.addAll(propertyNames(props));

        for (String key : keys) {
            String value = props.getProperty(key);
            if (value != null && !"signature".equals(key)) {
                buffer.append(key);
                buffer.append(value);
            }
        }

        return buffer.toString();
    }

    protected void setLicenseVersion(int version) {
        if (isSigned()) {
            throwSignedException();
        }

        props.setProperty("version", Integer.toString(version));
    }

    protected void setCreationDate(Date creationDate) {
        if (isSigned()) {
            throwSignedException();
        }

        props.setProperty("creationDate", Long.toString(creationDate.getTime()));
    }

    private void throwSignedException() {
        throw new IllegalStateException("Cannot modify a signed license");
    }

    // //////////////////////////////////////////////////////////////////////////
    // Utility Methods
    // //////////////////////////////////////////////////////////////////////////

    private String convertInterfaceBytesToString(byte[] interfaceBytes) {
        return new String(Hex.encodeHex(interfaceBytes));
    }

    private SortedSet<String> extractHardwareAddresses(String addresses) {
        TreeSet<String> set = new TreeSet<String>();

        // Addresses are a space separated String of hex characters
        for (String address : addresses.split(" ")) {
            address = address.trim();
            if (address.length() > 0) {
                set.add(address);
            }
        }

        return set;
    }

    private String combineHardwareAddresses(Set<String> set) {
        String addresses = "";

        for (String address : set) {
            addresses += address + " ";
        }

        return addresses.trim();
    }

    // //////////////////////////////////////////////////////////////////////////
    // Interface Methods
    // //////////////////////////////////////////////////////////////////////////

    public synchronized Date getCreationDate() {
        String str = props.getProperty("creationDate");
        long time = Long.parseLong(str);
        return new Date(time);
    }

    public synchronized Date getExpirationDate() {
        Date expirationDate = null;
        String str = props.getProperty("expirationDate");
        if (str != null) {
            long time = Long.parseLong(str);
            expirationDate = new Date(time);
        }

        return expirationDate;
    }

    public synchronized Date getStartDate() {
        String str = props.getProperty("startDate");
        long time = Long.parseLong(str);
        return new Date(time);
    }

    public synchronized Long getFloatingExpirationPeriod() {
        Long period = null;

        String str = props.getProperty("floatingExpiration");
        if (str != null) {
            try {
                period = Long.parseLong(str);
            } catch (Exception e) {
            }
        }
        return period;

    }

    public synchronized Properties getProperties() {
        Properties properties = new Properties();
        for (String property : propertyNames(props)) {
            String value = props.getProperty(property);
            if (property.startsWith(prependString)) {
                properties.setProperty(property.substring(prependString.length()), value);
            }
        }

        return properties;
    }

    public synchronized String getProperty(String propertyName) {
        return getProperty(propertyName, null);
    }

    public synchronized String getProperty(String propertyName, String defaultValue) {
        String value = defaultValue;
        propertyName = prependString + propertyName;
        String v = props.getProperty(propertyName);
        if (v != null) {
            value = v;
        }
        return value;
    }

    public synchronized String getLicenseSignatureString() {
        return props.getProperty("signature");

    }

    public synchronized byte[] getLicenseSignature() {
        byte[] sig = null;

        String str = props.getProperty("signature");
        if (str != null) {
            try {
                sig = Hex.decodeHex(str.toCharArray());
            } catch (DecoderException ex) {
                logger.log(Level.SEVERE, null, ex);
            }
        }

        return sig;
    }

    public synchronized boolean isSigned() {
        return getLicenseSignatureString() != null;
    }

    public synchronized int getLicenseVersion() {
        String version = props.getProperty("version");
        if (version != null) {
            return Integer.parseInt(version);
        } else
            return 1;
    }

    public synchronized Set<String> getHardwareAddresses() {
        return extractHardwareAddresses(props.getProperty("hardwareAddresses", ""));
    }

    public synchronized void setStartDate(Date d) {
        if (isSigned()) {
            throwSignedException();
        }

        props.setProperty("startDate", Long.toString(d.getTime()));
    }

    public synchronized void setExpirationDate(Date d) {
        if (isSigned()) {
            throwSignedException();
        }

        if (d != null) {
            props.setProperty("expirationDate", Long.toString(d.getTime()));
        } else {
            props.remove("expirationDate");
        }
    }

    public synchronized void setFloatingExpirationPeriod(Long period) {
        if (isSigned()) {
            throwSignedException();
        }

        if (period == null) {
            props.remove("floatingExpiration");
        } else if (period < 0) {
            throw new IllegalArgumentException("Period cannot be less than zero");
        } else {
            props.setProperty("floatingExpiration", period.toString());
        }
    }

    public synchronized void addProperty(String propertyName, String value) {
        if (isSigned()) {
            throwSignedException();
        }

        if (value == null) {
            props.remove(prependString + propertyName);
        } else {
            props.setProperty(prependString + propertyName, value);
        }
    }

    public synchronized void addHardwareAddress(byte[] networkInterface) {
        addHardwareAddress(convertInterfaceBytesToString(networkInterface));
    }

    public synchronized void addHardwareAddress(String address) {
        if (isSigned()) {
            throwSignedException();
        }

        if (getLicenseVersion() < 2) {
            throw new UnsupportedOperationException("This license version does not support hardware locking");
        }

        address = address.toLowerCase();
        address = address.replaceAll("[^a-f0-9]", "").trim();

        // Make sure we don't have a duplicate
        String addresses = props.getProperty("hardwareAddresses");
        if (addresses == null) {
            addresses = "";
        }

        if (!addresses.contains(address)) {
            addresses = addresses + " " + address;
        }

        props.setProperty("hardwareAddresses", addresses.trim());
    }

    public synchronized void removeHardwareAddress(byte[] networkInterface) {
        removeHardwareAddress(convertInterfaceBytesToString(networkInterface));
    }

    public synchronized void removeHardwareAddress(String address) {
        if (isSigned()) {
            throwSignedException();
        }

        address = address.toLowerCase();
        address = address.replaceAll("[^a-f0-9]", "").trim();

        Set<String> addresses = this.extractHardwareAddresses(props.getProperty("hardwareAddresses", ""));
        for (Iterator<String> iter = addresses.iterator(); iter.hasNext();) {
            if (iter.next().equals(address)) {
                iter.remove();
            }
        }

        String values = combineHardwareAddresses(addresses);
        props.setProperty("hardwareAddresses", values);
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (obj instanceof LicenseImpl) {
            LicenseImpl other = (LicenseImpl) obj;
            Properties properties = other.props;
            if (props.size() != properties.size())
                return false;
            for (String key : propertyNames(props)) {
                String value = props.getProperty(key);
                if (!value.equals(properties.getProperty(key))) {
                    return false;
                }
            }

            return true;

        } else
            return false;
    }

    @Override
    public String toString() {
        StringBuilder propString = new StringBuilder("[");

        for (String key : propertyNames(props)) {
            propString.append(key + "=" + props.get(key) + ",");
        }

        propString.append("]");

        return propString.toString();
    }

    @Override
    public int hashCode() {
        int hash = 11;
        for (String key : propertyNames(props)) {
            hash = hash + key.hashCode() + props.getProperty(key).hashCode();
        }
        return hash;
    }

    public License cloneLicense() {
        LicenseImpl l = new LicenseImpl(props);

        // Remove the signature, if any
        l.props.remove("signature");

        // Set creation date to now
        l.setCreationDate(new Date());

        return l;
    }

    /**
     * Get a copy of the underlying Properties object that makes up this license.
     */
    public synchronized Properties getRawProperties() {
        Properties p = new Properties();

        for (String key : propertyNames(props)) {
            p.setProperty(key, props.getProperty(key));
        }

        return p;
    }
}