package org.linkgrammar; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import java.util.List; import java.util.Map; /** *

* A client of the {@link LGService} when it is run in server mode. The main * method to call is {@link parse} which produces a {@link ParseResult}. At * a minimum, the hostname and port properties * must be set beforehand. Configuration settings of the remote parser itself * are specified with the config property. *

* *

* This client is persistent in its attempts to perform a parse. By default * it will keep trying to connect to a server indefinitely (unless the host is * unknown). To change that, * set the connectRetryCount property. Also, if call to the server * fails for whatever reason, it will retry at least once. The increase the number * of retries, set the parseRetryCount property. *

* * @author Borislav Iordanov * */ public class LGRemoteClient { private LGConfig config = new LGConfig(); private String parserVersion; private String hostname = "localhost"; private int port = 9000; private int parseRetryCount = 2; private int connectRetryCount = Integer.MAX_VALUE; private long connectRetryWait = 1000l; @SuppressWarnings({"unchecked"}) private ParseResult jsonToParseResult(String json) { JSONReader reader = new JSONReader(); Map top = (Map)reader.read(json); ParseResult result = new ParseResult(); result.setParserVersion((String)top.get("version")); result.setNumSkippedWords(((Number)top.get("numSkippedWords")).intValue()); for (Map x : (List)top.get("linkages")) { Linkage linkage = new Linkage(); linkage.setDisjunctCost(((Number)x.get("disjunctCost")).doubleValue()); linkage.setLinkCost(((Number)x.get("linkageCost")).doubleValue()); linkage.setNumViolations(((Number)x.get("numViolations")).intValue()); linkage.setWords(((List)(x.get("words"))).toArray(new String[0])); linkage.setDisjuncts(((List)(x.get("disjuncts"))).toArray(new String[0])); linkage.setLinkedWordCount(linkage.getWords().length); // TODO?? is this right? for (Map y : (List)x.get("links")) { Link link = new Link(); link.setLabel((String)y.get("label")); link.setLeftLabel((String)y.get("leftLabel")); link.setRightLabel((String)y.get("rightLabel")); link.setLeft(((Number)y.get("left")).intValue()); link.setRight(((Number)y.get("right")).intValue()); linkage.getLinks().add(link); } if (config.isStoreConstituentString()) linkage.setConstituentString((String)x.get("constituentString")); result.getLinkages().add(linkage); } return result; } private String makeLGRequest(String text) { if (config != null) return "storeConstituentString:" + config.isStoreConstituentString() + "\0" + "maxCost:" + config.getMaxCost() + "\0" + "maxLinkages:" + config.getMaxLinkages() + "\0" + "maxParseSeconds:" + config.getMaxParseSeconds() + "\0" + "text:" + text + "\0"; else return "text:" + text + "\0"; } private String readResponse(BufferedReader in, int size) throws IOException { char [] buf = new char[size]; for (int count = 0; count < size; ) count += in.read(buf, count, size-count); return new String(buf, 0, size); } private String callParser(String request) throws InterruptedException, IOException { if (hostname == null || hostname.length() == 0 || port <= 1024) throw new RuntimeException("No hostname for remote parser or invalid port number < 1024"); // // Connect: // Socket socket = null; for (int i = 0; i < connectRetryCount && socket == null; i++) { try { socket = new Socket(hostname, port); } catch (UnknownHostException ex) { throw new RuntimeException("Host '" + hostname + "' not found."); } catch (IOException ex) { // ignore, retry... } if (socket == null) Thread.sleep(connectRetryWait); } if (socket == null) throw new RuntimeException("Failed to connect to " + hostname + ":" + port); // // Call parser: // PrintWriter out = null; BufferedReader in = null; try { out = new PrintWriter(socket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out.print(request); out.print('\n'); out.flush(); String size = in.readLine(); if (size == null) throw new RuntimeException("Parser returned no response."); return readResponse(in, Integer.parseInt(size)); } finally { if (out != null) try { out.close(); } catch (Throwable t) { } if (in != null) try { in.close(); } catch (Throwable t) { } try { socket.close(); } catch (Throwable t) { } } } /** *

Return the link grammar version. A call to the server is made to * obtain the version which is henceforth cached.

*/ @SuppressWarnings("unchecked") public String getVersion() { if (parserVersion == null) { try { String json = callParser("get:version\0"); JSONReader reader = new JSONReader(); Map top = (Map)reader.read(json); parserVersion = (String)top.get("version"); } catch (IOException ex) { parserVersion = "unavailable"; } catch (InterruptedException ex) { throw new RuntimeException("Thread interrupted.", ex); } } return parserVersion; } public ParseResult parse(String sentence) throws InterruptedException { String parserResponse = null; for (int i = 0; i < parseRetryCount && parserResponse == null; i++) try { parserResponse = callParser(makeLGRequest(sentence)); } catch (IOException ex) { if (i == 0) // Trace exception only on the first failure. { System.err.println("Link grammar called failed on '" + sentence + "'" + ", will retry " + Integer.toString(parseRetryCount - 1) + " more time."); ex.printStackTrace(); } } if (parserResponse == null) return null; ParseResult parseResult = jsonToParseResult(parserResponse); parseResult.setText(sentence); return parseResult; } public LGConfig getConfig() { return config; } public void setConfig(LGConfig config) { this.config = config; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getParseRetryCount() { return parseRetryCount; } public void setParseRetryCount(int parseRetryCount) { this.parseRetryCount = parseRetryCount; } public int getConnectRetryCount() { return connectRetryCount; } public void setConnectRetryCount(int connectRetryCount) { this.connectRetryCount = connectRetryCount; } public long getConnectRetryWait() { return connectRetryWait; } public void setConnectRetryWait(long connectRetryWait) { this.connectRetryWait = connectRetryWait; } }