package com.owera.xaps.tr069.decision.shelljob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.owera.common.db.NoAvailableConnectionException;
import com.owera.common.util.Cache;
import com.owera.common.util.CacheValue;
import com.owera.xaps.base.Log;
import com.owera.xaps.base.UnitJob;
import com.owera.xaps.base.db.DBAccess;
import com.owera.xaps.base.db.DBAccessSessionTR069;
import com.owera.xaps.dbi.Job;
import com.owera.xaps.dbi.ScriptExecution;
import com.owera.xaps.dbi.ScriptExecutions;
import com.owera.xaps.dbi.Unit;
import com.owera.xaps.dbi.UnitJobStatus;
import com.owera.xaps.dbi.UnitParameter;
import com.owera.xaps.dbi.UnittypeParameter;
import com.owera.xaps.dbi.UnittypeParameters;
import com.owera.xaps.dbi.XAPS;
import com.owera.xaps.dbi.XAPSUnit;
import com.owera.xaps.dbi.util.SystemParameters;
import com.owera.xaps.tr069.CPEParameters;
import com.owera.xaps.tr069.Provisioning;
import com.owera.xaps.tr069.SessionData;
import com.owera.xaps.tr069.exception.TR069DatabaseException;
import com.owera.xaps.tr069.exception.TR069Exception;
import com.owera.xaps.tr069.exception.TR069ExceptionShortMessage;
import com.owera.xaps.tr069.methods.GPVDecision;
import com.owera.xaps.tr069.xml.ParameterList;
import com.owera.xaps.tr069.xml.ParameterValueStruct;
/**
* This class performs a SHELL-job
*/
public class ShellJobLogic {
private static Random random = new Random();
/**
* We need a monitor to synchronize, so that two devices using the same
* unit-id (same ACS-username), do not run the shell-script on the same
* context at the same time. To prevent this we use the unit-id is used to
* lookup a simple Object(). We cannot use the unit-id String object directly,
* because even if the String-object encapsulates the same string, it is not
* the same object.
*/
private static Cache monitorCache = new Cache();
/**
* @param sessionData
* @param job
* @param uj
* @throws TR069DatabaseException
* @throws SQLException
* @throws NoAvailableConnectionException
*/
public static void execute(SessionData sessionData, Job job, UnitJob uj) throws TR069Exception {
String unitId = sessionData.getUnitId();
CacheValue cv = monitorCache.get(unitId);
if (cv == null) {
cv = new CacheValue(new Object()); // default settings: session-timeout for 30 minutes
monitorCache.put(unitId, cv);
}
synchronized (cv.getObject()) {
// read parameters from device and save it to the unit
ShellJobLogic.importReadOnlyParameters(sessionData);
// execute changes using the shell-script, all changes are written to database
ShellJobLogic.executeShellScript(sessionData, job, uj);
// read the changes from the database and send to CPE
ShellJobLogic.prepareSPV(sessionData);
}
}
/**
* Responsible for executing a shell script. The following tasks must be done
* <p/>
* 1. Retrieve shell script from job
* 2. Retrieve shell daemon. If necessary start new shell daemon.
* If not allowed to make more daemons and waiting for more than 10 seconds, abort - should result in Job verification fail (not sure how)
* 3. Feed shell script into shell daemon. Wait for the script to be executed.
* If shell daemon returns error - should result in Job verification fail (not sure how)
*
* @param job
* @throws TR069DatabaseException
* @throws
*/
private static void executeShellScript(SessionData sessionData, Job job, UnitJob uj) throws TR069Exception {
ScriptExecutions executions = Provisioning.getExecutions();
String scriptArgs = "\"-uut:" + sessionData.getUnittype().getName() + "/pr:" + sessionData.getProfile().getName() + "/un:" + sessionData.getUnitId() + "\"";
String requestId = "JOB:" + job.getId() + ":" + random.nextInt(1000000); // should be a unique Id
try {
executions.requestExecution(job.getFile(), scriptArgs, requestId);
} catch (SQLException e) {
throw new TR069DatabaseException("Could not request a script execution", e);
}
long timeWaited = 0;
long timeWaitFactor = 4;
while (true) {
try {
long timeWait = timeWaitFactor * timeWaitFactor * timeWaitFactor; // will wait longer and longer
Thread.sleep(timeWait);
timeWaited += timeWait;
timeWaitFactor += 2;
ScriptExecution se = executions.getExecution(sessionData.getUnittype(), requestId);
if (se.getExitStatus() != null) {
if (se.getExitStatus() == true) { // ERROR OCCURRED
Log.error(ShellJobLogic.class, se.getErrorMessage());
uj.stop(UnitJobStatus.CONFIRMED_FAILED);
} else
uj.stop(UnitJobStatus.COMPLETED_OK);
break;
}
if (timeWaited > 30000) {
Log.error(ShellJobLogic.class, "The execution of the shell script did not complete within 30 sec");
uj.stop(UnitJobStatus.CONFIRMED_FAILED);
break;
}
} catch (Throwable t) {
throw new TR069Exception("An error occurred in the scheduling/registration of a shell-script", TR069ExceptionShortMessage.MISC, t);
}
}
}
/**
* Read unit parameters from database, to see if any changes
* have occurred (during the shell script execution). If ReadWrite
* parameters differ from CPE, then send them to the CPE.
*
* @param sessionData
* @throws TR069DatabaseException
*/
private static void toCPE(SessionData sessionData) throws TR069DatabaseException {
UnittypeParameters utps = sessionData.getUnittype().getUnittypeParameters();
XAPS xaps = sessionData.getDbAccess().getXaps();
Unit unit;
try {
XAPSUnit xapsUnit = DBAccess.getXAPSUnit(xaps);
unit = xapsUnit.getUnitById(sessionData.getUnitId());
} catch (SQLException e) {
throw new TR069DatabaseException(e);
}
ParameterList toCPE = new ParameterList();
for (int i = 0; i < sessionData.getFromCPE().size(); i++) {
ParameterValueStruct pvsCPE = sessionData.getFromCPE().get(i);
if (pvsCPE == null || pvsCPE.getValue() == null || pvsCPE.getName() == null)
continue;
UnittypeParameter utp = utps.getByName(pvsCPE.getName());
if (utp == null || !utp.getFlag().isReadWrite())
continue;
UnitParameter up = unit.getUnitParameters().get(utp.getName());
if (up == null || up.getValue() == null)
continue;
if (!up.getValue().equals(pvsCPE.getValue())) {
if (pvsCPE.getType() != null) {
toCPE.addParameterValueStruct(new ParameterValueStruct(utp.getName(), up.getValue(), pvsCPE.getType()));
} else {
toCPE.addParameterValueStruct(new ParameterValueStruct(utp.getName(), up.getValue()));
}
}
}
sessionData.setToCPE(toCPE);
}
/**
* In order for the shell script to run with the correct parameters,
* we must read them from the device and write it to the database, before
* the script starts.
*
* @param sessionData
* @throws TR069DatabaseException
*/
private static void importReadOnlyParameters(SessionData sessionData) throws TR069DatabaseException {
// List<ParameterValueStruct> toDB = new ArrayList<ParameterValueStruct>();
List<UnitParameter> unitParameters = new ArrayList<UnitParameter>();
UnittypeParameters utps = sessionData.getUnittype().getUnittypeParameters();
for (int i = 0; i < sessionData.getFromCPE().size(); i++) {
ParameterValueStruct pvsCPE = sessionData.getFromCPE().get(i);
if (pvsCPE == null || pvsCPE.getValue() == null || pvsCPE.getName() == null)
continue;
UnittypeParameter utp = utps.getByName(pvsCPE.getName());
if (utp == null || !utp.getFlag().isReadOnly())
continue;
ParameterValueStruct pvsDB = sessionData.getFromDB().get(pvsCPE.getName());
/* Make sure that all AlwaysRead-params and all populated Read-params are written to DB here. This
* Will make sure DB has the right state when the script is executed in the next step.
*/
if (utp.getFlag().isAlwaysRead())
unitParameters.add(new UnitParameter(utp, sessionData.getUnitId(), pvsCPE.getValue(), sessionData.getProfile()));
// toDB.add(pvsCPE);
else if (pvsDB != null && pvsDB.getValue() != null)
unitParameters.add(new UnitParameter(utp, sessionData.getUnitId(), pvsCPE.getValue(), sessionData.getProfile()));
// toDB.add(pvsCPE);
}
if (unitParameters.size() > 0) {
try {
XAPS xaps = DBAccess.getDBI().getXaps();
XAPSUnit xapsUnit = DBAccess.getXAPSUnit(xaps);
xapsUnit.addOrChangeUnitParameters(unitParameters, sessionData.getProfile());
} catch (SQLException sqle) {
throw new TR069DatabaseException(sqle);
}
// sessionData.setToDB(toDB);
// DBAccessSessionTR069 dbAccessSessionTR069 = new DBAccessSessionTR069(DBAccess.getDBI(), sessionData.getDbAccess());
// dbAccessSessionTR069.writeValueMap(sessionData);
}
}
private static void prepareSPV(SessionData sessionData) throws TR069DatabaseException {
toCPE(sessionData);
List<ParameterValueStruct> toDB = new ArrayList<ParameterValueStruct>();
sessionData.setToDB(toDB);
CPEParameters cpeParams = sessionData.getCpeParameters();
String PII = cpeParams.PERIODIC_INFORM_INTERVAL;
String nextPII = "" + sessionData.getPIIDecision().nextPII();
sessionData.getProvisioningMessage().setPeriodicInformInterval(new Integer(nextPII));
sessionData.getToCPE().addOrChangeParameterValueStruct(PII, nextPII, "xsd:unsignedInt");
Log.debug(GPVDecision.class, "-ACS->CPE " + PII + " CPE[" + nextPII + "] ACS[" + nextPII + "] Decided by ACS");
sessionData.getToDB().add(new ParameterValueStruct(PII, "" + nextPII));
Log.debug(GPVDecision.class, "-ACS->ACS " + PII + " CPE[" + nextPII + "] ACS[" + nextPII + "] Decided by ACS");
sessionData.getToDB().add(new ParameterValueStruct(SystemParameters.PERIODIC_INTERVAL, "" + nextPII));
Log.debug(GPVDecision.class, "-ACS->ACS " + SystemParameters.PERIODIC_INTERVAL + " CPE[" + nextPII + "] ACS[" + nextPII + "] Decided by ACS");
// DBAccessSessionTR069 dbAccessSessionTR069 = new DBAccessSessionTR069(DBAccess.getDBI(), sessionData.getDbAccess());
DBAccessSessionTR069.writeUnitParams(sessionData);
}
}