ControlService.java [src/csip] Revision: default Date:
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* a Model-as-a-Service framework, API and application suite.
*
* 2012-2022, Olaf David and others, OMSLab, Colorado State University.
*
* OMSLab licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
*/
package csip;
import csip.utils.*;
import java.io.*;
import java.text.DateFormat;
import java.util.*;
import java.util.logging.*;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.client.*;
import javax.ws.rs.core.*;
import org.apache.commons.io.*;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
/**
* ControlService. Use services in this class to control the backend.
*
* @author od
*/
@Path("c")
public class ControlService {
static final Logger LOG = Config.LOG;
/**
* Clean up the whole session store.
*
* @param uriInfo uri info
* @param req servlet request
* @return string of total, removed, null, skipped sessions
* @throws JSONException when analyzing/building JSONObjects
* @throws Exception when analyzing the servlet request
*/
@GET
@Path("clean")
@Produces(MediaType.APPLICATION_JSON)
public String getClean(@Context UriInfo uriInfo, @Context HttpServletRequest req) throws JSONException, Exception {
Utils.checkRemoteAccessACL(req);
SessionStore s = Config.getSessionStore();
Set<String> keys = s.keys(0, Integer.MAX_VALUE, null, true);
int isnull = 0;
int removed = 0;
int skipped = 0;
for (String id : keys) {
String result = remove(uriInfo, req, id);
JSONObject r = new JSONObject(result);
if (r.has("isnull")) {
isnull++;
} else if (r.has("removed")) {
removed++;
} else if (r.has("skipped")) {
skipped++;
}
}
JSONObject o = new JSONObject();
o.put("sessions.total", keys.size());
o.put("sessions.removed", removed);
o.put("sessions.isnull", isnull);
o.put("sessions.skipped", skipped);
return o.toString(4);
}
/**
* Remove the session if expired. cleanup also the workspace and results.
*
* @param uriInfo uri info
* @param req servlet request
* @param suid session uid
* @return string of cleanup session
* @throws Exception when getting ModelSession and building JSONObject
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("clean/{suid}")
public String remove(@Context UriInfo uriInfo, @Context HttpServletRequest req, @PathParam("suid") String suid) throws Exception {
Utils.checkRemoteAccessACL(req);
ModelSession session = Config.getSessionStore().getSession(suid);
JSONObject o = new JSONObject();
if (session == null) {
o.put("isnull", 1);
return o.toString();
}
if (session.getNodeIP().equals(Services.LOCAL_IP_ADDR)) {
DateFormat df = Dates.newISOFormat();
String now = df.format(new Date());
String exp = session.getExpDate();
if (exp.compareTo(now) < 0) { // check is zombie that is passt expiration
File res = Services.getResultsDir(suid);
if (res.exists()) {
FileUtils.deleteQuietly(res);
}
File work = Services.getWorkDir(suid);
if (work.exists()) {
FileUtils.deleteQuietly(work);
}
Config.getSessionStore().removeSession(suid);
o.put("removed", 1);
return o.toString();
}
o.put("skipped", 1);
return o.toString();
} else {
String redirect = Utils.replaceHostinURI(uriInfo.getBaseUri(), session.getNodeIP());
LOG.info("Redirect query to: " + redirect + "c/clean/" + suid);
javax.ws.rs.client.Client client = ClientBuilder.newClient();
WebTarget service = client.target(redirect + "c/clean/" + suid);
Response response = service.request(MediaType.APPLICATION_JSON).get();
return response.readEntity(String.class);
}
}
/**
* Get the Configuration as JSON.
*
* @param uriInfo uri info
* @param req servlet request
* @return the configuration as a String
* @throws JSONException when building JSONObject configuration
*/
@GET
@Path("conf")
@Produces(MediaType.APPLICATION_JSON)
public String getJSON(@Context UriInfo uriInfo, @Context HttpServletRequest req) throws JSONException {
Utils.checkRemoteAccessACL(req);
Properties p = Config.getMergedProperties();
Map<Object, Object> m = new TreeMap<>();
for (Object key : p.keySet()) {
String key_ = key.toString();
String val = p.get(key).toString();
val = val.replaceAll("password=.+[;]?", "password=****;");
if (key_.toLowerCase().contains("password")) {
val = "****";
}
m.put(key_, val);
}
JSONObject theconfig = new JSONObject(m);
return theconfig.toString(2);
}
// update from properties, this is for silent property update.
static void updateConfig(Properties p1) {
if (p1.isEmpty()) {
return;
}
Properties p = Config.getProperties();
synchronized (p) {
for (String key : p1.stringPropertyNames()) {
String val = p1.getProperty(key);
LOG.info("Set Config: " + key + " = " + val);
p.setProperty(key, val);
}
Config.update();
}
}
// update from YAML, this is for silent property update.
static void updateConfig(Map<String, String> p1) {
if (p1.isEmpty()) {
return;
}
Properties p = Config.getProperties();
synchronized (p) {
for (String key : p1.keySet()) {
String val = p1.get(key);
if (val != null) {
LOG.info(" " + key + " = " + val);
p.setProperty(key, val);
}
}
Config.update();
}
}
static String updateConfig(String inputObj) throws JSONException {
Properties p = Config.getProperties();
synchronized (p) {
try {
JSONObject o = new JSONObject(inputObj);
Iterator<?> i = o.keys();
while (i.hasNext()) {
String key = i.next().toString();
if (key.trim().equals("#") || key.trim().equals("//") || key.toLowerCase().startsWith("note")) {
continue;
}
LOG.info(" Set Config: " + key + " = " + o.getString(key));
p.setProperty(key, o.getString(key));
}
} catch (JSONException | NumberFormatException ex) {
LOG.log(Level.WARNING, null, ex);
return "Error: " + ex.getMessage();
}
Config.update();
}
LOG.log(Level.INFO, "config complete");
JSONObject theconfig = new JSONObject(new TreeMap<>(p));
return theconfig.toString(2);
}
/**
* Change the configuration.
*
* @param uriInfo uri info
* @param req servlet request
* @param inputObj request as string
* @return updated configuration as string
* @throws JSONException when updating the configuration
*/
@POST
@Path("conf")
@Consumes(MediaType.APPLICATION_JSON)
public String putJSON(@Context UriInfo uriInfo, @Context HttpServletRequest req, String inputObj) throws JSONException {
LOG.log(Level.INFO, "HTTP/POST {0}", uriInfo.getRequestUri().toString() + " " + inputObj);
Utils.checkRemoteAccessACL(req);
return updateConfig(inputObj);
}
/**
* Cancel the request.
*
* @param uriInfo uri info
* @param suid session uid
* @return error/canceled/unknown suid
* @throws Exception when getting ModelSession, modifying/building uri
*/
@GET
@Path("cancel/{suid}")
@Produces(MediaType.APPLICATION_JSON)
public String cancel(@Context UriInfo uriInfo, @PathParam("suid") String suid) throws Exception {
LOG.log(Level.INFO, "HTTP/GET {0}", uriInfo.getRequestUri().toString());
ModelSession session = Config.getSessionStore().getSession(suid);
if (session == null) {
return JSONUtils.error("suid unknown: " + suid).toString();
}
if (session.getNodeIP().equals(Services.LOCAL_IP_ADDR)) {
// check the local box first.
for (ModelDataService.Task modelTask : Config.getModelTasks()) {
if (modelTask.getService().getSUID().equals(suid)) {
modelTask.cancel();
LOG.log(Level.INFO, "canceled " + suid);
return JSONUtils.ok(suid + " canceled").toString();
}
}
LOG.log(Level.WARNING, " not found to cancel: " + suid);
return JSONUtils.error("suid unknown: " + suid).toString();
} else {
// redrection here
// where is that session?
String redirect = Utils.replaceHostinURI(uriInfo.getBaseUri(), session.getNodeIP());
javax.ws.rs.client.Client client = ClientBuilder.newClient();
WebTarget service = client.target(UriBuilder.fromUri(redirect + "c/cancel/" + suid).build());
return service.request().get().readEntity(String.class);
}
}
@GET
@Path("cleanresults")
@Produces(MediaType.APPLICATION_JSON)
public String dropResults(@Context UriInfo uriInfo) {
LOG.log(Level.INFO, "HTTP/GET {0}", uriInfo.getRequestUri().toString());
Config.getResultStore().purge();
return JSONUtils.ok("purged").toString();
}
}