Note: This code is written in Scala but can be converted to Java
First we'll need to create a servlet class that extends HttpServlet. The servlet class, for every GET request, will check if our thread (explained later) is open and if not tries to start it up. Note that we are using a try catch with the start method because if the state of the thread is TERMINATED isAlive will return true (I have no idea why).
I'm also implementing the destroy method to kill our thread if the servlet dies. I'm not sure what happens to running threads if the servlet dies but just in case...
Servlet.scala:
package com.example
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class Servlet extends HttpServlet {
override def doGet(req: HttpServletRequest, resp: HttpServletResponse) = {
if (!CLIThread.isAlive) {
try {
CLIThread.start
}
catch {
case _ => resp.getOutputStream().println("Error: thread state is, " + CLIThread.getState)
}
}
resp.getOutputStream().println(CLIThread.pool)
}
override def destroy(): Unit = {
super.destroy()
CLIThread.shutdown
}
}
Our thread (CLIThread) is scala object extending the Thread class.
CLIThread has 2 methods, pool & shutdown, and both of them are passed to our Runnable implementation.
CLIRunnable is the Runnable implemented object that is passed to the Thread constructor.
CLIRunnable has a nulled ServerSocket (listener), Socket (socket) & InputStream (in), a true Boolean (keepAlive) and an empty String (_pool) as variables.
The run method:
- Assigns a new ServerSocket to the listener variable. I use a while loop, a boolean, a try-catch block and a random function to assign a serverSocket that listens to an empty port, otherwise it would have thrown an Exception if the port was used
- Assign the accepted socket from the listener object (this method holds the thread until a a socket connects to the listener, which is why we use a thread)
- Assigns the socket input stream to in
- As long as keepAlive is true checks if the input stream is not empty and if so it fills the _pool variable with it.
The pool method:
- If the in variable is not null (CLIThread was started and a socket connected to our port), we return the _pool String and empty it.
- Otherwise If the above is false but listener is not null (a socket was not yet accepted) we print out the value of the listener for the user to use to connect to our port.
- If all the above fails than listener is nulled which means the thread never started, we print the string "listener == null..."
The shutdown method:
- Sets keep alive to false (releases the thread from the while loop)
- If listener is not null:
- If no socket connected to our port, creates a new socket that connects and closes it, to release our listener from it's loop.
- If socket is not null closes it.
- Closes the listener
CLIThread.scala
package com.example
import java.io.InputStream
import java.net.ServerSocket
import java.net.Socket
object CLIThread extends Thread(CLIRunner) {
def pool: String = CLIRunner.pool
def shutdown() = CLIRunner.shutdown()
}
protected final object CLIRunner extends Runnable {
var listener: ServerSocket = null
var socket: Socket = null
var in: InputStream = null
private var keepAlive = true
private var _pool = ""
def run: Unit = {
var ok = false
while (!ok) {
try {
listener = new ServerSocket((math.random * 10000).toInt)
ok = true;
}
catch {
case _ => {}
}
}
socket = listener.accept()
in = socket.getInputStream
while (keepAlive) {
while (in.available() > 0) _pool += in.read().toChar
}
}
def pool: String = if (in != null) {
val temp = _pool
_pool = ""
return temp
}
else if (listener != null) (listener.getInetAddress, listener.getLocalPort).toString
else "listener == null..."
def shutdown() {
keepAlive = false
if (listener != null) {
if (socket == null)
(new Socket(listener.getInetAddress, listener.getLocalPort)).close()
if (socket != null)
socket.close()
listener.close()
}
}
}
CLI.scala
package com.example
import java.net.Socket
import java.net.URL
object CLI extends App {
val addr = args(0) // The server address (example.com:8080)
val addrUrl = new URL("http://" + addr + "/~cli/addr")
var con = addrUrl.openConnection()
var in = con.getInputStream()
var cliAddr = ""
while (in.available() > 0)
cliAddr += in.read.toChar
val portUrl = new URL("http://" + addr + "/~cli/port")
con = portUrl.openConnection()
in = con.getInputStream()
var cliPort = ""
while (in.available() > 0)
cliPort += in.read.toChar
val socket = new Socket(cliAddr, Integer.valueOf(cliPort))
implicit def stringToByteArray(s: String) = s.toCharArray.map(c => c.toByte)
socket.getOutputStream().write("Hellllo from CLI process")
}
CliAddr.scala
package org.sdms
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class CliAddr extends HttpServlet {
override def doGet(req: HttpServletRequest, resp: HttpServletResponse) {
resp.getWriter.print(CLIRunner.listener.getInetAddress.getHostAddress)
}
}
CliPort.scala
package com.example
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class CliPort extends HttpServlet {
override def doGet(req: HttpServletRequest, resp: HttpServletResponse) {
resp.getWriter.print(CLIRunner.listener.getLocalPort)
}
}