Skip to content
Permalink
Browse files

Fixed a bug in the CSV output of queries and another that prevented t…

…he command line server from responding to SIGTERM and SIGINT
  • Loading branch information
Ian Emmons
Ian Emmons committed Oct 11, 2019
1 parent 7527516 commit 0499f109c22c9725df7a4a6d9ffc14a66275f72e
@@ -6,6 +6,7 @@ log/
/build.properties
/dependencies/
/doc/UserGuide/acronyms.tex
/doc/UserGuide/missfont.log
/doc/UserGuide/ParliamentUserGuide.aux
/doc/UserGuide/ParliamentUserGuide.bbl
/doc/UserGuide/ParliamentUserGuide.bcf
@@ -253,7 +253,7 @@ \subsubsection{\acp{pmnt} Main Configuration File}

\item[readOnly] When set to ``yes'', prevents \ac{pmnt} from changing the underlying storage files in any way. \emph{Default: ``no''}

\item[fileSyncTimerDelay] The number of milliseconds between flushings of the \ac{kb} files to disk. The flush is performed asynchronously, and so has minimal impact on overall performance. This decreases the chances of a file corruption, and it limits the amount of time required to shut down \ac{pmnt} gracefully. Set to zero to disable flushing the files to disk. This setting applies only to \ac{pmnt} deployed as a server using Jena, Joseki, and Jetty. Any other deployment will ignore this setting. \emph{Default: ``15000''}
\item[fileSyncTimerDelay] \ac{pmnt} periodically flushes its underlying data files to disk to decrease the chances of a file corruption and to limit the amount of time required to shut down \ac{pmnt} gracefully. This parameter is the time interval in milliseconds between flushes. Set it to zero to disable flushing the files to disk. This setting applies only to \ac{pmnt} deployed as a server using Jena, Joseki, and Jetty. Any other deployment will ignore this setting. \emph{Default: ``15000''}

\item[initialRsrcCapacity] The number of resources \ac{pmnt} should allocate space for when creating a new \ac{kb}. \emph{Default: ``300000''}

@@ -6,33 +6,91 @@

package com.bbn.parliament.jena.jetty;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CmdLineJettyServer {
private static final InterThreadSignal serverShouldShutDown = new InterThreadSignal();
private static final Logger LOG = LoggerFactory.getLogger(CmdLineJettyServer.class);

private static class StdinMonitorThread extends Thread {
public StdinMonitorThread() {
super("Stdin Monitor Thread");
setDaemon(true);
}

@Override
public void run() {
try {
for (boolean timeToExit = false; !timeToExit;) {
printPrompt();
byte[] bytes = new byte[256];
int count = System.in.read(bytes);
String consoleInput = new String(bytes, 0, count, StandardCharsets.UTF_8).trim();
timeToExit = consoleInput.equalsIgnoreCase("exit");
}
} catch (IOException ex) {
LOG.error("IOException:", ex);
} finally {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending shutdown signal from {}", Thread.currentThread().getName());
}
serverShouldShutDown.sendSignal();
}
}

private static void printPrompt() {
System.out.format("%n"
+ "%n"
+ "%n"
+ "######################################################################%n"
+ "######################################################################%n"
+ "##### #####%n"
+ "##### Warning: Killing the server process by any means other #####%n"
+ "##### than graceful shutdown may result in corrupt knowledge #####%n"
+ "##### base files. Please shut down the server by typing #####%n"
+ "##### 'exit' at the prompt below. #####%n"
+ "##### #####%n"
+ "######################################################################%n"
+ "######################################################################%n"
+ "%n"
+ "%n"
+ "Type 'exit' and press <return> or <enter> to shut down the server.%n");
}
}

private static class ShutdownHook extends Thread {
public ShutdownHook() {
super("Shutdown Hook Thread");
setDaemon(true);
}

@Override
public void run() {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending shutdown signal from {}", Thread.currentThread().getName());
}
serverShouldShutDown.sendSignal();
}
}

/** Default entry point. */
public static void main(String[] args) {
try {
JettyServerCore.initialize();
JettyServerCore.getInstance().start();

try {
Thread.sleep(4000);
} catch (InterruptedException ex) {
// Do nothing
}
Thread.sleep(4000);

for (boolean timeToExit = false; !timeToExit;) {
printPrompt();
byte[] bytes = new byte[256];
int count = System.in.read(bytes);
String consoleInput = new String(bytes, 0, count, StandardCharsets.UTF_8).trim();
timeToExit = consoleInput.equalsIgnoreCase("exit");
}
Runtime.getRuntime().addShutdownHook(new ShutdownHook());

Thread stdinMonitorThread = new StdinMonitorThread();
stdinMonitorThread.start();

serverShouldShutDown.waitForSignal();

System.out.format("Shutting down server%n");
} catch (Exception ex) {
@@ -41,23 +99,4 @@ public static void main(String[] args) {
JettyServerCore.getInstance().stop();
}
}

private static void printPrompt() {
System.out.format("%n"
+ "%n"
+ "%n"
+ "######################################################################%n"
+ "######################################################################%n"
+ "##### #####%n"
+ "##### Warning: Killing the server process by any means other #####%n"
+ "##### than graceful shutdown may result in corrupt knowledge #####%n"
+ "##### base files. Please shut down the server by typing #####%n"
+ "##### 'exit' at the prompt below. #####%n"
+ "##### #####%n"
+ "######################################################################%n"
+ "######################################################################%n"
+ "%n"
+ "%n"
+ "Type 'exit' and press <return> or <enter> to shut down the server.%n");
}
}
@@ -0,0 +1,26 @@
package com.bbn.parliament.jena.jetty;

public class InterThreadSignal {
private boolean signalSent;
private final Object lock;

public InterThreadSignal() {
signalSent = false;
lock = new Object();
}

public void waitForSignal() throws InterruptedException {
synchronized (lock) {
while (!signalSent) {
lock.wait();
}
}
}

public void sendSignal() {
synchronized (lock) {
signalSent = true;
lock.notifyAll();
}
}
}
@@ -444,36 +444,37 @@ public void csvQuotingTest() throws IOException {
String sStr = s.isAnon()
? s.getId().getLabelString()
: s.getURI();
log.info(" {} {} \"{}\"", sStr, p.getURI(), o.getLexicalForm());
log.info(" {} {} \"{}\"", sStr, p.getURI(), o.getLexicalForm());
}
}

String actualResponse;
Map<String, Object> params = new HashMap<>();
params.put("query", String.format(CSV_QUOTING_TEST_QUERY));
params.put("stylesheet", "/xml-to-csv.xsl");
try (
InputStream is = rm.sendRequest(params);
Reader rdr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader brdr = new BufferedReader(rdr);
) {
actualResponse = brdr.lines().collect(Collectors.joining(System.lineSeparator()));
try (InputStream is = rm.sendRequest(params)) {
actualResponse = readStreamToEnd(is, "RemoteModel.sendRequest() returned null");
}
log.info("CSV quote result as CSV:{}{}", System.lineSeparator(), actualResponse);

String expectedResponse;
try (InputStream is = getClass().getResourceAsStream(CSV_QUOTE_TEST_EXPECTED_RESULT)) {
expectedResponse = readStreamToEnd(is, "Unable to find resource '%1$s'", CSV_QUOTE_TEST_EXPECTED_RESULT);
}

assertEquals(expectedResponse, actualResponse);
}

private static String readStreamToEnd(InputStream is, String errorMsg, Object... args) throws IOException {
if (is == null) {
fail(String.format(errorMsg, args));
}
try (
InputStream is = getClass().getResourceAsStream(CSV_QUOTE_TEST_EXPECTED_RESULT);
Reader rdr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader brdr = new BufferedReader(rdr);
) {
if (is == null) {
fail(String.format("Unable to find resource '%1$s'", CSV_QUOTE_TEST_EXPECTED_RESULT));
}
expectedResponse = brdr.lines().collect(Collectors.joining(System.lineSeparator()));
return brdr.lines().collect(Collectors.joining(System.lineSeparator()));
}

assertEquals(expectedResponse, actualResponse);
}

private static ResultSet doQuery(String queryFmt, Object... args) {
@@ -2,62 +2,45 @@

import static org.junit.jupiter.api.Assertions.fail;

import com.bbn.parliament.jena.jetty.InterThreadSignal;
import com.bbn.parliament.jena.jetty.JettyServerCore;

public class ParliamentTestServer {
private static class InterThreadSignal {
private boolean signalSent;
private final Object lock;

public InterThreadSignal() {
signalSent = false;
lock = new Object();
}
private static final InterThreadSignal serverHasStarted = new InterThreadSignal();
private static final InterThreadSignal serverShouldShutDown = new InterThreadSignal();

public void waitForSignal() {
synchronized (lock) {
while (!signalSent) {
try {
lock.wait(5000);
} catch (InterruptedException ex) {
fail(ex.getMessage());
}
}
}
private static class TestServerThread extends Thread {
public TestServerThread() {
super("Test Server Thread");
setDaemon(true);
}

public void sendSignal() {
synchronized (lock) {
signalSent = true;
lock.notifyAll();
@Override
public void run() {
try {
JettyServerCore.initialize();
JettyServerCore.getInstance().start();
serverHasStarted.sendSignal();
serverShouldShutDown.waitForSignal();
} catch (Exception ex) {
serverHasStarted.sendSignal();
fail(ex.getMessage());
} finally {
JettyServerCore.getInstance().stop();
}
}
}

private static final InterThreadSignal serverHasStarted = new InterThreadSignal();
private static final InterThreadSignal serverShouldShutDown = new InterThreadSignal();

// Call from @BeforeClass
public static void createServer() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
JettyServerCore.initialize();
JettyServerCore.getInstance().start();
serverHasStarted.sendSignal();
serverShouldShutDown.waitForSignal();
} catch (Exception ex) {
serverHasStarted.sendSignal();
fail(ex.getMessage());
} finally {
JettyServerCore.getInstance().stop();
}
}
});
t.setDaemon(true);
t.start();
serverHasStarted.waitForSignal();
Thread testServerThread = new TestServerThread();
testServerThread.start();

try {
serverHasStarted.waitForSignal();
} catch (InterruptedException ex) {
fail(ex.getMessage());
}
}

// Call from @AfterClass
@@ -21,7 +21,7 @@
<xsl:call-template name="QuoteColumnValue">
<xsl:with-param name="text" select="@name"/>
</xsl:call-template>
<xsl:text>&#xD;&#xA;</xsl:text>
<xsl:text>&#xA;</xsl:text>
</xsl:template>

<xsl:template match="sr:result">
@@ -46,10 +46,10 @@

<xsl:template match="sr:binding">
<xsl:param name="isLastColumn"/>
<xsl:apply-templates select="child::*"/>
<xsl:apply-templates select="sr:uri|sr:bnode|sr:literal"/>
<xsl:choose>
<xsl:when test="$isLastColumn">
<xsl:text>&#xD;&#xA;</xsl:text>
<xsl:text>&#xA;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>

0 comments on commit 0499f10

Please sign in to comment.
You can’t perform that action at this time.