V1_3.java [src/java/m/rusle2] Revision: d9545dbb6690e20c2b3ed3180501c2fa8389581a Date: Wed Oct 07 18:48:45 MDT 2015
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package m.rusle2;
import c.PostGIS;
import csip.Config;
import csip.ServiceException;
import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.concurrent.Callable;
import javax.ws.rs.*;
import oms3.annotations.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import org.codehaus.jettison.json.JSONException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import csip.ModelDataService;
import static csip.ModelDataService.EXEC_FAILED;
import static csip.ModelDataService.EXEC_OK;
import static csip.ModelDataService.KEY_REQUEST_RESULTS;
import static csip.ModelDataService.REPORT_DIM;
import static csip.ModelDataService.REPORT_NAME;
import static csip.ModelDataService.REPORT_TYPE;
import static csip.ModelDataService.REPORT_VALUE;
import static csip.ModelDataService.VALUE;
import static util.ErosionConst.*;
import csip.utils.Binaries;
import csip.utils.JSONUtils;
import csip.utils.Services;
import csip.annotations.Polling;
import static m.rusle2.R2Run.LOG;
/**
* REST Web Service. Front end callable.
*
* @author wlloyd, od
*/
@Name("Rusle2")
@Description("IET / Phacil version of R2")
@VersionInfo("1.3")
@Path("m/rusle2/1.3")
@Polling(first = 1000, next = 1000)
public class V1_3 extends ModelDataService {
static final boolean PRODUCTION_MODE = true;
//
static final String R2_TMP_FILENAME = "r2";
static final String R2_TMP_FILEEXT = ".rsh";
static final String IO_TIMING_FILENAME = "/tmp/io-timing";
static final String IO_TIMING_FILEEXT = ".txt";
static final String REPORT_JSON_TEMPLATE_FILENAME = "Rusle2_Report.json";
static final String REPORT_RSH_FILENAME = "rusle2_report.rsh";
/*
* The R2 Run
*/
final R2Run r2run = new R2Run();
// These are the allowable potential results returnable from the R2 service
static final List<String> potResults = Collections.unmodifiableList(Arrays.asList(
RES_SLOPE_DELIVERY,
RES_SLOPE_T_VALUE,
RES_SLOPE_DEGRAD,
RES_SLOPE_EQUIV_DIESEL_USE_PER_AREA,
RES_SOIL_COND_INDEX_STIR_VAL,
RES_SOIL_COND_INDEX_RESULT,
RES_SEG_SIM_DAY_LIVE_BIOMASS,
RES_SEG_SIM_DAY_COVER_MASS_SUM,
RES_SEG_SIM_DAY_STAND_MASS_SUM,
RES_SEG_SIM_DAY_CANOPY_COVER,
RES_SEG_SIM_DAY_PERENN_VEG_LIVE_HEIGHT,
RES_SLOPE_SIM_DAY_DEGRAD,
RES_SURF_RES_OUTPUTS_SURF_COV_AT_OP,
RES_SOIL_COND_INDEX_OM_SUBFACTOR,
RES_SOIL_COND_INDEX_FO_SUBFACTOR,
RES_SOIL_COND_INDEX_ER_SUBFACTOR));
//
String climate;
String soil;
String mgmt;
List<String> operations;
List<String> vegetations;
List<String> residues;
//
@Override
protected void preprocess() throws Exception {
// check for requested output.
try {
LOG.info("\n\n\n\n\nMETAINFO:");
LOG.info("\n" + getMetainfo().toString());
LOG.info("\n\n\n");
JSONUtils.checkValidResultRequest(getMetainfo(), potResults);
} catch (ServiceException se) {
LOG.severe("\n\n\nERROR!!!\n\n\n" + se.toString());
LOG.warning("No Rusle2 return parameters requested! Will use defaults: SLOPE_DELIVERY, SLOPE_T_VALUE, SLOPE_DEGRAD");
List<String> params = new LinkedList<String>();
params.add(RES_SLOPE_DELIVERY);
params.add(RES_SLOPE_T_VALUE);
params.add(RES_SLOPE_DEGRAD);
// check if these are being set
params.add("CONTOUR_SYSTEM_PTR");
params.add("STRIP_BARRIER_SYSTEM_PTR");
params.add("HYD_ELEM_SYSTEM_PTR");
getMetainfo().put(KEY_REQUEST_RESULTS, params);
}
// check if sufficient input is there.
//JSONUtils.checkKeyExists(getParamMap(), KEY_CLIMATES);
JSONUtils.checkKeyExists(getParamMap(), KEY_SOILS);
// JSONUtils.checkKeyExists(getParamMap(), KEY_MANAGEMENTS);
JSONUtils.checkKeyExists(getParamMap(), KEY_LENGTH);
JSONUtils.checkKeyExists(getParamMap(), KEY_STEEPNESS);
JSONUtils.checkKeyExists(getParamMap(), KEY_MGMTS);
File r2script = new File(getWorkspaceDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT);
createInputFile(r2script, getMetainfo(), getParamMap());
}
@Override
protected Callable<String> createCallable() throws Exception {
return new Callable<String>() {
@Override
public String call() throws Exception {
int result = r2run.execute(new File(getWorkspaceDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT));
if (result != 0) {
return EXEC_FAILED;
}
return EXEC_OK;
}
};
}
@Override
protected JSONArray createResults() throws Exception {
JSONArray results = new JSONArray();
int errors = 0;
for (int i = 0; i < operations.size(); i++)
{
String op = operations.get(i);
if (r2run.isUrlReachable(op))
results.put(JSONUtils.dataDesc("OPERATION_" + (i+1), op, "operation is valid"));
else
{
results.put(JSONUtils.dataDesc("OPERATION_INVALID_" + (i+1), op, "OPERATION IS MISSING!"));
errors ++;
}
}
for (int i = 0; i < vegetations.size(); i++)
{
String vege = vegetations.get(i);
if (r2run.isUrlReachable(vege))
results.put(JSONUtils.dataDesc("VEGETATION_" + (i+1), vege, "vegetation is valid"));
else
{
results.put(JSONUtils.dataDesc("VEGETATION_INVALID_" + (i+1), vege, "VEGETATION IS MISSING"));
errors ++;
}
}
for (int i = 0; i < residues.size(); i++)
{
String res = residues.get(i);
if (r2run.isUrlReachable(res))
results.put(JSONUtils.dataDesc("RESIDUE_" + (i+1), res, "RESIDUE IS VALID"));
else
{
results.put(JSONUtils.dataDesc("RESIDUE_INVALID_" + (i+1), res, "RESIDUE IS MISSING"));
errors ++;
}
}
// results.put(JSONUtils.data(KEY_SLOPE_DELIVERY, 3.0));
for (String r : JSONUtils.getRequestedResults(getMetainfo()))
{
if ((errors > 0) && (r.equals(RES_SLOPE_DEGRAD)))
results.put(JSONUtils.dataDesc(r, r2run.getResult(r), R2_MISSING_XML_FILES_WARNING_MSG));
else
results.put(JSONUtils.data(r, r2run.getResult(r)));
}
if (errors > 0)
appendMetainfoWarning(R2_MISSING_XML_FILES_WARNING_MSG);
results.put(JSONUtils.data(KEY_CLIMATES, climate));
results.put(JSONUtils.data(KEY_SOILS, soil));
results.put(JSONUtils.data(KEY_MANAGEMENTS, mgmt));
// results.put(JSONUtils.data(KEY_ALT_R2DB, in_ALT_R2DB));
return results;
}
private void createInputFile(File file, JSONObject metainfo, Map<String, JSONObject> param)
throws Exception {
double steepness = JSONUtils.getDoubleParam(param, KEY_STEEPNESS, 0.0);
double length = JSONUtils.getDoubleParam(param, KEY_LENGTH, 0.0);
boolean resolveLoc = JSONUtils.getBooleanParam(param, KEY_RESOLVE_LOCATION, false);
double latitude = JSONUtils.getDoubleParam(param, KEY_LATITUDE, 0.0);
double longitude = JSONUtils.getDoubleParam(param, KEY_LONGITUDE, 0.0);
String climatePtr = JSONUtils.getStringParam(param, KEY_CLIMATES, "");
String soilPtr = JSONUtils.getStringParam(param, KEY_SOILS, "");
String managementFormalName[] = new String[]{};
String contourSystem = JSONUtils.getStringParam(param, KEY_CONTOUR_SYSTEM_PTR, "");
String stripBarrierSystem = JSONUtils.getStringParam(param, KEY_STRIP_BARRIER_SYSTEM_PTR, "");
String hydElemSystem = JSONUtils.getStringParam(param, KEY_HYD_ELEM_SYSTEM_PTR, "");
// String mgmtPtr = JSONUtils.getStringParam(param, KEY_MANAGEMENTS, "");
// if (!mgmtPtr.startsWith("managements\\")) {
// mgmtPtr = "managements\\" + mgmtPtr;
// }
// management conversion
// Make file line separator unix compatible. ???? not sure if needed.
System.setProperty("line.separator", "\n");
//
JSONArray managements = JSONUtils.getJSONArrayParam(param, KEY_MGMTS);
LOG.info("managements array=" + managements.toString());
managementFormalName = new String[managements.length()];
for (int i = 0; i < managements.length(); i++) {
LOG.info("management #" + i);
JSONObject lmod = (JSONObject) managements.get(i);
LOG.info(lmod.toString());
LOG.info("creating Jim's translator.");
lmod2rusle2.Rusle2Translator translator = new lmod2rusle2.Rusle2Translator();
LOG.info("trying to put in the lmod json");
translator.readJsonString(lmod.toString());
LOG.info("trying to translate=" + translator.Translate());
// // extract the element as JSON
// Document lmod_xml = Lmod2Rusle2.readJSON(lmod, false);
// // Translate the file into RUSLE2 format
// Document r2_xml = Lmod2Rusle2.LmodXmlToRusle2Xml(lmod_xml, "F");
Document r2_xml = translator.getDocument();
LOG.info("R2 XML FILE from trhe translator:\n\n" + translator.getRusle2Xml());
LOG.info("R2 XML FILE:\n\n" + translator.getRusle2Xml());
//LOG.info("LMOD XML FILE:\n\n" + lmod_xml.getTextContent());
// if ((lmod_xml != null) && (lmod_xml.getElementById("Filename") != null))
// managementFormalName[i] = lmod_xml.getElementById("Filename").getTextContent();
if ((r2_xml != null) && (r2_xml.getElementsByTagName("Filename") != null)) {
NodeList nl = r2_xml.getElementsByTagName("Filename");
for (int ii = 0; ii < nl.getLength(); ii++) {
LOG.info("filename node=" + nl.item(ii).getTextContent());
managementFormalName[i] = nl.item(ii).getTextContent();
}
}
translator.writeRusle2Xml(getWorkspaceDir().toString(), "lmod_file" + i + ".xml");
operations = translator.getOperationFiles();
vegetations = translator.getVegetationFiles();
residues = translator.getResidueFiles();
for (String operation : translator.getOperationFiles())
System.out.println("operation=" + operation);
for (String vege : translator.getVegetationFiles())
System.out.println("vegetation=" + vege);
for (String residue : translator.getResidueFiles())
System.out.println("residue=" + residue);
}
String altR2db = null;
if (param.get(KEY_ALT_R2DB) != null) {
altR2db = param.get(KEY_ALT_R2DB).getString(VALUE);
}
PostGIS db = PostGIS.singleton();
// If this is a Lat/Lng request, then we need to make a DB lookup
// to obtain the Climate and Soil information and ignore whatever
// may have been passed in
LOG.info("Rulse 2 model request resolveLocation=" + resolveLoc);
//if (resolveLoc) {
if (db != null) {
PostGIS.FileQryResult climate = db.findClimate(latitude, longitude);
if (climate == null) {
LOG.warning("no climate for lat=" + latitude + "\nfor long=" + longitude + "\n");
climatePtr = "";
} else {
climatePtr = climate.file_path + "\\" + climate.file_name;
}
// to do
// use only the first soil for now - later we need to use all of them
String cokey = soilPtr.replace(",", " ");
// strip first and last char
String t2 = cokey.substring(1, cokey.length() - 1);
String delims = "\"";
String tokens[] = t2.split(delims);
LinkedList<String> lst = new LinkedList();
for (String token : tokens) {
if (token.trim().length() > 0) {
lst.add(token);
}
}
cokey = lst.get(0); // first soil only for now
PostGIS.FileQryResult soil = db.findSoilsByCokey(cokey, longitude);
if (soil == null) {
LOG.warning("No soil for lat=" + latitude + "\nfor long=" + longitude + "\n");
appendMetainfoWarning(R2_NO_SOIL_FOR_PROVIDED_COKEY);
soilPtr = "";
} else {
soilPtr = soil.file_path + "\\" + soil.file_name;
}
} else {
LOG.warning("WARNING: Unable to access database to resolve the climate and soil information for this lat/long driven rusle2 model run. Using deafults.");
}
//}
FileOutputStream fos = new FileOutputStream(file);
// to enable/disable romeconsole logging
if (!PRODUCTION_MODE) {
fos.write("Log ON\n".getBytes());
}
fos.write("JSON ON\n".getBytes());
fos.write("RomeInit\n".getBytes());
if (!PRODUCTION_MODE) {
String timingFilename = IO_TIMING_FILENAME + getFilenumber(file.getName()) + IO_TIMING_FILEEXT;
fos.write(("RomeSetTitle \"App.Files.IoLogging\" \"" + timingFilename + "\"\n").getBytes());
}
String r2db = Config.getString("r2.db", "http://oms-db.engr.colostate.edu/r2");
if (altR2db == null) {
fos.write(("DatabaseOpen \"" + r2db + "\"\n").getBytes());
} else {
String altr2db = r2db.substring(0, r2db.lastIndexOf("/")) + "/model-data/" + altR2db;
fos.write(("DatabaseOpen \"" + altr2db + "\"\n").getBytes());
}
fos.write("FilesOpen profiles\\#ENTRY_MODEL PRIVATE\n".getBytes());
fos.write("Activate profiles\\model\n".getBytes());
// OLD VERSION, WHERE WE GOT CLIMATE DIRECTLY FROM NGINX AND DIDN'T MODIFY IT
// fos.write(("RomeFileSetAttrValue CLIMATE_PTR \"" + climatePtr + "\"\n").getBytes());
// NEW VERSION, Where the climate file must be modified to work
fos.write(("FilesOpen \"#XML:" + new File(getWorkspaceDir(), "cli_file" + 0 + ".xml" + "\"\n")).getBytes());
fos.write(("RomeFileSetAttrValue CLIMATE_PTR \"climates\\aaa\"\n").getBytes());
// New version, where we have to process the nginx file so R2 reads it
// climate stub if needed
//fos.write("RomeFileSetAttrValue CLIMATE_PTR \"climates\\USA\\Idaho\\Power County\\ID_Power_R_11\"\n".getBytes());
// soil stub if needed
//fos.write("RomeFileSetAttrValue SOIL_PTR \"soils\\Power County Area, Idaho\\40 Neeley silt loam, 2 to 4 percent slopes\\Neeley silt loam 90%\"\n".getBytes());
// fos.write(("RomeFileSetAttrValue MAN_BASE_PTR \"" + mgmtPtr + "\"\n").getBytes());
// multiple managements
// to do
// Don't worry about multiple mgmts for now
// for (int i = 0; i < managements.length(); i++) {
// //reference the lmod file(s)
// fos.write(("RomeFileSetAttrValue MAN_PTR \"" + new File(getWorkspace(), "lmod_file" + i + ".xml" + "\"\n")).getBytes());
// }
if ((managements != null) && (managements.length() >= 1)) {
fos.write(("FilesOpen \"#XML:" + new File(getWorkspaceDir(), "lmod_file" + 0 + ".xml" + "\"\n")).getBytes());
// fos.write(("Activate \"" + managementFormalName[0] + "\"\n").getBytes());
fos.write(("RomeFileSetAttrValue MAN_BASE_PTR \"" + managementFormalName[0] + "\"\n").getBytes());
mgmt = managementFormalName[0];
}
// to do
// commented out - using default for now
fos.write(("FilesOpen \"#XML:" + new File(getWorkspaceDir(), "soils_file0.xml" + "\"\n")).getBytes());
String soilHttpPtr = r2db + "/" + soilPtr.replace("\\","/") + ".xml";
r2run.prepareSoilsFile(soilHttpPtr, new File(getWorkspaceDir(), "soils_file0.xml"), false);
String cliHttpPtr = r2db + "/" + climatePtr.replace("\\", "/") + ".xml";
r2run.prepareClimateFile(cliHttpPtr, new File(getWorkspaceDir(), "cli_file0.xml"));
fos.write(("RomeFileSetAttrValue SOIL_PTR \"" + soilPtr + ".xml\"\n").getBytes());
fos.write(("RomeFileSetAttrValue SLOPE_HORIZ \"" + length + "\"\n").getBytes());
fos.write(("RomeFileSetAttrValue SLOPE_STEEP \"" + steepness + "\"\n").getBytes());
if ((contourSystem != null) && (contourSystem.length() > 1)) {
fos.write(("RomeFileSetAttrValue CONTOUR_SYSTEM_PTR \"" + contourSystem + "\"\n").getBytes());
}
if ((stripBarrierSystem != null) && (stripBarrierSystem.length() > 1)) {
fos.write(("RomeFileSetAttrValue STRIP_BARRIER_SYSTEM_PTR \"" + stripBarrierSystem + "\"\n").getBytes());
}
if ((hydElemSystem != null) && (hydElemSystem.length() > 1)) {
fos.write(("RomeFileSetAttrValue HYD_ELEM_SYSTEM_PTR \"" + hydElemSystem + "\"\n").getBytes());
}
fos.write("RomeEngineRun\n".getBytes());
for (String r : JSONUtils.getRequestedResults(metainfo)) {
fos.write(("RomeFileGetAttrValue " + r + "\n").getBytes());
}
// fos.write(("RomeFileGetAttrValue CONTOUR_SYSTEM_PTR\n").getBytes());
// fos.write(("RomeFileGetAttrValue STRIP_BARRIER_SYSTEM_PTR\n").getBytes());
// fos.write(("RomeFileGetAttrValue HYD_ELEMENT_SYSTEM_PTR\n").getBytes());
// optionally only include this line when reporting is requested to save service execution time
fos.write("READ \"rusle2_report.rsh\"\n".getBytes());
fos.write("RomeExit\n".getBytes());
fos.write("Exit\n".getBytes());
fos.close();
// Unpack the report rsh file in the work space dir
Binaries.unpackResourceAbsolute("/bin/win-x86/" + REPORT_RSH_FILENAME, getWorkspaceDir().toString() + "/" + REPORT_RSH_FILENAME);
climate = climatePtr;
soil = soilPtr;
// mgmt = mgmtPtr;
LOG.info("R2 script: " + file.toString());
}
private String getFilenumber(String filename) {
return filename.substring(R2_TMP_FILENAME.length(), filename.length() - R2_TMP_FILEEXT.length());
}
@Override
protected JSONArray createReport() throws Exception {
String sessionWorkDir = getWorkspaceDir().toString();
String reportJSON = Binaries.unpackResourceAbsolute("/bin/win-x86/" + REPORT_JSON_TEMPLATE_FILENAME, sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME).toString();
File reportTemplate = new File(sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME);
String sReportJSON = FileUtils.readFileToString(reportTemplate);
JSONArray reportItemsFromTemplate = new JSONArray(sReportJSON);
JSONArray reportItemsOutput = new JSONArray();
for (int i = 0; i < reportItemsFromTemplate.length(); i++) {
JSONObject obj = (JSONObject) reportItemsFromTemplate.get(i);
String itemName = obj.getString(REPORT_NAME);
String dimension = JSONUtils.getJSONString(obj, REPORT_DIM, null);
String type = JSONUtils.getJSONString(obj, REPORT_TYPE, "");
//LOG.info("Attempting to fetch results for:" + itemName);
String value = r2run.getResult("\"" + itemName + "\"");
//LOG.info("the value='" + value + "'");
if ((value != null) && (value.equals("null"))) {
obj.put(REPORT_VALUE, (String) null);
// if there is no value, then item should be removed from the array
} else {
if ((dimension != null) && (dimension.length() > 0) && (value != null) && (!value.equals("null"))) {
try {
obj.put(REPORT_VALUE, new JSONArray(value));
} catch (JSONException je) {
obj = processReportElement(obj, type, value);
}
} else {
obj = processReportElement(obj, type, value);
}
obj.remove(REPORT_TYPE);
//LOG.info("units output is='" + JSONUtils.getString(obj,REPORT_UNITS,null) + "'");
//LOG.info("obj to add=" + obj.toString());
reportItemsOutput.put(obj);
}
}
return reportItemsOutput;
//LOG.info("report jsonarray=" + reportItemsOutput.toString());
//LOG.info("Rusle2 report output obj=" + reportObj.toString());
}
private JSONObject processReportElement(JSONObject obj, String type, String value) throws Exception {
if ((type.equals("TEXT")) || (type.equals("FILENAME")) || (type.equals("DATE"))) {
// because the RomeShell returns escaped strings, and the JSONObject in Java re-escapes them
// we have to remove one level of "escaping" !!!
String newString = StringEscapeUtils.unescapeJava(value);
obj.put(REPORT_VALUE, Services.removeFirstLastChar(newString));
}
if (type.equals("INTEGER") && (value != null) && (!value.equals("null")) && (!value.equals("\"\""))) {
obj.put("value", new Double(value).intValue());
}
if (type.equals("REAL") && (value != null)) {
if ((!value.equals("null")) && (!value.equals("\"\""))) {
obj.put(REPORT_VALUE, new Double(value).doubleValue());
}
if (value.equals("\"\"")) {
obj.put(REPORT_VALUE, new Double(0).doubleValue());
}
}
if (type.equals("")) {
obj.put(REPORT_VALUE, value);
}
return obj;
}
}