/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * @(#)LicenseManager.java	1.22 06/28/07
 */ 

package com.sun.messaging.jmq.jmsserver.license;

import com.sun.messaging.jmq.jmsserver.util.BrokerException;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.resources.*;
import com.sun.messaging.jmq.util.log.Logger;

import java.io.*;
import java.util.*;
import java.security.*;
import java.lang.reflect.*;

/**
 * Represents a broker license.
 */
public class LicenseManager {
    private static BrokerResources br = Globals.getBrokerResources();
    private static Logger logger = Globals.getLogger();

    /** Cache the current license object */
    private LicenseBase currentLicense = null;
    private LicenseBase base = null;

    public LicenseManager() {}

    /**
     * This method loads the appropriate license.
     */
    public LicenseBase getLicense(String filestr) throws BrokerException {
        if (currentLicense != null)
            return currentLicense;
    // Step 1 : Choose the best LicenseBase implementation class.
    base = getLicenseBase();
    if (base == null) {
            // No license base class in the package!
            // This should never happen. No I18N necessary.
            throw new BrokerException(
                "Could not find license base class." +
                "This is a broker packaging error.");
        }

        // Step 2 : Load the specified license file. If no license
        // file is specified, load all license files and select the
        // one that is most generous.
        FileLicense fl = null;
        try {
            fl = loadLicenseFile(filestr);
       }
        catch (BrokerException e) {
           //BUG FIX 5025985
           throw e;
        }

        // Step 3 : If a license file was loaded from the disk
        // it must be re-written in order to handle the license
        // expiration correctly.
        if (fl != null) {
            try {
                fl.rewriteLicense();
            } catch (IOException e) {
                throw new BrokerException(
                    br.getString(br.E_BAD_LICENSE_DATA), e);
            }
        }

        // Step 4 : Superimpose the license file contents on the
        // base class chosen in step 1.
        if (fl != null) {
            if (filestr != null ||
                fl.getPrecedence() > base.getPrecedence()) {
                base.superimpose(fl.getProperties());

                // log where the license comes from
                logger.log(Logger.DEBUG, br.getString(br.I_LICENSE_FILE,
                        fl.getLicenseFile()));
            }
        }
        else if (base.isLicenseFileRequired()) {
            // If there is no license file, and the base class
            // requires a license file, throw an exception.
            throw new BrokerException(
                br.getString(br.E_NO_VALID_LICENSE));
        }

        currentLicense = base;
        return base;
    }

    //
    // Following constants are used by getLicenseBase() method.
    //
    private static final String LICENSE_BASE_PKG_PREFIX =
        "com.sun.messaging.jmq.jmsserver.license.";
    private static final String STANDALONE_LICENSE_BASE =
        "StandaloneLicense";
    private static final String LICENSE_BASE_CLASS_PREFIX = "L";
    private static final int MAX_LICENSE_BASE_CLASSES = 16;

    /**
     * Load the most generous license base class.
     */
    private LicenseBase getLicenseBase() throws BrokerException {
        Class cl = null;
        
        // First try to load the StandaloneLicense class.
        String cname = LICENSE_BASE_PKG_PREFIX + STANDALONE_LICENSE_BASE;
        try {
            cl = Class.forName(cname);
            return newInstance(cl);
        }
        catch (Exception e) { /* Ignore */ }

        // If StandaloneLicense is not found, search for other
        // license classes.
        ArrayList list = new ArrayList();
        for (int i = 0; i < MAX_LICENSE_BASE_CLASSES; i++) {
            cname = LICENSE_BASE_PKG_PREFIX +
                LICENSE_BASE_CLASS_PREFIX +
                i;

            try {
               cl = Class.forName(cname);
               list.add(newInstance(cl));
           }
           catch (Exception e) {
           /* Ignore */ }
        }

        LicenseBase[] licenses = (LicenseBase[])list.toArray(new LicenseBase[0]);
        return selectBestLicense(licenses);
    }

    private LicenseBase newInstance(Class cl) throws Exception {
        Constructor co = cl.getConstructor(null);
        return (LicenseBase) co.newInstance(null);
    }

    /**
     * Select the most generous license.
     */
    private LicenseBase selectBestLicense(LicenseBase[] licenses)
        throws BrokerException {
        if (licenses == null || licenses.length == 0) {
            throw new BrokerException(
                br.getString(br.E_NO_VALID_LICENSE));
        }

        // pick a license to use:
        // - pick the one with the highest precedence value.
        // - a non-expiring license has higher priority, i.e., a
        //   license with no expiration date but lower precedence
        //   will be picked instead of a license with an
        //   expiration date but higher precedence.

        LicenseBase lb = licenses[0];
        
        for (int i = 1; i < licenses.length; i++) {
            boolean current = lb.willExpire();
            boolean next = licenses[i].willExpire();
            // If the current license expires, and the next one
            // does not, then use the next one.
            if (current && !next) {
                lb = licenses[i];
            }

            // If the current and next license have same
            // expiration condition, then use the one with higher
            // precedence.
            if (current == next &&
                lb.getPrecedence() <
                    licenses[i].getPrecedence()) {
                lb = licenses[i];
            }
        }

        return lb;
    }

    //
    // Following constants are used by the loadLicenseFile() method.
    //
    private static final String LICENSE_DIR =
            Globals.JMQ_ETC_HOME 
                + File.separator + "lic" + File.separator;
    private static final String TRIAL_LICENSE_DIR = Globals.JMQ_VAR_HOME + File.separator + "lic" + File.separator; 

    private static final String LICENSE_FILE_PREFIX = "imqbroker";
    private static final String LICENSE_FILE_SUBFIX = ".lic";

    // file name filter for licence files (imqbroker*.lic)
    private static FilenameFilter licFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return (name.startsWith(LICENSE_FILE_PREFIX)
                && name.endsWith(LICENSE_FILE_SUBFIX));
        }
    };

    private FileLicense loadLicenseFile(String filestr)
        throws BrokerException {
        
        File dir = new File(LICENSE_DIR);
        File trialdir = new File(TRIAL_LICENSE_DIR);
        FileLicense fl = null;
        LicenseBase lbase = null;
        if (filestr != null) {
            //Bug 6157397
            String strname = base.getProperty(LicenseBase.PROP_LICENSE_TYPE);
            if(filestr.equalsIgnoreCase(strname))
             return null;
                     
            // try to load the specified file
            String licenseFile = LICENSE_FILE_PREFIX + filestr + LICENSE_FILE_SUBFIX;
            File file = null;
            //For bug fix 4995767
            if(filestr.equalsIgnoreCase("try"))
               file = new File(trialdir, licenseFile);
            else 
               file = new File(dir,licenseFile); 
            
            //BUG FIX 5025985
            if(!file.exists())
              throw new BrokerException(br.getString(br.E_LOAD_LICENSE,filestr));  
            //BUG FIX 5057293
            else if(!file.canRead())
              throw new BrokerException(br.getString(br.E_LICENSE_FILE_NOT_READABLE,licenseFile));  
            
            fl = new FileLicense(file);
        }
        else {
            FileLicense[] licenses = loadFileLicenses();
            if(loadFileLicenses().length !=0)
            {    
              fl = (FileLicense) selectBestLicense(licenses);
              File f = fl.getLicenseFile();
              if(!f.canRead())
                throw new BrokerException(br.getString(br.E_LICENSE_FILE_NOT_READABLE,f.toString())); 
              
              //BUG FIX 4996564
              LicenseBase lb = (LicenseBase)fl;
              Properties prop = lb.props;
              String datestring = prop.getProperty(lb.PROP_DATE_STRING);
              if(datestring.startsWith(lb.TRY_STRING)){
               //BUG FIX 5054057
               return null;
              }
            }
        }

        return fl;
    }

    /**
     * Load all valid licenses from from the license directory.
     * @return an array of License objects
     */
    public static FileLicense[] loadFileLicenses() {
        File dir = new File(LICENSE_DIR);
        File trialdir = new File(TRIAL_LICENSE_DIR);
        String[] names = dir.list(licFilter);

        ArrayList list = new ArrayList();
        for (int i = 0; names != null && i < names.length; i++) {
            try {
                File file = new File(dir, names[i]);

                FileLicense lic = new FileLicense(file);
                list.add(lic);
            } catch (BrokerException e) {
                // ignore all license files with bad format or that
                // are expired log it and continue don't print out any
                // info in production code
                /*
                logger.log(logger.DEBUG, "loading " + names[i] + ", got: "
                    + e.toString());
                */
            }
        }
        /** For Bug Fix 4995767 
            The trial license is present in a new location now
            var_home/lic
        */
        String[] trialnames = trialdir.list(licFilter);
        for(int j=0;trialnames!=null && j < trialnames.length; j++)
        {
          try{
             File file = new File(trialdir, trialnames[j]);
             FileLicense lic = new FileLicense(file);
             list.add(lic);
             } catch(BrokerException e) {
             }
        } 

        return (FileLicense[])list.toArray(new FileLicense[0]);
    }

    public static LicenseBase[] loadLicenses() {
        // return all licenses
        LicenseManager lm = new LicenseManager();
        LicenseBase lb = null;
        try {
            lb = lm.getLicenseBase();
        } catch (Exception ex) {
// log some error here
        }
        LicenseBase fl[] =  loadFileLicenses();

        int length = fl.length + (lb == null? 0 : 1);
        LicenseBase rl[] = new LicenseBase[fl.length + 1];
        for (int i= 0; i < fl.length ; i ++)
           rl[i]=fl[i];
        if (lb != null)
           rl[fl.length] = lb;
        return rl;
    }
}

/*
 * EOF
 */
