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

com.enioka.jqm.tools.ClassloaderManager Maven / Gradle / Ivy

There is a newer version: 2.2.9
Show newest version
package com.enioka.jqm.tools;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.NamingException;
import javax.naming.spi.NamingManager;

import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.enioka.jqm.jdbc.DbConn;
import com.enioka.jqm.model.Cl;
import com.enioka.jqm.model.GlobalParameter;
import com.enioka.jqm.model.JobDef;
import com.enioka.jqm.model.JobInstance;

/**
 * This class holds all the {@link JarClassLoader} and is the only place to create one. There should be one instance per engine.
* We use a specific object rather than static objects in the {@link Loader} class to allow multiple engine instantiations. It also allows * to centralise all CL creation methods and have cleaner code in Loader and in JCL. */ class ClassloaderManager { private Logger jqmlogger = LoggerFactory.getLogger(ClassloaderManager.class); /** * The CL corresponding to "one CL to rule them all" mode. */ private JarClassLoader sharedClassLoader = null; /** * The CL for loading plugins (for the engine) */ private ClassLoader pluginClassLoader = null; private boolean hasPlugins = true; /** * The CLs corresponding to "one CL per jar" mode. */ private Map sharedJarClassLoader = new HashMap(); /** * The CLs corresponding to specific keys (specified inside {@link JobDef#getSpecificIsolationContext()}). Key is Cl object ID. */ private Map persistentClassLoaders = new HashMap(); /** * The different runners which may be involved inside the class loaders. Simple class names. */ private List runnerClasses = new ArrayList(); /** * The default CL mode. Values can be: null, Shared, SharedJar. */ private String launchIsolationDefault = null; private final LibraryResolverFS fsResolver; private final LibraryResolverMaven mavenResolver; ClassloaderManager() { this.fsResolver = new LibraryResolverFS(); this.mavenResolver = new LibraryResolverMaven(); } void setIsolationDefault(DbConn cnx) { this.launchIsolationDefault = GlobalParameter.getParameter(cnx, "launch_isolation_default", "Isolated"); String rns = GlobalParameter.getParameter(cnx, "job_runners", "com.enioka.jqm.tools.LegacyRunner,com.enioka.jqm.tools.MainRunner,com.enioka.jqm.tools.RunnableRunner"); for (String s : rns.split(",")) { runnerClasses.add(s); jqmlogger.info("Detected a job instance runner named " + s); } } JarClassLoader getClassloader(JobInstance ji, DbConn cnx) throws MalformedURLException, JqmPayloadException, RuntimeException { final JarClassLoader jobClassLoader; JobDef jd = ji.getJD(); // Extract the jar actual path File jarFile = new File(FilenameUtils.concat(new File(ji.getNode().getRepo()).getAbsolutePath(), jd.getJarPath())); // The parent class loader is normally the CL with EXT on its CL. But if no lib load, user current one (happens for external // payloads) ClassLoader parent = getParentClassLoader(ji); // Priority is: // 1 - a specific context // 2 - general mode (jar or global) Cl cldef = jd.getClassLoader(); if (cldef != null) { //////////////////////////////////// // Specific CL options were given String clSharingKey = cldef.getName(); // We take great care to reduce locking as much as possible, so this code may seem to be too complicated at first glance. if (cldef.isPersistent()) { if (persistentClassLoaders.containsKey(cldef.getId())) { jqmlogger.info("Using an existing specific isolation context : " + clSharingKey); jobClassLoader = persistentClassLoaders.get(cldef.getId()); } else { synchronized (persistentClassLoaders) { if (persistentClassLoaders.containsKey(cldef.getId())) { jqmlogger.info("Using an existing specific isolation context : " + clSharingKey); jobClassLoader = persistentClassLoaders.get(cldef.getId()); } else { jqmlogger.info("Creating a new persistent specific isolation context: " + clSharingKey); jobClassLoader = new JarClassLoader(parent); jobClassLoader.setReferenceJobDefName(jd.getApplicationName()); jobClassLoader.mayBeShared(cldef.isPersistent()); jobClassLoader.setHiddenJavaClasses(cldef.getHiddenClasses()); jobClassLoader.setTracing(cldef.isTracingEnabled()); jobClassLoader.setChildFirstClassLoader(cldef.isChildFirst()); persistentClassLoaders.put(cldef.getId(), jobClassLoader); } } } } else { jqmlogger.info("Creating a new transient specific isolation context: " + clSharingKey); jobClassLoader = new JarClassLoader(parent); jobClassLoader.setReferenceJobDefName(jd.getApplicationName()); jobClassLoader.mayBeShared(cldef.isPersistent()); jobClassLoader.setHiddenJavaClasses(cldef.getHiddenClasses()); jobClassLoader.setTracing(cldef.isTracingEnabled()); jobClassLoader.setChildFirstClassLoader(cldef.isChildFirst()); } // END SPECIFIC CL OPTIONS //////////////////////////////////// } else { // Use default CL options. We accept double creation on edge sync states in this case. if ("Shared".equals(launchIsolationDefault)) { if (sharedClassLoader != null) { jqmlogger.info("Using sharedClassLoader"); jobClassLoader = sharedClassLoader; } else { jqmlogger.info("Creating sharedClassLoader"); jobClassLoader = new JarClassLoader(parent); jobClassLoader.mayBeShared(true); sharedClassLoader = jobClassLoader; } } else if ("SharedJar".equals(launchIsolationDefault)) { if (sharedJarClassLoader.containsKey(jd.getJarPath())) { // check if jarUrl has already a class loader jqmlogger.info("Using shared Jar CL"); jobClassLoader = sharedJarClassLoader.get(jd.getJarPath()); } else { jqmlogger.info("Creating shared Jar CL"); jobClassLoader = new JarClassLoader(parent); jobClassLoader.mayBeShared(true); sharedJarClassLoader.put(jd.getJarPath(), jobClassLoader); } } else { // Standard case: all launches are independent. We create a transient CL. jqmlogger.debug("Using an isolated transient CL with default parameters"); jobClassLoader = new JarClassLoader(parent); jobClassLoader.mayBeShared(false); } } // Resolve the libraries and add them to the classpath final URL[] classpath = getClasspath(ji, cnx); // Remember to also add the jar file itself... as CL can be shared, there is no telling if it already present or not. jobClassLoader.extendUrls(jarFile.toURI().toURL(), classpath); // Some debug display jqmlogger.trace("CL URLs:"); for (URL url : jobClassLoader.getURLs()) { jqmlogger.trace(" - " + url.toString()); } return jobClassLoader; } private ClassLoader getExtensionClassloader() { ClassLoader extLoader = null; try { extLoader = ((JndiContext) NamingManager.getInitialContext(null)).getExtCl(); } catch (NamingException e) { jqmlogger.warn("could not find ext directory class loader. No parent classloader will be used", e); } return extLoader; } /** * Returns all the URL that should be inside the classpath. This includes the jar itself if any. * * @throws JqmPayloadException */ private URL[] getClasspath(JobInstance ji, DbConn cnx) throws JqmPayloadException { switch (ji.getJD().getPathType()) { case MAVEN: return mavenResolver.resolve(ji, cnx); case MEMORY: return new URL[0]; case FS: default: return fsResolver.getLibraries(ji.getNode(), ji.getJD(), cnx); } } private ClassLoader getParentClassLoader(JobInstance ji) { switch (ji.getJD().getPathType()) { case MAVEN: return getExtensionClassloader(); case MEMORY: return Thread.currentThread().getContextClassLoader(); default: case FS: return getExtensionClassloader(); } } List getJobRunnerClasses() { return this.runnerClasses; } ClassLoader getPluginClassLoader() { if (hasPlugins && pluginClassLoader == null) { File extDir = new File("plugins/"); List urls = new ArrayList(); if (extDir.isDirectory()) { for (File f : extDir.listFiles()) { if (!f.canRead()) { throw new RuntimeException("can't access file " + f.getAbsolutePath()); } try { urls.add(f.toURI().toURL()); } catch (MalformedURLException e) { jqmlogger.error("Error when parsing the content of plugin directory. File will be ignored", e); } } // Create classloader final URL[] aUrls = urls.toArray(new URL[0]); for (URL u : aUrls) { jqmlogger.trace(u.toString()); } pluginClassLoader = AccessController.doPrivileged(new PrivilegedAction() { @Override public URLClassLoader run() { return new URLClassLoader(aUrls, null); } }); } else { hasPlugins = false; pluginClassLoader = ClassloaderManager.class.getClassLoader(); } } return pluginClassLoader; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy