Java tutorial
/* * GeoPeril - A platform for the computation and web-mapping of hazard specific * geospatial data, as well as for serving functionality to handle, share, and * communicate threat specific information in a collaborative environment. * * Copyright (C) 2013 GFZ German Research Centre for Geosciences * * 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://apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * Contributors: * Johannes Spazier (GFZ) - initial implementation * Sven Reissland (GFZ) - initial implementation * Martin Hammitzsch (GFZ) - initial implementation */ package GeoHazardServices; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.TimeZone; import java.util.UUID; import javax.inject.Singleton; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.CookieParam; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.bson.types.ObjectId; import FloodPrototype.FloodProvider; import FloodPrototype.FloodTask; import FloodPrototype.Location; import Misc.IDataProvider; import Misc.User; import Misc.GsonUTCdateAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import com.mongodb.AggregationOutput; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.MongoException; import com.mongodb.ServerAddress; import com.mongodb.util.Base64Codec; class Inst extends User { public String secret; public Inst(DBObject obj) { super(obj); this.name = (String) obj.get("name"); this.secret = (String) obj.get("secret"); this.inst = this.name; } } class CompId { private String compId; public String inst; public String id; public long refineId; public CompId(String compId) { this.compId = compId; setSingleIds(); } public CompId(String inst, String id, long refineId) { this.inst = inst; this.id = id; this.refineId = refineId; this.compId = getCompId(); } private String getCompId() { return id + "_" + inst + "_" + refineId; } private void setSingleIds() { String[] parts = compId.split("_"); int len = parts.length; inst = parts[--len]; refineId = Integer.valueOf(parts[--len]); id = parts[0]; for (int i = 1; i < len; i++) id += "_" + parts[i]; } @Override public String toString() { return compId; } } class DateComparator implements Comparator<DBObject> { private String key; private int order; public DateComparator(String key, int order) { this.key = key; this.order = order; } @Override public int compare(DBObject o1, DBObject o2) { Date d1 = (Date) o1.get(key); Date d2 = (Date) o2.get(key); return d1.compareTo(d2) * order; } } @Path("") @Singleton /* Use one global instance of this class instead of creating a new one for each request */ public class Services { private IScheduler scheduler; private ArrayList<WorkerThread> worker; private MongoClient mongoClient; private DB db; private Gson gson; private Map<String, Inst> institutions; private final int STATUS_FAILED = -1; private final int STATUS_NO_COMP = -2; private final int STATUS_ABORT = -3; private Map<String, IDataProvider> providers; public Services() { System.out.println("Constructor"); Locale.setDefault(new Locale("en", "US")); scheduler = new FairScheduler(); worker = new ArrayList<WorkerThread>(); URL config; try { /* Try to load environment from context. */ javax.naming.Context initCtx = new InitialContext(); config = new File((String) initCtx.lookup("java:comp/env/config")).toURI().toURL(); } catch (NamingException e1) { /* Otherwise, use the local properties file. */ config = this.getClass().getClassLoader().getResource("config.properties"); } catch (MalformedURLException e) { System.err.println("Malformed URL in environment context."); e.printStackTrace(); return; } try { Properties prop = new Properties(); prop.load(config.openStream()); String dbname = prop.getProperty("db"); String url = prop.getProperty("url"); if (dbname == null || url == null) throw new IOException("Could not load properties."); String nodes[] = url.split(","); List<ServerAddress> addresses = new ArrayList<ServerAddress>(); for (String node : nodes) addresses.add(new ServerAddress(node)); System.out.println(addresses); mongoClient = new MongoClient(addresses); db = mongoClient.getDB(dbname); } catch (IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); return; } loadSettings(); /* Start scheduler thread. */ new Thread(scheduler).start(); loadInstitutions(); /* Required to deliver dates in UTC instead of simply dropping the time zone as per default !!! */ gson = new GsonBuilder().registerTypeAdapter(Date.class, new GsonUTCdateAdapter()).create(); /* TODO: find right place */ providers = new HashMap<String, IDataProvider>(); providers.put("floodsim", new FloodProvider(db)); Listener.registerService(this); } public void destroy() { mongoClient.close(); for (WorkerThread w : worker) w.stop(); } private void loadSettings() { System.out.println("Load settings..."); DBCollection coll = db.getCollection("settings"); DBCursor cursor = coll.find(new BasicDBObject("type", "parameter")); for (DBObject obj : cursor) { String name = (String) obj.get("name"); String value = (String) obj.get("value"); GlobalParameter.map.put(name, value); System.out.println("Parameter " + name + ": " + value); } cursor.close(); cursor = coll.find(new BasicDBObject("type", "jet_color")); for (DBObject obj : cursor) { Double threshold = (Double) obj.get("threshold"); String color = (String) obj.get("color"); GlobalParameter.jets.put(threshold, color); System.out.println("Tsunami-Jet-Threshold " + threshold + ": " + color); } cursor.close(); DBObject urls = coll.findOne(new BasicDBObject("type", "urls")); GlobalParameter.wsgi_url = (String) urls.get("wsgi"); int j = 0; cursor = coll.find(new BasicDBObject("type", "worker")); for (DBObject obj : cursor) { String hardware = (String) obj.get("hardware"); String user = (String) obj.get("user"); String host = (String) obj.get("host"); String dir = (String) obj.get("dir"); String args = (String) obj.get("args"); /* MongoDB stores all integer values as Long (bug?), so convert back here */ Integer count = ((Long) obj.get("count")).intValue(); int priority = ((Long) obj.get("priority")).intValue(); boolean remote = (boolean) obj.get("remote"); int slots[] = getSlots(obj.get("slots"), count); if (count == null) count = 1; System.out.print("Worker " + count + "x " + hardware + " @ " + priority); if (remote) System.out.print(", Remote: " + user + "@" + host + ":" + dir); System.out.println(", Args: " + args); for (int i = 0; i < count; i++, j++) { WorkerThread thread; try { thread = new WorkerThread(scheduler, mongoClient.getServerAddressList(), db.getName(), GlobalParameter.map.get("localdir") + "/w" + j); } catch (IOException e) { System.err.println("Error: Could not create worker thread."); e.printStackTrace(); continue; } thread.setHardware(hardware); thread.setArgs(args); thread.setRemote(user, host, dir + i); thread.setPriority(priority); thread.setSlot(slots[i]); worker.add(thread); thread.start(); } } cursor.close(); } /* Input: Either integer object with single slot value or an array of values. */ private int[] getSlots(Object obj, int count) { int[] slots = new int[count]; if (obj instanceof Number) { Arrays.fill(slots, ((Number) obj).intValue()); } else if (obj instanceof ArrayList<?>) { int idx = 0; for (Object slot : (ArrayList<?>) obj) { slots[idx++] = ((Number) slot).intValue(); } } return slots; } private void loadInstitutions() { institutions = new HashMap<String, Inst>(); DBCollection coll = db.getCollection("institutions"); DBCursor cursor = coll.find(); for (DBObject obj : cursor) { Inst inst = new Inst(obj); institutions.put(inst.name, inst); } cursor.close(); } private boolean checkParams(HttpServletRequest request, Object[] list) { for (Object o : list) { if (o == null) return false; } return true; } private Object getField(DBObject obj, String field) { String[] parts = field.split("\\."); Object sub = obj; for (String p : parts) { if (sub == null) return null; if (!p.matches("d+")) { sub = ((DBObject) sub).get(p); } else { try { sub = ((BasicDBList) sub).get(Integer.valueOf(p)); } catch (IndexOutOfBoundsException e) { return null; } } } return sub; } private boolean checkPerm(User user, String perm) { if (user == null) return false; DBObject obj = db.getCollection("users").findOne(new BasicDBObject("_id", user.objId)); if (obj == null) return false; DBObject permObj = (DBObject) obj.get("permissions"); if (permObj == null) return false; Boolean set = (Boolean) permObj.get(perm); if (set == null || set == false) return false; return true; } private String _computeById(User user, String evtid, Integer dur, Integer accel, Integer gridres, String algo) { DBObject eq = db.getCollection("eqs").findOne(new BasicDBObject("_id", evtid)); if (eq == null) return null; BasicDBObject process = new BasicDBObject("process", new BasicDBList()); BasicDBObject set = new BasicDBObject("$set", process); db.getCollection("eqs").update(eq, set); /* extract properties to pass them to the request method */ BasicDBObject prop = (BasicDBObject) eq.get("prop"); double lat = prop.getDouble("latitude"); double lon = prop.getDouble("longitude"); double dip = prop.getDouble("dip"); double strike = prop.getDouble("strike"); double rake = prop.getDouble("rake"); double depth = prop.getDouble("depth"); Date date = prop.getDate("date"); EQParameter eqp; double mag = 0.0; double slip = 0.0; double length = 0.0; double width = 0.0; if (prop.get("magnitude") == null) { slip = prop.getDouble("slip"); length = prop.getDouble("length"); width = prop.getDouble("width"); eqp = new EQParameter(lon, lat, slip, length, width, depth, dip, strike, rake, date); } else { mag = prop.getDouble("magnitude"); eqp = new EQParameter(lon, lat, mag, depth, dip, strike, rake, date); } if (accel == null) accel = 1; /* start request */ EQTask task = new EQTask(eqp, evtid, user, dur, accel, gridres); task.algo = algo; task.setSlots(IScheduler.SLOT_NORMAL, IScheduler.SLOT_EXCLUSIVE); return request(evtid, task); } @POST @Path("/computeById") @Produces(MediaType.APPLICATION_JSON) public String computeById(@Context HttpServletRequest request, @FormParam("inst") String inst, @FormParam("secret") String secret, @FormParam("id") String id, @FormParam("refineId") Long refineId, @FormParam("dur") Integer dur, @FormParam("accel") Integer accel, @FormParam("apikey") String apikey, @FormParam("evtid") String evtid, @FormParam("raw") @DefaultValue("0") Integer raw, @FormParam("gridres") Integer gridres, @FormParam("dt_out") @DefaultValue("10") Integer dt_out, @FormParam("algo") @DefaultValue("easywave") String algo) { /* Check for invalid parameter configurations. */ if ((inst != null || secret != null) && apikey != null) return jsfailure("Don't mix 'apikey' and 'secret'."); /* Support 'inst' and 'secret' for compatibility reasons. */ if (inst != null && secret != null) { /* Obtain the 'apikey' and pretend a call to the new api. */ DBObject query = new BasicDBObject("name", inst).append("secret", secret); DBObject tmp_inst = db.getCollection("institutions").findOne(query); if (tmp_inst == null) return jsdenied(); apikey = (String) ((DBObject) tmp_inst.get("api")).get("key"); if (apikey == null) return jsfailure("No 'apikey' set for this institution!"); } /* Authenticate user. */ DBObject db_user = auth_api(apikey, "user"); DBObject db_inst = auth_api(apikey, "inst"); User user; if (db_user != null) { user = new User(db_user, getInst(db_user)); } else if (db_inst != null) { user = new Inst(db_inst); } else { return jsdenied(); } /* Check for invalid parameter configurations. */ if ((id != null || refineId != null) && evtid != null) return jsfailure("Don't mix 'id' and 'evtid'."); if (evtid == null) evtid = new CompId(inst, id, refineId).toString(); /* Check for missing parameters */ if (evtid == null) return jsfailure("Missing parameter."); /* search for given id */ BasicDBObject query = new BasicDBObject("_id", evtid).append("user", user.objId); DBObject entry = db.getCollection("eqs").findOne(query); /* return if id not found */ if (entry == null) return jsfailure("Event ID not found."); /* check if already computed */ Integer progress = _status(evtid, raw); if (progress != STATUS_NO_COMP) { if (raw == 0) return jsfailure("Re-computation not allowed."); if (progress != 100) return jsfailure("A computation is currently running."); } /* Use same duration as in original simulation if available. */ if (dur == null) { Number n = (Number) getField(entry, "process.0.simTime"); /* Duration could not be determined. */ if (n == null) return jsfailure("Missing parameter."); dur = n.intValue(); } /* Use grid resolution of original computation or default to 120 seconds. */ if (gridres == null) { Number res = (Number) getField(entry, "process.0.resolution"); gridres = res == null ? 120 : (int) (res.doubleValue() * 60); } /* get properties of returned entry */ BasicDBObject prop = (BasicDBObject) entry.get("prop"); BasicDBObject process = new BasicDBObject("raw_progress", 0); if (raw == 0) { process.append("process", new BasicDBList()); } BasicDBObject set = new BasicDBObject("$set", process); db.getCollection("eqs").update(entry, set); /* extract properties to pass them to the request method */ double lat = prop.getDouble("latitude"); double lon = prop.getDouble("longitude"); double mag = prop.getDouble("magnitude"); double dip = prop.getDouble("dip"); double strike = prop.getDouble("strike"); double rake = prop.getDouble("rake"); double depth = prop.getDouble("depth"); Date date = prop.getDate("date"); if (accel == null) accel = 1; /* prepare the simulation for execution */ EQParameter eqp = new EQParameter(lon, lat, mag, depth, dip, strike, rake, date); EQTask task = new EQTask(eqp, evtid, user, dur, accel, gridres); task.raw = raw; task.dt_out = dt_out; task.algo = algo; String ret_id = request(evtid, task); return jssuccess(new BasicDBObject("_id", ret_id)); } private String request(String id, Task task) { scheduler.submit(task); return id; } String newRandomId(String username) { Random rand = new Random(); String id; DBObject obj1, obj2; final String alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; final int N = alphabet.length(); do { id = username + "."; for (int i = 0; i < 5; i++) { id += alphabet.charAt(rand.nextInt(N)); } obj1 = db.getCollection("eqs").findOne(new BasicDBObject("_id", id)); obj2 = db.getCollection("evtsets").findOne(new BasicDBObject("_id", id)); } while (obj1 != null || obj2 != null); return id; } private List<Double> range_values(Double min, Double step, Double max) { List<Double> values = new ArrayList<Double>(); if (min == null || step == null || max == null) return null; /*int count = (int)( (max - min) / step) + 1; if( count <= 0 || count > 30 ) return null;*/ for (Double i = min; i <= max; i += step) { values.add(i); } return values; } private List<Double> list_values(String str) { List<Double> list = new ArrayList<Double>(); for (String s : str.split(",")) { try { list.add(Double.valueOf(s)); } catch (NumberFormatException ex) { return null; } } return list; } private List<Double> get_values(String list, Double min, Double step, Double max) { if (list != null) return list_values(list); return range_values(min, step, max); } private Double get_mean(List<Double> list) { double mean = 0; for (Double d : list) mean += d; return list.isEmpty() ? 0.0 : mean / list.size(); } @POST @Path("/evtset_comp") @Produces(MediaType.APPLICATION_JSON) public String evtset_comp(@Context HttpServletRequest request, @FormParam("apikey") String apikey, @FormParam("name") @DefaultValue("Custom Event Set") String name, @FormParam("lon") Double lon, @FormParam("lat") Double lat, @FormParam("mag") String mag_list, @FormParam("mag_min") Double mag_min, @FormParam("mag_step") Double mag_step, @FormParam("mag_max") Double mag_max, @FormParam("depth") String depth_list, @FormParam("depth_min") Double depth_min, @FormParam("depth_step") Double depth_step, @FormParam("depth_max") Double depth_max, @FormParam("dip") String dip_list, @FormParam("dip_min") Double dip_min, @FormParam("dip_step") Double dip_step, @FormParam("dip_max") Double dip_max, @FormParam("strike") String strike_list, @FormParam("strike_min") Double strike_min, @FormParam("strike_step") Double strike_step, @FormParam("strike_max") Double strike_max, @FormParam("rake") String rake_list, @FormParam("rake_min") Double rake_min, @FormParam("rake_step") Double rake_step, @FormParam("rake_max") Double rake_max, @FormParam("dur") @DefaultValue("180") Integer dur, @CookieParam("server_cookie") String session) { final int max_value = Integer.MAX_VALUE; /* Authenticate user. */ User user = auth(apikey, session); if (user == null) { return jsdenied(); } if (!checkPerm(user, "evtset")) return jsdenied(); if (lon == null || lat == null) return jsfailure("Missing parameters."); List<Double> mags = get_values(mag_list, mag_min, mag_step, mag_max); List<Double> depths = get_values(depth_list, depth_min, depth_step, depth_max); List<Double> dips = get_values(dip_list, dip_min, dip_step, dip_max); List<Double> strikes = get_values(strike_list, strike_min, strike_step, strike_max); List<Double> rakes = get_values(rake_list, rake_min, rake_step, rake_max); if (mags == null || depths == null || dips == null || strikes == null || rakes == null) return jsfailure("Invalid range given."); int count = mags.size() * depths.size() * dips.size() * strikes.size() * rakes.size(); if (count > max_value) return jsfailure("Too many combinations. At most " + max_value + " are allowed."); String setid = newRandomId(user.name); EventSet evtset = new EventSet(setid, count, count * (dur + 10)); Date date = new Date(); List<String> evtids = new ArrayList<String>(); int i = 0; for (Double mag : mags) { for (Double depth : depths) { for (Double dip : dips) { for (Double strike : strikes) { for (Double rake : rakes) { EQParameter eqp = new EQParameter(lon, lat, mag, depth, dip, strike, rake, date); String retid = _compute(eqp, user, name + " " + i, null, null, dur, evtset, "easywave", 120); evtids.add(retid); i++; } } } } } BasicDBObject prop = new BasicDBObject("latitude", lat); prop.append("longitude", lon); prop.append("magnitude", get_mean(mags)); prop.append("mag_list", mags); prop.append("mag_min", mag_min); prop.append("mag_step", mag_step); prop.append("mag_max", mag_max); prop.append("depth_list", depths); prop.append("depth_min", depth_min); prop.append("depth_step", depth_step); prop.append("depth_max", depth_max); prop.append("dip_list", dips); prop.append("dip_min", dip_min); prop.append("dip_step", dip_step); prop.append("dip_max", dip_max); prop.append("strike_list", strikes); prop.append("strike_min", strike_min); prop.append("strike_step", strike_step); prop.append("strike_max", strike_max); prop.append("rake_list", rakes); prop.append("rake_min", rake_min); prop.append("rake_step", rake_step); prop.append("rake_max", rake_max); BasicDBObject set = new BasicDBObject("_id", setid); set.append("evtids", evtids); set.append("timestamp", date); set.append("name", name); set.append("duration", dur); set.append("prop", prop); set.append("user", user.objId); set.append("progress", 0.0); db.getCollection("evtsets").insert(set); /* create a new event */ BasicDBObject event = new BasicDBObject(); event.put("id", setid); event.put("user", user.objId); event.put("timestamp", new Date()); event.put("event", "new_evtset"); db.getCollection("events").insert(event); return jssuccess(new BasicDBObject("setid", setid).append("evtids", evtids)); } @POST @Path("/evtset_abort") @Produces(MediaType.APPLICATION_JSON) public String evtset_abort(@Context HttpServletRequest request, @FormParam("apikey") String apikey, @FormParam("setid") String setid) { User user = auth_api(apikey); if (user == null) return jsdenied(); DBObject query = new BasicDBObject("_id", setid).append("user", user.objId); DBObject evtset = db.getCollection("evtsets").findOne(query); if (evtset == null) return jsfailure("Event-Set not found."); /* Get events and abort one task after the other. */ boolean aborted = false; BasicDBList evtids = (BasicDBList) evtset.get("evtids"); for (Object evtid : evtids) { aborted |= _abortEvent((String) evtid); } if (!aborted) return jsfailure("Computation cannot be aborted."); /* Update earthquake in database. */ DBObject set = new BasicDBObject("$set", new BasicDBObject("abort", true)); db.getCollection("evtsets").update(new BasicDBObject("_id", setid), set); /* Create a new event. */ BasicDBObject event = new BasicDBObject(); event.put("id", setid); event.put("user", user.objId); event.put("timestamp", new Date()); event.put("event", "abort"); db.getCollection("events").insert(event); return jssuccess(); } @POST @Path("/evt_abort") @Produces(MediaType.APPLICATION_JSON) public String evt_abort(@Context HttpServletRequest request, @FormParam("apikey") String apikey, @FormParam("evtid") String evtid) { User user = auth_api(apikey); if (user == null) return jsdenied(); DBObject query = new BasicDBObject("_id", evtid).append("user", user.objId); DBObject evt = db.getCollection("eqs").findOne(query); if (evt == null) return jsfailure("Event not found."); if (!_abortEvent(evtid)) return jsfailure("Computation cannot be aborted."); /* Update earthquake in database. */ DBObject set = new BasicDBObject("$set", new BasicDBObject("abort", true)); db.getCollection("eqs").update(new BasicDBObject("_id", evtid), set); /* Create a new event. */ BasicDBObject event = new BasicDBObject(); event.put("id", evtid); event.put("user", user.objId); event.put("timestamp", new Date()); event.put("event", "abort"); db.getCollection("events").insert(event); return jssuccess(); } private boolean _abortEvent(String evtid) { Task task = scheduler.getTask(evtid); if (task == null) return false; return task.markAsAbort(); } private String _compute(EQParameter eqp, User user, String name, String parent, String root, Integer dur, EventSet evtSet, String algo, Integer gridres) { /* create a unique ID that is not already present in the DB */ String id = newRandomId(user.name); DBObject parentObj = db.getCollection("eqs").findOne(new BasicDBObject("_id", parent)); int accel = 1; if (parentObj != null) { Integer val = (Integer) parentObj.get("accel"); if (val != null) accel = val; } /* get current timestamp */ Date timestamp = new Date(); /* create new sub object that stores the properties */ BasicDBObject sub = new BasicDBObject(); sub.put("date", eqp.date); sub.put("region", name); sub.put("latitude", eqp.lat); sub.put("longitude", eqp.lon); sub.put("magnitude", eqp.mw); sub.put("slip", eqp.slip); sub.put("length", eqp.length); sub.put("width", eqp.width); sub.put("depth", eqp.depth); sub.put("dip", eqp.dip); sub.put("strike", eqp.strike); sub.put("rake", eqp.rake); /* create new DB object that should be added to the earthquake collection */ BasicDBObject obj = new BasicDBObject(); obj.put("_id", id); obj.put("id", id); obj.put("user", user.objId); obj.put("timestamp", timestamp); obj.put("process", new ArrayList<>()); obj.put("prop", sub); obj.put("root", root); obj.put("parent", parent); obj.put("accel", accel); if (evtSet != null) obj.put("evtset", evtSet.setid); /* insert object into collection */ db.getCollection("eqs").insert(obj); /* create a new event */ BasicDBObject event = new BasicDBObject(); event.put("id", id); event.put("user", user.objId); event.put("timestamp", timestamp); event.put("event", "new"); /* insert new event into 'events'-collection */ db.getCollection("events").insert(event); /* start request */ EQTask task = new EQTask(eqp, id, user, dur, accel); task.evtset = evtSet; task.algo = algo; task.gridres = gridres; if (evtSet == null) { if (algo.equals("hysea")) { task.setSlots(IScheduler.SLOT_HYSEA); } else { task.setSlots(IScheduler.SLOT_NORMAL, IScheduler.SLOT_EXCLUSIVE); } } else { task.setSlots(IScheduler.SLOT_NORMAL); } return request(id, task); } @POST @Path("/compute") @Produces(MediaType.APPLICATION_JSON) public String compute(@Context HttpServletRequest request, @FormParam("name") @DefaultValue("Custom") String name, @FormParam("lon") Double lon, @FormParam("lat") Double lat, @FormParam("mag") Double mag, @FormParam("slip") Double slip, @FormParam("length") Double length, @FormParam("width") Double width, @FormParam("depth") Double depth, @FormParam("dip") Double dip, @FormParam("strike") Double strike, @FormParam("rake") Double rake, @FormParam("dur") Integer dur, @FormParam("date") String dateStr, @FormParam("root") String root, @FormParam("parent") String parent, @FormParam("algo") @DefaultValue("easywave") String algo, @FormParam("gridres") @DefaultValue("120") Integer gridres, @CookieParam("server_cookie") String session) { Object[] required1 = { name, lon, lat, depth, dip, strike, rake, dur, algo, gridres }; if (!checkParams(request, required1)) return jsfailure(); Object[] required2 = { slip, length, width }; if (!checkParams(request, required2) && (mag == null || !algo.equals("easywave"))) return jsfailure(); /* only privileged users are allowed to compute own scenarios - check for valid session */ if (session == null) return jsdenied(); /* check if session is valid and if the user is logged in */ User user = signedIn(session); if (user == null) return jsdenied(); Date date; if (dateStr != null && !dateStr.equals("")) { /* get Date object from date string */ date = parseIsoDate(dateStr); } else { /* get current timestamp */ date = new Date(); } if (date == null) return jsfailure(); System.out.println(new Date() + ": User " + user.name + " requested a computation of " + dur + " minutes."); /* upon here, we assume an authorized user */ if (root != null && root.equals("")) root = null; if (parent != null && parent.equals("")) parent = null; EQParameter eqp = (mag == null) ? new EQParameter(lon, lat, slip, length, width, depth, dip, strike, rake, date) : new EQParameter(lon, lat, mag, depth, dip, strike, rake, date); String ret_id = _compute(eqp, user, name, parent, root, dur, null, algo, gridres); return jssuccess(new BasicDBObject("_id", ret_id)); } private DBObject auth_api(String key, String kind) { if (key == null) return null; BasicDBObject query = new BasicDBObject("api.key", key).append("api.enabled", true); DBObject inst = db.getCollection("institutions").findOne(query); DBObject user = db.getCollection("users").findOne(query.append("permissions.api", true)); if (user != null && (kind == null || kind.equals("user"))) return user; if (inst != null && (kind == null || kind.equals("inst"))) return inst; return null; } private User auth_api(String key) { DBObject db_user = auth_api(key, "user"); DBObject db_inst = auth_api(key, "inst"); User user = null; if (db_user != null) { user = new User(db_user, getInst(db_user)); } else if (db_inst != null) { user = new Inst(db_inst); } return user; } private User auth(String key, String session) { User user = auth_api(key); if (user != null) return user; user = signedIn(session); return user; } private String getInst(DBObject userObj) { ObjectId instId = (ObjectId) userObj.get("inst"); DBObject instObj = db.getCollection("institutions").findOne(new BasicDBObject("_id", instId)); if (instObj == null) return null; return (String) instObj.get("name"); } @POST @Path("/data_insert") @Produces(MediaType.APPLICATION_JSON) public String data_insert(@Context HttpServletRequest request, @FormParam("inst") String inst, @FormParam("secret") String secret, @FormParam("id") String id, @FormParam("name") String name, @FormParam("lon") Double lon, @FormParam("lat") Double lat, @FormParam("mag") Double mag, @FormParam("slip") Double slip, @FormParam("length") Double length, @FormParam("width") Double width, @FormParam("depth") Double depth, @FormParam("dip") Double dip, @FormParam("strike") Double strike, @FormParam("rake") Double rake, @FormParam("date") String dateStr, @FormParam("sea_area") String sea_area, @FormParam("root") String root, @FormParam("parent") String parent, @FormParam("comp") Integer comp, @FormParam("accel") Integer accel, @FormParam("gridres") Integer gridres, @FormParam("apikey") String apikey, @FormParam("algo") @DefaultValue("easywave") String algo) { /* Check for invalid parameter configurations. */ if ((inst != null || secret != null) && apikey != null) return jsfailure("Don't mix 'apikey' and 'secret'."); if (mag != null && (slip != null || length != null || width != null)) return jsfailure("Don't mix 'mag' with 'slip', 'length' and 'width'."); /* Support 'inst' and 'secret' for compatibility reasons. */ if (inst != null && secret != null) { /* Obtain the 'apikey' and pretend a call to the new api. */ DBObject query = new BasicDBObject("name", inst).append("secret", secret); DBObject tmp_inst = db.getCollection("institutions").findOne(query); if (tmp_inst == null) return jsdenied(); apikey = (String) ((DBObject) tmp_inst.get("api")).get("key"); if (apikey == null) return jsfailure("No 'apikey' set for this institution!"); } /* Continue with the new API. */ Object[] required = { apikey, id, name, dateStr }; if (!checkParams(request, required)) return jsfailure(); DBObject db_user = auth_api(apikey, "user"); DBObject db_inst = auth_api(apikey, "inst"); /* check if we got a valid institution and the correct secret */ ObjectId user_id; String user_name; User user; if (db_user != null) { user_id = (ObjectId) db_user.get("_id"); user_name = (String) db_user.get("username"); user = new User(db_user, getInst(db_user)); } else if (db_inst != null) { user_id = (ObjectId) db_inst.get("_id"); user_name = (String) db_inst.get("name"); user = new Inst(db_inst); } else { return jsdenied(); } System.out.println(user.name + " - " + user.inst); /* get Date object from date string */ System.out.println(dateStr); Date date = parseIsoDate(dateStr); if (date == null) return jsfailure("Invalid date format."); System.out.println(id); /* get current timestamp */ Date timestamp = new Date(); /* create new sub object that stores the properties */ BasicDBObject sub = new BasicDBObject(); sub.put("date", date); sub.put("region", name); sub.put("latitude", lat); sub.put("longitude", lon); sub.put("magnitude", mag); sub.put("slip", slip); sub.put("length", length); sub.put("width", width); sub.put("depth", depth); sub.put("dip", dip); sub.put("strike", strike); sub.put("rake", rake); sub.put("sea_area", sea_area); if (accel == null) accel = 1; /* create new DB object that should be added to the eqs collection */ BasicDBObject obj = new BasicDBObject(); obj.put("id", id); obj.put("user", user_id); obj.put("timestamp", timestamp); obj.put("prop", sub); obj.put("root", root); obj.put("parent", parent); obj.put("accel", accel); //obj.put( "gridres", gridres ); /* create a new event */ BasicDBObject event = new BasicDBObject(); event.put("user", user_id); event.put("timestamp", timestamp); event.put("event", "new"); Long refineId = 0L; /* get earthquake collection */ DBCollection coll = db.getCollection("eqs"); /* search for given id */ BasicDBObject inQuery = new BasicDBObject("id", id).append("user", user_id); DBCursor cursor = coll.find(inQuery).sort(new BasicDBObject("refineId", -1)); BasicDBObject entry = null; /* if id is already used, make a refinement */ if (cursor.hasNext()) { /* get properties of returned entry */ entry = (BasicDBObject) cursor.next(); /* update entry ID in database by appending deprecated field */ BasicDBObject depr = new BasicDBObject("depr", true); coll.update(entry, new BasicDBObject("$set", depr)); refineId = (Long) entry.get("refineId"); if (refineId == null) { refineId = new Long(0); } refineId++; /* override parent and root attributes */ root = entry.get("root") == null ? (String) entry.get("_id") : (String) entry.get("root"); obj.put("root", root); obj.put("parent", entry.get("_id")); /* override event type */ event.put("event", "update"); } /* set refinement and compound Ids */ final CompId compId = new CompId(user_name, id, refineId); obj.put("_id", compId.toString()); obj.put("refineId", refineId); event.put("id", compId.toString()); /* clean up query */ cursor.close(); /* insert object into 'eqs' collection */ coll.insert(obj); System.out.println(obj); Object[] reqComp1 = { id, lon, lat, mag, depth, dip, strike, rake }; Object[] reqComp2 = { id, lon, lat, slip, length, width, depth, dip, strike, rake }; boolean simulate = comp != null && (checkParams(request, reqComp1) || checkParams(request, reqComp2)); /* insert new event into 'events'-collection */ db.getCollection("events").insert(event); System.out.println(simulate); if (simulate) //computeById( request, null, null, id, refineId, comp, accel, apikey ); _computeById(user, compId.toString(), comp, accel, gridres, algo); else /* run request in a separate thread to avoid blocking */ new Thread() { public void run() { sendPost(GlobalParameter.wsgi_url + "webguisrv/post_compute", "evtid=" + compId.toString()); } }.start(); return jssuccess(new BasicDBObject("refineId", refineId).append("evtid", compId.toString())); } private Integer _status(String evtid, Integer raw) { /* Check if event exists. */ DBCollection eqs = db.getCollection("eqs"); BasicDBObject query = new BasicDBObject("_id", evtid); DBObject event = eqs.findOne(query); if (event == null) return null; /* Check if computation has been initiated. */ Integer p; if (raw > 0) { query.append("raw_progress", new BasicDBObject("$ne", null)); event = eqs.findOne(query); if (event == null) return STATUS_NO_COMP; p = ((BasicDBObject) event).getInt("raw_progress"); } else { query.append("process", new BasicDBObject("$ne", null)); if (eqs.findOne(query) == null) return STATUS_NO_COMP; /* Check if computation was started successfully. */ query.append("process", new BasicDBObject("$size", 1)); if (eqs.findOne(query) == null) return STATUS_FAILED; /* Extract the progress from the database object with this clear command ;) */ p = ((BasicDBObject) ((BasicDBList) event.get("process")).get(0)).getInt("progress"); } return p; } private Integer _evtset_status(String setid) { /* Check if event-set exists. */ DBCollection evtsets = db.getCollection("evtsets"); BasicDBObject query = new BasicDBObject("_id", setid); DBObject evtset = evtsets.findOne(query); if (evtset == null) return null; if (evtset.get("abort") != null) return STATUS_ABORT; Double progress = (Double) getField(evtset, "progress"); if (progress == null) return STATUS_NO_COMP; return progress.intValue(); } @POST @Path("/status") @Produces(MediaType.APPLICATION_JSON) public String status(@Context HttpServletRequest request, @FormParam("apikey") String apikey, @FormParam("evtid") String evtid, @FormParam("raw") @DefaultValue("0") Integer raw) { /* Validate API-key. For now, it is possible to query foreign events. */ if (auth_api(apikey, null) == null) return jsdenied(); Integer progress = _status(evtid, raw); if (progress == null) return jsfailure("Event not found."); if (progress == STATUS_NO_COMP) return jssuccess(new BasicDBObject("comp", "none")); if (progress == STATUS_FAILED) return jssuccess(new BasicDBObject("comp", "failed")); String comp = progress == 100 ? "success" : "pending"; return jssuccess(new BasicDBObject("comp", comp).append("progress", progress.toString())); } @POST @Path("/evtset_status") @Produces(MediaType.APPLICATION_JSON) public String evtset_status(@Context HttpServletRequest request, @FormParam("apikey") String apikey, @FormParam("setid") String setid, @CookieParam("server_cookie") String session) { /* Validate API-key. For now, it is possible to query foreign events. */ if (auth_api(apikey, null) == null && signedIn(session) == null) return jsdenied(); Integer progress = _evtset_status(setid); if (progress == null) return jsfailure("Event-set not found."); if (progress == STATUS_ABORT) return jssuccess(new BasicDBObject("comp", "abort")); if (progress == STATUS_NO_COMP) return jssuccess(new BasicDBObject("comp", "none")); String comp = progress == 100 ? "success" : "pending"; BasicDBObject ret = new BasicDBObject("comp", comp).append("progress", progress.toString()); /* Append runtime information. */ DBObject evtset = db.getCollection("evtsets").findOne(new BasicDBObject("_id", setid)); ret.append("calcTime", getField(evtset, "calcTime")); return jssuccess(ret); } @POST @Path("/signin") @Produces(MediaType.APPLICATION_JSON) public String signin(@Context HttpServletRequest request, @Context HttpServletResponse response, @FormParam("username") String username, @FormParam("password") String password) { Cookie sessionCookie = new Cookie("server_cookie", "java!"); sessionCookie.setPath("/"); sessionCookie.setHttpOnly(true); //sessionCookie.setSecure( true ); if (username == null || password == null || username.equals("") || password.equals("")) return jsfailure(); DBCollection coll = db.getCollection("users"); DBCursor cursor = coll.find(new BasicDBObject("username", username)); DBObject obj; if (cursor.hasNext()) { obj = cursor.next(); String hash = (String) obj.get("password"); String session = (String) obj.get("session"); MessageDigest sha256; try { sha256 = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { return "{ \"status\": \"error\" }"; } Base64Codec base64Codec = new Base64Codec(); if (hash.equals(base64Codec.encode(sha256.digest(password.getBytes())))) { if (session == null) { session = getSessionKey(); obj.put("session", session); coll.update(new BasicDBObject("username", username), obj); } sessionCookie.setValue(session); response.addCookie(sessionCookie); BasicDBObject result = new BasicDBObject("status", "success"); result.put("user", getUserObj(username)); BasicDBObject perm = (BasicDBObject) obj.get("permissions"); if (perm != null && perm.getBoolean("nologin") == true) return jsdenied(new BasicDBObject("nologin", true)); System.out.println("CLOUD " + new Date() + " SignIn from user " + username); DBObject login = new BasicDBObject("date", new Date()).append("user", username); db.getCollection("logins").insert(login); return gson.toJson(result); } } return jsfailure(); } @POST @Path("/signout") @Produces(MediaType.APPLICATION_JSON) public String signout(@Context HttpServletRequest request, @Context HttpServletResponse response, @FormParam("username") String username, @CookieParam("server_cookie") String session) { if (username == null || username.equals("") || session == null) return jsfailure(); DBCollection coll = db.getCollection("users"); DBCursor cursor = coll.find(new BasicDBObject("username", username)); DBObject obj; if (cursor.hasNext()) { obj = cursor.next(); if (session.equals((String) obj.get("session"))) { obj.put("session", null); coll.update(new BasicDBObject("username", username), obj); Cookie sessionCookie = new Cookie("server_cookie", ""); sessionCookie.setPath("/"); sessionCookie.setMaxAge(0); response.addCookie(sessionCookie); System.out.println("CLOUD " + new Date() + " SignOut from user " + username); return jssuccess(); } } return jsfailure(); } @POST @Path("/session") @Produces(MediaType.APPLICATION_JSON) public String session(@Context HttpServletRequest request, @Context HttpServletResponse response, @CookieParam("server_cookie") String session) { if (session == null) return jsfailure(); User user = signedIn(session); if (user != null) { DBObject userObj = getUserObj(user.name); BasicDBObject result = new BasicDBObject("status", "success"); result.put("user", userObj); BasicDBObject perm = (BasicDBObject) userObj.get("permissions"); if (perm != null && perm.getBoolean("nologin") == true) return jsdenied(new BasicDBObject("nologin", true)); System.out.println("CLOUD " + new Date() + " Resuming session for user " + user.name); return gson.toJson(result); } return jsfailure(); } private DBObject getUserObj(String username) { DBCollection coll = db.getCollection("users"); DBCursor cursor = coll.find(new BasicDBObject("username", username)); if (!cursor.hasNext()) return null; DBObject obj = cursor.next(); cursor.close(); BasicDBObject userObj = new BasicDBObject("username", obj.get("username")); userObj.put("_id", obj.get("_id")); userObj.put("permissions", obj.get("permissions")); userObj.put("properties", obj.get("properties")); userObj.put("notify", obj.get("notify")); userObj.put("api", obj.get("api")); ObjectId instId = (ObjectId) obj.get("inst"); cursor = db.getCollection("institutions").find(new BasicDBObject("_id", instId)); String instName = null; if (cursor.hasNext()) { DBObject inst = cursor.next(); inst.removeField("_id"); inst.removeField("secret"); userObj.put("inst", inst); instName = (String) inst.get("name"); } cursor.close(); if (instName == null || instName.equals("gfz") || instName.equals("tdss15")) instName = "gfz_ex_test"; /* get all available country codes and count elements in each group */ DBObject groupFields = new BasicDBObject("_id", "$country"); groupFields.put("count", new BasicDBObject("$sum", 1)); DBObject group = new BasicDBObject("$group", groupFields); BasicDBList types = new BasicDBList(); types.add(new BasicDBObject("sensor", "rad")); types.add(new BasicDBObject("sensor", "prs")); types.add(new BasicDBObject("sensor", "pr1")); types.add(new BasicDBObject("sensor", "flt")); types.add(new BasicDBObject("sensor", null)); DBObject filterFields = new BasicDBObject("$or", types); BasicDBList andList = new BasicDBList(); andList.add(filterFields); andList.add(new BasicDBObject("inst", instName)); DBObject andObj = new BasicDBObject("$and", andList); DBObject filter = new BasicDBObject("$match", andObj); /* sort alphabetically */ DBObject sortFields = new BasicDBObject("_id", 1); DBObject sort = new BasicDBObject("$sort", sortFields); AggregationOutput output = db.getCollection("stations").aggregate(filter, group, sort); BasicDBList countries = new BasicDBList(); /* convert answer into string list */ @SuppressWarnings("unchecked") List<String> clist = (List<String>) obj.get("countries"); for (DBObject res : output.results()) { String code = (String) res.get("_id"); if (code == null) continue; boolean isOn = (clist != null) && clist.contains(code); res.put("on", isOn); countries.add(res); } userObj.put("countries", countries); return userObj; } private User signedIn(String session) { if (session == null) return null; DBCollection coll = db.getCollection("users"); DBCollection insts = db.getCollection("institutions"); DBCursor cursor = coll.find(new BasicDBObject("session", session)); DBObject obj; DBObject inst = null; if (cursor.hasNext()) { /* we have found a valid session key */ obj = cursor.next(); cursor.close(); cursor = insts.find(new BasicDBObject("_id", obj.get("inst"))); if (cursor.hasNext()) inst = cursor.next(); return new User(obj, inst); } return null; } public String getSessionKey() { return UUID.randomUUID().toString(); } @POST @Path("/fetch") @Produces(MediaType.APPLICATION_JSON) public String fetch(@Context HttpServletRequest request, @FormParam("limit") @DefaultValue("0") int limit, @FormParam("delay") @DefaultValue("0") int delay, @FormParam("undersea") @DefaultValue("false") boolean undersea, @CookieParam("server_cookie") String session) { /* check session key and find out if the request comes from an authorized user */ User user = signedIn(session); /* returns null if user is not logged in */ /* create lists for general and user specific earthquake entries */ ArrayList<DBObject> mlist = new ArrayList<DBObject>(); ArrayList<DBObject> ulist = new ArrayList<DBObject>(); /* we want all entries since the beginning of time */ Date maxTimestamp = new Date(); /* used to convert to desired time format used by MongoDB */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); /* select collection which contain the earthquake entries */ DBCollection coll = db.getCollection("eqs"); //ArrayList<User> users = new ArrayList<User>( institutions.values() ); ArrayList<User> users = new ArrayList<User>(); users.add(user); if (user != null) { if (user.inst != null) { users.add(institutions.get(user.inst)); } else { users.add(institutions.get("gfz")); } DBCursor csr = db.getCollection("users").find( new BasicDBObject("username", user.name).append("provider", new BasicDBObject("$ne", null))); if (csr.hasNext()) { for (Object p : (BasicDBList) csr.next().get("provider")) { DBObject inst = db.getCollection("institutions").findOne(new BasicDBObject("_id", p)); if (inst != null) users.add(institutions.get(inst.get("name"))); } } } /* return only entries that are older than 'delay' minutes */ Date upperTimeLimit = new Date(System.currentTimeMillis() - delay * 60 * 1000); /* get earthquakes for each of the given users */ for (User curUser : users) { if (curUser == null) continue; /* create DB query */ BasicDBObject inQuery = new BasicDBObject("user", curUser.objId); if (undersea) inQuery.append("prop.sea_area", new BasicDBObject("$ne", null)); if (delay > 0) inQuery.append("prop.date", new BasicDBObject("$lt", upperTimeLimit)); inQuery.append("depr", new BasicDBObject("$ne", true)); inQuery.append("evtset", null); /* query DB, sort the results by date and limit the number of returned entries */ DBCursor cursor = coll.find(inQuery).sort(new BasicDBObject("prop.date", -1)); if (limit > 0) cursor = cursor.limit(limit); /* walk through the returned entries */ for (DBObject obj : cursor) { obj.removeField("image"); /* check if entry belongs to general or user specific list */ if (user != null && obj.get("user").equals(user.objId)) { ulist.add(obj); } else { mlist.add(obj); } /* update timestamp */ Date timestamp = (Date) obj.get("timestamp"); if (timestamp.after(maxTimestamp)) { maxTimestamp = timestamp; } } /* clean up query */ cursor.close(); } /* create new JSON object that can be used directly within JavaScript */ JsonObject jsonObj = new JsonObject(); jsonObj.add("main", gson.toJsonTree(mlist)); jsonObj.add("user", gson.toJsonTree(ulist)); if (user != null) { List<DBObject> msglist = msg(limit, user); if (!msglist.isEmpty()) { Date timestamp = (Date) msglist.get(0).get("CreatedTime"); if (timestamp.after(maxTimestamp)) { maxTimestamp = timestamp; } } jsonObj.add("msg", gson.toJsonTree(msglist)); } else { jsonObj.add("msg", gson.toJsonTree(new ArrayList<DBObject>())); } List<DBObject> evtsets = new ArrayList<DBObject>(); if (user != null) { BasicDBObject query = new BasicDBObject("user", user.objId); query.append("timestamp", new BasicDBObject("$lte", maxTimestamp)); DBCursor cursor = db.getCollection("evtsets").find(query).sort(new BasicDBObject("timestamp", -1)) .limit(100); evtsets = cursor.toArray(); } jsonObj.add("evtsets", gson.toJsonTree(evtsets)); /* TODO */ if (user != null) { for (Map.Entry<String, IDataProvider> entry : providers.entrySet()) { List<DBObject> list = entry.getValue().fetch(user, maxTimestamp, limit); jsonObj.add(entry.getKey(), gson.toJsonTree(list)); } } jsonObj.addProperty("ts", sdf.format(maxTimestamp)); return jsonObj.toString(); } @POST @Path("/update") @Produces(MediaType.APPLICATION_JSON) public String update(@Context HttpServletRequest request, @FormParam("ts") String ts, @FormParam("delay") @DefaultValue("0") int delay, @CookieParam("server_cookie") String session) { /* check session key and find out if the request comes from an authorized user */ User user = signedIn(session); /* create lists for general and user specific earthquake entries */ ArrayList<DBObject> mlist = new ArrayList<DBObject>(); ArrayList<DBObject> ulist = new ArrayList<DBObject>(); ArrayList<DBObject> evtsets = new ArrayList<DBObject>(); /* used to convert to desired time format used by MongoDB */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); /* convert timestamp from String to Date; return on error */ Date timestamp; try { timestamp = sdf.parse(ts); } catch (ParseException e) { e.printStackTrace(); return null; } /* select collection which contain the events */ DBCollection coll = db.getCollection("events"); /* create list of DB objects that contains all desired users */ BasicDBList users = new BasicDBList(); // for( User curUser: institutions.values() ) // users.add( new BasicDBObject( "user", curUser.objId ) ); if (user != null) { users.add(new BasicDBObject("user", user.objId)); if (user.inst != null) { users.add(new BasicDBObject("user", institutions.get(user.inst).objId)); } else { users.add(new BasicDBObject("user", institutions.get("gfz").objId)); } DBCursor csr = db.getCollection("users").find( new BasicDBObject("username", user.name).append("provider", new BasicDBObject("$ne", null))); if (csr.hasNext()) { for (Object p : (BasicDBList) csr.next().get("provider")) { users.add(new BasicDBObject("user", p)); } } } /* return only entries that are older than 'delay' minutes */ Date upperTimeLimit = new Date(System.currentTimeMillis() - delay * 60 * 1000); /* create DB query - search for newer events related to the general list or the user */ BasicDBList time = new BasicDBList(); time.add(new BasicDBObject("timestamp", new BasicDBObject("$gt", timestamp))); BasicDBObject inQuery = new BasicDBObject("$and", time); inQuery.put("$or", users); boolean first = true; Map<String, List<DBObject>> lists = new HashMap<String, List<DBObject>>(); for (Map.Entry<String, IDataProvider> entry : providers.entrySet()) { lists.put(entry.getKey(), new ArrayList<DBObject>()); } /* walk through the returned entries */ if (user != null) { /* query DB, sort the results by timestamp */ DBCursor cursor = coll.find(inQuery).sort(new BasicDBObject("timestamp", -1)); for (DBObject obj : cursor) { if (first) { timestamp = (Date) obj.get("timestamp"); first = false; } /* get corresponding entry from earthquake collection */ String id = (String) obj.get("id"); BasicDBObject objQuery = new BasicDBObject(); objQuery.put("olduser", new BasicDBObject("$exists", false)); if (delay > 0) objQuery.put("prop.date", new BasicDBObject("$lt", upperTimeLimit)); DBObject obj2 = null; if (obj.get("event").equals("msg_sent")) { objQuery.put("Message-ID", id); obj2 = db.getCollection("messages_sent").findOne(objQuery); } else if (obj.get("event").equals("msg_recv")) { objQuery.put("Message-ID", id); obj2 = db.getCollection("messages_received").findOne(objQuery); } else if (obj.get("event").equals("new_evtset")) { objQuery.put("_id", id); obj2 = db.getCollection("evtsets").findOne(objQuery); } else { objQuery.put("_id", id); obj2 = db.getCollection("eqs").findOne(objQuery); if (obj2 == null) obj2 = db.getCollection("evtsets").findOne(objQuery); } for (Map.Entry<String, IDataProvider> entry : providers.entrySet()) { entry.getValue().add(lists.get(entry.getKey()), obj); } /* */ if (obj2 != null) { /* add event type to entry */ String event = (String) obj.get("event"); obj2.put("event", event); if (obj.get("event").equals("msg_recv")) { obj2.put("Dir", "in"); obj2.put("To", new String[] { user.name }); DBCursor csrUser = db.getCollection("users") .find(new BasicDBObject("_id", obj2.get("SenderID"))); if (csrUser.hasNext()) obj2.put("From", (String) csrUser.next().get("username")); DBCursor csrParent = db.getCollection("eqs") .find(new BasicDBObject("_id", obj2.get("ParentId"))); if (csrParent.hasNext()) obj2.put("parentEvt", csrParent.next()); } if (event.equals("new_evtset")) { evtsets.add(obj2); } else { /* check if entry belongs to general or user specific list */ if (user != null && obj.get("user").equals(user.objId)) { ulist.add(obj2); } else { mlist.add(obj2); } } /* update timestamp */ /* TODO: this is just a temporary solution, because progress events could be delivered multiple times */ if (delay <= 0 || event.equals("new")) { if (first) { timestamp = (Date) obj.get("timestamp"); first = false; } } } } /* clean up query */ cursor.close(); } /* create new JSON object that can be used directly within JavaScript */ JsonObject jsonObj = new JsonObject(); jsonObj.addProperty("serverTime", sdf.format(new Date())); jsonObj.addProperty("ts", sdf.format(timestamp)); jsonObj.add("main", gson.toJsonTree(mlist)); jsonObj.add("user", gson.toJsonTree(ulist)); jsonObj.add("evtsets", gson.toJsonTree(evtsets)); for (Map.Entry<String, IDataProvider> entry : providers.entrySet()) { jsonObj.add(entry.getKey(), gson.toJsonTree(lists.get(entry.getKey()))); } return jsonObj.toString(); } @POST @Path("/search") @Produces(MediaType.APPLICATION_JSON) public String search(@Context HttpServletRequest request, @FormParam("text") String text, @CookieParam("server_cookie") String session) { /* check session key and find out if the request comes from an authorized user */ User user = signedIn(session); /* create list of DB objects that contains all desired users */ BasicDBList users = new BasicDBList(); for (User curUser : institutions.values()) users.add(new BasicDBObject("user", curUser.objId)); if (user != null) users.add(new BasicDBObject("user", user.objId)); DBCollection coll = db.getCollection("eqs"); DBCollection msgColl = db.getCollection("messages_sent"); DBCollection recvColl = db.getCollection("messages_received"); DBCollection evtsetColl = db.getCollection("evtsets"); List<DBObject> refinements = coll.find(new BasicDBObject("id", text)).toArray(); BasicDBList list = new BasicDBList(); list.add(new BasicDBObject("_id", text)); list.add(new BasicDBObject("id", text)); list.add(new BasicDBObject("root", text)); list.add(new BasicDBObject("parent", text)); for (DBObject obj : refinements) { String compId = (String) obj.get("_id"); list.add(new BasicDBObject("root", compId)); list.add(new BasicDBObject("parent", compId)); } BasicDBList and = new BasicDBList(); and.add(new BasicDBObject("$or", list)); and.add(new BasicDBObject("$or", users)); BasicDBObject inQuery = new BasicDBObject("$and", and); BasicDBObject sort = new BasicDBObject("timestamp", -1); sort.put("prop.date", -1); DBCursor cursor = coll.find(inQuery).sort(sort); List<DBObject> results = new ArrayList<DBObject>(); results.addAll(cursor.toArray()); cursor.close(); /* TODO: generalize field names */ list = new BasicDBList(); list.add(new BasicDBObject("EventID", text)); list.add(new BasicDBObject("ParentId", text)); for (DBObject obj : refinements) { String compId = (String) obj.get("_id"); list.add(new BasicDBObject("EventID", compId)); list.add(new BasicDBObject("ParentId", compId)); } and = new BasicDBList(); and.add(new BasicDBObject("$or", list)); and.add(new BasicDBObject("SenderID", user.objId)); inQuery = new BasicDBObject("$and", and); cursor = msgColl.find(inQuery).sort(new BasicDBObject("CreatedTime", -1)); for (DBObject obj : cursor) { obj.put("kind", "msg"); obj.put("Dir", "out"); results.add(obj); } cursor.close(); and = new BasicDBList(); and.add(new BasicDBObject("$or", list)); and.add(new BasicDBObject("ReceiverID", user.objId)); inQuery = new BasicDBObject("$and", and); cursor = recvColl.find(inQuery).sort(new BasicDBObject("CreatedTime", -1)); for (DBObject obj : cursor) { obj.put("kind", "msg"); obj.put("Dir", "in"); results.add(obj); } cursor.close(); DBObject evtset = evtsetColl.findOne(new BasicDBObject("_id", text)); if (evtset != null) { List<DBObject> evts = coll.find(new BasicDBObject("id", new BasicDBObject("$in", evtset.get("evtids")))) .toArray(); results.addAll(evts); } /* returning only cursor.toArray().toString() makes problems with the date fields */ return gson.toJsonTree(results).toString(); } @POST @Path("/delete") @Produces(MediaType.APPLICATION_JSON) public String delete(@Context HttpServletRequest request, @FormParam("id") String id, @FormParam("type") String type, @CookieParam("server_cookie") String session) { Object[] required = { id }; if (!checkParams(request, required)) return jsfailure(); /* check session key and find out if the request comes from an authorized user */ User user = signedIn(session); if (user == null) return jsdenied(); DBCollection coll; BasicDBObject inQuery = new BasicDBObject(); BasicDBObject fields = new BasicDBObject(); if (type != null && type.equals("msg_in")) { coll = db.getCollection("messages_received"); inQuery.put("ReceiverID", user.objId); inQuery.put("Message-ID", id); fields.put("ReceiverID", null); } else if (type != null && type.equals("msg_out")) { coll = db.getCollection("messages_sent"); inQuery.put("SenderID", user.objId); inQuery.put("Message-ID", id); fields.put("SenderID", null); } else { coll = db.getCollection("eqs"); inQuery.put("user", user.objId); inQuery.put("_id", id); fields.put("user", null); } fields.put("olduser", user.objId); //int num = coll.remove( inQuery ).getN(); BasicDBObject set = new BasicDBObject("$set", fields); System.out.println(set); int num = coll.update(inQuery, set).getN(); if (num > 0) return jssuccess(); return jsfailure(); } @POST @Path("/staticLnk") @Produces(MediaType.APPLICATION_JSON) public String staticLnk(@Context HttpServletRequest request, @FormParam("id") String id, @FormParam("lon") Double lon, @FormParam("lat") Double lat, @FormParam("zoom") Double zoom, @CookieParam("server_cookie") String session) { Object[] required = { id, lon, lat, zoom }; if (!checkParams(request, required)) return jsfailure(); /* check session key and find out if the request comes from an authorized user */ User user = signedIn(session); if (user == null) return jsdenied(); /* TODO: check if this id exists and their usage is authorized */ String key = static_int(id, lon, lat, zoom, user.objId); BasicDBObject result = new BasicDBObject("key", key); return jssuccess(result); } private String static_int(String id, Double lon, Double lat, Double zoom, Object uid) { DBCollection coll = db.getCollection("shared_links"); BasicDBObject inQuery = new BasicDBObject("evtid", id); inQuery.put("lon", lon); inQuery.put("lat", lat); inQuery.put("zoom", zoom); inQuery.put("timestamp", new Date()); inQuery.put("userid", uid); coll.insert(inQuery); ObjectId objId = (ObjectId) inQuery.get("_id"); return objId.toString(); } private String getIP(HttpServletRequest request) { String ip = request.getHeader("X-FORWARDED-FOR"); if (ip == null) ip = request.getRemoteAddr(); return ip; } @POST @Path("/getShared") @Produces(MediaType.APPLICATION_JSON) public String getShared(@Context HttpServletRequest request, @FormParam("lnkid") String lnkId, @CookieParam("server_cookie") String session) { Object[] required = { lnkId }; if (!checkParams(request, required)) return jsfailure(); ObjectId objId; try { objId = new ObjectId(lnkId); } catch (IllegalArgumentException e) { return jsfailure(); } DBCollection coll = db.getCollection("shared_links"); BasicDBObject inQuery = new BasicDBObject("_id", objId); DBCursor cursor = coll.find(inQuery); if (!cursor.hasNext()) return jsfailure(); DBObject lnkObj = cursor.next(); Object evtId = lnkObj.get("evtid"); /* store meta data */ User user = signedIn(session); Object userId = null; if (user != null) userId = user.objId; DBObject access = new BasicDBObject("timestamp", new Date()); access.put("user", userId); access.put("ip", getIP(request)); DBObject elem = new BasicDBObject("access", access); DBObject update = new BasicDBObject("$push", elem); db.getCollection("shared_links").findAndModify(inQuery, update); cursor.close(); BasicDBObject event = new BasicDBObject("_id", evtId); cursor = db.getCollection("eqs").find(event); if (!cursor.hasNext()) return jsfailure(); /* needed to preserve the expected date format for JavaScript */ /* TODO: dates are really difficult to parse between different languages * --> we need some consistent way to handle these problems */ JsonObject json = new JsonObject(); json.add("pos", gson.toJsonTree(lnkObj)); json.add("eq", gson.toJsonTree(cursor.next())); cursor.close(); return jssuccess(json); } @POST @Path("/copyToUser") @Produces(MediaType.APPLICATION_JSON) public String copyToUser(@Context HttpServletRequest request, @FormParam("srcId") String srcId, @CookieParam("server_cookie") String session) { Object[] required = { srcId }; if (!checkParams(request, required)) return jsfailure(); User user = signedIn(session); if (user == null) return jsdenied(); /* do not copy the event again if there is already one copy for that user */ BasicDBObject inQuery = new BasicDBObject("copied", srcId); inQuery.put("user", user.objId); DBCursor cursor = db.getCollection("eqs").find(inQuery); if (cursor.hasNext()) { cursor.close(); return jssuccess(new BasicDBObject("msg", "Copy already exists.")); } cursor.close(); inQuery = new BasicDBObject("_id", srcId); cursor = db.getCollection("eqs").find(inQuery); if (!cursor.hasNext()) return jsfailure(); DBObject obj = cursor.next(); cursor.close(); String id = newRandomId(user.name); obj.put("user", user.objId); obj.put("_id", id); obj.put("id", id); obj.put("timestamp", new Date()); obj.put("copied", srcId); db.getCollection("eqs").insert(obj); /* copy computation results */ cursor = db.getCollection("comp").find(new BasicDBObject("EventID", srcId)); for (DBObject res : cursor) { res.put("EventID", id); res.removeField("_id"); db.getCollection("comp").insert(res); } cursor.close(); return jssuccess(new BasicDBObject("msg", "Event successfully copied.")); } private List<DBObject> msg(int limit, User user) { if (user == null) return null; DBCollection coll = db.getCollection("messages_sent"); BasicDBObject inQuery = new BasicDBObject("SenderID", user.objId); /* query DB, sort the results by date and limit the number of returned entries */ DBCursor cursor = coll.find(inQuery).sort(new BasicDBObject("CreatedTime", -1)); if (limit > 0) cursor = cursor.limit(limit); List<DBObject> result = cursor.toArray(); cursor.close(); inQuery = new BasicDBObject("ReceiverID", user.objId); coll = db.getCollection("messages_received"); cursor = coll.find(inQuery).sort(new BasicDBObject("CreatedTime", -1)); if (limit > 0) cursor = cursor.limit(limit); for (DBObject obj : cursor) { DBCursor csrUser = db.getCollection("users").find(new BasicDBObject("_id", obj.get("SenderID"))); if (csrUser.hasNext()) obj.put("From", (String) csrUser.next().get("username")); obj.put("To", new String[] { user.name }); obj.put("Dir", "in"); result.add(obj); } cursor.close(); Collections.sort(result, new DateComparator("CreatedTime", -1)); /* add parent event as sub-object because we want to show it on click */ for (DBObject msg : result) { DBCursor csr = db.getCollection("eqs").find(new BasicDBObject("_id", msg.get("ParentId"))); if (csr.hasNext()) { DBObject obj = csr.next(); obj.removeField("image"); msg.put("parentEvt", obj); } } return result; } @POST @Path("/data_insert_tfp") @Produces(MediaType.APPLICATION_JSON) public String data_insert_tfp(@Context HttpServletRequest request, @FormParam("inst") String inst, @FormParam("secret") String secret, @FormParam("country") String country, @FormParam("code") String code, @FormParam("lon_real") Double lon_real, @FormParam("lat_real") Double lat_real, @FormParam("lon_sea") Double lon_sea, @FormParam("lat_sea") Double lat_sea, @FormParam("name") String name, @FormParam("desc") String desc, @FormParam("type") String type) { Object[] required = { inst, secret, country, code, lon_real, lat_real, lon_sea, lat_sea, name, desc, type }; if (!checkParams(request, required)) return jsfailure(); /* check if we got a valid institution and the correct secret */ Inst instObj = institutions.get(inst); if (instObj == null || !instObj.secret.equals(secret)) return jsdenied(); BasicDBObject tfp = new BasicDBObject(); tfp.put("inst", instObj.objId); tfp.put("country", country); tfp.put("code", code); tfp.put("lon_real", lon_real); tfp.put("lat_real", lat_real); tfp.put("lon_sea", lon_sea); tfp.put("lat_sea", lat_sea); tfp.put("name", name); tfp.put("desc", desc); tfp.put("type", type); try { db.getCollection("tfps").insert(tfp); } catch (MongoException ex) { System.err.println(ex.getMessage()); } return jssuccess(); } /* TODO: split params into tokens and encode them separately */ public static String sendPost(String url, String params) { HttpURLConnection con = null; try { con = (HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("POST"); con.setDoOutput(true); DataOutputStream wr = new DataOutputStream(con.getOutputStream()); wr.writeBytes(params); wr.flush(); wr.close(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); } catch (IOException e) { return null; } } private Date parseIsoDate(String dateStr) { /* used to convert to desired time format used by MongoDB */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); Date date; try { date = sdf.parse(dateStr); } catch (ParseException e) { return null; } return date; } private String jssuccess(DBObject obj) { obj.put("status", "success"); return obj.toString(); } private String jssuccess() { return "{ \"status\": \"success\" }"; } private String jsfailure() { return "{ \"status\": \"failure\" }"; } private String jsfailure(String msg) { return jsfailure(new BasicDBObject("msg", msg)); } private String jsfailure(DBObject obj) { obj.put("status", "failure"); return obj.toString(); } private String jsdenied() { return "{ \"status\": \"denied\" }"; } private String jsdenied(DBObject obj) { obj.put("status", "denied"); return obj.toString(); } /* this is nearly the same as 'jssuccess( DBObject obj )' but translates * the date objects better for later use in JavaScript */ private String jssuccess(JsonObject js) { js.add("status", gson.toJsonTree("success")); return js.toString(); } @POST @Path("/getCFCZ") @Produces(MediaType.APPLICATION_JSON) public String getCFCZ() { DBCollection coll = db.getCollection("cfcz"); DBCursor cursor = coll.find(); return cursor.toArray().toString(); } /* Flood - Prototype */ @POST @Path("/flood_compute") @Produces(MediaType.APPLICATION_JSON) public String flood_compute(@Context HttpServletRequest request, @FormParam("name") @DefaultValue("Custom") String name, @FormParam("test") String test, @CookieParam("server_cookie") String session) { Object[] required = { name }; if (!checkParams(request, required)) return jsfailure(); /* check if session is valid and if the user is logged in */ User user = signedIn(session); if (user == null) return jsdenied(); List<Location> locations; try { Type listType = new TypeToken<ArrayList<Location>>() { }.getType(); locations = new Gson().fromJson(test, listType); } catch (JsonSyntaxException e) { return jsfailure("Invalid JSON input string."); } System.out.println(new Date() + ": User " + user.name + " requested a FLOOD computation."); /* upon here, we assume an authorized user */ String ret_id = _flood_compute(user, name, locations); return jssuccess(new BasicDBObject("_id", ret_id)); } private String _flood_compute(User user, String name, List<Location> locations) { /* create a unique ID that is not already present in the DB */ String id = newRandomId(user.name); /* get current timestamp */ Date timestamp = new Date(); /* create new sub object that stores the properties */ BasicDBObject sub = new BasicDBObject(); sub.put("name", name); sub.put("locations", locations.size()); /* create new DB object that should be added to the earthquake collection */ BasicDBObject obj = new BasicDBObject(); obj.put("_id", id); obj.put("id", id); obj.put("user", user.objId); obj.put("timestamp", timestamp); obj.put("prop", sub); /* insert object into collection */ db.getCollection("floodsims").insert(obj); /* create a new event */ BasicDBObject event = new BasicDBObject(); event.put("id", id); event.put("user", user.objId); event.put("timestamp", timestamp); event.put("event", "new"); event.put("class", "flood"); /* insert new event into 'events'-collection */ db.getCollection("events").insert(event); System.out.println(locations.size()); /* start request */ Task task = new FloodTask(id, user, locations); task.setSlots(IScheduler.SLOT_NORMAL, IScheduler.SLOT_EXCLUSIVE); return request(id, task); } }