package lt.emasina.resthub.model;
import au.com.bytecode.opencsv.CSVReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import lombok.Getter;
import lombok.Setter;
import lt.emasina.resthub.util.Helper;
import org.json.JSONException;
import org.json.JSONObject;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
import org.restlet.util.Series;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class QueryManager {
private final String url;
@Getter
private final String entity;
@Getter
private String id;
@Getter
private final Map<String, Object> params = new HashMap<>();
@Getter @Setter
private HashMap headers;
@Getter @Setter
private long page = 0;
@Getter @Setter
private long ppage = 0;
private final ReentrantReadWriteLock readWriteLock;
private final Lock read;
private final Lock write;
public QueryManager(String url, String sql) {
this.url = url;
this.entity = sql;
this.readWriteLock = new ReentrantReadWriteLock();
this.read = readWriteLock.readLock();
this.write = readWriteLock.writeLock();
}
/**
* Refreshes query in server. There is one request to server.
* @throws java.io.IOException
*/
public void refresh() throws IOException {
this.write.lock();
try {
ClientResource client = new ClientResource(this.url + "/query");
this.id = client.post(this.entity).getText();
client.release();
} finally {
this.write.unlock();
}
}
public JSONObject getMetadata() throws JSONException, IOException {
return getMetadata(false);
}
/**
* Gets metadata from server and returns JSONObject. There is one request to
* server.
*
* @param forceRefresh
* @return An JSONObject object.
* @throws org.json.JSONException
* @throws java.io.IOException
*/
public JSONObject getMetadata(boolean forceRefresh) throws JSONException, IOException {
if (id == null || forceRefresh) {
refresh();
}
this.read.lock();
try {
String path = "/query/" + id;
ClientResource client = new ClientResource(this.url + path);
client.get();
client.release();
Representation r = client.getResponseEntity();
return new JSONObject(r.getText());
} catch (ResourceException e) {
this.read.unlock();
if (!forceRefresh && e.getStatus().getCode() == 404) {
return getMetadata(true);
} else {
return null;
}
} finally {
this.read.unlock();
}
}
/**
* Gets getData from server and returns String. There is one request to
* server.
*
* @param contentType Content type of the string.
* @return An String object.
* @throws java.io.IOException
* @throws org.json.JSONException
*/
public DataResponse getData(String contentType) throws IOException, JSONException {
return getData(MediaType.valueOf(contentType), false);
}
/**
* Gets getData from server and returns String. There is one request to
* server.
*
* @param contentType Content type of the string.
* @param forceRefresh
* @return An String object.
* @throws java.io.IOException
* @throws org.json.JSONException
*/
public DataResponse getData(String contentType, boolean forceRefresh) throws IOException, JSONException {
return getData(MediaType.valueOf(contentType), forceRefresh);
}
/**
* Gets getData from server and returns String. There is one request to
* server.
*
* @param mediaType MediaType of the string.
* @return An String object.
* @throws java.io.IOException
* @throws org.json.JSONException
*/
public DataResponse getData(MediaType mediaType) throws IOException, JSONException {
return getData(mediaType, false);
}
/**
* Gets getData from server and returns String. There is one request to
* server.
*
* @param mediaType MediaType of the string.
* @param forceRefresh
* @return An String object.
* @throws java.io.IOException
* @throws org.json.JSONException
*/
public DataResponse getData(MediaType mediaType, boolean forceRefresh) throws IOException, JSONException {
if (id == null || forceRefresh) {
refresh();
}
this.read.lock();
try {
String path = "/query/" + id;
if (ppage > 0 && page > 0) {
path += "/page/" + ppage + "/" + page;
}
path += "/data";
if (!this.params.isEmpty()) {
StringBuilder sb = new StringBuilder();
path += "?";
Iterator<Map.Entry<String, Object>> entries = this.params.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Object> entry = entries.next();
sb.append(entry.getKey());
sb.append('=');
sb.append(entry.getValue());
if(entries.hasNext()){
sb.append('&');
}
}
path += sb.toString();
}
ClientResource client = new ClientResource(this.url + path);
if (headers != null) {
addHeaders(client);
}
try {
if (mediaType == null) {
client.get();
} else {
client.get(mediaType);
}
client.release();
} catch (ResourceException e) {
if (!forceRefresh && e.getStatus().getCode() == 404) {
this.read.unlock();
try {
return getData(mediaType, true);
} finally {
this.read.lock();
}
} else {
return null;
}
}
Representation r = client.getResponseEntity();
Map responseHeaders = ((Series)client.getResponseAttributes().get("org.restlet.http.headers")).getValuesMap();
int result = client.getStatus().getCode();
return new DataResponse(r, responseHeaders, result);
} finally {
this.read.unlock();
}
}
/**
* Gets getData from server and returns JSONObject. There is one request to
* server.
*
* @return An JSONObject object.
* @throws org.json.JSONException
* @throws java.io.IOException
*/
public JSONObject getDataJSON() throws JSONException, IOException{
String s = getData(MediaType.APPLICATION_JSON).getString();
return new JSONObject(s);
}
/**
* Gets getData from server and returns XML Document. There is one request to
* server.
*
* @return An Document object.
* @throws java.io.IOException
* @throws javax.xml.parsers.ParserConfigurationException
* @throws org.json.JSONException
* @throws org.xml.sax.SAXException
*/
public Document getDataXML() throws IOException, JSONException, SAXException, ParserConfigurationException{
String s = getData(MediaType.APPLICATION_XML).getString();
return stringToDom(s);
}
/**
* Gets getData from server and returns table. There is one
* request to server.
*
* @return An Two-Dimensional Array.
* @throws java.io.IOException
* @throws org.json.JSONException
*/
public String[][] getDataTable() throws IOException, JSONException {
Reader r = getData(MediaType.TEXT_CSV).getReader();
CSVReader csvReader = new CSVReader(r);
List<String[]> list = csvReader.readAll();
String[][] dataArr = new String[list.size()][];
dataArr = list.toArray(dataArr);
return dataArr;
}
private static Document stringToDom(String xmlSource)
throws SAXException, ParserConfigurationException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(new StringReader(xmlSource)));
}
private void addHeaders(ClientResource client) {
Series<Header> reqHeaders = client.getRequest().getHeaders();
Iterator it = headers.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry) it.next();
reqHeaders.add(new Header(pairs.getKey().toString(), pairs.getValue().toString()));
}
}
/**
* Deletes the query from server.
*/
public void delete() {
this.write.lock();
try {
if (id != null) {
String path = "/query/" + this.id;
ClientResource client = new ClientResource(this.url + path);
if (headers != null) {
addHeaders(client);
}
client.delete();
client.release();
}
} finally {
this.write.unlock();
}
}
public Map options() throws IOException {
return options(false);
}
public Map options(boolean forceRefresh) throws IOException {
if (id == null || forceRefresh) {
refresh();
}
this.read.lock();
try {
String path = "/query/" + id + "/data";
ClientResource client = new ClientResource(this.url + path);
if (headers != null) {
addHeaders(client);
}
client.options();
client.release();
return ((Series) client.getResponseAttributes().get("org.restlet.http.headers")).getValuesMap();
} finally {
this.read.unlock();
}
}
private Query getQ(boolean v, boolean forceRefresh) throws JSONException, IOException {
if ((id == null) || (forceRefresh)) {
refresh();
}
String localUrl = this.url + "/query/" + id;
if (v) {
localUrl += "?v=true";
}
JSONObject jsonObject = Helper.getJSONObject(localUrl);
return new Query(id, jsonObject, v);
}
/**
* Gets a Query object. There is one request to server.
*
* @return Created Query object.
* @throws org.json.JSONException
* @throws java.io.IOException
*/
public Query getQuery() throws JSONException, IOException {
return getQ(false, false);
}
/**
* Gets a Query object with additional getData. There is one
* request to server.
*
* @return Created Query object.
* @throws org.json.JSONException
* @throws java.io.IOException
*/
public Query getVerboseQuery() throws JSONException, IOException {
return getQ(true, false);
}
/**
* Adds a parameter to QueryManager.
*
* @param key Key of the parameter.
* @param object Value of the parameter.
*/
public void addParameter(String key, Object object) {
this.params.put(key, object);
}
}