DDTClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for DDTClassLoader.java

Source

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * Created with IntelliJ IDEA.
 * User: Avraham (Bey) Melamed
 * Date: 07/14/14
 * Time: 9:22 PM
 * Selenium Based Automation
 *
 * =============================================================================
 * Copyright 2014 Avraham (Bey) Melamed.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 *
 * Description - Provides class loading from a project / folder other than the main project (this one.)
 * This provides the ability to develop inline test string provider classes apart from the main project and load these from their own project's test-classes folder (or another location.)
 *
 * History
 * When         |Who      |What
 * =============|=========|====================================
 * 07/14/14     |Bey      |Initial Version
 * =============|=========|====================================
 */

public class DDTClassLoader extends ClassLoader {

    private static String defaultLoadFolder;
    private static String loadableClassNames;

    private String errors;

    public static void setDefaultLoadFolder(String value) {
        defaultLoadFolder = value;
    }

    public static String getDefaultLoadFolder() {
        if (isBlank(defaultLoadFolder))
            setDefaultLoadFolder("c:/javaddtext/target/test-classes/");
        return defaultLoadFolder;
    }

    public DDTClassLoader(ClassLoader parent) {
        super(parent);
    }

    private void setErrors(String value) {
        errors = value;
    }

    public String getErrors() {
        if (isBlank(errors))
            setErrors("");
        return errors;
    }

    public static void setLoadableClassNames(String value) {
        loadableClassNames = value;
    }

    public static String getLoadableClassNames() {
        if (isBlank(loadableClassNames))
            setLoadableClassNames(findLoadableClassNames());
        return loadableClassNames;
    }

    /**
     * Get the loadable classes from the default class load folder
     * The folder is a folder where java .class files exist
     *
     * @return a String of class files delimited by "," and enclosed in "," (or an empty string) - if any, the base classes and other invalid ones for the purpose are ignored.
     */
    public static String findLoadableClassNames() {
        String loadFromFolder = getDefaultLoadFolder();
        String result = "";
        String excludedClassNames = ",TestStringsProvider.class,InlineTestStringsProvider.class,DDTClassLoader.class,TestStringsProviderSpecs.class,"
                .toLowerCase();
        File loadFromFolderFile = new File(loadFromFolder);
        if (loadFromFolderFile.exists()) {
            File[] fileList = loadFromFolderFile.listFiles();
            int size = fileList.length;
            if (size > 0) {
                for (int i = 0; i < size; i++) {
                    File theFile = fileList[i];
                    String theName = theFile.getName();
                    if (theName.toLowerCase().endsWith(".class")) {
                        if (!excludedClassNames.contains(theName.toLowerCase())) {
                            result += theName + ",";
                        }
                    }
                }
                if (!isBlank(result)) {
                    result = "," + result;
                }
            }
        }
        return result;
    }

    @Override
    /**
     * This is the overriding method used to load any desired external class
     * The logic of using this class loader or the parent has to do with the recursive nature of implementing class loader.
     * This particular class loader should be applied only to external classes whose parent classes exist in this project (not in the project of the external class)
     * Thus, parent classe(s) are loaded by the parent loader that CAN be recursive in this project
     * @return Class
     */
    public Class loadClass(String name) throws ClassNotFoundException {
        // Check the class name against a list of external classes
        if (getLoadableClassNames().contains(name)) {
            // Use external class loader (see below)
            return getClass(name);
        }
        // Use the parent's class loader.
        return super.loadClass(name);
    }

    /**
     *
     * @param name  - Full path specs of class name to load
     * @return      - A byte buffer representing the .class representation of the name.
     * @throws IOException
     */
    private byte[] loadClassFileData(String name) throws IOException {

        // Ensure proper separator and name extension for the class name to load
        String tmpName = name;
        if (tmpName.toLowerCase().endsWith(".class"))
            tmpName = tmpName.substring(0, tmpName.length() - 6);
        tmpName = tmpName.replace('.', '/') + ".class";

        // Convert the file name to a URL
        URL myUrl;
        try {
            if (name.toLowerCase().startsWith("http:")) {
                tmpName = DDTSettings.asValidOSPath(tmpName, false);
                myUrl = new URL(tmpName);
            } else
                myUrl = new URL("file:" + tmpName);

            URLConnection connection = myUrl.openConnection();
            InputStream input = connection.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream(8192);
            int data = input.read();

            while (data != -1) {
                buffer.write(data);
                data = input.read();
            }

            input.close();

            byte[] classData = buffer.toByteArray();
            return classData;
        } catch (IOException e) {
            setErrors(e.getMessage().toString());
        } catch (Exception e) {
            setErrors(e.getMessage().toString());
        }
        return null;
    }

    /**
     * Loads the class from the file system. The class file should be located in
     * the file system. The name should be relative to get the file location
     *
     * @param name
     *            Fully Classified name of class, for example com.dynabytes.TestStringsGenerator
     */
    private Class getClass(String name) throws ClassNotFoundException {
        String file = getDefaultLoadFolder() + name.replace('.', File.separatorChar) + ".class";
        byte[] b = null;
        try {
            // This loads the byte code data from the physical file
            b = loadClassFileData(file);
            // defineClass is inherited from the ClassLoader class - it converts byte array into a Class.
            // defineClass is Final so we cannot override it
            Class c = defineClass(name, b, 0, b.length);
            resolveClass(c);
            return c;
        } catch (IOException e) {
            setErrors(e.getMessage().toString());
            return null;
        }
    }
}