placebo http server

Placebo is a java based web server/library designed to work well with small admin portals or web based control panels. Its capable of handling sessions, and has a built in "plugin" format for designing simple http apps. To launch placebo, simply run the jar file and the application will start in gui mode. For command line options try launching with --help

front end

detailed logging

Creating a plugin

Here is a sample plugin class. This is a simple keystore using a JSONObject.

/* Copyright (C) 2010 Brian Dunigan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package org.openstatic.placebo.plugins; import org.openstatic.http.HttpRequest; import org.openstatic.http.HttpResponse; import org.openstatic.placebo.*; import org.openstatic.util.JSONUtil; import java.util.Date; import java.util.Properties; import java.util.Enumeration; import java.util.StringTokenizer; import javax.swing.JTextPane; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import java.awt.Font; import org.json.*; public class KeyStore implements PlaceboPlugin { protected JSONObject store; private JTextPane json_box; private boolean swing_mode; protected boolean keep_running; private Thread key_gc; private int key_timeout; public boolean startup(CoreServer core, String mount_location) { System.err.println("KeyStore: Initializing Keystore"); this.keep_running = true; this.key_timeout = Integer.valueOf(core.getSettings().getProperty("keystore_timeout", "3600")).intValue(); store = new JSONObject(); key_gc = new Thread() { public void run() { while(KeyStore.this.keep_running) { try { expire_check(; Thread.sleep(1000); } catch (Exception e) {} } } public void expire_check(JSONObject jo) throws Exception { String[] fnames = JSONObject.getNames(jo); if (fnames != null) { for (int i = 0; i < fnames.length; i++) { Object value = jo.get(fnames[i]); if (value instanceof JSONObject) { JSONObject nd = (JSONObject) value; boolean expired = false; try { int exp = nd.getInt("_expires"); exp--; if (exp <= 0) expired = true; nd.put("_expires", exp); } catch (Exception e) {} if (expired) jo.remove(fnames[i]); else expire_check(nd); } } } } }; key_gc.start(); if (core.isSwingOk()) { json_box = new JTextPane(); json_box.setContentType("text/html"); Font font = new Font("Monospaced", Font.BOLD, 12); json_box.setFont(font); JScrollPane scroller = new JScrollPane(json_box); scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); core.addPanel("Keystore [" + mount_location + "]", scroller); this.swing_mode = true; } else { this.swing_mode = false; } return true; } public void updateBox(JSONObject data) throws Exception { if (this.swing_mode) json_box.setText("" + JSONUtil.json2table(store) + ""); } public void onRequest(HttpRequest request) throws Exception { HttpResponse response = new HttpResponse(); if (request.getPath().equals("/set/")) { for(Enumeration keys = request.getGetKeys(); keys.hasMoreElements(); ) { String key = keys.nextElement(); store.put(key, request.getGetValue(key)); } updateBox(store); } else if (request.getPath().startsWith("/set/") && request.getPath().endsWith("/")) { String storage_path = request.getPath().substring(5); storage_path = storage_path.substring(0, storage_path.length() - 1); StringTokenizer st = new StringTokenizer(storage_path, "/"); JSONObject current_object =; while (st.hasMoreTokens()) { String current_node = st.nextToken(); try { current_object = current_object.getJSONObject(current_node); current_object.put("_expires", this.key_timeout); } catch (Exception js_exc) { JSONObject new_obj = new JSONObject(); new_obj.put("_expires", this.key_timeout); current_object.put(current_node, new_obj); current_object = current_object.getJSONObject(current_node); } if (!st.hasMoreTokens()) { for(Enumeration keys = request.getGetKeys(); keys.hasMoreElements(); ) { String key = keys.nextElement(); current_object.put(key, request.getGetValue(key)); } } } updateBox(store); } if (request.getPath().equals("/get/")) { response.setData(store); } else if (request.getPath().startsWith("/get/") && request.getPath().endsWith("/")) { JSONObject this_response = new JSONObject(); String requested_key = request.getPath().substring(5); requested_key = requested_key.substring(0, requested_key.length() - 1); StringTokenizer st = new StringTokenizer(requested_key,"/"); JSONObject current_object =; while (st.hasMoreTokens()) { String current_node = st.nextToken(); Object temp_object = current_object.get(current_node); if (temp_object instanceof JSONObject) { current_object = (JSONObject) temp_object; if (!st.hasMoreTokens()) { response.setData(current_object); } } else { if (!st.hasMoreTokens()) { response.setData((String) temp_object); } } } } request.sendResponse(response); } public boolean shutdown() { this.keep_running = false; System.err.println("KeyStore: Shutdown"); return true; } }

This demo program is built into the placebo library and can be executed using
java -jar placebohttp.jar --start --debug --plugin /keystore/ org.openstatic.placebo.plugins.KeyStore

When this simple program is mounted within placebo, It can be used like this
(delicious is returned)

Also /keystore/get/ would return a json object with all keys
{"food": "delicious"}

This keystore also supports layered keys with paths

Would result in a json object returned with all of the subkeys and objects of car