Java tutorial
/* * Copyright 2014 TWO SIGMA OPEN SOURCE, LLC * * 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://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.twosigma.beaker.core.rest; import com.google.inject.Inject; import com.google.inject.Singleton; import com.twosigma.beaker.core.module.config.BeakerConfig; import com.twosigma.beaker.shared.module.util.GeneralUtils; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.POST; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; /** * RESTful API for general utilities */ @Path("util") @Produces(MediaType.APPLICATION_JSON) @Singleton public class UtilRest { private final BeakerConfig bkConfig; private final GeneralUtils utils; @Inject public UtilRest(BeakerConfig bkConfig, GeneralUtils generalUtils) { this.bkConfig = bkConfig; this.utils = generalUtils; resetConfig(); } @GET @Path("whoami") @Produces(MediaType.TEXT_PLAIN) public String whoami(@Context HttpServletRequest request) { return "\"" + System.getProperty("user.name") + "\""; } @GET @Path("getVersionInfo") @Produces(MediaType.APPLICATION_JSON) public String getVersionInfo(@Context HttpServletRequest request) { return "{\"buildTime\": \"" + this.bkConfig.getBuildTime() + "\"," + " \"version\":\"" + this.bkConfig.getVersion() + "\"}"; } @GET @Path("allStackTraces") public List<Map<String, Object>> getAllStackTraces() { List<Map<String, Object>> out = new ArrayList<>(); Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); for (Thread t : allStackTraces.keySet()) { Map<String, Object> map = new HashMap<>(); map.put("thread", t); map.put("stackTraces", allStackTraces.get(t)); out.add(map); } return out; } private static String clean(String js) { // Remove slash-star comments. This regexp is not correct // since it removes comments that are in a string // constant. Should really parse. This is why we don't remove // double-slash comments, because they are likely to appear // (think http://). XXX js = js.replaceAll("/\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/", ""); int i, j; String tripleQuote = "\"\"\""; while ((i = js.indexOf(tripleQuote)) > 0) { if ((j = js.indexOf(tripleQuote, i + 1)) < 0) { break; } String pre = js.substring(0, i); String lines = js.substring(i + 3, j); String post = js.substring(j + 3); lines = lines.replaceAll("\n", "\\\\n"); js = pre + "\"" + lines + "\"" + post; } return js; } @GET @Path("getDefaultNotebook") @Produces(MediaType.TEXT_PLAIN) public String getDefaultNotebook() { final String defaultNotebookUrl = this.bkConfig.getDefaultNotebookUrl(); // TODO, assume the url is a file path for now. java.nio.file.Path defaultNotebookFile = Paths.get(defaultNotebookUrl); String content = this.utils.readFile(defaultNotebookFile); if (content == null) { System.out.println("Warning, default notebook is empty"); return ""; } return clean(content); } /** * This function returns a Boolean setting as string. If the setting is null in the preference, * it will use the setting in the config, otherwise, what is set in preference is used. * @param settingsListName * @param configs * @param prefs * @return */ private static String mergeBooleanSetting(String settingName, JSONObject configs, JSONObject prefs) { Boolean sc = (Boolean) configs.get(settingName); Boolean sp = (Boolean) prefs.get(settingName); Boolean s = (sp != null) ? sp : sc; String setting = null; if (s != null) { if (s.equals(Boolean.TRUE)) { setting = "true"; } else if (s.equals(Boolean.FALSE)) { setting = "false"; } } return setting; } /** * This function returns a list of settings from the result of merging setting gotten from * both the config and preference files and having to-remove items specified in the preference * file removed * @param settingsListName * @param configs * @param prefs * @return */ private static final String TO_REMOVE_PREFIX = "remove--"; private static Set<String> mergeListSetting(String settingsListName, JSONObject configs, JSONObject prefs) { Set<String> settings = new LinkedHashSet<>(); Set<String> settingsToRemove = new HashSet<>(); { // settings from config JSONArray s = (JSONArray) configs.get(settingsListName); if (s != null) { @SuppressWarnings("unchecked") Iterator<String> iterator = s.iterator(); while (iterator.hasNext()) { settings.add(iterator.next()); } } } { // settings from preference JSONArray s = (JSONArray) prefs.get(settingsListName); if (s != null) { @SuppressWarnings("unchecked") Iterator<String> iterator = s.iterator(); while (iterator.hasNext()) { settings.add(iterator.next()); } } } { // to-remove settings from preference JSONArray s = (JSONArray) prefs.get(TO_REMOVE_PREFIX + settingsListName); if (s != null) { @SuppressWarnings("unchecked") Iterator<String> iterator = s.iterator(); while (iterator.hasNext()) { settingsToRemove.add(iterator.next()); } } } settings.removeAll(settingsToRemove); return settings; } private void resetConfig() { final String configFileUrl = this.bkConfig.getConfigFileUrl(); final String preferenceFileUrl = this.bkConfig.getPreferenceFileUrl(); // TODO, assume the url is a file path for now. java.nio.file.Path configFile = Paths.get(configFileUrl); java.nio.file.Path preferenceFile = Paths.get(preferenceFileUrl); try { JSONParser parser = new JSONParser(); JSONObject configJsonObject = (JSONObject) parser.parse(this.utils.readFile(configFile)); JSONObject preferenceJsonObject = (JSONObject) parser.parse(this.utils.readFile(preferenceFile)); String isAllowTracking = mergeBooleanSetting("allow-anonymous-usage-tracking", configJsonObject, preferenceJsonObject); setAllowAnonymousTracking(isAllowTracking); String isUseAdvancedMode = mergeBooleanSetting("advanced-mode", configJsonObject, preferenceJsonObject); setUseAdvancedMode(isUseAdvancedMode); this.initPlugins.addAll(mergeListSetting("init", configJsonObject, preferenceJsonObject)); this.controlPanelMenuPlugins .addAll(mergeListSetting("control-panel-menu-plugins", configJsonObject, preferenceJsonObject)); this.menuPlugins .addAll(mergeListSetting("notebook-app-menu-plugins", configJsonObject, preferenceJsonObject)); this.cellMenuPlugins .addAll(mergeListSetting("notebook-cell-menu-plugins", configJsonObject, preferenceJsonObject)); } catch (ParseException e) { throw new RuntimeException("failed getting beaker configurations from config file", e); } } private Boolean isAllowAnonymousTracking = null; @POST @Path("setAllowAnonymousTracking") public void setAllowAnonymousTracking(@FormParam("allow") String allow) { if (allow == null) { this.isAllowAnonymousTracking = null; } else if (allow.equals("true")) { this.isAllowAnonymousTracking = Boolean.TRUE; } else if (allow.equals("false")) { this.isAllowAnonymousTracking = Boolean.FALSE; } else { this.isAllowAnonymousTracking = null; } final String preferenceFileUrl = this.bkConfig.getPreferenceFileUrl(); // TODO, assume the url is a file path for now. java.nio.file.Path preferenceFile = Paths.get(preferenceFileUrl); try { ObjectMapper om = new ObjectMapper(); TypeReference readType = new TypeReference<HashMap<String, Object>>() { }; Map<String, Object> prefs = om.readValue(preferenceFile.toFile(), readType); Boolean oldValue = (Boolean) prefs.get("allow-anonymous-usage-tracking"); // If value changed, write it to the file too if ((this.isAllowAnonymousTracking == null && oldValue != null) || (this.isAllowAnonymousTracking != null && !(this.isAllowAnonymousTracking.equals(oldValue)))) { prefs.put("allow-anonymous-usage-tracking", this.isAllowAnonymousTracking); om.writerWithDefaultPrettyPrinter().writeValue(preferenceFile.toFile(), prefs); } } catch (IOException e) { e.printStackTrace(); } } @GET @Path("isAllowAnonymousTracking") public Boolean isAllowAnonymousTracking() { return this.isAllowAnonymousTracking; } private Boolean isUseAdvancedMode = null; @POST @Path("setUseAdvancedMode") public void setUseAdvancedMode(@FormParam("advancedmode") String advancedMode) { if (advancedMode == null) { this.isUseAdvancedMode = null; } else if (advancedMode.equals("true")) { this.isUseAdvancedMode = Boolean.TRUE; } else if (advancedMode.equals("false")) { this.isUseAdvancedMode = Boolean.FALSE; } else { this.isUseAdvancedMode = null; } final String preferenceFileUrl = this.bkConfig.getPreferenceFileUrl(); // TODO, assume the url is a file path for now. java.nio.file.Path preferenceFile = Paths.get(preferenceFileUrl); try { ObjectMapper om = new ObjectMapper(); TypeReference readType = new TypeReference<HashMap<String, Object>>() { }; Map<String, Object> prefs = om.readValue(preferenceFile.toFile(), readType); Boolean oldValue = (Boolean) prefs.get("advanced-mode"); // If value changed, write it to the file too if ((this.isUseAdvancedMode == null && oldValue != null) || (this.isUseAdvancedMode != null && !(this.isUseAdvancedMode.equals(oldValue)))) { prefs.put("advanced-mode", this.isUseAdvancedMode); om.writerWithDefaultPrettyPrinter().writeValue(preferenceFile.toFile(), prefs); } } catch (IOException e) { e.printStackTrace(); } } @GET @Path("isUseAdvancedMode") public Boolean isUseAdvancedMode() { return this.isUseAdvancedMode; } /* Init Plugins */ private final Set<String> initPlugins = new LinkedHashSet<>(); @GET @Path("getInitPlugins") public Set<String> getInitPlugins() { return this.initPlugins; } /* bkApp Menu Plugins */ private final Set<String> menuPlugins = new LinkedHashSet<>(); @POST @Path("addMenuPlugin") public void addMenuPlugin(@FormParam("url") String urlAsString) { this.menuPlugins.add(urlAsString); } @GET @Path("getMenuPlugins") public Set<String> getMenuPlugins() { return this.menuPlugins; } /* bkControl Menu Plugins */ private final Set<String> controlPanelMenuPlugins = new LinkedHashSet<>(); @POST @Path("addControlMenuPlugin") public void addControlPanelMenuPlugin(@FormParam("url") String urlAsString) { this.controlPanelMenuPlugins.add(urlAsString); } @GET @Path("getControlPanelMenuPlugins") public Set<String> getControlPanelMenuPlugins() { return this.controlPanelMenuPlugins; } /* bkCell Menu Plugins */ private final Set<String> cellMenuPlugins = new LinkedHashSet<>(); @POST @Path("addCellMenuPlugin") public void addCellMenuPlugin(String p) { this.cellMenuPlugins.add(p); } @GET @Path("getCellMenuPlugins") public Set<String> getCellMenuPlugins() { return this.cellMenuPlugins; } @GET @Path("getMainPage") @Produces(MediaType.TEXT_HTML) public String getMainPage(@Context HttpServletRequest request) { String data = utils.readFile(bkConfig.getMainPageFileName()); data = data.replace("URL-HASH-TO-REPLACE", bkConfig.getHash()); return data; } @GET @Path("getPluginPrefs") @Produces(MediaType.APPLICATION_JSON) public Object getPluginPrefs() { return bkConfig.getPluginPrefs(); } @POST @Path("setPluginPrefs") public void setPluginPrefs(JSONObject pluginPrefs) { // Merge pluginPrefs into prefs, and save the file, like the above methods do. bkConfig.setPluginPrefs(pluginPrefs); } }