/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Axis" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.axis.client;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintStream;

/**
 * Client side equivalent of happyaxis
 */
public class HappyClient {

    /**
     * test for a class existing
     * @param classname
     * @return class iff present
     */
    Class classExists(String classname) {
        try {
            return Class.forName(classname);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    /**
     * test for resource on the classpath
     * @param resource
     * @return true iff present
     */
    boolean resourceExists(String resource) {
        boolean found;
        InputStream instream = this.getClass().getResourceAsStream(resource);
        found = instream != null;
        if (instream != null) {
            try {
                instream.close();
            } catch (IOException e) {
            }
        }
        return found;
    }

    /**
     * probe for a class, print an error message is missing
     * @param out stream to print stuff
     * @param category text like "warning" or "error"
     * @param classname class to look for
     * @param jarFile where this class comes from
     * @param errorText extra error text
     * @param homePage where to d/l the library
     * @return the number of missing classes
     * @throws java.io.IOException
     */
    int probeClass(PrintStream out,
                   String category,
                   String classname,
                   String jarFile,
                   String description,
                   String errorText,
                   String homePage) throws IOException {
        String url = "";
        if (homePage != null) {
            url = "\n  fetch this from " + homePage+"\n";
        }
        try {
            Class clazz = classExists(classname);
            if (clazz == null) {
                out.println(category + ": could not find class " + classname
                        + " from file " + jarFile
                        + "\n  *" + errorText +"*"
                        + url);
                return 1;
            } else {
                String location = getLocation(out, clazz);
                if (location == null) {
                    out.println("Found " + description + " (" + classname + ")");
                } else {
                    out.println("Found " + description + " (" + classname + ") \n  at " + location);
                }
                return 0;
            }
        } catch (NoClassDefFoundError ncdfe) {
            out.println(category + ": could not find a dependency"
                    + " of class " + classname
                    + " from file " + jarFile
                    + "\n" + errorText
                    + url
                    + "\nThe root cause was: " + ncdfe.getMessage()
                    + "\nThis can happen e.g. if " + classname + " is in"
                    + " the 'common' classpath, but a dependency like "
                    + " activation.jar is only in the webapp classpath.\n");
            return 1;
        }
    }

    /**
     * get the location of a class
     * @param out
     * @param clazz
     * @return the jar file or path where a class was found
     */

    String getLocation(PrintStream out,
                       Class clazz) {
        try {
            java.net.URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
            String location = url.toString();
            if (location.startsWith("jar")) {
                url = ((java.net.JarURLConnection) url.openConnection()).getJarFileURL();
                location = url.toString();
            }

            if (location.startsWith("file")) {
                java.io.File file = new java.io.File(url.getFile());
                return file.getAbsolutePath();
            } else {
                return url.toString();
            }
        } catch (Throwable t) {
        }
        return "an unknown location";
    }

    /**
     * a class we need if a class is missing
     * @param out stream to print stuff
     * @param classname class to look for
     * @param jarFile where this class comes from
     * @param errorText extra error text
     * @param homePage where to d/l the library
     * @throws java.io.IOException when needed
     * @return the number of missing libraries (0 or 1)
     */
    int needClass(PrintStream out,
                  String classname,
                  String jarFile,
                  String description,
                  String errorText,
                  String homePage) throws IOException {
        return probeClass(out,
                "Error",
                classname,
                jarFile,
                description,
                errorText,
                homePage);
    }

    /**
     * print warning message if a class is missing
     * @param out stream to print stuff
     * @param classname class to look for
     * @param jarFile where this class comes from
     * @param errorText extra error text
     * @param homePage where to d/l the library
     * @throws java.io.IOException when needed
     * @return the number of missing libraries (0 or 1)
     */
    int wantClass(PrintStream out,
                  String classname,
                  String jarFile,
                  String description,
                  String errorText,
                  String homePage) throws IOException {
        return probeClass(out,
                "Warning",
                classname,
                jarFile,
                description,
                errorText,
                homePage);
    }

    /**
     * probe for a resource existing,
     * @param out
     * @param resource
     * @param errorText
     * @throws Exception
     */
    int wantResource(PrintStream out,
                     String resource,
                     String errorText) throws Exception {
        if (!resourceExists(resource)) {
            out.println("Warning: could not find resource " + resource
                    + "\n"
                    + errorText);
            return 0;
        } else {
            out.println("found " + resource);
            return 1;
        }
    }


    /**
     * what parser are we using.
     * @return the classname of the parser
     */
    private String getParserName() {
        SAXParser saxParser = getSAXParser();
        if (saxParser == null) {
            return "Could not create an XML Parser";
        }

        // check to what is in the classname
        String saxParserName = saxParser.getClass().getName();
        return saxParserName;
    }

    /**
     * Create a JAXP SAXParser
     * @return parser or null for trouble
     */
    private SAXParser getSAXParser() {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        if (saxParserFactory == null) {
            return null;
        }
        SAXParser saxParser = null;
        try {
            saxParser = saxParserFactory.newSAXParser();
        } catch (Exception e) {
        }
        return saxParser;
    }

    /**
     * get the location of the parser
     * @return path or null for trouble in tracking it down
     */

    private String getParserLocation(PrintStream out) {
        SAXParser saxParser = getSAXParser();
        if (saxParser == null) {
            return null;
        }
        String location = getLocation(out, saxParser.getClass());
        return location;
    }

    /**
     * calculate the java version number by probing for classes;
     * this tactic works across many jvm implementations.
     * @return JRE version as 10,11,12,13,14,...
     */
    public int getJavaVersionNumber() {
        // Determine the Java version by looking at available classes
        // java.lang.CharSequence was introduced in JDK 1.4
        // java.lang.StrictMath was introduced in JDK 1.3
        // java.lang.ThreadLocal was introduced in JDK 1.2
        // java.lang.Void was introduced in JDK 1.1
        // Count up version until a NoClassDefFoundError ends the try
        int javaVersionNumber=10;
        try {
            Class.forName("java.lang.Void");
            javaVersionNumber++;
            Class.forName("java.lang.ThreadLocal");
            javaVersionNumber++;
            Class.forName("java.lang.StrictMath");
            javaVersionNumber++;
            Class.forName("java.lang.CharSequence");
            javaVersionNumber++;
        } catch (Throwable t) {
            // swallow as we've hit the max class version that
            // we have
        }
        return javaVersionNumber;
    }

    /**
     *  Audit the client, print out status
     * @param out where to print messages
     * @param warningsAsErrors should any warning result in failure?
     * @return true if we are happy
     * @throws IOException
     */
    public boolean  verifyClientIsHappy(PrintStream out, boolean warningsAsErrors) throws IOException {
        int needed = 0,wanted = 0;

        out.println("Verifying Axis client configuration");
        out.println();
        out.println("Needed components");
        out.println("=================");

        /**
         * the essentials, without these Axis is not going to work
         */
        needed = needClass(out, "javax.xml.soap.SOAPMessage",
                "saaj.jar",
                "SAAJ API",
                "Axis will not work",
                "http://xml.apache.org/axis/");

        needed += needClass(out, "javax.xml.rpc.Service",
                "jaxrpc.jar",
                "JAX-RPC API",
                "Axis will not work",
                "http://xml.apache.org/axis/");

        needed += needClass(out, "org.apache.commons.discovery.Resource",
                "commons-discovery.jar",
                "Jakarta-Commons Discovery",
                "Axis will not work",
                "http://jakarta.apache.org/commons/discovery.html");

        needed += needClass(out, "org.apache.commons.logging.Log",
                "commons-logging.jar",
                "Jakarta-Commons Logging",
                "Axis will not work",
                "http://jakarta.apache.org/commons/logging.html");

        needed += needClass(out, "org.apache.log4j.Layout",
                "log4j-1.2.4.jar",
                "Log4j",
                "Axis may not work",
                "http://jakarta.apache.org/log4j");

        //should we search for a javax.wsdl file here, to hint that it needs
        //to go into an approved directory? because we dont seem to need to do that.
        needed += needClass(out, "com.ibm.wsdl.factory.WSDLFactoryImpl",
                "wsdl4j.jar",
                "IBM's WSDL4Java",
                "Axis will not work",
                null);

        needed += needClass(out, "javax.xml.parsers.SAXParserFactory",
                "xerces.jar",
                "JAXP implementation",
                "Axis will not work",
                "http://xml.apache.org/xerces-j/");


        out.println();
        out.println("Optional Components");
        out.println("===================");

        wanted += wantClass(out, "javax.mail.internet.MimeMessage",
                "mail.jar",
                "Mail API",
                "Attachments will not work",
                "http://java.sun.com/products/javamail/");

        wanted += wantClass(out, "javax.activation.DataHandler",
                "activation.jar",
                "Activation API",
                "Attachments will not work",
                "http://java.sun.com/products/javabeans/glasgow/jaf.html");

        wanted += wantClass(out, "org.apache.xml.security.Init",
                "xmlsec.jar",
                "XML Security API",
                "XML Security is not supported",
                "http://xml.apache.org/security/");

        wanted += wantClass(out, "javax.net.ssl.SSLSocketFactory",
                "jsse.jar or java1.4+ runtime",
                "Java Secure Socket Extension",
                "https is not supported",
                "http://java.sun.com/products/jsse/");


        /*
        * resources on the classpath path
        */
        int warningMessages=0;

        String xmlParser = getParserName();
        String xmlParserLocation = getParserLocation(out);
        out.println("\nXML parser :" + xmlParser + "\n  from " + xmlParserLocation);
        if (xmlParser.indexOf("xerces") <= 0) {
            warningMessages++;
            out.println();
            out.println("Axis recommends Xerces 2 "
                    + "(http://xml.apache.org/xerces2-j) as the XML Parser");
        }
        if (getJavaVersionNumber() < 13) {
            warningMessages++;
            out.println();
            out.println("Warning: Axis does not support this version of Java. \n"
                    + "  Use at your own risk, and do not file bug reports if something fails");
        }
        /* add more libraries here */

        //print the summary information
        boolean happy;
        out.println();
        out.println("Summary");
        out.println("=======");
        out.println();

        //is everythng we need here
        if (needed == 0) {
            //yes, be happy
            out.println("The core axis libraries are present.");
            happy=true;
        } else {
            happy=false;
            //no, be very unhappy
            out.println(""
                    + needed
                    + " core axis librar"
                    + (needed == 1 ? "y is" : "ies are")
                    + " missing");
        }
        //now look at wanted stuff
        if (wanted > 0) {
            out.println("\n"
                    + wanted
                    + " optional axis librar"
                    +(wanted==1?"y is":"ies are")
                    +" missing");
            if (warningsAsErrors) {
                happy = false;
            }
        } else {
            out.println("The optional components are present.");
        }
        if (warningMessages > 0) {
            out.println("\n"
                    + warningMessages
                    + " warning message(s) were printed");
            if (warningsAsErrors) {
                happy = false;
            }
        }

        return happy;
    }

    /**
     * public happiness test
     */
    public static void main(String args[]) {
        HappyClient happy=new HappyClient();
        boolean isHappy;
        try {
            isHappy = happy.verifyClientIsHappy(System.out, false);
        } catch (IOException e) {
            e.printStackTrace();
            isHappy=false;
        }
        System.exit(isHappy?0:-1);
    }
}
