/**
* Copyright © 2013 enioka. All rights reserved
*
* 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.util.Calendar;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import javax.naming.spi.NamingManager;
import org.apache.log4j.Logger;
import com.enioka.jqm.jdbc.DbConn;
import com.enioka.jqm.jdbc.NoResultException;
import com.enioka.jqm.model.GlobalParameter;
import com.enioka.jqm.model.Node;
/**
* The internal poller is responsible for doing all the repetitive tasks of an engine (excluding polling queues). Namely: check if
* {@link Node#isStop()} has become true (stop order) and update {@link Node#setLastSeenAlive(java.util.Calendar)} to make visible to the
* whole cluster that the engine is still alive and that no other engine should start with the same node name.
*/
class InternalPoller implements Runnable
{
private static Logger jqmlogger = Logger.getLogger(InternalPoller.class);
private boolean run = true;
private JqmEngine engine = null;
private Thread localThread = null;
private long step;
private Node node = null;
private String logLevel = null;
private Semaphore loop = new Semaphore(0);
InternalPoller(JqmEngine e)
{
this.engine = e;
DbConn cnx = Helpers.getNewDbSession();
// Get configuration data
this.node = this.engine.getNode();
this.step = Long.parseLong(GlobalParameter.getParameter(cnx, "internalPollingPeriodMs", "60000"));
this.logLevel = this.node.getRootLogLevel();
cnx.close();
}
void stop()
{
// The test is important: it prevents the engine from calling interrupt() when stopping
// ... which can be triggered inside InternalPoller.run!
jqmlogger.info("Internal poller has received a stop request");
if (this.run)
{
this.run = false;
if (this.localThread != null)
{
this.localThread.interrupt();
}
}
}
void forceLoop()
{
this.loop.release(1);
}
@Override
public void run()
{
Thread.currentThread().setName("INTERNAL_POLLER;polling orders;");
jqmlogger.info("Start of the internal poller");
DbConn cnx = null;
this.localThread = Thread.currentThread();
Calendar latestJettyRestart = Calendar.getInstance(), lastJndiPurge = latestJettyRestart;
String nodePrms = null;
// Launch main loop
while (true)
{
try
{
loop.tryAcquire(this.step, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
run = false;
}
if (!run)
{
break;
}
try
{
// Get session
cnx = Helpers.getNewDbSession();
// Check if stop order
try
{
node = Node.select_single(cnx, "node_select_by_id", node.getId());
}
catch (NoResultException e)
{
node = null;
}
if (node == null || node.isStop())
{
jqmlogger.info("Node has received a stop order from the database or was removed from the database");
jqmlogger.trace(
"At stop order time, there are " + this.engine.getCurrentlyRunningJobCount() + " jobs running in the node");
this.run = false;
this.engine.stop();
break;
}
// Change log level?
if (!this.logLevel.equals(node.getRootLogLevel()))
{
this.logLevel = node.getRootLogLevel();
Helpers.setLogLevel(this.logLevel);
}
// I am alive
cnx.runUpdate("node_update_alive_by_id", node.getId());
cnx.commit();
// Have queue bindings changed, or is engine disabled?
this.engine.syncPollers(cnx, node);
// Jetty restart. Conditions are:
// * some parameters (such as security parameters) have changed
// * node parameter change such as start or stop an API.
Calendar bflkpm = Calendar.getInstance();
String np = node.getDns() + node.getPort() + node.getLoadApiAdmin() + node.getLoadApiClient() + node.getLoapApiSimple();
if (nodePrms == null)
{
nodePrms = np;
}
int i = cnx.runSelectSingle("globalprm_select_count_modified_jetty", Integer.class, latestJettyRestart);
if (i > 0 || !np.equals(nodePrms))
{
this.engine.getJetty().start(node, cnx);
latestJettyRestart = bflkpm;
nodePrms = np;
}
// Should JNDI cache be purged?
i = cnx.runSelectSingle("jndi_select_count_changed", Integer.class, lastJndiPurge, lastJndiPurge);
if (i > 0L)
{
try
{
((JndiContext) NamingManager.getInitialContext(null)).resetSingletons();
lastJndiPurge = bflkpm;
}
catch (Exception e)
{
jqmlogger.warn(
"Could not reset JNDI singleton resources. New parameters won't be used. Restart engine to update them.",
e);
}
}
}
catch (RuntimeException e)
{
if (Helpers.testDbFailure(e))
{
jqmlogger.error("connection to database lost - stopping internal poller");
jqmlogger.trace("connection error was:", e.getCause());
run = false;
this.engine.startDbRestarter();
break;
}
else
{
throw e;
}
}
finally
{
// Loop is done, let session go
Helpers.closeQuietly(cnx);
}
}
jqmlogger.info("End of the internal poller");
}
}