/**
* Copyright © 2013 enioka. All rights reserved
* Authors: Marc-Antoine GOUILLART (marc-antoine.gouillart@enioka.com)
* Pierre COPPEE (pierre.coppee@enioka.com)
*
* 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.enioka.jqm.model;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.enioka.jqm.jdbc.DatabaseException;
import com.enioka.jqm.jdbc.DbConn;
import com.enioka.jqm.jdbc.NoResultException;
import com.enioka.jqm.jdbc.QueryResult;
/**
* <strong>Not part of any API - this an internal JQM class and may change without notice.</strong> <br>
* Persistence class for storing the definition of all the user codes (the payloads) that can be run by the JQM engines. It contains all the
* metadata needed to create an execution request (a {@link JobInstance}).
*/
public class JobDef implements Serializable
{
private static final long serialVersionUID = -3276834475433922990L;
public enum PathType {
/**
* The path is a local file system path.
*/
FS,
/**
* The path is made of a single Maven coordinates set.
*/
MAVEN,
/**
* The payload is actually already in memory - the path is meaningless.
*/
MEMORY
}
private Integer id = null;
private String applicationName;
private String description;
private boolean enabled = true;
private String javaClassName;
private PathType pathType;
private String jarPath;
private int queue_id;
private boolean canBeRestarted = true;
private Integer maxTimeRunning;
private String application;
private String module;
private String keyword1;
private String keyword2;
private String keyword3;
private boolean highlander = false;
private boolean external = false;
private String javaOpts;
private List<JobDefParameter> parameters = new ArrayList<JobDefParameter>();
private Integer classLoader;
/**
* A technical ID without any meaning. Generated by the database.
*/
public Integer getId()
{
return id;
}
/**
* True if instances of this {@link JobDef} can be restarted (i.e. run with exactly the same parameters and context). Default is true.
*/
public boolean isCanBeRestarted()
{
return canBeRestarted;
}
/**
* See {@link #isCanBeRestarted()}
*/
public void setCanBeRestarted(final boolean canBeRestarted)
{
this.canBeRestarted = canBeRestarted;
}
/**
* The "main" class of the payload. I.e. the class containing a static main function, or implementing {@link Runnable}.<br>
* Must be a fully qualified name.<br>
* Max length is 100.
*/
public String getJavaClassName()
{
return javaClassName;
}
/**
* See {@link #getJavaClassName()}
*/
public void setJavaClassName(final String javaClassName)
{
this.javaClassName = javaClassName;
}
/**
* An optional hint giving the run time after which an alert should be raised. In minutes.
*/
public Integer getMaxTimeRunning()
{
return maxTimeRunning;
}
/**
* See {@link #getMaxTimeRunning()}
*/
public void setMaxTimeRunning(final Integer maxTimeRunning)
{
this.maxTimeRunning = maxTimeRunning;
}
/**
* The applicative key of the {@link JobDef}. {@link JobDef} are always retrieved through this name.<br>
* Max length is 100.
*/
public String getApplicationName()
{
return applicationName;
}
/**
* See {@link #getApplicationName()}
*/
public void setApplicationName(final String applicationName)
{
this.applicationName = applicationName;
}
/**
* An optional classification tag (default is NULL).<br>
* Max length is 50.
*/
public String getApplication()
{
return application;
}
/**
* See {@link #getApplication()}
*/
public void setApplication(final String application)
{
this.application = application;
}
/**
* An optional classification tag (default is NULL).<br>
* Max length is 50.
*/
public String getModule()
{
return module;
}
/**
* See {@link #getModule()}
*/
public void setModule(final String module)
{
this.module = module;
}
/**
* An optional classification tag (default is NULL).<br>
* Max length is 50.
*/
public String getKeyword1()
{
return keyword1;
}
/**
* See {@link #getKeyword1()}
*/
public void setKeyword1(final String keyword1)
{
this.keyword1 = keyword1;
}
/**
* An optional classification tag (default is NULL).<br>
* Max length is 50.
*/
public String getKeyword2()
{
return keyword2;
}
/**
* See {@link #getKeyword2()}
*/
public void setKeyword2(final String keyword2)
{
this.keyword2 = keyword2;
}
/**
* An optional classification tag (default is NULL).<br>
* Max length is 50.
*/
public String getKeyword3()
{
return keyword3;
}
/**
* See {@link #getKeyword3()}
*/
public void setKeyword3(final String keyword3)
{
this.keyword3 = keyword3;
}
/**
* Set to true to enable Highlander mode: never more than one concurrent execution of the same {@link JobDef} inside the whole cluster.
* Default is false.
*/
public boolean isHighlander()
{
return highlander;
}
/**
* See {@link #isHighlander()}
*/
public void setHighlander(final boolean highlander)
{
this.highlander = highlander;
}
/**
* The {@link Queue} on which the instances created from this {@link JobDef} should run. This is only the "default" queue - it may be
* overloaded inside the execution request.
*/
public Integer getQueue()
{
return queue_id;
}
public Queue getQueue(DbConn cnx)
{
List<Queue> qq = Queue.select(cnx, "q_select_by_id", this.queue_id);
if (qq.size() == 0)
{
throw new NoResultException("No queue found");
}
return qq.get(0);
}
/**
* See {@link #getQueue()}
*/
public void setQueue(final int queue)
{
this.queue_id = queue;
}
/**
* The path of the jar file containing the payload to run. The path must be relative to the job repository root ({@link Node#getRepo()}
* ).<br>
* Max length is 1024.
*/
public String getJarPath()
{
return jarPath;
}
/**
* See {@link #getJarPath()}
*/
public void setJarPath(final String jarPath)
{
this.jarPath = jarPath;
}
/**
* Parameters (i.e. key/value pairs) that should be present for all instances created from this JobDef. This list may be empty.<br>
* These are only the "default" parameters - each parameter may be overloaded inside the execution request (which may even specify
* parameters which are not present in the default parameters).
*/
public List<JobDefParameter> getParameters(DbConn cnx)
{
return JobDefParameter.select(cnx, "jdprm_select_all_for_jd", this.id);
}
public Map<String, String> getParametersMap(DbConn cnx)
{
return JobDefParameter.select_map(cnx, "jdprm_select_all_for_jd", this.id);
}
/**
* A (compulsory) description of what this paylod does.<br>
* Max length is 1024.
*/
public String getDescription()
{
return description;
}
/**
* See {@link #getDescription()}
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* The options passed to the JVM when launching this job definition. Only used if {@link #isExternal()} is <code>true</code>.<br>
* These options are split on spaces and passed individually to the JVM.<br>
* If <code>null</code>, the global parameter <code>defaultExternalOpts</code> is used. It this parameter is null too, default values
* are used.
*/
public String getJavaOpts()
{
return javaOpts;
}
/**
* See {@link #getJavaOpts()}
*/
public void setJavaOpts(String javaOpts)
{
this.javaOpts = javaOpts;
}
private Cl clCache = null;
/**
* Details on the class loader to use to run instances created from this JobDef. A JobDef may have its own class loader or share it with
* other JobDef.
*/
public Cl getClassLoader()
{
return clCache;
}
public Cl getClassLoader(DbConn cnx)
{
List<Cl> cls = Cl.select(cnx, "cl_select_by_id", this.classLoader);
clCache = cls.size() > 0 ? cls.get(0) : null;
return clCache;
}
public void setClassLoader(Integer id)
{
this.classLoader = id;
}
/**
* If true, the instances created from this JobDef will be run inside a dedicated JVM instead of simply being a thread inside an engine.
* Default is <code>false</code>.<br>
* If using this, JVM options specific to this JobDef may be set through {@link #getJavaOpts()}.
*/
public boolean isExternal()
{
return external;
}
/**
* See {@link #isExternal()}
*/
public void setExternal(boolean external)
{
this.external = external;
}
/**
* If <code>false</code>, the instances created from this JobDef won't actually run: the engine will simply fake a successful run.<br>
* Default is <code>true</code>
*/
public boolean isEnabled()
{
return enabled;
}
/**
* See {@link #isEnabled()}
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* This specifies how to interpret {@link #getJarPath()}.
*/
public PathType getPathType()
{
return this.pathType;
}
/**
* See {@link #getPathType()}
*/
public void setPathType(PathType type)
{
this.pathType = type;
}
/**
* ResultSet is not modified (no rs.next called).
*
* @param rs
* @return
*/
static JobDef map(ResultSet rs, int colShift)
{
JobDef tmp = new JobDef();
try
{
tmp.id = rs.getInt(1 + colShift);
tmp.application = rs.getString(2 + colShift);
tmp.applicationName = rs.getString(3 + colShift);
tmp.canBeRestarted = true;
tmp.classLoader = rs.getInt(4 + colShift);
tmp.description = rs.getString(5 + colShift);
tmp.enabled = rs.getBoolean(6 + colShift);
tmp.external = rs.getBoolean(7 + colShift);
tmp.highlander = rs.getBoolean(8 + colShift);
tmp.jarPath = rs.getString(9 + colShift);
tmp.javaClassName = rs.getString(10 + colShift);
tmp.javaOpts = rs.getString(11 + colShift);
tmp.keyword1 = rs.getString(12 + colShift);
tmp.keyword2 = rs.getString(13 + colShift);
tmp.keyword3 = rs.getString(14 + colShift);
tmp.maxTimeRunning = rs.getInt(15 + colShift);
tmp.module = rs.getString(16 + colShift);
tmp.pathType = PathType.valueOf(rs.getString(17 + colShift));
tmp.queue_id = rs.getInt(18 + colShift);
}
catch (SQLException e)
{
throw new DatabaseException(e);
}
return tmp;
}
public static List<JobDef> select(DbConn cnx, String query_key, Object... args)
{
List<JobDef> res = new ArrayList<JobDef>();
try
{
ResultSet rs = cnx.runSelect(query_key, args);
while (rs.next())
{
JobDef tmp = map(rs, 0);
res.add(tmp);
}
// TODO: pre fetch parameters as we always need them.
}
catch (SQLException e)
{
throw new DatabaseException(e);
}
return res;
}
public static int create(DbConn cnx, String description, String javaClassName, Map<String, String> parameters, String jarPath,
int queue_id, Integer maxTimeRunning, String applicationName, String application, String module, String keyword1,
String keyword2, String keyword3, boolean highlander, Integer classLoaderId, PathType pathType)
{
QueryResult r = cnx.runUpdate("jd_insert", application, applicationName, classLoaderId, description, true, false, highlander,
jarPath, javaClassName, null, keyword1, keyword2, keyword3, maxTimeRunning, module, pathType.toString(), queue_id);
int newId = r.getGeneratedId();
if (parameters != null)
{
for (Map.Entry<String, String> prm : parameters.entrySet())
{
cnx.runUpdate("jdprm_insert", prm.getKey(), prm.getValue(), newId);
}
}
return newId;
}
public static JobDef select_key(DbConn cnx, String name)
{
List<JobDef> res = select(cnx, "jd_select_by_key", name);
if (res.isEmpty())
{
throw new NoResultException("no result for query by key for key " + name);
}
if (res.size() > 1)
{
throw new DatabaseException("Inconsistent database! Multiple results for query by key for key " + name);
}
return res.get(0);
}
public void update(DbConn cnx, Map<String, String> parameters)
{
if (id == null)
{
this.id = JobDef.create(cnx, description, javaClassName, parameters, jarPath, queue_id, maxTimeRunning, applicationName,
application, module, keyword1, keyword2, keyword3, highlander, classLoader, pathType);
}
else
{
cnx.runUpdate("jd_update_all_fields_by_id", application, applicationName, description, enabled, external, highlander, jarPath,
javaClassName, javaOpts, keyword1, keyword2, keyword3, maxTimeRunning, module, pathType, classLoader, queue_id, id);
cnx.runUpdate("jdprm_delete_all_for_jd", this.id);
for (Map.Entry<String, String> prm : parameters.entrySet())
{
cnx.runUpdate("jdprm_insert", prm.getKey(), prm.getValue(), this.id);
}
}
}
public static void setExternal(DbConn cnx, Integer jdId)
{
cnx.runUpdate("jd_update_set_external_by_id", jdId);
}
}