com.enioka.jqm.tools.Helpers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jqm-engine Show documentation
Show all versions of jqm-engine Show documentation
A library containing the JQM engine. Cannot be used alone.
/**
* Copyright © 2013 enioka. All rights reserved
* Authors: Marc-Antoine GOUILLART ([email protected])
* Pierre COPPEE ([email protected])
*
* 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.tools;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.sql.SQLTransientException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import javax.mail.MessagingException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.spi.NamingManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Persistence;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.RollingFileAppender;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Sha512Hash;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.StringUtils;
import org.hibernate.LazyInitializationException;
import org.hibernate.exception.JDBCConnectionException;
import com.enioka.jqm.jpamodel.Deliverable;
import com.enioka.jqm.jpamodel.DeploymentParameter;
import com.enioka.jqm.jpamodel.GlobalParameter;
import com.enioka.jqm.jpamodel.History;
import com.enioka.jqm.jpamodel.JndiObjectResource;
import com.enioka.jqm.jpamodel.JndiObjectResourceParameter;
import com.enioka.jqm.jpamodel.JobDef;
import com.enioka.jqm.jpamodel.JobInstance;
import com.enioka.jqm.jpamodel.Message;
import com.enioka.jqm.jpamodel.Node;
import com.enioka.jqm.jpamodel.Queue;
import com.enioka.jqm.jpamodel.RPermission;
import com.enioka.jqm.jpamodel.RRole;
import com.enioka.jqm.jpamodel.RUser;
import com.enioka.jqm.jpamodel.State;
/**
* This is a helper class for internal use only.
*
*/
final class Helpers
{
private static final String PERSISTENCE_UNIT = "jobqueue-api-pu";
private static Logger jqmlogger = Logger.getLogger(Helpers.class);
// The one and only EMF in the engine.
private static Properties props = new Properties();
private static EntityManagerFactory emf;
// Resource file contains at least the jqm jdbc connection definition. Static because JNDI root context is common to the whole JVM.
static String resourceFile = "resources.xml";
private Helpers()
{
}
/**
* Get a fresh EM on the jobqueue-api-pu persistence Unit
*
* @return an EntityManager
*/
static EntityManager getNewEm()
{
getEmf();
return emf.createEntityManager();
}
static void setEmf(EntityManagerFactory newEmf)
{
emf = newEmf;
}
static EntityManagerFactory getEmf()
{
if (emf == null)
{
emf = createFactory();
}
return emf;
}
private static EntityManagerFactory createFactory()
{
InputStream fis = null;
try
{
Properties p = new Properties();
fis = Helpers.class.getClassLoader().getResourceAsStream("jqm.properties");
if (fis != null)
{
jqmlogger.trace("A jqm.properties file was found");
p.load(fis);
IOUtils.closeQuietly(fis);
props.putAll(p);
}
return Persistence.createEntityManagerFactory(PERSISTENCE_UNIT, props);
}
catch (IOException e)
{
jqmlogger.fatal("conf/jqm.properties file is invalid", e);
IOUtils.closeQuietly(fis);
throw new JqmInitError("Invalid JQM configuration file", e);
}
catch (Exception e)
{
jqmlogger.fatal("Unable to connect with the database. Maybe your configuration file is wrong. "
+ "Please check the password or the url in the $JQM_DIR/conf/resources.xml", e);
throw new JqmInitError("Database connection issue", e);
}
finally
{
IOUtils.closeQuietly(fis);
}
}
static void closeQuietly(EntityManager em)
{
try
{
if (em != null)
{
if (em.getTransaction().isActive())
{
em.getTransaction().rollback();
}
em.close();
}
}
catch (Exception e)
{
// fail silently
}
}
static void allowCreateSchema()
{
props.put("hibernate.hbm2ddl.auto", "update");
}
static void registerJndiIfNeeded()
{
try
{
JndiContext.createJndiContext();
}
catch (NamingException e)
{
throw new JqmInitError("Could not register the JNDI provider", e);
}
}
/**
* For internal test use only
* WARNING This will invalidate all open EntityManagers!
*/
static void resetEmf()
{
if (emf != null)
{
emf.close();
emf = null;
}
}
static void setLogFileName(String name)
{
Appender a = Logger.getRootLogger().getAppender("rollingfile");
if (a == null)
{
return;
}
RollingFileAppender r = (RollingFileAppender) a;
r.setFile("./logs/jqm-" + name + ".log");
r.activateOptions();
}
static void setLogLevel(String level)
{
try
{
Logger.getRootLogger().setLevel(Level.toLevel(level));
Logger.getLogger("com.enioka").setLevel(Level.toLevel(level));
jqmlogger.info("Setting general log level at " + level + " which translates as log4j level " + Level.toLevel(level));
}
catch (Exception e)
{
jqmlogger.warn("Log level could not be set", e);
}
}
/**
* Create a text message that will be stored in the database. Must be called inside a JPA transaction.
*
* @return the JPA message created
*/
static Message createMessage(String textMessage, JobInstance jobInstance, EntityManager em)
{
Message m = new Message();
m.setTextMessage(textMessage);
m.setJi(jobInstance.getId());
em.persist(m);
return m;
}
/**
* Create a Deliverable inside the database that will track a file created by a JobInstance Must be called from inside a JPA transaction
*
* @param path
* FilePath (relative to a root directory - cf. Node)
* @param originalFileName
* FileName
* @param fileFamily
* File family (may be null). E.g.: "daily report"
* @param jobId
* Job Instance ID
* @param em
* the EM to use.
*/
static Deliverable createDeliverable(String path, String originalFileName, String fileFamily, Integer jobId, EntityManager em)
{
Deliverable j = new Deliverable();
j.setFilePath(path);
j.setRandomId(UUID.randomUUID().toString());
j.setFileFamily(fileFamily);
j.setJobId(jobId);
j.setOriginalFileName(originalFileName);
em.persist(j);
return j;
}
/**
* Retrieve the value of a single-valued parameter.
*
* @param key
* @param defaultValue
* @param em
*/
static String getParameter(String key, String defaultValue, EntityManager em)
{
try
{
GlobalParameter gp = em.createQuery("SELECT n from GlobalParameter n WHERE n.key = :key", GlobalParameter.class)
.setParameter("key", key).getSingleResult();
return gp.getValue();
}
catch (NoResultException e)
{
return defaultValue;
}
}
/**
* Checks if a parameter exists. If it exists, it is left untouched. If it doesn't, it is created. Only works for parameters which key
* is unique. Must be called from within an open JPA transaction.
*/
static void initSingleParam(String key, String initValue, EntityManager em)
{
try
{
em.createQuery("SELECT n from GlobalParameter n WHERE n.key = :key", GlobalParameter.class).setParameter("key", key)
.getSingleResult();
return;
}
catch (NoResultException e)
{
GlobalParameter gp = new GlobalParameter();
gp.setKey(key);
gp.setValue(initValue);
em.persist(gp);
}
catch (NonUniqueResultException e)
{
// It exists! Nothing to do...
}
}
/**
* Checks if a parameter exists. If it exists, it is updated. If it doesn't, it is created. Only works for parameters which key is
* unique. Will create a transaction on the given entity manager.
*/
static void setSingleParam(String key, String value, EntityManager em)
{
try
{
em.getTransaction().begin();
GlobalParameter prm = em.createQuery("SELECT n from GlobalParameter n WHERE n.key = :key", GlobalParameter.class)
.setParameter("key", key).getSingleResult();
prm.setValue(value);
em.getTransaction().commit();
}
catch (NoResultException e)
{
GlobalParameter gp = new GlobalParameter();
gp.setKey(key);
gp.setValue(value);
em.persist(gp);
em.getTransaction().commit();
}
}
static void checkConfiguration(String nodeName, EntityManager em)
{
// Node
long n = em.createQuery("SELECT COUNT(n) FROM Node n WHERE n.name = :l", Long.class).setParameter("l", nodeName).getSingleResult();
if (n == 0L)
{
throw new JqmInitError("The node does not exist. It must be referenced (CLI option createnode) before it can be used");
}
Node nn = em.createQuery("SELECT n FROM Node n WHERE n.name = :l", Node.class).setParameter("l", nodeName).getSingleResult();
if (!StringUtils.hasText(nn.getDlRepo()) || !StringUtils.hasText(nn.getRepo()) || !StringUtils.hasText(nn.getTmpDirectory()))
{
throw new JqmInitError(
"The node does not have all its paths specified. Check node configuration (or recreate it with the CLI).");
}
// Default queue
long i = (Long) em.createQuery("SELECT COUNT(qu) FROM Queue qu where qu.defaultQueue = true").getSingleResult();
if (i == 0L)
{
throw new JqmInitError("There is no default queue. Correct this (for example with CLI option -u, or with the web admin)");
}
if (i > 1L)
{
throw new JqmInitError(
"There is more than one default queue. Correct this (for example with CLI option -u, or with the web admin)");
}
// Deployment parameters
i = (Long) em.createQuery("SELECT COUNT(dp) FROM DeploymentParameter dp WHERE dp.node.name = :localnode", Long.class)
.setParameter("localnode", nodeName).getSingleResult();
if (i == 0L)
{
jqmlogger
.warn("This node is not bound to any queue. Either use the GUI to bind it or use CLI option -u to bind it to the default queue");
}
// Roles
i = em.createQuery("SELECT count(rr) from RRole rr WHERE rr.name = :rr", Long.class).setParameter("rr", "administrator")
.getSingleResult();
if (i == 0L)
{
throw new JqmInitError("The 'administrator' role does not exist. It is needed for the APIs. Run CLI option -u to create it.");
}
// Mail session
i = (Long) em.createQuery("SELECT COUNT(r) FROM JndiObjectResource r WHERE r.name = :nn").setParameter("nn", "mail/default")
.getSingleResult();
if (i == 0L)
{
throw new JqmInitError("Mail session named mail/default does not exist but is required for the engine to run"
+ ". Use CLI option -u to create an empty one or use the admin web GUI to create it.");
}
}
/**
* Creates or updates a node.
* This method makes the assumption metadata is valid. e.g. there MUST be a single default queue.
* Call {@link #updateConfiguration(EntityManager)} before to be sure if necessary.
*
* @param nodeName
* name of the node that should be created or updated (if incompletely defined only)
* @param em
* an EntityManager on which a transaction will be opened.
*/
static void updateNodeConfiguration(String nodeName, EntityManager em)
{
// Node
Node n = null;
try
{
n = em.createQuery("SELECT n FROM Node n WHERE n.name = :l", Node.class).setParameter("l", nodeName).getSingleResult();
}
catch (NoResultException e)
{
jqmlogger.info("Node " + nodeName + " does not exist in the configuration and will be created with default values");
em.getTransaction().begin();
n = new Node();
n.setDlRepo(System.getProperty("user.dir") + "/outputfiles/");
n.setName(nodeName);
n.setPort(0);
n.setRepo(System.getProperty("user.dir") + "/jobs/");
n.setTmpDirectory(System.getProperty("user.dir") + "/tmp/");
n.setRootLogLevel("INFO");
em.persist(n);
em.getTransaction().commit();
}
// Deployment parameters
DeploymentParameter dp = null;
long i = (Long) em.createQuery("SELECT COUNT(dp) FROM DeploymentParameter dp WHERE dp.node = :localnode")
.setParameter("localnode", n).getSingleResult();
if (i == 0)
{
jqmlogger.info("As this node is not bound to any queue, it will be set to poll from the default queue with default parameters");
Queue q = em.createQuery("SELECT q FROM Queue q WHERE q.defaultQueue = true", Queue.class).getSingleResult();
em.getTransaction().begin();
dp = new DeploymentParameter();
dp.setNbThread(5);
dp.setNode(n);
dp.setPollingInterval(1000);
dp.setQueue(q);
em.persist(dp);
em.getTransaction().commit();
}
}
/**
* Creates or updates metadata common to all nodes: default queue, global parameters, roles...
* It is idempotent. It also has the effect of making broken metadata viable again.
*/
static void updateConfiguration(EntityManager em)
{
em.getTransaction().begin();
// Default queue
Queue q = null;
long i = (Long) em.createQuery("SELECT COUNT(qu) FROM Queue qu").getSingleResult();
if (i == 0L)
{
q = new Queue();
q.setDefaultQueue(true);
q.setDescription("default queue");
q.setTimeToLive(1024);
q.setName("DEFAULT");
em.persist(q);
jqmlogger.info("A default queue was created in the configuration");
}
else
{
try
{
q = em.createQuery("SELECT q FROM Queue q WHERE q.defaultQueue = true", Queue.class).getSingleResult();
jqmlogger.info("Default queue is named " + q.getName());
}
catch (NonUniqueResultException e)
{
// Faulty configuration, but why not
q = em.createQuery("SELECT q FROM Queue q", Queue.class).getResultList().get(0);
q.setDefaultQueue(true);
jqmlogger.info("Queue " + q.getName() + " was modified to become the default queue as there were mutliple default queue");
}
catch (NoResultException e)
{
// Faulty configuration, but why not
q = em.createQuery("SELECT q FROM Queue q", Queue.class).getResultList().get(0);
q.setDefaultQueue(true);
jqmlogger.warn("Queue " + q.getName() + " was modified to become the default queue as there was no default queue");
}
}
// Global parameters
initSingleParam("mavenRepo", "http://repo1.maven.org/maven2/", em);
initSingleParam(Constants.GP_DEFAULT_CONNECTION_KEY, Constants.GP_JQM_CONNECTION_ALIAS, em);
initSingleParam("logFilePerLaunch", "true", em);
initSingleParam("internalPollingPeriodMs", "60000", em);
initSingleParam("disableWsApi", "false", em);
initSingleParam("enableWsApiSsl", "false", em);
initSingleParam("enableWsApiAuth", "true", em);
initSingleParam("enableInternalPki", "true", em);
// Roles
RRole adminr = createRoleIfMissing(em, "administrator", "all permissions without exception", "*:*");
createRoleIfMissing(em, "config admin", "can read and write all configuration, except security configuration", "node:*", "queue:*",
"qmapping:*", "jndi:*", "prm:*", "jd:*");
createRoleIfMissing(em, "config viewer", "can read all configuration except for security configuration", "node:read", "queue:read",
"qmapping:read", "jndi:read", "prm:read", "jd:read");
createRoleIfMissing(em, "client", "can use the full client API except reading logs, files and altering position", "node:read",
"queue:read", "job_instance:*", "jd:read");
createRoleIfMissing(em, "client power user", "can use the full client API", "node:read", "queue:read", "job_instance:*", "jd:read",
"logs:read", "queue_position:create", "files:read");
createRoleIfMissing(em, "client read only", "can query job instances and get their files", "queue:read", "job_instance:read",
"logs:read", "files:read");
// Users
createUserIfMissing(em, "root", "all powerfull user", adminr);
// Mail session
i = (Long) em.createQuery("SELECT COUNT(r) FROM JndiObjectResource r WHERE r.name = :nn").setParameter("nn", "mail/default")
.getSingleResult();
if (i == 0)
{
HashMap prms = new HashMap();
prms.put("smtpServerHost", "smtp.gmail.com");
JndiObjectResource res = new JndiObjectResource();
res.setAuth(null);
res.setDescription("default parameters used to send e-mails");
res.setFactory("com.enioka.jqm.providers.MailSessionFactory");
res.setName("mail/default");
res.setType("javax.mail.Session");
res.setSingleton(true);
em.persist(res);
JndiObjectResourceParameter prm = new JndiObjectResourceParameter();
prm.setKey("smtpServerHost");
prm.setValue("smtp.gmail.com");
em.persist(prm);
res.getParameters().add(prm);
prm.setResource(res);
}
// Done
em.getTransaction().commit();
}
static RRole createRoleIfMissing(EntityManager em, String roleName, String description, String... permissions)
{
try
{
return em.createQuery("SELECT rr from RRole rr WHERE rr.name = :r", RRole.class).setParameter("r", roleName).getSingleResult();
}
catch (NoResultException e)
{
RRole r = new RRole();
r.setName(roleName);
r.setDescription(description);
em.persist(r);
for (String s : permissions)
{
RPermission p = new RPermission();
p.setName(s);
p.setRole(r);
em.persist(p);
r.getPermissions().add(p);
}
return r;
}
}
static RUser createUserIfMissing(EntityManager em, String login, String description, RRole... roles)
{
RUser res = null;
try
{
res = em.createQuery("SELECT r from RUser r WHERE r.login = :l", RUser.class).setParameter("l", login).getSingleResult();
}
catch (NoResultException e)
{
res = new RUser();
res.setFreeText(description);
res.setLogin(login);
res.setPassword(String.valueOf((new SecureRandom()).nextInt()));
encodePassword(res);
em.persist(res);
}
res.setLocked(false);
for (RRole r : res.getRoles())
{
r.getUsers().remove(res);
}
res.getRoles().clear();
for (RRole r : roles)
{
res.getRoles().add(r);
r.getUsers().add(res);
}
return res;
}
static void encodePassword(RUser user)
{
ByteSource salt = new SecureRandomNumberGenerator().nextBytes();
user.setPassword(new Sha512Hash(user.getPassword(), salt, 100000).toHex());
user.setHashSalt(salt.toHex());
}
/**
* Transaction is not opened nor committed here but needed.
*
*/
static History createHistory(JobInstance job, EntityManager em, State finalState, Calendar endDate)
{
History h = new History();
h.setId(job.getId());
h.setJd(job.getJd());
h.setApplicationName(job.getJd().getApplicationName());
h.setSessionId(job.getSessionID());
h.setQueue(job.getQueue());
h.setQueueName(job.getQueue().getName());
h.setEnqueueDate(job.getCreationDate());
h.setEndDate(endDate);
h.setAttributionDate(job.getAttributionDate());
h.setExecutionDate(job.getExecutionDate());
h.setUserName(job.getUserName());
h.setEmail(job.getEmail());
h.setParentJobId(job.getParentId());
h.setApplication(job.getJd().getApplication());
h.setModule(job.getJd().getModule());
h.setKeyword1(job.getJd().getKeyword1());
h.setKeyword2(job.getJd().getKeyword2());
h.setKeyword3(job.getJd().getKeyword3());
h.setInstanceApplication(job.getApplication());
h.setInstanceKeyword1(job.getKeyword1());
h.setInstanceKeyword2(job.getKeyword2());
h.setInstanceKeyword3(job.getKeyword3());
h.setInstanceModule(job.getModule());
h.setProgress(job.getProgress());
h.setStatus(finalState);
h.setNode(job.getNode());
h.setNodeName(job.getNode().getName());
em.persist(h);
return h;
}
static String getMavenVersion()
{
String res = System.getProperty("mavenVersion");
if (res != null)
{
return res;
}
InputStream is = Main.class.getResourceAsStream("/META-INF/maven/com.enioka.jqm/jqm-engine/pom.properties");
Properties p = new Properties();
try
{
p.load(is);
res = p.getProperty("version");
}
catch (Exception e)
{
res = "maven version not found";
jqmlogger.warn("maven version not found", e);
}
return res;
}
static JobDef findJobDef(String applicationName, EntityManager em)
{
try
{
return em.createQuery("SELECT j FROM JobDef j WHERE j.applicationName = :n", JobDef.class).setParameter("n", applicationName)
.getSingleResult();
}
catch (NoResultException ex)
{
return null;
}
}
static Queue findQueue(String qName, EntityManager em)
{
try
{
return em.createQuery("SELECT q FROM Queue q WHERE q.name = :name", Queue.class).setParameter("name", qName).getSingleResult();
}
catch (NoResultException ex)
{
return null;
}
}
static void dumpParameters(EntityManager em, Node n)
{
String terse = getParameter("disableVerboseStartup", "false", em);
if ("false".equals(terse))
{
jqmlogger.info("Global cluster parameters are as follow:");
List prms = em.createQuery("SELECT gp FROM GlobalParameter gp", GlobalParameter.class).getResultList();
for (GlobalParameter prm : prms)
{
jqmlogger.info(String.format("\t%1$s = %2$s", prm.getKey(), prm.getValue()));
}
jqmlogger.info("Node parameters are as follow:");
jqmlogger.info("\tfile produced storage directory: " + n.getDlRepo());
jqmlogger.info("\tHTTP listening interface: " + n.getDns());
jqmlogger.info("\tlooks for payloads inside: " + n.getRepo());
jqmlogger.info("\tlog level: " + n.getRootLogLevel());
jqmlogger.info("\ttemp files will be created inside: " + n.getTmpDirectory());
jqmlogger.info("\tJMX registry port: " + n.getJmxRegistryPort());
jqmlogger.info("\tJMX server port: " + n.getJmxServerPort());
jqmlogger.info("\tHTTP listening port: " + n.getPort());
jqmlogger.info("\tAPI admin enabled: " + n.getLoadApiAdmin());
jqmlogger.info("\tAPI client enabled: " + n.getLoadApiClient());
jqmlogger.info("\tAPI simple enabled: " + n.getLoapApiSimple());
jqmlogger.info("Node polling parameters are as follow:");
List dps = em
.createQuery("SELECT dp FROM DeploymentParameter dp WHERE dp.node.id = :n", DeploymentParameter.class)
.setParameter("n", n.getId()).getResultList();
// Pollers
for (DeploymentParameter dp : dps)
{
jqmlogger.info("\t" + dp.getQueue().getName() + " - every " + dp.getPollingInterval() + "ms - maximum " + dp.getNbThread()
+ " concurrent threads");
}
}
}
/**
* Send a mail message using a JNDI resource.
* As JNDI resource providers are inside the EXT class loader, this uses reflection. This method is basically a bonus on top of the
* MailSessionFactory offered to payloads, making it accessible also to the engine.
*
* @param to
* @param subject
* @param body
* @param mailSessionJndiAlias
* @throws MessagingException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
static void sendMessage(String to, String subject, String body, String mailSessionJndiAlias) throws MessagingException
{
jqmlogger.debug("sending mail to " + to + " - subject is " + subject);
ClassLoader extLoader = getExtClassLoader();
ClassLoader old = Thread.currentThread().getContextClassLoader();
Object mailSession = null;
try
{
mailSession = InitialContext.doLookup(mailSessionJndiAlias);
}
catch (NamingException e)
{
throw new MessagingException("could not find mail session description", e);
}
try
{
Thread.currentThread().setContextClassLoader(extLoader);
Class transportZ = extLoader.loadClass("javax.mail.Transport");
Class sessionZ = extLoader.loadClass("javax.mail.Session");
Class mimeMessageZ = extLoader.loadClass("javax.mail.internet.MimeMessage");
Class messageZ = extLoader.loadClass("javax.mail.Message");
Class recipientTypeZ = extLoader.loadClass("javax.mail.Message$RecipientType");
Object msg = mimeMessageZ.getConstructor(sessionZ).newInstance(mailSession);
mimeMessageZ.getMethod("setRecipients", recipientTypeZ, String.class).invoke(msg, recipientTypeZ.getField("TO").get(null), to);
mimeMessageZ.getMethod("setSubject", String.class).invoke(msg, subject);
mimeMessageZ.getMethod("setText", String.class).invoke(msg, body);
transportZ.getMethod("send", messageZ).invoke(null, msg);
jqmlogger.trace("Mail was sent");
}
catch (Exception e)
{
throw new MessagingException("an exception occurred during mail sending", e);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
static void sendEndMessage(JobInstance ji)
{
try
{
String message = "The Job number " + ji.getId() + " finished correctly\n\n" + "Job description:\n" + "- Job definition: "
+ ji.getJd().getApplicationName() + "\n" + "- Parent: " + ji.getParentId() + "\n" + "- User name: " + ji.getUserName()
+ "\n" + "- Session ID: " + ji.getSessionID() + "\n" + "- Queue: " + ji.getQueue().getName() + "\n" + "- Node: "
+ ji.getNode().getName() + "\n" + "Best regards,\n";
sendMessage(ji.getEmail(), "[JQM] Job: " + ji.getId() + " ENDED", message, "mail/default");
}
catch (Exception e)
{
jqmlogger.warn("Could not send email. Job has nevertheless run correctly", e);
}
}
static ClassLoader getExtClassLoader()
{
try
{
return ((JndiContext) NamingManager.getInitialContext(null)).getExtCl();
}
catch (NamingException e)
{
// Don't do anything - this actually cannot happen. Death to checked exceptions.
return null;
}
}
static boolean testDbFailure(Exception e)
{
return (e instanceof LazyInitializationException)
|| (e instanceof JDBCConnectionException)
|| (e.getCause() instanceof JDBCConnectionException)
|| (e.getCause() != null && e.getCause().getCause() instanceof JDBCConnectionException)
|| (e.getCause() instanceof SQLTransientException)
|| (e.getCause() != null && e.getCause().getCause() instanceof SQLTransientException)
|| (e.getCause() != null && e.getCause().getCause() != null && e.getCause().getCause().getCause() instanceof SQLTransientException)
|| (e.getCause() != null && e.getCause().getCause() != null && e.getCause().getCause().getCause() != null && e.getCause()
.getCause().getCause().getCause() instanceof SQLTransientException);
}
}