package com.enioka.jqm.test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.enioka.jqm.api.JqmClient; import com.enioka.jqm.model.JobDefParameter; import com.enioka.jqm.model.JobDef.PathType; /** * Helper class to define a new payload. This will help create an actual job definition, as if it had been imported from an XML deployment * descriptor.<br> * There are two possibilities to create a job definition: from a class inside the current class path, or from an external Jar. */ public class TestJobDefinition { // Identity String description; String name; // Where the payload is String javaClassName; String path = "/dev/null"; PathType pathType = PathType.MEMORY; // How it should run String queueName; boolean highlander = false; // Parameters Map<String, String> parameters = new HashMap<String, String>(); // Optional classifiers String application, module, keyword1, keyword2, keyword3; // Class loading options String specificIsolationContext; boolean childFirstClassLoader = false; List<String> hiddenJavaClasses = new ArrayList<String>(); boolean classLoaderTracing = false; /////////////////////////////////////////////////////////////////////////// // CONSTRUCTION /////////////////////////////////////////////////////////////////////////// private TestJobDefinition() {} /** * This creates a job definition from a given class. This class must be present inside the current class path, and will be loaded by the * engine with the current class loader. This means that most specific class loading option (child first, ...) are mostly useless in * this case. * * @param name * the name to give the new job definition. This is the name that is later reused in client APIs, such as * {@link JqmClient#enqueue(String, String)}. * @param description * a free text describing the job definition * @param testedClass * the class containing the job to run. * @return the object itself (fluid API) */ public static TestJobDefinition createFromClassPath(String name, String description, Class<? extends Object> testedClass) { TestJobDefinition res = new TestJobDefinition(); res.name = name; res.description = description; res.javaClassName = testedClass.getCanonicalName(); res.path = "/dev/null"; res.pathType = PathType.MEMORY; return res; } /** * Create a job definition from a class inside an existing jar file. This creates the exact replica of what happens inside a production * JQM cluster, including taking into account all the various class loading options (child first, external library directory...). * * @param name * the name to give the new job definition. This is the name that is later reused in client APIs, such as * {@link JqmClient#enqueue(String, String)}. * @param description * a free text describing the job definition * @param testedClassCanonicalName * the class containing the job to run (full canonical name, i.e. including package name) * @param jarPath * path to the jar file, relative to the current directory. * @return */ public static TestJobDefinition createFromJar(String name, String description, String testedClassCanonicalName, String jarPath) { TestJobDefinition res = new TestJobDefinition(); res.name = name; res.description = description; res.javaClassName = testedClassCanonicalName; res.path = jarPath; res.pathType = PathType.FS; return res; } /////////////////////////////////////////////////////////////////////////// // FLUID API /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Compulsory parameters String getDescription() { return description; } void setDescription(String description) { this.description = description; } String getName() { return name; } void setName(String name) { this.name = name; } String getJavaClassName() { return javaClassName; } void setJavaClassName(String javaClassName) { this.javaClassName = javaClassName; } String getPath() { return path; } void setPath(String path) { this.path = path; } PathType getPathType() { return pathType; } void setPathType(PathType pathType) { this.pathType = pathType; } String getQueueName() { return queueName; } // end compulsory parameters /////////////////////////////////////////////////////////////////////////// /** * The jobs will run on this queue. If no set (or set to null) will run on the default queue. * * @param queueName */ public TestJobDefinition setQueueName(String queueName) { this.queueName = queueName; return this; } boolean isHighlander() { return highlander; } /** * This enables Highlander mode. See the documentation on Highlander jobs. */ public TestJobDefinition enableHighlander() { this.highlander = true; return this; } List<JobDefParameter> getParameters() { List<JobDefParameter> prms = new ArrayList<JobDefParameter>(this.parameters.size()); for (Map.Entry<String, String> e : this.parameters.entrySet()) { JobDefParameter prm = new JobDefParameter(); prm.setKey(e.getKey()); prm.setValue(e.getValue()); prms.add(prm); } return prms; } /** * Add a parameter. * * @param key * must be unique * @param value */ public TestJobDefinition addParameter(String key, String value) { this.parameters.put(key, value); return this; } String getApplication() { return application; } /** * An optional classifier. * * @param application */ public TestJobDefinition setApplication(String application) { this.application = application; return this; } String getModule() { return module; } /** * An optional classifier. * * @param module */ public TestJobDefinition setModule(String module) { this.module = module; return this; } String getKeyword1() { return keyword1; } /** * An optional classifier. * * @param keyword1 */ public TestJobDefinition setKeyword1(String keyword1) { this.keyword1 = keyword1; return this; } String getKeyword2() { return keyword2; } /** * An optional classifier. * * @param keyword2 */ public TestJobDefinition setKeyword2(String keyword2) { this.keyword2 = keyword2; return this; } String getKeyword3() { return keyword3; } /** * An optional classifier. * * @param keyword3 */ public TestJobDefinition setKeyword3(String keyword3) { this.keyword3 = keyword3; return this; } String getSpecificIsolationContext() { return specificIsolationContext; } /** * By default jobs run inside a dedicated class loader thrown out after the run. By setting this to a non null value, this job will run * inside a re-used class loader. All jobs using the same specificIsolationContext share the same class loader. See documentation on * class loading inside JQM. * * @param specificIsolationContext * name of the class loader to use or null to use default behaviour. */ public TestJobDefinition setSpecificIsolationContext(String specificIsolationContext) { this.specificIsolationContext = specificIsolationContext; return this; } boolean isChildFirstClassLoader() { return childFirstClassLoader; } /** * This enables child-first class loading.<br> * Note that if the payload is actually inside your unit test class path, this won't do much. See documentation on class loading inside * JQM. * * @param childFirstClassLoader */ public TestJobDefinition setChildFirstClassLoader(boolean childFirstClassLoader) { this.childFirstClassLoader = childFirstClassLoader; return this; } String getHiddenJavaClasses() { String res = ""; for (String s : hiddenJavaClasses) { res += s + ","; } if (hiddenJavaClasses.size() > 0) { return res.substring(0, res.length() - 2); } return ""; } /** * This prevents the given class from being loaded by a parent class loader. Id es: if the class is not directly available to the * payload (inside the payload libraries for example) it will not be loaded. * * @param canonicalClassName */ public TestJobDefinition addHiddenJavaClasses(String canonicalClassName) { this.hiddenJavaClasses.add(canonicalClassName); return this; } boolean isClassLoaderTracing() { return classLoaderTracing; } /** * Enable verbose class loading fot his payload. */ public TestJobDefinition enableClassLoaderTracing() { this.classLoaderTracing = true; return this; } }