UIService.java [src/csip] Revision: 71821307bfe742c00c6dc582c171224a9ac59935 Date: Fri Apr 21 11:46:19 MDT 2017
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* a Model-as-a-Service framework, API and application suite.
*
* 2012-2017, 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.net.URI;
import java.text.DateFormat;
import java.util.*;
import java.util.logging.*;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.codehaus.jettison.json.JSONException;
/**
* ControlService
*
* @author od
*/
@Path("ui")
public class UIService {
static final String ARCHIVE_HEADER = "<tr bgcolor=\"#D5DADE\">"
+ "<th>suid</th>"
+ "<th>status</th>"
+ "<th>req IP</th>"
+ "<th>archived at</th>"
+ "<th>expiration</th>"
+ "<th>service</th>"
+ "<th>files</th>"
+ "<th> </th>"
+ "</tr>\n";
static final String ARCHIVE_ROW = "<tr bgcolor=\"%6$s\">"
+ "<td>%1$s</td>"
+ "<td>%2$s</td>"
+ "<td><a href=\"http://freegeoip.net/?q=%4$s&map=1\">%3$s</a></td>"
+ "<td>%4$s</td>"
+ "<td>%8$s</td>"
+ "<td>%5$s</td>"
+ "<td><a href=\"/%7$s/a/download/%1$s\">download</a></td>"
+ "<td><a href=\"/%7$s/a/remove/%1$s\">del</a> </td>"
+ "</tr>\n";
static final String HEADER = "<tr bgcolor=\"#D5DADE\">"
+ "<th colspan=2>suid/json</th>"
+ "<th>status</th>"
+ "<th>node IP</th>"
+ "<th>req IP</th>"
+ "<th>start</th>"
+ "<th>expiration</th>"
+ "<th>runtime</th>"
+ "<th>service</th>"
+ "<th>attach</th>"
+ "<th> </th>"
+ "</tr>\n";
//
static final String DEF_BACKGROUND = "#F0F0F0";
static final String ZOMBIE_BACKGROUND = "#E19BFF"; //purple
static final String FAILED_BACKGROUND = "#FFCFD8"; // red
static final String FINISHED_BACKGROUND = "#D7FFC2"; // green
static final Logger LOG = Logger.getLogger(UIService.class.getName());
static StringBuilder appendLine(StringBuilder b, String col, String... td) {
b.append("<tr bgcolor=\"").append(col).append("\">");
for (String t : td) {
b.append("<td>").append(t).append("</td>");
}
b.append("</tr>\n");
return b;
}
static String getIPLink(String ip) {
if ("127.0.0.1".equals(ip)) {
return ip;
}
StringBuilder b = new StringBuilder();
b.append("<a href=\"http://freegeoip.net/?q=").append(ip).append("&map=1\">").append(ip).append("</a></td>");
return b.toString();
}
static String getLinks(String[] row, String context) {
boolean service_done = false;
String status = row[1];
String suid = row[0];
String hasReport = row[9];
if (status.equals(ModelDataService.FAILED) || status.equals(ModelDataService.FINISHED)) {
service_done = true;
}
StringBuilder b = new StringBuilder();
b.append("<a href=\"/").append(context).append("/q/req/").append(suid).append("\">req</a>");
if (service_done) {
b.append(" <a href=\"/").append(context).append("/q/res/").append(suid).append("\">res</a>");
if (Boolean.parseBoolean(hasReport)) {
b.append(" <a href=\"/").append(context).append("/r/").append(suid).append("\">rep</a>");
}
}
return b.toString();
}
String getLogLink(String context, String id) {
return "<a href=\"/" + context + "/q/log/" + id + "\">log</a>";
}
/////////////////////////////////
/**
* Session UI
*
* @param u
* @param skip
* @param limit
* @param sort
* @param order
* @return
* @throws Exception
*/
@GET
@Path("")
@Produces(MediaType.TEXT_HTML)
public String getSessions(@Context UriInfo u, @Context HttpServletRequest req,
@DefaultValue("0")
@QueryParam("skip") int skip,
@DefaultValue("25")
@QueryParam("limit") int limit,
@DefaultValue("tst")
@QueryParam("sort") String sort,
@DefaultValue("d")
@QueryParam("order") String order)
throws Exception {
Utils.checkRemoteAccessACL(req);
URI uri = u.getBaseUri();
String path = uri.getPath();
String context = uri.getPath();
context = context.substring(1, context.indexOf('/', 2));
SessionStore s = Config.getSessionStore();
long start = System.currentTimeMillis();
Set<String> keys = s.keys(skip, limit, sort, order.equalsIgnoreCase("a"));
Date now = new Date();
List<String[]> l = new ArrayList<>();
for (String id : keys) {
ModelSession v = s.getSession(id);
if (v == null) {
continue; // this should not happen
}
Date startsim = Dates.parse(v.getTstamp());
String cputime = v.getCputime();
String duration = cputime.isEmpty() ? Dates.duration(startsim, now) : Dates.duration(Long.parseLong(cputime));
l.add(new String[]{
id.substring(id.indexOf(':') + 1),
v.getStatus(),
v.getNodeIP(),
v.getReqIP(),
v.getTstamp(),
v.getExpDate(),
duration,
v.getService(),
Arrays.toString(v.getAttachments()),
Boolean.toString(v.hasReport())
});
}
int this_start = skip + 1;
int this_end = keys.size() + skip;
int prev_start = this_start - limit - 1;
int next_start = this_end;
long count = s.getCount();
String prev_link = prev_start < 0 ? "" : "href=\"" + path + "ui?skip=" + prev_start + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"";
String next_link = count == this_end ? "" : "href=\"" + path + "ui?skip=" + next_start + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"";
String prev = "<a " + prev_link + "><</a>";
String next = "<a " + next_link + ">></a>";
String begin = "<a " + "href=\"" + path + "ui?skip=" + 0 + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"" + "><<</a>";
String end = "<a " + "href=\"" + path + "ui?skip=" + (count - count % limit) + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"" + ">>></a>";
StringBuilder b = new StringBuilder();
b.append("<html><head><title>Sessions - " + context + "</title></head><body>");
b.append("<table width=\"100%\" border=\"0\" cellpadding=\"1\"><tr>");
b.append("<td width=\"33%\">");
b.append("<a href=\"" + path + "ui/archive\">Archive</a> | ");
b.append("<a href=\"" + path + "\">Services</a> ");
b.append("<a href=\"" + path + "ui/conf\">Config</a> | ");
b.append("<a href=\"" + path + "q/check\">Check</a> ");
b.append("<a href=\"" + path + "c/clean\">Clean</a>");
// b.append("<a href=\"" + path + "a/clean\">CleanArchive</a> ");
b.append("</td>");
b.append("<td width=\"33%\">");
b.append("<p align=\"center\">" + begin + " " + prev + " "
+ this_start + " - " + this_end + " (of " + count + ") " + next + " " + end + "</p>");
b.append("</td width=\"33%\">");
b.append("<td><p align=\"right\"> <b>Sessions</b> at ");
b.append(Dates.nowISO(Config.getString("csip.timezone", "UTC")));
b.append("</p></td>");
b.append("</tr></table>");
b.append("<table width=\"100%\" border=\"0\" cellpadding=\"5\">");
b.append("<tbody align=\"left\" style=\"font-family:monospaced;\">");
b.append(HEADER);
DateFormat df = Dates.newISOFormat();
String no = df.format(new Date());
for (String[] c : l) {
String status = c[1];
String color = DEF_BACKGROUND;
String exp = c[5];
if (status.equals(ModelDataService.FAILED)) {
color = FAILED_BACKGROUND;
} else if (status.equals(ModelDataService.FINISHED)) {
color = FINISHED_BACKGROUND;
}
if (!exp.isEmpty() && exp.compareTo(no) < 0) { // check is zombie that is past expiration
color = ZOMBIE_BACKGROUND;
}
String links = getLinks(c, context);
String iplink = getIPLink(c[3]);
String logLink = getLogLink(context, c[0]);
String canLink = "<a href=\"/" + context + "/c/cancel/" + c[0] + "\">stop</a>";
appendLine(b, color, c[0], links, c[1], c[2], iplink, c[4], c[5], c[6], c[7], c[8], logLink + " " + canLink);
}
b.append("</tbody></table>");
long end_ = System.currentTimeMillis();
b.append("<em>" + (end_ - start) + " ms</em>");
b.append("</body><html>");
return b.toString();
}
/**
* Archive UI.
*
* @param u
* @param skip
* @param limit
* @param sort
* @param order
* @return
* @throws Exception
*/
@GET
@Path("archive")
@Produces(MediaType.TEXT_HTML)
public String getArchives(@Context UriInfo u, @Context HttpServletRequest req,
@DefaultValue("0") @QueryParam("skip") int skip,
@DefaultValue("25") @QueryParam("limit") int limit,
@DefaultValue("ctime") @QueryParam("sort") String sort,
@DefaultValue("d") @QueryParam("order") String order) // descending order
throws Exception {
Utils.checkRemoteAccessACL(req);
if (!Config.isArchiveEnabled()) {
return "<html><body>Archive not enabled.</body></html>";
}
URI uri = u.getBaseUri();
String path = uri.getPath();
String context = uri.getPath();
context = context.substring(1, context.indexOf('/', 2));
ArchiveStore s = Config.getArchiveStore();
Set<String> keys = s.keys(skip, limit, sort, order.equalsIgnoreCase("a"));
long start = System.currentTimeMillis();
List<String[]> l = new ArrayList<>();
for (String id : keys) {
ModelArchive v = s.getArchive(id);
if (v == null) {
continue; // this should not happen
}
l.add(new String[]{
id,
v.getStatus(),
v.getReqIP(),
v.getCtime(),
v.getService(),
v.getEtime()
});
}
int this_start = skip + 1;
int this_end = keys.size() + skip;
int prev_start = this_start - limit - 1;
int next_start = this_end;
String prev_link = prev_start < 0 ? "" : "href=\"" + path + "ui/archive?skip=" + prev_start + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"";
String next_link = s.getCount() == this_end ? "" : "href=\"" + path + "ui/archive?skip=" + next_start + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"";
String prev = "<a " + prev_link + "><</a>";
String next = "<a " + next_link + ">></a>";
String begin = "<a " + "href=\"" + path + "ui/archive?skip=" + 0 + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"" + "><<</a>";
String end = "<a " + "href=\"" + path + "ui/archive?skip=" + (s.getCount() - s.getCount() % limit) + "&limit=" + limit + "&sort=" + sort + "&order=" + order + "\"" + ">>></a>";
// create the page.
StringBuilder b = new StringBuilder();
b.append("<html><head><title> Archives - " + context + "</title></head><body>");
b.append("<table width=\"100%\" border=\"0\" cellpadding=\"1\"><tr>");
b.append("<td width=\"33%\">");
b.append("<a href=\"" + path + "ui\">Sessions</a> | ");
b.append("<a href=\"" + path + "a/clean\">Clean</a> ");
b.append("</td>");
b.append("<td width=\"33%\">");
b.append("<p align=\"center\">" + begin + " " + prev + " "
+ this_start + " - " + this_end + " (of " + s.getCount() + ") " + next + " " + end + "</p>");
b.append("</td width=\"33%\">");
b.append("<td><p align=\"right\"> <b>Archives</b> at ");
b.append(Dates.nowISO(Config.getString("csip.timezone", "UTC")));
b.append("</p></td>");
b.append("</tr></table>");
b.append("<table width=\"100%\" border=\"0\" cellpadding=\"5\">");
b.append("<tbody align=\"left\" style=\"font-family:monospaced;\">");
b.append(ARCHIVE_HEADER);
for (String[] c : l) {
String color = c[1].equals(ModelDataService.FAILED) ? FAILED_BACKGROUND : FINISHED_BACKGROUND;
b.append(String.format(ARCHIVE_ROW, c[0], c[1], c[2], c[3], c[4], color, context, c[5]));
}
b.append("</tbody></table>");
long endtime = System.currentTimeMillis();
b.append("<em>" + (endtime - start) + " ms</em>");
b.append("</body><html>");
return b.toString();
}
/**
* Get the service configuration
* @return
* @throws JSONException
*/
@GET
@Path("conf")
@Produces(MediaType.TEXT_HTML)
public String getHtml(@Context UriInfo u, @Context HttpServletRequest req) throws JSONException {
Utils.checkRemoteAccessACL(req);
Properties p = Config.getMergedProperties();
Map<Object, Object> m = new TreeMap<>(p);
StringBuilder o = new StringBuilder();
for (Object key : m.keySet()) {
String val = p.get(key).toString();
String key_ = key.toString();
val = val.replaceAll("password=.+[;]?", "password=****;");
if (key_.toLowerCase().contains("password")) {
val = "****";
}
o.append("<b>" + key_ + "</b>: " + val + "<br>");
}
// Add chunk DBs if applicable
Collection<PostgresChunk> pcs = Config.getPostgresChunks();
// Add PGChunks to output if they exist...
if ((pcs != null) && (pcs.size() > 0)) {
int i = 1;
for (PostgresChunk ch : pcs) {
o.append("<b>pgdb" + i + "</b>: " + ch.getJSON().toString() + "<br>");
i++;
}
}
return "<html><body><h1>CSIP Configuration </h1>" + Services.LOCAL_IP_ADDR + "<p>" + o.toString() + "</p></body></html>";
}
}