/*
* Copyright 2014 DataGenerator Contributors
*
* 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 org.finra.datagenerator.samples.manager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;
/**
* Marshall Peters
* Date: 8/15/14
*/
public class JettyManager implements WorkManager {
private Handler jettyHandler;
private AtomicLong globalLineCounter;
private AtomicLong remainingBlocks;
private int listeningPort;
private String hostName;
private Server server;
private AtomicLong time;
private final long maxScenarios;
private long startTime;
private long lastCount;
private Queue<WorkBlock> blocks;
private AtomicLong serial;
/**
* Constructor
*
* @param maxScenarios the number of output rows to produce
*/
public JettyManager(final long maxScenarios) {
server = null;
blocks = new LinkedList<>();
serial = new AtomicLong(0);
remainingBlocks = new AtomicLong(0);
this.maxScenarios = maxScenarios;
}
/**
* Returns a description a block of work, or "exit" if no more blocks exist
*
* @param name the assigned name of the consumer requesting a block of work
* @return a description a block of work, or "exit" if no more blocks exist
*/
public synchronized String requestBlock(String name) {
if (blocks.isEmpty()) {
return "exit";
}
remainingBlocks.decrementAndGet();
return blocks.poll().createResponse();
}
/**
* Assigns unique names to each consumer to use in reporting
*
* @return an unused name for identifying a consumer
*/
public synchronized String requestName() {
return String.valueOf(serial.incrementAndGet());
}
/**
* Handles reports by consumers
*
* @param name the name of the reporting consumer
* @param report the number of lines the consumer has written since last report
* @return "exit" if maxScenarios has been reached, "ok" otherwise
*/
public String makeReport(String name, String report) {
long increment = Long.valueOf(report);
long currentLineCount = globalLineCounter.addAndGet(increment);
if (currentLineCount >= maxScenarios) {
return "exit";
} else {
return "ok";
}
}
/**
* Adds a provided WorkBlock to the Queue of WorkBlocks to be processed by consumers
*
* @param block a WorkBlock
*/
public void addBlock(WorkBlock block) {
blocks.add(block);
remainingBlocks.incrementAndGet();
}
/**
* Establishes a thread that on one second intervals reports the number of remaining WorkBlocks enqueued and the
* total number of lines written and reported by consumers
*/
public void prepareStatus() {
globalLineCounter = new AtomicLong(0);
time = new AtomicLong(System.currentTimeMillis());
startTime = System.currentTimeMillis();
lastCount = 0;
// Status thread regularly reports on what is happening
Thread statusThread = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Status thread interrupted");
}
long thisTime = System.currentTimeMillis();
long currentCount = globalLineCounter.get();
if (thisTime - time.get() > 1000) {
long oldTime = time.get();
time.set(thisTime);
double avgRate = 1000.0 * currentCount / (thisTime - startTime);
double instRate = 1000.0 * (currentCount - lastCount) / (thisTime - oldTime);
lastCount = currentCount;
System.out.println(currentCount + " AvgRage:" + ((int) avgRate) + " lines/sec instRate:"
+ ((int) instRate) + " lines/sec Unassigned Work: "
+ remainingBlocks.get() + " blocks");
}
}
}
};
statusThread.start();
}
/**
* Prepares a Jetty server for communicating with consumers.
*/
public void prepareServer() {
try {
server = new Server(0);
jettyHandler = new AbstractHandler() {
public void handle(String target, Request req, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/plain");
String[] operands = request.getRequestURI().split("/");
String name = "";
String command = "";
String value = "";
//operands[0] = "", request starts with a "/"
if (operands.length >= 2) {
name = operands[1];
}
if (operands.length >= 3) {
command = operands[2];
}
if (operands.length >= 4) {
value = operands[3];
}
if (command.equals("report")) { //report a number of lines written
response.getWriter().write(makeReport(name, value));
} else if (command.equals("request") && value.equals("block")) { //request a new block of work
response.getWriter().write(requestBlock(name));
} else if (command.equals("request") && value.equals("name")) { //request a new name to report with
response.getWriter().write(requestName());
} else { //non recognized response
response.getWriter().write("exit");
}
((Request) request).setHandled(true);
}
};
server.setHandler(jettyHandler);
// Select any available port
server.start();
Connector[] connectors = server.getConnectors();
NetworkConnector nc = (NetworkConnector) connectors[0];
listeningPort = nc.getLocalPort();
hostName = InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the port the Jetty server will be listening on
*
* @return the port number
*/
public int getListeningPort() {
return listeningPort;
}
/**
* Returns the host name of the Jetty server
*
* @return the host name
*/
public String getHostName() {
return hostName;
}
}