Code illustrating use of the HTTP server included in Java JDK 1.6. via Groovy to present a browser-based UI to an app.
Result
The code allows this usage:
main.SimpleServer.serve(0){ s, t, p -> // handle the request, response here... // now shut down, s.stopServer() }
Listing 1, How it’s used.
This will create a web server using the host “localhost” at an unused port p, and then automatically open the default browser at “http://localhost:p/”. The closure will be the application. Neat. Of course, you would only use this behind a firewall, etc.
The above code, as a I later discovered, is very similar to other frameworks. Here is a new one I just learned about:
vertx.createHttpServer().requestHandler { req -> def file = req.uri == "/" ? "index.html" : req.uri req.response.sendFile "webroot/$file" }.listen(8080)
A sample session running the example code and using telnet is:
telnet localhost 21224 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /?reply=24 HTTP/1.1 HTTP/1.1 200 OK Content-length: 15</pre> <h1>Wrong!</h1> <pre>Connection closed by foreign host.
The Eclipse console contains:
+++++++ Simple Server ++++++++++++++++++ uri: [/?reply=24] protocol: [HTTP/1.1] query: [reply=24] path: [/] params: [[reply:[24]]] ------------------------- Stopping server ... stopped!
The browser will show:

Scenario
You have to supply a GUI for running a local Java application. One example could be the setup of a product build. Builds, though usually executed with Ant, Maven, or Gradle, may still require the user to select some target parameters or build type. Why not just use the browser? It can present a more modern interface and with good design, allow more fault tolerant use then that with command line or multiple prompt boxes.
This approach also allows future remote build server use, since the designed browser UI can be reused. The browser is ubiquitous and creating web pages is relatively easy (though can be a hair pulling nightmare sometimes). And, with the use of AJAX and high-level frameworks, like Dojo, JQuery, or React, browser clients can more easily duplicate the usability of dedicated thick client apps. HTML5 (see HTML Cheatsheet) is also helping to obliterate any remaining reasons for using a thick-client app.
An embedded server as used here, is great when the task is ad hoc, short lived, or single user. In the provided example, once the input is received the server shuts down. For more complex or ubiquitous use a standard server or more powerful embedded server should be used.
Embedded Server
For a local application that accesses system resources and uses the browser as the UI, using an embedded server is the simplest approach. Pure Javascript, Applets, and other means are very complex and may run against configuration issues with Browser security settings and so forth. In the Java world, that would mean using the more popular Tomcat or Jetty servers.
However, Java 1.6 now includes a light-weight HTTP Server. So, the configuration and requirements are more tractable. Nothing more to download and programmatic configuration concerns are minor. Note that the Java HTTP server does not offer many features, one augments this with custom code. For example, parsing of the request query is not present.
That the package of this server is com.sun… is problematic. Will it stay around, become part of the javax packages, etc? Should the JDK even have this built in? According to this post by M. MacMahone
… is that the API and implementation are a fully supported, publicly accessible component of Sun’s implementation of Java SE 6. It does mean however, that the packages are not formally part of the Java SE platform, and are therefore not guaranteed to be available on all other (non Sun) implementations of Java SE 6.
Incidentally, the Groovy language has an import system, Grape, that can also make use of Tomcat or Jetty as transparently as using the JDK embedded server. See the Further Reading below for an example using Groovlets.
This code illustrates
(more for my future reference)
The list below was some of the things the code used and the final code listed here may no longer have them.
- com.sun.net.httpserver API use.
- Using JQuery in external js files.
- How to stop the server.
- With AJAX post
- Timeout
- HTTP context
- Using ScheduledExecutorService to limit runtime.
- A console ASCII spinner.
- Groovy GString use.
- Launching the default browser.
- Selecting an unused port.
- Simplistic state machine configuration.
- Detecting Java version.
- AJAX using JQuery.
- Groovy object construction from script.
- Use of Closure.
- Basic authentication
- Quasi Anonymous class use in Groovy
- access to resources
Of course, not great example of the above, but … Warning, code is not production ready, etc. There is no real exception handling!
How it works.
TODO: Give some idea what all that code does.
The index.html creates a simple form with three buttons (submit, ping, and end), an input field, and a ‘console’ output area.
– submit: send the answer to the server which then gives feedback, correct or wrong. The server then deliberately shuts down.
– ping: sends an AJAX request to the ping context which just sends back the time, the response is appended to the console.
– end: sends an AJAX request to the ‘stop’ context. The server responds with ‘stopping server …’, then shuts down. All buttons are disabled.
Why Groovy
Groovy is a dynamic JVM based language whose most prominent feature is that it extends the Java syntax to be more usable, i.e., less wordy,. From the example, a method that prints the contents of a map can be defined as:
def showInfo(info){info.each{ k,v -> println "$k = $v" }}
Then invoked as:
showInfo(uri:uri,protocol:protocol,query:query,path:path)
The Groovy In Action book is pretty thorough. Chapter 1 makes a good case, and may still be available here.
Why not Groovy? Well, being dynamic can be rough, especially if it impacts the ability of a IDE to offer the features that come from using Java, like completions, etc. The Eclipse plug-in is getting much better, and I read the IntelliJ IDEA groovy support is top-notch. But, the worlds most popular language, JavaScript, is dynamic, and it hasn’t bothered too many people (maybe end users?).
Source
When I first wrote this, I created a complicated “app” with a bunch of class files and so forth. Then I simplified it. Later I said, yuck. Finally I decided that this should just be a simple one method invocation as shown at the beginning of this post. Consequently, a bunch of stuff in this sample code is guarded by conditionals and it works but sure can be simplified.
While coding I ran into a strange classloader issue see this post. There are a few files in this demo
- SimpleServer.groovy: provides the ‘facade’ to HTTPServer. Handles the “/” context.
- AppContext.groovy: An example of an “app” that gets mapped to a context path.
- server.properties: Externalizes some properties.
- index.html: The initial app interface.
- scripts.js: The JQuery AJAX button handlers
- stylesheet.css: Stylesheet, just to show that the code can load it.
- TestServe.groovy: An example of just using the serve method. Not really a test.
The source code can be downloaded at: here
Listing 4. TestServe.groovy (not a unit test, btw)
//import static main.SimpleServer as SS; // still won't work!!! GROOVY-4386 import com.sun.net.httpserver.HttpExchange; import main.SimpleServer main.SimpleServer.serve(0){ ss, t, params -> if(!(params.size())){ def path = t.getRequestURI().getRawPath() if(path.length()>1){ ss.sendPage(t,path) }else if(path =="/") { ss.sendPage(t,"/src/index.html") } }else{ def answer = params.get("reply") reply = (!answer || answer[0] != '"42"') ? "Wrong!" : "Correct!" ss.sendString(t,"</pre> <h1>$reply</h1> <pre>") ss.stopServer() } }
/** * File: SimpleServer.groovy * Date: 20110314T2125-05:00 * Author: jbetancourt */ package main import java.io.IOException; import java.io.OutputStream; import java.net.Authenticator; import java.nio.channels.* import java.nio.* import java.text.SimpleDateFormat import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit as TU; import java.util.zip.*; import com.sun.net.httpserver.Authenticator; import com.sun.net.httpserver.Authenticator.*; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.BasicAuthenticator; import java.util.concurrent.ScheduledExecutorService; import org.codehaus.groovy.reflection.ReflectionUtils import edu.stanford.ejalbert.BrowserLauncher; /** * Example of using the Java 1.6 HTTP server com.sun.net.httpserver. * * @see "http://download.oracle.com/javase/6/docs/jre/api/net/httpserver/ * spec/com/sun/net/httpserver/package-summary.html" * * Version 0.02 * * @author jbetancourt */ class SimpleServer implements HttpHandler{ static final STOP_WAIT = 2 static final HOST = "localhost" static final HTTP_PROTOCOL = "http://" static final splashText = " Embedded Java 1.6 HTTP server example (ver $version) " static final company = " 20110314T2125-05:00 jbetancourt" static final UTF_8 = "UTF-8" static final version = "0.3" static final realmName = "my realm" static port =0 // if zero, get unused port static url static int counter = 0 // for spinner static String basedir // docbase static String scriptDir HttpServer server static SimpleServer simpleServer def SRC_INDEX_HTML = "/src/index.html" def serverPropFilePath = "server.properties" def props = new Properties() def browserLauncher def ScheduledExecutorService cancelExec def authenticate = false def Closure handleParams /** * The only thing you really need. * @param port if 0, unused port will be used * @param closure handles the request,response * @return */ static serve(port, Closure closure){ def ss = new SimpleServer() ss.port = port ss.handleParams = closure ss.serverPropFilePath = "" ss.exec(new String[0]) return ss } /** * Entry point into the demo. * @param args */ static main(args) { splash() if(!validJavaVersion()){ return } simpleServer = new SimpleServer() simpleServer.exec(args) } // end main /** */ def exec(args){ def basedir = new java.io.File(".").getAbsolutePath() configure(args) createHTTPServer() start() println("server started. url=$url,basedir=$basedir,scriptDir=$scriptDir,") def spb = props.getProperty("progressBar") cancelExec = Executors.newScheduledThreadPool(1) keepAlive(false, TU.MILLISECONDS.convert(1L, TU.MINUTES)) } /** * Handle the given request/response. * * The first response is the input form. The subsequent * request evaluates the answer if any, and then the server * is stopped. Any exception will also stop the server. * */ @Override public void handle(HttpExchange t) throws IOException { try{ def uri = t.getRequestURI() def query = uri.getRawQuery() def path = uri.getRawPath() def params = parseQuery(query) showInfo( uri:uri,protocol:t.getProtocol(), query:query,path:path,params:params) if(handleParams){ handleParams(this, t, params) }else{ if(query == null){ if(path.length()>1){ sendPage(t,path) }else if(path =="/") { sendPage(t,SRC_INDEX_HTML) } } } }catch(Exception ex){ ex.printStackTrace() stopServer() throw ex; } } /** */ def showInfo(info){ println "+++++++ Simple Server ++++++++++++++++++" info.each{ k,v -> println k + (v ? ': [' + v +']' : ": []") } println "-------------------------" } /** */ static splash(){ println("$splashTextnn$company") } /** */ def configure(args){ try{ basedir = new java.io.File(".").getAbsolutePath() scriptDir = "$basedir/src/" def propFileName = (args.length) >0 ? args[0] :serverPropFilePath port = unusedPort(HOST) url = "$HTTP_PROTOCOL$HOST:$port/" if(propFileName){ def getResource = {def resource-> ReflectionUtils.getCallingClass(0). getResourceAsStream(resource) } InputStream context = getResource(propFileName) if(context){ props.load(context) context.close() } } }catch(Throwable ex){ handleException(ex) }finally { //System.exit(1) } } /** */ def createHTTPServer(){ server = HttpServer.create(new InetSocketAddress(port),0); props.propertyNames().iterator().each{key -> if(key.matches(".*Context$")){ def conf = props.get(key).split(",") def className = conf[0] def contextPath = conf[1]? conf[1].trim(): "/" def obj = createObjectFromScript(className, this) def context = server.createContext(contextPath, obj) if(conf.length>2){ def authClassName = conf[2] def ac = createObjectFromScript(authClassName, conf.length > 3? conf[3] : realmName) context.setAuthenticator(ac) } def iProp = key + ".initialState" if(props.containsKey(iProp)){ obj.currentState = props.getProperty(iProp) } iProp = key + ".transitions" if(props.containsKey(iProp)){ obj.setTransitions(props.getProperty(iProp)) } } } def context = server.createContext("/", this) if(authenticate){ context.setAuthenticator(new MyAuthenticator("my realm")) } server.createContext("/stop", [ handle:{ println("in handler for stop .. t[" + it + "]") sendString(it,"stopping server ....") stopServer() } ] as HttpHandler); server.createContext("/ping", [ handle:{ ct -> println("pinging ...") ping(ct); } ] as HttpHandler); } // end createHTTPServer /** * * @return */ static boolean validJavaVersion(){ def flag = true def ver = System.getProperty("java.version"); if(!ver.contains("1.6") && !ver.contains("1.7")){ println("ERROR *** Requires Java 1.6 or above. Detected: $ver"); flag = false; } return flag; } /** * Send the initial query page. * * @param t the request handler * @param filePath the html file * @return nothing */ static sendPage(HttpExchange t, String filePath) throws IOException { OutputStream os = null; try{ def fPath = new File(basedir + filePath) def uri = fPath.toURI() Map>map = t.getResponseHeaders() def binary = false if(filePath.endsWith( ".js")){ map.set("Content-Type", "text/javascript; charset=UTF-8") }else if(filePath.endsWith(".gif")){ map.set("Content-Type", "image/gif;") binary = true }else if (filePath.endsWith(".jpg")) { map.set("Content-Type", "image/jpeg;") binary = true } println("sending .... " + fPath) if(binary){ byte[] bytes = readFile(fPath.getPath()) t.getResponseHeaders().set("Content-Encoding", "gzip") t.sendResponseHeaders(HttpURLConnection.HTTP_OK,0) GZIPOutputStream gos = new GZIPOutputStream(t.getResponseBody()) gos.write(bytes) gos.finish() t.close() }else{ def response = fPath.getText() t.sendResponseHeaders(HttpURLConnection.HTTP_ACCEPTED, response.length()); os = t.getResponseBody(); os.write(response.getBytes()); t.close() } }catch(FileNotFoundException ex){ println(ex.getMessage()) }catch(Exception ex){ ex.printStackTrace() if(os){ println("close output stream ...") os.close(); } } } /** * Read file into buffer. * @param path * @return */ static byte[] readFile(String path){ File file = new File(path) FileInputStream inStream = new FileInputStream(file) FileChannel inChannel = inStream.getChannel(); def bb = ByteBuffer.allocate(1024*1024) while(true){ int bytesRead = inChannel.read bb if(bytesRead == -1){ break; } } return bb.array() } /** * Send the resulting score based on response content. * @param t * @param answer * @return */ static sendString(t, answer ) throws IOException { t.sendResponseHeaders(HttpURLConnection.HTTP_OK, answer.length()); OutputStream os = t.getResponseBody(); os.write(answer.getBytes()); os.close(); } /** * Just followed example at: * @see http://download.oracle.com/javase/6/docs/api/java/util * /concurrent/ScheduledExecutorService.html */ def keepAlive(showProgressBar, Long maxTime){ def handleBeep = cancelExec.scheduleAtFixedRate(new Runnable(){ public void run(){ if(showProgressBar){ progressBar() } } }, 1, 4, TU.SECONDS); cancelExec.schedule(new Runnable(){ public void run(){ println("ncancel beeping") handleBeep.cancel(true) stopServer() System.exit(0) } },4, TU.MINUTES); } /** * In shell console, ASCII spinner gives visual feedback of running server. * Got idea for approach at * @see http://blogs.msdn.com/b/brada/archive/2005/06/11/428308.aspx * But, then took out the use of a switch. As Charles Moore would say, * never use conditionals when it can be calculated. */ static def progressBar(){ print("b${["/","-","","-"][counter++ % 4]}") } /** */ Object createObjectFromScript( String className, Object... args ) throws Exception { println "Creating $className" def gcl = new GroovyClassLoader(this.class.classLoader) def path = "$scriptDir${className.replace('.','/')}.groovy" def cl = gcl.parseClass( new File(path)) def ni = cl.newInstance(args) return ni; } /** * Get an unused port for server and browser url. * If port is non-zero. * BTW, at shell: * On windows: netstat -an * On linux: netstat -an | grep -i listen * * @see http://stackoverflow.com/questions/573361 * /how-can-i-detect-a-free-port-on-the-server-by-code-from-client-side * * I tried simpler ways, but they didn't work. Like using 0 as port. * * @param hostname * @return port number */ static int unusedPort(String hostname) throws IOException { if(port){ return port } int minPort = 8000 int range = 0xFFFF - 8000 while (true) { int port = minPort + (int) (range * Math.random()); try { Socket s = new Socket(hostname, port); s.close(); // is this wise? } catch (ConnectException e) { return port; } catch (IOException e) { if (e.getMessage().contains("refused")){ return port; } throw e; } } } /** */ def ping(t){ def now = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date()) sendString(t, "$now") } /** */ String getServerProperty(key){ return props.getProperty(key) } /** */ def start(){ server.start(); launchBrowser(url) } /** */ def launchBrowser(url){ new BrowserLauncher().openURLinBrowser(url) } /** */ def stopServer(){ print("nStopping server ... ") ((HttpServer)server).stop(2) print("stopped!") System.exit(0) } /** */ static handleException(Exception ex){ println("ERROR: ${ex.getMessage()}") ex.printStackTrace() throw ex } /** * Parse query into list of values array. * * @see http://stackoverflow.com/questions/1667278/parsing-query-strings-in-java * @param query * @return */ static Map parseQuery(final String query){ Map params = new HashMap(); if(!query || query.length() == 0){ return params } def key,val for (String param : query.split("&")) { String[] pair = param.split("="); if(pair.length > 0){ key = URLDecoder.decode(pair[0], UTF_8); } val="" if(pair.length > 1){ val = URLDecoder.decode(pair[1], UTF_8); } List values = params.get(key); if (values == null) { values = new ArrayList(); params.put(key, values); } values.add(!val ? "":val ); } return params; } } // end SimpleServer // the authenticator class. Should have been just a simple inner class. class MyAuthenticator extends BasicAuthenticator { /** */ public MyAuthenticator(String realm){ super(realm) } @Override public Authenticator.Result authenticate(HttpExchange t){ return super.authenticate(t) } @Override public boolean checkCredentials(String username, String password){ //printf("user=%s, pass=%s%n", username, password) return true } } // end MyAuthenticator class
Listing 6 server.properties If there is a setting for a property file it will be loaded and used. This is an example:
# SimpleServer configuration # # context = FQCN,path[,authenticator FQCN, domain name]* controllerContext = main.AppContext,/question controllerContext.initialState = running # simple state transitions = state:next_state[,state:next_state]* controllerContext.transitions = init:running,running:end # Misc host=localhost stop_wait = 2 basedir= scriptdir= progressBar=true browserLauncher=main.BrowserLauncher
Listing 7 index.html The ‘app’ UI:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script> <!-- <script src="/src/jquery.blockUI.js" type="text/javascript"></script> --><script type="text/javascript" src="/src/scripts.js"></script><script type="text/javascript">// <![CDATA[ $(document).ready(function(){ //$(document).ajaxStart($.blockUI).ajaxStop($.unblockUI); pingServer(); stopServer(); }); // ]]></script></pre> <div class="box middle lightGrey"> <div class="middle">Embedded HttpServer Demo <span class="tiny">(com.sun.net.httpserver)</span></div> <hr /> <form id="form1" class="box internal grey" action="/question" method="get" name="form1"> <input id="mode" type="hidden" name="mode" value="mathInput" /> <table width="100%"> <tbody> <tr> <td></td> </tr> <tr style="margin: 12px;"> <td><span id="lblReply" class="heavy"> What is "4"+"2"? </span></td> <td><input id="reply" type="text" name="reply" /></td> <td><input id="submitButton" title="submit" type="submit" value="submit" /></td> <td><input id="pingButton" type="button" value="ping" /></td> <td><input id="stopButton" type="button" value="end" /></td> </tr> <tr> <td></td> </tr> </tbody> </table> </form></div> <pre></pre> <pre>
Listing 8 scripts.js JQuery stuff:
// file: scrips.js // author: jbetancourt // // External JQuery use. // technique reference: http://www.latentmotion.com/separating-jquery-functions-into-external-files-without-selectors/ /* <![CDATA[ */ var pingServer; var stopServer; (function($){ pingServer = function(){ $('#pingButton').click(function(){ $.get('/ping', {mode:"ping"}, function(data){ $('#target').append(" "+data) },"html") .error(function(response,status,xhr){ var msg = "Server does not responsd: "; $("#target").html(msg + " " + status + (xhr.status ? " xhr.status: [" + xhr.status + "] " + "] xhr.statusText: [" + xhr.statusText + "]" : "") ); }); }); }; })(jQuery); (function($){ stopServer = function(){ $('#stopButton').click(function(){ $("#submitButton").attr('disabled','disabled'); $("#pingButton").attr('disabled','disabled'); $("#stopButton").attr('disabled','disabled'); $('#target').load('/stop', function(response,status,xhr){ if(status == "error"){ var msg = "Server does not responsd; "; $("#target").html(msg + xhr.status + " " + xhr.statusText); } }); }); }; })(jQuery); /* ]]> */
.heavy{ font-weight:bolder;} .box{ border:2px solid black;} .middle{ margin-left:auto;margin-right:auto;width:60%;} .internal{ margin:2em;background:#F8F8F8 ;} .horzCenter { margin-left:auto;margin-right:auto;width:50%;} .grey { background:#F8F8F8;} .lightGrey { background:#F0F0F0;} .large{ font-size:xx-large;padding-top:4px; padding-bottom:4px} body{ width:960px } .tiny{font-size:small;}
Listing 10 example app AppContext.groovy
/** * File: AppContext.groovy * Date: 20110320T1952-05:00 * Author: jbetancourt */ package main import java.text.DateFormat; import java.text.SimpleDateFormat import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; /** * The context that hosts the application. * * @author jbetancourt * */ class AppContext implements HttpHandler{ def SimpleServer server; def static RUNNING = 'running' def currentState def transitions = [:] /** */ AppContext(SimpleServer server){ this.server = server currentState = server.getServerProperty("initialState") def statesProperty = server.getServerProperty("transitions") } /** * Handle the given request/response. * * The first response is the input form. The subsequent * request evaluates the answer if any, and then the server * is stopped. Any exception will also stop the server. * */ @Override public void handle(HttpExchange t) throws IOException { try{ def uri = t.getRequestURI() def final query = uri.getRawQuery() def path = uri.getRawPath() def params = server.parseQuery(query) showInfo([query:query,uri:uri,path:path,currentState:currentState,params:params]) def mode = params.get("mode") if(atState("running")){ if(mode){ if(mode[0] == "mathInput"){ def reply = params.get("reply") evaluateAnswer(t,reply) transitionNextState() }else if (mode[0]=="ping") { server.sendString(t,"huh?") } } } if(atState("end")){ server.stopServer() } }catch(Exception ex){ ex.printStackTrace() server.stopServer() throw ex; } } /** * And, send response. */ def evaluateAnswer(t,answer){ def reply try{ reply = (answer[0] != '"23"') ? "Wrong!" : "Correct!" }catch(Exception ex){ reply = "wrong" } server.sendString(t,"</pre> <center> <h1>$reply</h1> </center> <pre>") } /** */ def showInfo(info){ println "++++++++ AppContext +++++++++++++++++" info.each{ k,v -> println k + (v ? ': [' + v +']' : ": []") } println "-------------------------" } /** */ def ping(t){ def now = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date()) server.sendString(t, "$now") } /** */ def setTransitions(s){ s.split(",").each{ tran -> def kv = tran.split(":") transitions.put(kv[0],kv[1]) } } /** */ def atState(s){ return currentState == s } /** */ def transitionNextState(){ print("currentState[$currentState],") def ns = transitions[currentState] currentState = (ns ? ns : "end") println(" next state=[$currentState] ") } } // end AppContext class
Summary
Shown was a simple example of using the JDK 1.6 embedded HTTP server using the Groovy language.
Updates
- April 15, 2011: Added some beginnings of code to handle image resources.
- August 4, 2011: Just saw an old post on same subject. “Groovy++ in action: DSL for embedding HttpServer”. Added it to references. That one creates a DSL to use the embedded HTTP server. Nice.
- Dec 4, 2011: This post has an example of using Gretty via Groovy: Five Cool Things You Can Do With Groovy Scripts
- Feb 11, 2012: The internal JDK Http server is being used here: “How ION uses Virgo“.
Required Software
• Oracle Java JDK 1.6.0.24
• Groovy 1.8-rc3
• BrowserLauncher2
Dev Software
• Eclipse: Helios Service Release 2
• Windows 7 Pro, 64bit
• Mercurial 1.8.1
• TortiseHG 2.0.2
• MercurialEclipse 1.0.0
• Groovy-Eclipse 2.1.3
Further Reading
API
com.sun.net.httpserver
http://download.oracle.com/javase/6/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/package-summary.html
Other Servers
Vert.x
Gretty
“Java development 2.0: Ultra-lightweight Java web services with Gretty”
Tomcat
Jetty
- Exposing Functionality Over HTTP with Groovy and Ultra-Lightweight HTTP Servers
- Jetty
- Spark – A small web framework for Java
- Jetty/Tutorial/Embedding Jetty
- Grizzly
HTTP Implementations in other languages
Python
Lib/http/server.py
CherryPy
Misc
* http://www.redtoad.ca/ataylor/2012/02/simple-servlets-in-groovy/
* Groovy++ in action: DSL for embedding HttpServer
* Java non-blocking servers, and what I expect node.js to do if it is to become mature
* Sun’s secret web server
* Using com.sun.net.httpserver
* node.groovy?
* Ratpack
* AsynchronousChannel
* Mp3d project (which uses com.sun.httpserver)
* Groovy Goodness: Groovlets as Lightweight Servlets
* Making a simple web server in Python.
* Embedded HTTP server
* Comparison of web server software
* Groovlets
* Practically Groovy: MVC programming with Groovy templates
* HTTP server API in Sun’s Java SE 6
* Using Sun Java 6 HttpServer to write a functional HTTP test
* Package com.sun.net.httpserver
* Example for Java HTTP Server API and Ruby WEBrick HTTP Server
* Loop back connection leak
* EchoServer.java
* JDK modules source
* Java Documentation 6.0 JDK Modules
* ServerImpl
* D. Ferrin resource access solution
* Simple Java HttpServer / Handler
* post by hiro345
* com.sun.net.httpserver.HttpServer for comet/cometd
* Separating jQuery Functions into External Files (without selectors!)