/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (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/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package com.ericsson.ssa.dd;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.jar.JarFile;


public class SipApplicationValidator {
    /**
     * This constant specifies the sip archive classes base directory.
     */
    public static final String SIP_ARCHIVE_CLASSES_BASE_DIR = "WEB-INF/classes";

    /**
     * Private constructor since it is only a utility class
     */
    private SipApplicationValidator() {
    }

    /**
     * Validates the presence of the servlet class in the archive according to
     * what is specified in the deployment descriptor. Also, it validates the
     * presence of the icons in the archive and the relations between the
     * security role reference, security role constraints with the security
     * roles.
     *
     * @param moduleJarFile
     *        The archive file
     * @param sipApplication
     *        The corresponding sip application
     * @throws EASModuleDeployException
     */
    public static void validateSipDeploymentDescriptor(JarFile moduleJarFile,
        SipApplication sipApplication) throws ModuleDeployException {
        validateServletClass(moduleJarFile, sipApplication);
        validateSecurityConstraintRoles(sipApplication);
        validateSecurityRoleReferences(sipApplication);
        validateIcons(moduleJarFile, sipApplication);
    }

    /**
     * Validates the presence of the role-link of the security-role-ref element
     * in the sercurity roles of the sip application
     *
     * @param sipApplication
     *        The corresponding sip application
     * @throws EASModuleDeployException
     */
    private static void validateSecurityRoleReferences(
        SipApplication sipApplication) throws ModuleDeployException {
        StringBuffer missingRoles = new StringBuffer();
        Map<String, SecurityRole> securityRoles = sipApplication.getSecurityRoles();
        Map<String, Servlet> servlets = sipApplication.getServlets();
        Iterator<String> servletIterator = servlets.keySet().iterator();

        while (servletIterator.hasNext()) {
            Object key = servletIterator.next();
            Servlet servlet = servlets.get(key);
            Collection<SecurityRoleReference> securityRoleReferences = servlet.getSecurityRoleReferences();
            Iterator<SecurityRoleReference> roleIterator = securityRoleReferences.iterator();

            while (roleIterator.hasNext()) {
                String role = ((SecurityRoleReference) roleIterator.next()).getRoleLink();

                if ((securityRoles.get(role) == null) && !role.equals("*")) {
                    missingRoles.append(role);
                    missingRoles.append("\n");
                }
            }
        }

        if (missingRoles.toString().trim().length() > 0) {
            throw new ModuleDeployException("The following " +
                "security role references are undefined " +
                "in the security roles:\n" + missingRoles);
        }
    }

    /**
     * Validates the presence of the role-name
     * (auth-constraint/security-constraint) element in the sercurity roles of
     * the sip application
     *
     * @param sipApplication
     *        The corresponding sip application
     * @throws EASModuleDeployException
     */
    private static void validateSecurityConstraintRoles(
        SipApplication sipApplication) throws ModuleDeployException {
        StringBuffer missingRoles = new StringBuffer();
        Map<String, SecurityRole> securityRoles = sipApplication.getSecurityRoles();
        Collection<SecurityConstraint> securityConstraints = sipApplication.getSecurityConstraints();
        Iterator<SecurityConstraint> securityIterator = securityConstraints.iterator();

        while (securityIterator.hasNext()) {
            Collection<String> roleNames = (securityIterator.next()).getAuthorizationConstraintRoleNames();
            Iterator<String> roleIterator = roleNames.iterator();

            while (roleIterator.hasNext()) {
                String role = roleIterator.next();

                if ((securityRoles.get(role) == null) && !role.equals("*")) {
                    missingRoles.append(role);
                    missingRoles.append("\n");
                }
            }
        }

        if (missingRoles.toString().trim().length() > 0) {
            throw new ModuleDeployException("The following " +
                "security constraint role names are undefined " +
                "in the security roles:\n" + missingRoles);
        }
    }

    /**
     * Validates the presence of the servlet class in the archive according to
     * what is specified in the deployment descriptor.
     *
     * @param jarFile
     *        The archive
     * @param sipApplication
     *        The corresponding sip application
     * @throws EASModuleDeployException
     */
    private static void validateServletClass(JarFile jarFile,
        SipApplication sipApplication) throws ModuleDeployException {
        Collection<String> servletClasses = new HashSet<String>();
        Map<String, Servlet> servlets = sipApplication.getServlets();
        Iterator<String> iterator = servlets.keySet().iterator();

        while (iterator.hasNext()) {
            Object key = iterator.next();
            Servlet servlet = servlets.get(key);
            String servletClass = servlet.getServletClass();
            StringBuffer servletPath = new StringBuffer(SIP_ARCHIVE_CLASSES_BASE_DIR);
            // The separator in a jar file is '/' and does not depend
            // on the operating system (do not use File.separatorChar)
            servletPath.append("/");
            servletPath.append(servletClass.replace('.', '/'));
            servletPath.append(".class");
            servletClasses.add(servletPath.toString());
        }

        String names = getNameOfFilesNotInJar(jarFile, servletClasses);

        if (names.trim().length() > 0) {
            throw new ModuleDeployException("The following servlet " +
                "files are missing from the archive:\n" + names);
        }
    }

    /**
     * Validates the presence of the icons in the archive
     *
     * @param jarFile
     *        The archive
     * @param sipApplication
     *        The corresponding sip application
     * @throws EASModuleDeployException
     */
    private static void validateIcons(JarFile jarFile,
        SipApplication sipApplication) throws ModuleDeployException {
        Collection<String> icons = new HashSet<String>();
        icons.add(sipApplication.getSmallIcon());
        icons.add(sipApplication.getLargeIcon());

        Map<String, Servlet> servlets = sipApplication.getServlets();
        Iterator<String> iterator = servlets.keySet().iterator();

        while (iterator.hasNext()) {
            Object key = iterator.next();
            Servlet servlet = servlets.get(key);
            icons.add(servlet.getSmallIcon());
            icons.add(servlet.getLargeIcon());
        }

        String names = getNameOfFilesNotInJar(jarFile, icons);

        if (names.trim().length() > 0) {
            throw new ModuleDeployException("The following icon " +
                "files are missing from the archive:\n" + names);
        }
    }

    /**
     * Returns as a string the names of the fileNames specified in the collection
     * that are not contained in the archive
     *
     * @param jarFile
     *        The archive
     * @param fileNames
     *        The collection of file names to check
     * @return
     */
    private static String getNameOfFilesNotInJar(JarFile jarFile,
        Collection<String> fileNames) {
        StringBuffer missingNames = new StringBuffer();
        Iterator<String> iterator = fileNames.iterator();

        while (iterator.hasNext()) {
            String name = iterator.next();

            if (name.charAt(0) == '/') {
                // Remove the first character since the getJarEntry method
                // checks for the file name (starting at root) without the '/'
                name = name.substring(1, name.length());
            }

            if ((name != null) && (name.trim().length() != 0) &&
                    (jarFile.getJarEntry(name) == null)) {
                missingNames.append(name);
                missingNames.append("\n");
            }
        }

        return missingNames.toString();
    }
}
