package com.enioka.jqm.test;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import org.hsqldb.Server;
import org.hsqldb.jdbc.JDBCDataSource;
import com.enioka.jqm.api.JqmClientFactory;
import com.enioka.jqm.jdbc.Db;
import com.enioka.jqm.jdbc.DbConn;
import com.enioka.jqm.model.GlobalParameter;
import com.enioka.jqm.model.JobDef;
import com.enioka.jqm.model.JobInstance;
import com.enioka.jqm.model.Node;
import com.enioka.jqm.model.Queue;
import com.enioka.jqm.model.RuntimeParameter;
import com.enioka.jqm.model.JobDef.PathType;
import com.enioka.jqm.tools.JqmSingleRunner;
/**
* This class allows to start a stripped-down version of the JQM engine and run a payload synchronously inside it.<br>
* It is the only supported way to unit test a payload (be it with JUnit or a simple main() method). <br>
* <br>
* Limitations:
* <ul>
* <li>The job will actually run inside the current class loader (in the full engine, each job instance has its own class loader)</li>
* <li>This for testing one job only. Only one job instance will run! If the test itself enqueues new launch request, they will be ignored.
* For testing interactions between job instances, integration tests on an embedded JQM engine are required.</li>
* <li>If using resources (JNDI), they must be put inside a resource.xml file at the root of classloader search.</li>
* <li>Resource providers and corresponding drivers must be put inside testing class path (for example by putting them inside pom.xml with a
* <code>test</code> scope)</li>
* <li>To ease tests, the launch is synchronous. Obviously, real life instances are asynchronous. To test asynchronous launches, use an
* embedded engine (integration test) with the much more complicated {@link JqmAsyncTester}.</li>
* <li>If files are created by the payload, they are stored inside a temporary directory that is not removed at the end of the run.</li>
* </ul>
* <br>
* For example, a simple JUnit test could be:
*
* <pre>
* {@code public void testOne()
* {
* JobInstance res = JqmTester.create("com.enioka.jqm.test.Payload1").run();
* Assert.assertEquals(State.ENDED, res.getState());
* }
* </pre>
*/
public class JqmTester
{
private static Server s;
private Db db = null;
private DbConn cnx = null;
private Node node = null;
private Integer jd = null;
private Integer q = null;
private Integer ji = null;
private File resDirectoryPath;
private JqmTester(String className)
{
s = Common.createHsqlServer();
s.start();
JDBCDataSource ds = new JDBCDataSource();
ds.setDatabase("jdbc:hsqldb:mem:" + s.getDatabaseName(0, true));
db = new Db(ds, true);
cnx = db.getConn();
JqmSingleRunner.setConnection(db);
Properties p2 = new Properties();
p2.put("com.enioka.jqm.jdbc.contextobject", db);
JqmClientFactory.setProperties(p2);
// Needed parameters
GlobalParameter.setParameter(cnx, "defaultConnection", "");
cnx.commit();
// Ext dir
File extDir = new File("./ext");
if (!extDir.exists() && !extDir.mkdir())
{
throw new RuntimeException(new IOException("./ext directory does not exist and cannot create it"));
}
// Create node
resDirectoryPath = Common.createTempDirectory();
node = Node.create(cnx, "testtempnode", 12, resDirectoryPath.getAbsolutePath(), resDirectoryPath.getAbsolutePath(),
resDirectoryPath.getAbsolutePath(), "test");
q = Queue.create(cnx, "default", "default test queue", true); // Only useful because JobDef.queue is non-null
jd = JobDef.create(cnx, "test application", className, null, "/dev/null", q, 0, "TestApplication", null, null, null, null, null,
false, null, PathType.MEMORY);
ji = JobInstance.enqueue(cnx, q, jd, null, null, null, null, null, null, null, null, null, false, null);
cnx.runUpdate("ji_update_poll", node.getId(), q, 10);
cnx.commit();
}
public static JqmTester create(String className)
{
return new JqmTester(className);
}
public JqmTester addParameter(String key, String value)
{
RuntimeParameter.create(cnx, ji, key, value);
cnx.commit();
return this;
}
private void close()
{
s.stop();
s.shutdown();
cnx.close();
JqmClientFactory.resetClient();
JqmClientFactory.setProperties(new Properties());
}
public com.enioka.jqm.api.JobInstance run()
{
com.enioka.jqm.api.JobInstance res = JqmSingleRunner.run(ji);
close();
return res;
}
}