All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.centurylink.mdw.java.CompiledJavaCache Maven / Gradle / Ivy

There is a newer version: 6.1.39
Show newest version
/*
 * Copyright (C) 2017 CenturyLink, Inc.
 *
 * 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.
 */
package com.centurylink.mdw.java;

import com.centurylink.mdw.annotations.Monitor;
import com.centurylink.mdw.annotations.RegisteredService;
import com.centurylink.mdw.annotations.ScheduledJob;
import com.centurylink.mdw.app.ApplicationContext;
import com.centurylink.mdw.app.Compatibility;
import com.centurylink.mdw.app.Compatibility.SubstitutionResult;
import com.centurylink.mdw.cache.CachingException;
import com.centurylink.mdw.cache.ExcludableCache;
import com.centurylink.mdw.cache.PreloadableCache;
import com.centurylink.mdw.cache.impl.AssetCache;
import com.centurylink.mdw.cache.impl.PackageCache;
import com.centurylink.mdw.cloud.CloudClasspath;
import com.centurylink.mdw.common.service.DynamicJavaServiceRegistry;
import com.centurylink.mdw.common.service.JsonService;
import com.centurylink.mdw.common.service.XmlService;
import com.centurylink.mdw.config.PropertyManager;
import com.centurylink.mdw.constant.PropertyNames;
import com.centurylink.mdw.dataaccess.DataAccessException;
import com.centurylink.mdw.model.asset.Asset;
import com.centurylink.mdw.model.workflow.Package;
import com.centurylink.mdw.monitor.MonitorRegistry;
import com.centurylink.mdw.util.file.Packages;
import com.centurylink.mdw.util.log.LoggerUtil;
import com.centurylink.mdw.util.log.StandardLogger;

import javax.tools.*;
import javax.tools.JavaCompiler.CompilationTask;
import javax.ws.rs.Path;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Compiles and caches java asset classes, and provides a class-loading mechanism to allow these assets
 * to reference Jar classes as well.
 */
public class CompiledJavaCache implements PreloadableCache, ExcludableCache {

    public final static boolean classicLoading = PropertyManager.getBooleanProperty(PropertyNames.MDW_CONTAINER_CLASSIC_CLASSLOADING, false);
    private static StandardLogger logger = LoggerUtil.getStandardLogger();

    private static Map> compiledCache = new ConcurrentHashMap<>();
    private static Map classesNotFound = new ConcurrentHashMap<>();

    public CompiledJavaCache() {

    }

    private static String[] preCompiled;

    public void initialize(Map params) {
        if (params != null) {
            String preCompString = params.get("PreCompiled");
            if (preCompString != null && preCompString.trim().length() > 0) {
                List preCompList = new ArrayList();
                preCompiled = preCompString.split("\\\n");
                for (int i = 0; i < preCompiled.length; i++) {
                    String preLoad = preCompiled[i].trim();
                    if (!preLoad.isEmpty())
                      preCompList.add(preLoad);
                }
                preCompiled = preCompList.toArray(new String[]{});
            }
        }
    }

    public String getFormat() {
        return Asset.JAVA;
    }

    /**
     * To load all the dynamic java Registered services classes
     */
    private void loadDynamicJavaRegisteredServices() {
        Map> packagedJava = new HashMap<>();
        try {
            for (Asset javaAsset : AssetCache.getAssets(Asset.JAVA)) {
                // process RegisteredService-annotated classes
                if (javaAsset.getStringContent().indexOf("@RegisteredService") > 0 ||
                        javaAsset.getStringContent().indexOf("@Monitor") > 0 ||
                        javaAsset.getStringContent().indexOf("@Path") > 0 ||
                        javaAsset.getStringContent().indexOf("@Api") > 0 ||
                        javaAsset.getStringContent().indexOf("@ScheduledJob") > 0) {
                    String className = JavaNaming.getValidClassName(javaAsset.getName());
                    Package javaAssetPackage = PackageCache.getAssetPackage(javaAsset.getId());
                    if (javaAssetPackage == null) {
                        logger.severe("Omitting unpackaged Registered Service from compilation: " + javaAsset.getLabel());
                    }
                    else {
                        String qName = JavaNaming.getValidPackageName(javaAssetPackage.getName()) + "." + className;
                        Map javaSources = packagedJava.get(javaAssetPackage);
                        if (javaSources == null) {
                            javaSources = new HashMap<>();
                            packagedJava.put(javaAssetPackage, javaSources);
                        }
                        javaSources.put(qName, javaAsset.getStringContent());
                    }
                }
            }
        }
        catch (Exception ex) {
            logger.severeException(ex.getMessage(), ex);
        }

        if (!packagedJava.isEmpty()) {
            for (Package pkg : packagedJava.keySet()) {
                try {
                    List> classes = CompiledJavaCache.compileClasses(getClass().getClassLoader(), pkg, packagedJava.get(pkg), true);
                    for (Class clazz : classes) {
                        RegisteredService registeredService = clazz.getAnnotation(RegisteredService.class);
                        if (registeredService == null) {
                            // jax-rs services, and swagger Apis
                            Path pathAnnotation = clazz.getAnnotation(Path.class);
                            if (pathAnnotation != null) {
                                String resourcePath = pathAnnotation.value() == null ? clazz.getPackage().getName() + "/" + clazz.getSimpleName() : pathAnnotation.value();
                                if (JsonService.class.isAssignableFrom(clazz)) {
                                    logger.info("JAX-RS JSON Service: " + resourcePath + " --> '" + clazz + "'");
                                    DynamicJavaServiceRegistry.addRegisteredService(JsonService.class.getName(), clazz.getName(), resourcePath);
                                }
                                if (XmlService.class.isAssignableFrom(clazz)) {
                                    logger.info("JAX-RS XML Service: " + resourcePath + " --> '" + clazz + "'");
                                    DynamicJavaServiceRegistry.addRegisteredService(XmlService.class.getName(), clazz.getName(), resourcePath);
                                }
                            }
                            // Monitor annotation
                            Monitor monitorAnnotation = clazz.getAnnotation(Monitor.class);
                            if (monitorAnnotation != null) {
                                logger.info("Monitor Service: " + monitorAnnotation.value() + " --> '" + clazz + "'");
                                String monitorCategory = monitorAnnotation.category().getName();
                                MonitorRegistry.getInstance().addDynamicService(monitorCategory, clazz.getName());
                            }
                            // ScheduledJob annotation
                            ScheduledJob scheduledJobAnnotation = clazz.getAnnotation(ScheduledJob.class);
                            if (scheduledJobAnnotation != null) {
                                logger.info("Scheduled Job: " + scheduledJobAnnotation.value() + " --> '" + clazz + "'");
                                DynamicJavaServiceRegistry.addRegisteredService(com.centurylink.mdw.model.monitor.ScheduledJob.class.getName(), clazz.getName());
                            }
                        }
                        else {
                            for (int i = 0; i < registeredService.value().length; i++) {
                                String serviceName = registeredService.value()[i].getName();
                                logger.info("@RegisteredService: " + serviceName + " Class: " + clazz);
                                DynamicJavaServiceRegistry.addRegisteredService(serviceName, clazz.getName());
                            }
                        }
                    }
                }
                catch (Exception ex) {
                  // let other packages continue to process
                  logger.severeException("Failed to process Dynamic Java services in package " + pkg.getLabel() + ": " + ex.getMessage(), ex);
                }
            }
        }
    }

    /**
     * To clear dynamic java registered services
     */
    private void clearDynamicJavaRegisteredServices() {
        DynamicJavaServiceRegistry.clearRegisteredServices();
    }

    public static Class getClass(Package currentPackage, String className, String javaCode)
    throws ClassNotFoundException, IOException, MdwJavaException {
        return getClass(null, currentPackage, className, javaCode);
    }

    public static Class getClass(ClassLoader parentLoader, Package currentPackage, String className, String javaCode)
    throws ClassNotFoundException, IOException, MdwJavaException {
        return getClass(parentLoader, currentPackage, className, javaCode, true);
    }

    public static Class getClass(ClassLoader parentLoader, Package currentPackage, String className, String javaCode, boolean cache)
    throws ClassNotFoundException, IOException, MdwJavaException {
        Class clazz = cache ? compiledCache.get(className) : null;
        if (clazz == null) {
            try {
                if (Compatibility.hasCodeSubstitutions())
                    javaCode = doCompatibilityCodeSubstitutions(className, javaCode);
                compileJavaCode(parentLoader, currentPackage, className, javaCode, cache);
                clazz = DynamicJavaClassLoader.getInstance(parentLoader, currentPackage, cache).loadClass(className);
            }
            catch (ClassNotFoundException ex) {
                throw ex;
            }
            catch (IOException ex) {
                throw ex;
            }
            catch (MdwJavaException ex) {
                throw ex;
            }
            catch (Throwable t) {
                logger.severeException(t.getMessage(), t);
                // don't let compilation errors prevent startup
            }
        }
        return clazz;
    }

    /**
     * @param parentLoader parent classloader
     * @param currentPackage the package for compilation context
     * @param javaSources map of class name to source text
     * @param cache true to add compiled classes to cache
     * @return list of compiled classes
     */
    public static List> compileClasses(ClassLoader parentLoader, Package currentPackage, Map javaSources, boolean cache)
    throws ClassNotFoundException, IOException, MdwJavaException {
        try {
            for (String className : javaSources.keySet()) {
                if (Compatibility.hasCodeSubstitutions())
                    javaSources.put(className, doCompatibilityCodeSubstitutions(className, javaSources.get(className)));
            }
            List> classes = compileJava(parentLoader, currentPackage, javaSources, cache);
            return classes;
        }
        catch (ClassNotFoundException ex) {
            throw ex;
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (MdwJavaException ex) {
            throw ex;
        }
        catch (Throwable t) {
            logger.severeException(t.getMessage(), t);
            // don't let compilation errors prevent startup
            return null;
        }
    }

    public static Class getResourceClass(String className, ClassLoader parentLoader, Package currentPackage)
    throws ClassNotFoundException, IOException, MdwJavaException {
        if (className.indexOf('$') > 0) {
            // inner class -- check previously loaded
            Class clazz = compiledCache.get(className);
            if (clazz != null)
                return clazz;
        }
        Asset javaAsset = AssetCache.getAsset(className, Asset.JAVA);
        if (javaAsset == null)
            throw new ClassNotFoundException(className);

        try {
            if (currentPackage == null) {
                // use the java asset package
                int lastDot = className.lastIndexOf('.');
                if (lastDot == -1)  {
                    // default package
                    currentPackage = PackageCache.getDefaultPackage();
                }
                else {
                    String packageName = className.substring(0, lastDot);
                    currentPackage = PackageCache.getPackage(packageName);
                }
            }

            return getClass(parentLoader, currentPackage, className, javaAsset.getStringContent());
        }
        catch (CachingException ex) {
            throw new MdwJavaException(ex.getMessage(), ex);
        }
    }

    public static Object getInstance(String resourceClassName, ClassLoader parentLoader, Package currentPackage) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException, MdwJavaException  {
        return getResourceClass(resourceClassName, parentLoader, currentPackage).newInstance();
    }

    private static void compileJavaCode(ClassLoader parentLoader, final Package currentPackage, String className, String javaCode, boolean cache)
    throws IOException, MdwJavaException {
        if (parentLoader == null)
            parentLoader = CompiledJavaCache.class.getClassLoader();

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null)
            throw new MdwJavaException("No Java compiler available.  JDK must precede JRE on system PATH.");

        final JavaFileObject jfo = new StringJavaFileObject(className, javaCode);

        DiagnosticCollector diagnostics = new DiagnosticCollector();

        JavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null);
        MdwJavaFileManager mdwFileManager = new MdwJavaFileManager(standardFileManager);

        // compiler options
        List options = new ArrayList<>();

        // compiler classpath
        String pathSep = System.getProperty("path.separator");
        String classpath = getJavaCompilerClasspath(currentPackage);
        classpath += pathSep + getTempDir();  // include java source artifacts

        String debug = "Compiling Dynamic Java class: " + className;
        if (logger.isMdwDebugEnabled()) {
            String extra = "parent ClassLoader=" + parentLoader;
            if (currentPackage != null)
                extra += ", workflow package: " + currentPackage.getLabel();
            logger.debug(debug + " (" + extra + ")");
        }
        else if (logger.isDebugEnabled()) {
            logger.debug(debug);
        }

        if (logger.isMdwDebugEnabled()) {
            logger.mdwDebug("Dynamic Java Compiler Classpath: " + classpath);
        }

        options.addAll(Arrays.asList("-g", "-classpath", classpath));
        String extraOptions = PropertyManager.getProperty(PropertyNames.MDW_JAVA_COMPILER_OPTIONS);
        if (extraOptions != null)
            options.addAll(Arrays.asList(extraOptions.split(" ")));

        CompilationTask compileTask = compiler.getTask(null, mdwFileManager, diagnostics, options, null, Arrays.asList(jfo));
        // TODO jaxb processors
        // compileTask.setProcessors(processors);
        boolean hasErrors = false;
        if (!compileTask.call()) {
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                String msg = "\nJava Compilation " + diagnostic.getKind() + ":" + diagnostic.getSource()
                  + "(" + diagnostic.getLineNumber() + "," + diagnostic.getColumnNumber() + ")\n"
                  + "   " + diagnostic.getMessage(null) + "\n";
                logger.severe(msg);
                if (!hasErrors && diagnostic.getKind().equals(Diagnostic.Kind.ERROR))
                    hasErrors = true;
            }
            if (hasErrors) {
                logger.debug("Dynamic Java Compiler Classpath: " + classpath);
                throw new CompilationException("Compilation errors in Dynamic Java. See compiler output in log for details.");
            }
        }
    }

    private static List> compileJava(ClassLoader parentLoader, final Package currentPackage, Map javaSources, boolean cache)
    throws ClassNotFoundException, IOException, MdwJavaException {
        if (parentLoader == null)
            parentLoader = CompiledJavaCache.class.getClassLoader();

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null)
            throw new MdwJavaException("No Java compiler available");

        String classNames = "";
        List jfos = new ArrayList<>();
        for (String className : javaSources.keySet()) {
            jfos.add(new StringJavaFileObject(className, javaSources.get(className)));
            classNames += className + ", ";
        }
        classNames = classNames.substring(0, classNames.length() - 2);

        DiagnosticCollector diagnostics = new DiagnosticCollector<>();

        JavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null);
        MdwJavaFileManager mdwFileManager = new MdwJavaFileManager<>(standardFileManager);

        // compiler options
        List options = new ArrayList<>();

        // compiler classpath
        String pathSep = System.getProperty("path.separator");
        String classpath = getJavaCompilerClasspath(currentPackage);
        classpath += pathSep + getTempDir();  // include java source artifacts

        String debug = "Compiling Dynamic Java classes: " + classNames;
        if (logger.isMdwDebugEnabled()) {
            String extra = "parent ClassLoader=" + parentLoader;
            if (currentPackage != null)
                extra += ", workflow package: " + currentPackage.getLabel();
            logger.debug(debug + " (" + extra + ")");
        }
        else if (logger.isDebugEnabled()) {
            logger.info(debug);
        }

        if (logger.isMdwDebugEnabled()) {
            logger.mdwDebug("Dynamic Java Compiler Classpath: " + classpath);
        }

        options.addAll(Arrays.asList("-g", "-classpath", classpath));
        String extraOptions = PropertyManager.getProperty(PropertyNames.MDW_JAVA_COMPILER_OPTIONS);
        if (extraOptions != null)
            options.addAll(Arrays.asList(extraOptions.split(" ")));

        CompilationTask compileTask = compiler.getTask(null, mdwFileManager, diagnostics, options, null, jfos);
        boolean hasErrors = false;
        List erroredClasses = new ArrayList();
        List compilableClasses = null;
        if (!compileTask.call()) {
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                String msg = "\nJava Compilation " + diagnostic.getKind() + ":" + diagnostic.getSource()
                  + "(" + diagnostic.getLineNumber() + "," + diagnostic.getColumnNumber() + ")\n"
                  + "   " + diagnostic.getMessage(null) + "\n";
                logger.severe(msg);
                if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) {
                    hasErrors = true;
                    if (diagnostic.getSource() instanceof StringJavaFileObject) {
                        StringJavaFileObject source = (StringJavaFileObject)diagnostic.getSource();
                        if (!erroredClasses.contains(source.getClassName()))
                            erroredClasses.add(source.getClassName());
                    }
                }
            }
            if (hasErrors) {
                logger.debug("Dynamic Java Compiler Classpath: " + classpath);
                logger.severe("Compilation errors in Dynamic Java. See compiler output in log for details.");
                compilableClasses = new ArrayList();
                for (String className : javaSources.keySet()) {
                    if (!erroredClasses.contains(className))
                        compilableClasses.add(className);
                }
                // recompile only compilable classes (TODO: a better way)
                jfos.clear();
                for (String className : compilableClasses)
                    jfos.add(new StringJavaFileObject(className, javaSources.get(className)));
                if (compilableClasses.size() > 0) {
                    compileTask = compiler.getTask(null, mdwFileManager, diagnostics, options, null, jfos);
                    compileTask.call();
                }
            }
        }

        List> classes = new ArrayList<>();
        for (String className : javaSources.keySet()) {
            if (!erroredClasses.contains(className))
                classes.add(DynamicJavaClassLoader.getInstance(parentLoader, currentPackage, cache).loadClass(className));
        }
        return classes;
    }

    public void clearCache() {
        compiledCache.clear();
        compilerClasspaths.clear();
        classesNotFound.clear();
        MdwJavaFileManager.clearJfoCache();
        clearDynamicJavaRegisteredServices();
        DynamicJavaClassLoader.clearLoaderInstances();
        // garbage collection helps ensure dynamicism of loaded classes
        Runtime.getRuntime().gc();
    }

    public int getCacheSize() {
        return compiledCache.size();
    }

    public void loadCache() throws CachingException {

        try {
            logger.info("Loading Java cache...");
            long before = System.currentTimeMillis();
            initializeJavaSourceArtifacts();
            preCompileJavaSourceArtifacts();
            loadDynamicJavaRegisteredServices();
            if (logger.isDebugEnabled())
                logger.debug("Time to load Java cache: " + (System.currentTimeMillis() - before) + " ms");
        }
        catch (Exception ex) {
            throw new CachingException(ex.getMessage(), ex);
        }
    }

    public synchronized void refreshCache() throws CachingException {
        clearCache();
        loadCache();
    }

    private static volatile Map compilerClasspaths = new ConcurrentHashMap<>();
    public static String getJavaCompilerClasspath(Package packageVO) throws IOException {
        String key;
        if (packageVO == null || packageVO.getName() == null)
            key= Packages.MDW_BASE;
        else
            key = packageVO.getName();

        String classpath = compilerClasspaths.get(key);
        if (classpath == null) {
            synchronized(compilerClasspaths) {
                classpath = compilerClasspaths.get(key);
                if (classpath == null) {
                    CloudClasspath cloudClsPath = new CloudClasspath(packageVO.getCloudClassLoader());
                    cloudClsPath.read();
                    classpath = cloudClsPath.toString();
                    compilerClasspaths.put(key, classpath);
                }
            }
        }
        return classpath;
    }

    /**
     * Writes the java-language assets into the temporary directory.
     * This is only needed for compilation dependencies.
     */
    private static void initializeJavaSourceArtifacts() throws DataAccessException, IOException, CachingException {
        logger.info("Initializing Java source assets...");
        long before = System.currentTimeMillis();

        for (Asset javaSource : AssetCache.getAssets(Asset.JAVA)) {
            Package pkg = PackageCache.getAssetPackage(javaSource.getId());
            String packageName = pkg == null ? null : JavaNaming.getValidPackageName(pkg.getName());
            String className = JavaNaming.getValidClassName(javaSource.getName());
            File dir = createNeededDirs(packageName);
            File file = new File(dir + "/" + className + ".java");
            if (file.exists())
                file.delete();

            String javaCode = javaSource.getStringContent();
            if (javaCode != null) {
                javaCode = doCompatibilityCodeSubstitutions(packageName + "." + className, javaCode);
                FileWriter writer = new FileWriter(file);
                writer.write(javaCode);
                writer.close();
            }
        }
        if (logger.isDebugEnabled())
            logger.debug("Time to initialize Java source assets: " + (System.currentTimeMillis() - before) + " ms");
    }

    /**
     *  Precompile designated Java Source artifacts.
     */
    private static void preCompileJavaSourceArtifacts() {
        if (preCompiled != null) {
            for (String preCompClass : preCompiled) {
                logger.info("Precompiling dynamic Java asset class: " + preCompClass);
                try {
                    Asset javaAsset = AssetCache.getAsset(preCompClass, Asset.JAVA);
                    Package pkg = PackageCache.getAssetPackage(javaAsset.getId());
                    String packageName = pkg == null ? null : JavaNaming.getValidPackageName(pkg.getName());
                    String className = (pkg == null ? "" : packageName + ".") + JavaNaming.getValidClassName(javaAsset.getName());
                    getClass(null, pkg, className, javaAsset.getStringContent());
                }
                catch (Exception ex) {
                    // let other classes continue to process
                    logger.severeException(ex.getMessage(), ex);
                }
            }
        }
    }

    private static File createNeededDirs(String packageName) {
        File rootDir = new File(getTempDir());
        if (!rootDir.exists()) {
            rootDir.mkdirs();
        }

        String path = getTempDir();
        File dir = new File(path);
        if (packageName != null) {
            StringTokenizer st = new StringTokenizer(packageName, ".");
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                path += "/" + token;
                dir = new File(path);
                if (!dir.exists())
                    dir.mkdir();
            }
        }
        return dir;
    }

    private static String getTempDir() {
        return ApplicationContext.getTempDirectory();
    }

    public static class DynamicJavaClassLoader extends ClassLoader {

        private Package packageVO;
        private boolean cache;

        private static Map parallelLockMap = new ConcurrentHashMap<>();
        private static Map instances = new ConcurrentHashMap<>();

        static {
            ClassLoader.registerAsParallelCapable();
        }

        DynamicJavaClassLoader(ClassLoader parentLoader, Package packageVO, boolean cache) {
            super(DynamicJavaClassLoader.class.getClassLoader()); //packageVO == null ? DynamicJavaClassLoader.class.getClassLoader() : packageVO.getCloudClassLoader());
            this.packageVO = packageVO;
            this.cache = cache;
        }

        public static DynamicJavaClassLoader getInstance(ClassLoader parentLoader, Package packageVO, boolean cache) {
            String key = packageVO.getName() + ""; // To handle the default package which has null name
            if (instances.containsKey(key))
                return instances.get(key);
            else {
                synchronized (instances) {
                    Map temp = instances;
                    if (temp.containsKey(key))
                        return temp.get(key);
                    else {
                        DynamicJavaClassLoader loader = new DynamicJavaClassLoader(parentLoader, packageVO, cache);
                        instances.put(key, loader);
                        return loader;
                    }
                }
            }
        }

        public static void clearLoaderInstances() {
            instances.clear();
        }

        protected Class findClass(String name) throws ClassNotFoundException {
            if (logger.isMdwDebugEnabled())
                logger.mdwDebug("findClass(): " + name);
            Class cl = null;
            if (classicLoading || classesNotFound.get(name) == null) {
                // check the cache first
                cl = compiledCache.get(name);
                if (cl == null) {
                    JavaFileObject jfo = MdwJavaFileManager.getJavaFileObject(name);
                    if (jfo == null) {
                        Asset javaAsset = AssetCache.getAsset(name, Asset.JAVA);
                        if (javaAsset != null) {
                            try {
                                String javaCode = javaAsset.getStringContent();
                                if (Compatibility.hasCodeSubstitutions())
                                    javaCode = doCompatibilityCodeSubstitutions(name, javaCode);
                                compileJavaCode(getParent(), packageVO, name, javaCode, cache);
                                jfo = MdwJavaFileManager.getJavaFileObject(name);
                            } catch (Exception ex) {
                                logger.severeException(ex.getMessage(), ex);
                            }
                        }
                    }
                    if (jfo != null) {
                        if (packageVO != null && packageVO.getName() != null) {
                            java.lang.Package pkg = getPackage(packageVO.getName());
                            if (pkg == null)
                                definePackage(packageVO.getName(), null, null, null, "MDW", packageVO.getVersionString(), "CenturyLink", null);
                        }
                        byte[] bytes = ((ByteArrayJavaFileObject) jfo).getByteArray();
                        cl = defineClass(name, bytes, 0, bytes.length);
                    }
                } else  // Found it in cache
                    return cl;

                // try the cloud classloader to find class in assets
                if (cl == null && packageVO != null && packageVO.getCloudClassLoader().hasClass(name) /* prevent infinite loop */)
                    cl = packageVO.getCloudClassLoader().directFindClass(name);
            }
            if (cl == null) {
                if (!classicLoading) classesNotFound.putIfAbsent(name, true);
                // don't log: can happen when trying multiple bundles to resolve a class
                throw new ClassNotFoundException(cnfeMsg(name));
            } else if (cache) {
                Class tempCl = compiledCache.putIfAbsent(name, cl);
                if (tempCl != null)
                    cl = tempCl;
            }
            return cl;
        }

        private String cnfeMsg(String className) {
            String msg = className + " with Parent ClassLoader: " + getParent();
            if (packageVO != null)
                msg += "\nand Workflow Package: " + packageVO.getLabel() + " (ClassLoader: " + packageVO.getClassLoader() + ")";
            return msg;
        }

        public String toString() {
            String str = getClass().getName() + " with parent " + getParent();
            if (packageVO != null)
                str += "\nand Workflow Package: " + packageVO.getLabel() + " (ClassLoader: " + packageVO.getClassLoader() + ")";
            return str;
        }

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            Class loaded = null;
            try {
                synchronized (getStaticClassLoadingLock(name)) {  // Only allow 1 class loader instance to load same class
                    // Look in assets first (overrides classes provided by built-in JARs), unless classicLoading is true
                    loaded = classicLoading ? super.loadClass(name) : findClass(name);
                }
            } catch (ClassNotFoundException | LinkageError e) {
                if (classicLoading)
                    throw e;
                else
                    loaded = super.loadClass(name);  // Not in assets, so look in parent loaders.
            }
            if (logger.isMdwDebugEnabled()) {
                logger.mdwDebug("Loaded class: '" + name + "' from DynamicJavaClassLoader with parent: " + getParent());
                if (logger.isTraceEnabled())
                    logger.traceException("Stack trace: ", new Exception("ClassLoader stack trace"));
            }
            return loaded;
        }

        /**
         * Prevents multiple instances of dynamicJavaClassLoader from trying to load
         * the same class at the same time
         */
        private Object getStaticClassLoadingLock(String className) {
            Object lock = null;
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
            return lock;
        }
    }

    protected static String doCompatibilityCodeSubstitutions(String label, String in) throws IOException {
        SubstitutionResult substitutionResult = Compatibility.getInstance().performCodeSubstitutions(in);
        if (!substitutionResult.isEmpty()) {
            logger.warn("Compatibility substitutions applied for Java asset " + label + " (details logged at debug level).");
            if (logger.isDebugEnabled())
                logger.debug("Compatibility substitutions for " + label + ":\n" + substitutionResult.getDetails());
            if (logger.isMdwDebugEnabled())
                logger.mdwDebug("Substitution output for " + label + ":\n" + substitutionResult.getOutput());
            return substitutionResult.getOutput();
        }
        return in;
    }

    /**
     * @param parentClassLoader
     * @param className
     * @throws CachingException
     * @throws IOException
     * @throws ClassNotFoundException
     * @throws MdwJavaException
     */
    public static Class getClassFromAssetName(ClassLoader parentClassLoader, String className) throws CachingException, MdwJavaException, ClassNotFoundException, IOException {
        Class clazz = null;
        Package javaAssetPackage = PackageCache.getJavaAssetPackage(className);
        Asset javaAsset = AssetCache.getAsset(className, Asset.JAVA);
        if (parentClassLoader == null) {
            parentClassLoader = javaAssetPackage.getClassLoader();
        }
        clazz = getClass(parentClassLoader, javaAssetPackage, className, javaAsset.getStringContent());
        return clazz;
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy