//QuickBase Client API
//version 1.16 relocated from $cvsroot/quickbase to $cvsroot/platform, 8/29/2002.
// $Id: QuickBaseClient.java,v 1.15 2006/10/17 12:18:04 cvonroes Exp $
// $Source: /v/releng/cvsroot/platform/java/com/intuit/quickbase/util/QuickBaseClient.java,v $
package com.intuit.quickbase.util;
import java.io.*;
import java.util.*;
import com.intuit.util.HTTPConnection;
// XML classes
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.w3c.dom.CDATASection;
/**
* <p>QuickBaseClient class is a wrapper for a substantial portion of
* the <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
QuickBase HTTP API</a>.
*
* <p>See the main() method for examples of using this class.<p>
*
* You'll need JDK 1.4 or higher or
* if you're using JDK 1.3 or lower then you'll need:<ul>
* <li>JAXP any version
* <li>JSSE 1.0.1 or higher
* </ul>
*
* <p>Copyright (C) 2001-2003 Intuit Inc. All Rights Reserved.
* @author claude_von_roesgen@intuit.com
* @version $Revision: 1.15 $
*/
public class QuickBaseClient
{
private Vector v_QuickBaseCookies=new Vector();
private String QuickBaseUrlPrefix = null;
private String QDBusername="";
private String QDBpassword="";
private String QDBerrorcode="";
private String QDBerrortext="";
private String QDBticket="";
private String QDBappToken="";
private String proxyHost = null;
private int proxyPort = 0;
protected String httpConnectionClass = "com.intuit.util.HTTPConnection";
protected int timeout = 180000;
/**
* Set the username and password for subsequent calls to the QuickBase HTTP API.
* Please refer to the
* <a href="http://www.quickbase.com/">
* QuickBase home page</a> for more information on how to get a QuickBase account.
* Also sets the protocol (HTTP or HTTPS) for all requests to QuickBase.
* Note: Sun's JSSE Java SSL implementation is required to use this class unless you
* call the constructor with an HTTP URL.
* QuickBase databases are by default not accessible via HTTP. To allow HTTP access to a
* QuickBase database go to its main page and click on "Administration" under "SHORTCUTS".
* Then click on "Basic Properties". Next to "Options" you'll see a checkbox labeled
* "Allow non-SSL access (normally unchecked)". You'll need to check this box to allow HTTP
* access to the database.
*
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information.
* @param username Either a QuickBase screen name or email address of a registered QuickBase user.
* @param password The QuickBase password corresponding to the QuickBase username.
* @param URL The URL prefix for all QuickBase calls. The default is "https://www.quickbase.com/db/".
*
*/
public QuickBaseClient(String username, String password, String URL)
{
QuickBaseUrlPrefix=URL;
QDBusername = username;
QDBpassword = password;
}
/**
* Set the username and password for subsequent calls to the QuickBase HTTP API.
* Please refer to the
* <a href="http://www.quickbase.com/">
* QuickBase home page</a> for more information on how to get a QuickBase account.
* Also sets the protocol (HTTP or HTTPS) for all requests to QuickBase.
* Note: Sun's JSSE Java SSL implementation is required to use this class unless you
* call the constructor with the HTTPS parameter set to false.
* QuickBase databases are by default not accessible via HTTP. To allow HTTP access to a
* QuickBase database go to its main page and click on "Administration" under "SHORTCUTS".
* Then click on "Basic Properties". Next to "Options" you'll see a checkbox labeled
* "Allow non-SSL access (normally unchecked)". You'll need to check this box to allow HTTP
* access to the database.
*
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information.
* @param username Either a QuickBase screen name or email address of a registered QuickBase user.
* @param password The QuickBase password corresponding to the QuickBase username.
* @param HTTPS True for HTTPS and false for HTTP.
*
*/
public QuickBaseClient(String username, String password, boolean HTTPS)
{
if (HTTPS){
QuickBaseUrlPrefix="https://www.quickbase.com/db/";
}else{
QuickBaseUrlPrefix="http://www.quickbase.com/db/";
}
QDBusername = username;
QDBpassword = password;
}
/**
* Set the username and password for subsequent calls to the QuickBase HTTP API.
* Please refer to the
* <a href="http://www.quickbase.com/">
* QuickBase home page</a> for more information on how to get a QuickBase account.
* Note: Sun's JSSE Java SSL implementation is required to use this class.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information.
* @param username Either a QuickBase screen name or email address of a registered QuickBase user.
* @param password The QuickBase password corresponding to the QuickBase username.
*
*/
public QuickBaseClient(String username, String password)
{
this(username, password,"https://www.quickbase.com/db/");
}
/**
* Resets the username and password for subsequent calls to the QuickBase HTTP API.
* Please refer to the
* <a href="http://www.quickbase.com/">
* QuickBase home page</a> for more information on how to get a QuickBase account.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information.
* @param username Either a QuickBase screen name or email address of a registered QuickBase user.
* @param password The QuickBase password corresponding to the QuickBase username.
*
*/
public void changeCredentials (String username, String password){
QDBusername = username;
QDBpassword = password;
QDBticket = "";
return;
}
/**
* Set name of class to use for HTTPConnections.
* Should be a fully qualified classname that is assignable
* to type <code>com.intuit.util.HTTPConnection</code>.
*/
public void setHttpConnectionClass(String classname){
httpConnectionClass = classname;
}
/**
* Set the timeout on connections. CAUTION: do not mess with this
* except under special circumstances. i.e. you know
* with absolute certainty that an HTTP server will NEVER be slower
* than the timeout and succeed in the response. The deault
* 180000 ms (3 minutes), which is what most browsers use.
*/
public void setTimeout(int ms){
timeout = ms;
}
/**
* Sets the proxy hostname and port number for subsequent calls to the QuickBase HTTP API.
* @param host The host name of the proxy server to use for API connections.
* @param port The port number on the proxy host to use for API connections.
*
*/
public void setProxy (String host, int port){
proxyHost = host;
proxyPort = port;
return;
}
/**
* Adds the given qdbParameter/qdbValue pair to the qdbRequest XML document
* to be passed as input to a QuickBase HTTP API call.
* @param qdbRequest The XML document containing the QuickBase API parameters.
* @param qdbParameter The name of the API parameter to be added to the request.
* @param qdbValue The value of the API parameter to be added to the request.
*
*/
public void addRequestParameter(Document qdbRequest, String qdbParameter, String qdbValue){
Element rootElement = qdbRequest.getDocumentElement();
Element qdbapiElement = null;
/* Don't assume that caller created the root element */
if (rootElement == null){
qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
else{
qdbapiElement = rootElement;
}
Element qdbapiParameter = qdbRequest.createElement(qdbParameter);
Text qdbapiText = qdbRequest.createTextNode(qdbValue);
qdbapiParameter.appendChild(qdbapiText);
qdbapiElement.appendChild(qdbapiParameter);
}
/**
* Adds the given qdbParameter/qdbValue pair with the given attName/attValue to
* the qdbRequest XML document to be passed as input to a QuickBase HTTP API call.
* @param qdbRequest The XML document containing the QuickBase API parameters.
* @param qdbParameter The name of the API parameter to be added to the request.
* @param qdbValue The value of the API parameter to be added to the request.
* @param attName The name of the XML attribute to add to this API parameter.
* @param attValue The value of the XML attribute to add to this API parameter.
*
*/
public void addRequestParameter(Document qdbRequest, String qdbParameter,
String qdbValue, String attName, String attValue){
Element rootElement = qdbRequest.getDocumentElement();
Element qdbapiElement = null;
if (rootElement == null){
qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
else{
qdbapiElement = rootElement;
}
Element qdbapiParameter = qdbRequest.createElement(qdbParameter);
qdbapiParameter.setAttribute(attName, attValue);
Text qdbapiText = qdbRequest.createTextNode(qdbValue);
qdbapiParameter.appendChild(qdbapiText);
qdbapiElement.appendChild(qdbapiParameter);
}
/**
* Adds the given qdbParameter/qdbValue pair with the given attName/attValue to
* the qdbRequest XML document to be passed as input to a QuickBase HTTP API call.
* @param qdbRequest The XML document containing the QuickBase API parameters.
* @param qdbParameter The name of the API parameter to be added to the request.
* @param qdbValue The value of the API parameter to be added to the request.
* @param attName The name of the XML attribute to add to this API parameter.
* @param attValue The value of the XML attribute to add to this API parameter.
* @param filename The filename of the file attachment
*
*/
public void addRequestParameter(Document qdbRequest, String qdbParameter,
String qdbValue, String attName, String attValue, String filename){
Element rootElement = qdbRequest.getDocumentElement();
Element qdbapiElement = null;
if (rootElement == null){
qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
else{
qdbapiElement = rootElement;
}
Element qdbapiParameter = qdbRequest.createElement(qdbParameter);
qdbapiParameter.setAttribute(attName, attValue);
qdbapiParameter.setAttribute("filename", filename);
Text qdbapiText = qdbRequest.createTextNode(qdbValue);
qdbapiParameter.appendChild(qdbapiText);
qdbapiElement.appendChild(qdbapiParameter);
}
/**
* Adds the given qdbCSV string to the qdbRequest XML document
* to be passed as input to a QuickBase HTTP API call.
* @param qdbRequest The XML document containing the QuickBase API parameters.
* @param qdbCSV The string containing the csv data.
*
*/
public void addCSVCdata(Document qdbRequest, String qdbCSV){
Element rootElement = qdbRequest.getDocumentElement();
Element qdbapiElement = null;
/* Don't assume that caller created the root element */
if (rootElement == null){
qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
else{
qdbapiElement = rootElement;
}
Element qdbapiRecordsCSV = qdbRequest.createElement("records_csv");
CDATASection qdbapiCDATA = qdbRequest.createCDATASection(qdbCSV);
qdbapiRecordsCSV.appendChild(qdbapiCDATA);
qdbapiElement.appendChild(qdbapiRecordsCSV);
}
private Document xmlFromString (String XmlResponse) throws java.io.IOException, org.xml.sax.SAXException{
InputSource quickbaseXMLstream;
Document qdbResponse = null;
StringReader qdbsr = new StringReader(XmlResponse);
quickbaseXMLstream = new InputSource(qdbsr);
qdbResponse = createXmlDocument (quickbaseXMLstream, false); // false = don't validate
return qdbResponse;
}
private NodeList getNodeList(Document xmlDoc, String select){
String currentNodeName;
Element el = xmlDoc.getDocumentElement();
NodeList nl = null;
StringTokenizer st = new StringTokenizer(select, "/");
while (st.hasMoreTokens()){
currentNodeName = st.nextToken();
if (el ==null){return null;}
nl = el.getElementsByTagName(currentNodeName);
el = (Element) nl.item(0);
}
return nl;
}
/**
* Retrieve the database tables accessible to the current user.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_GrantedDBs.
*
* @param withEmbeddedTables will allow tables that are children of an application from being returned.
* @param excludeParents will exclude parent tables from being returned. Parent tables never have any fields or records in them. You need their dbid to clone an application.
* @param adminOnly will only include applications that the user has administration access to.
* @return a HashMap with table names as keys and database identifiers
* as values.
*
*/
public HashMap grantedDBs (boolean withEmbeddedTables, boolean excludeParents, boolean adminOnly) throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
if(!withEmbeddedTables)
{
addRequestParameter(qdbRequest, "withEmbeddedTables", "0");
}
if(excludeParents)
{
addRequestParameter(qdbRequest, "excludeParents", "1");
}
if(adminOnly)
{
addRequestParameter(qdbRequest, "adminOnly", "1");
}
Document qdbResponse = postApiXml ("main","API_GrantedDBs",qdbRequest);
NodeList tables = getNodeList(qdbResponse, "dbinfo");
if (tables == null){return null;}
HashMap result = new HashMap();
for (int tableCounter = 0; tableCounter < tables.getLength(); tableCounter++){
Element elTable = (Element)tables.item(tableCounter);
String dbname = elTable.getElementsByTagName("dbname").item(0).getChildNodes().item(0).getNodeValue();
String dbid = elTable.getElementsByTagName("dbid").item(0).getChildNodes().item(0).getNodeValue();
result.put(dbname, dbid);
}
return result;
}
/**
* Retrieve the database id associated with the database name.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_FindDbByName.
*
* @param Name the complete name of a QuickBase database.
* @return the QuickBase database ID
*
*/
public String findDbByName (String Name) throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "dbname", Name);
Document qdbResponse = postApiXml ("main","API_FindDbByName",qdbRequest);
return getNodeList(qdbResponse, "dbid").item(0).getChildNodes().item(0).getNodeValue();
}
/**
* Retrieve the xml database schema associated with the database id.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_GetSchema.
*
* @param dbid the QuickBase database identifier.
* @return the QuickBase database schema as an XML document
*
*/
public Document getSchema (String dbid) throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
Document qdbResponse = postApiXml (dbid, "API_GetSchema", qdbRequest);
return qdbResponse;
}
public String getOneTimeTicket () throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
Document qdbResponse = postApiXml ("main", "API_GetOneTimeTicket", qdbRequest);
return getNodeList(qdbResponse, "ticket").item(0).getChildNodes().item(0).getNodeValue();
}
/**
* Retrieve the HTML of a single database record.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_GetRecordAsHTML.
*
* @param dbid The unique identifier of a QuickBase database.
* @param rid The unique identifier of a QuickBase record.
* @return Two column HTML table of field names and field values.
*
*/
public String getRecordAsHTML(String dbid, String rid) throws IOException, Exception
{
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "rid", rid);
return postApi (dbid,"API_GetRecordAsHTML",qdbRequest);
}
/**
* Retrieve HTML representation of zero or more QuickBase records.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_GenResultsTable.
*
* @param dbid The unique identifier of a QuickBase database.
* @param query A QuickBase query that selects zero or more records.
* @param clist A period delimited list of field identifiers defining which fields to output.
* @param slist A period delimited list of field identifiers defining which fields to sort on.
* @param options A string indicating ascending vs descending sort order and data output format.
* @return HTML table of field names across the top and one row for each record.
*
*/
public String getHTMLTable (String dbid, String query, String clist, String slist, String options) throws IOException, Exception
{
if (query == null || query.length() == 0){ return "Please supply a query";}
String tablePage;
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "options", options);
addRequestParameter(qdbRequest, "clist", clist);
addRequestParameter(qdbRequest, "slist", slist);
if (query.startsWith("{") & query.endsWith("}")){
addRequestParameter(qdbRequest, "query", query);
}else{
if (String.valueOf(Integer.parseInt(query)).equals(query)){
addRequestParameter(qdbRequest, "qid", query);
}else{
addRequestParameter(qdbRequest, "qname", query);
}
}
return postApi(dbid, "API_GenResultsTable", qdbRequest);
}
/**
* Retrieve the number of records in a particular QuickBase database.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_GetNumRecords.
*
* @param dbid The unique identifier of a QuickBase database.
* @return The number of records in the QuickBase database.
*
*/
public String getNumRecords(String dbid)throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
Document qdbResponse = postApiXml(dbid, "API_GetNumRecords",qdbRequest);
try{
return getNodeList(qdbResponse, "num_records").item(0).getChildNodes().item(0).getNodeValue();
}catch(Exception e){
throw new NullPointerException("getNumRecords: "+QDBerrortext);
}
}
public Document doStructuredQuery(String dbid, String query, String clist,
String slist, String options) throws QuickBaseException, Exception {
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "options", options);
addRequestParameter(qdbRequest, "clist", clist);
addRequestParameter(qdbRequest, "slist", slist);
addRequestParameter(qdbRequest, "fmt", "structured");
if (query.startsWith("{") && query.endsWith("}")) {
addRequestParameter(qdbRequest, "query", query);
} else {
if (String.valueOf(Integer.parseInt(query)).equals(query)) {
addRequestParameter(qdbRequest, "qid", query);
} else {
addRequestParameter(qdbRequest, "qname", query);
}
}
Document qdbResponse = postApiXml(dbid, "API_DoQuery", qdbRequest);
return qdbResponse;
}
/**
* Retrieve Vector of zero or more QuickBase records.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_DoQuery.
*
* @param dbid The unique identifier of a QuickBase database.
* @param query A QuickBase query that selects zero or more records.
* @param clist A period delimited list of field identifiers defining which fields to output.
* @param slist A period delimited list of field identifiers defining which fields to sort on.
* @param options A string indicating ascending vs descending sort order and data output format.
* @return Vector of HashMaps, one HashMap per record, each HashMap contains pairs of field name and field value.
*
*/
public Vector doQuery (String dbid, String query, String clist, String slist, String options)throws QuickBaseException, Exception
{
Vector vResult = new Vector();
String content;
String field;
Vector vlabels = new Vector();
String fieldvalue;
int counter = 0;
int numfields;
int i;
String tablePage;
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "options", options);
addRequestParameter(qdbRequest, "clist", clist);
addRequestParameter(qdbRequest, "slist", slist);
addRequestParameter(qdbRequest, "fmt", "structured");
if (query.startsWith("{") && query.endsWith("}")){
addRequestParameter(qdbRequest, "query", query);
}else{
if (String.valueOf(Integer.parseInt(query)).equals(query)){
addRequestParameter(qdbRequest, "qid", query);
}else{
addRequestParameter(qdbRequest, "qname", query);
}
}
Document qdbResponse = postApiXml(dbid, "API_DoQuery", qdbRequest);
try{
NodeList fields = getNodeList(qdbResponse, "table/fields/field");
for (i = 0; i < fields.getLength(); i++){
Element elField = (Element)fields.item(i);
vlabels.addElement(elField.getElementsByTagName("label").item(0).getChildNodes().item(0).getNodeValue());
}
numfields = vlabels.size();
NodeList records = getNodeList(qdbResponse, "table/records/record");
if (records == null){return null;}
for (int recordCounter = 0; recordCounter < records.getLength(); recordCounter++){
Element elRecord = (Element)records.item(recordCounter);
NodeList cells = elRecord.getElementsByTagName("f");
HashMap record = new HashMap();
for (i = 0; i < cells.getLength(); i++){
Element elCell = (Element)cells.item(i);
NodeList cellContents = elCell.getChildNodes();
String cellValue = "";
for (int j = 0; j < cellContents.getLength(); j++) {
if (cellContents.item(j) != null){
if (cellContents.item(j).getNodeValue() != null) {
cellValue = cellValue + cellContents.item(j).getNodeValue();
}
else {
/* file attachment fields have a value with a child <url> element.
* append if present. So, the child must be named url, and
* have a child of it's own (the url value) */
String kidName = cellContents.item(j).getNodeName();
if (kidName != null && kidName.equals("url")
&& cellContents.item(j).hasChildNodes()) {
String urlValue = cellContents.item(j).getFirstChild().getNodeValue();
cellValue = cellValue + "<url>" + urlValue + "</url>";
}
}
}
}
record.put((String)vlabels.elementAt(i),cellValue);
}
vResult.addElement(record);
}
return vResult;
}catch( Exception e){
throw new NullPointerException("doQuery: "+QDBerrortext);
}
}
/**
* Retrieve XML document from a QuickBase HTTP API call that returns XML.
* For API calls that do not return XML please use postApi.
* You can use this method to call QuickBase API calls that are not already wrapped in this class for you.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on the XML input and output formats.
*
* @param dbid The unique identifier of a QuickBase database.
* @param action The name of the particular API call e.g. 'API_DoQuery'.
* @param qdbRequest The XML document defining the input parameters to the QuickBase API call.
* @return Document containing the output of the QuickBase API call.
*
*/
public Document postApiXml(String dbid, String action, Document qdbRequest) throws QuickBaseException, Exception
{
Document qdbResponse= null;
QDBerrorcode="-1";
QDBerrortext="No response from QuickBase.";
String qdbResp="";
// HTTPConnection URLConnect = new HTTPConnection(QuickBaseUrlPrefix+dbid+"?act="+action);
HTTPConnection URLConnect = null;
try {
URLConnect = (HTTPConnection) Class.forName(httpConnectionClass).newInstance();
}
catch (Exception e) {
throw new QuickBaseException( "Could not instantiate HTTPConnection object of class "
+ httpConnectionClass );
}
URLConnect.setURL(QuickBaseUrlPrefix+dbid+"?act="+action);
System.out.println ("Posting to QuickBase at "+QuickBaseUrlPrefix+dbid+"?act="+action);
URLConnect.setTimeout( timeout );
URLConnect.setMethod("POST");
URLConnect.setFollowRedirects(false);
if (proxyHost != null && proxyHost.length() > 0) {
if (proxyPort <= 0) proxyPort = (QuickBaseUrlPrefix.indexOf("https") == 0) ? 443 : 80;
URLConnect.setProxy(proxyHost, proxyPort);
}
URLConnect.addHeader("UserAgent", "QuickBaseJavaAPI/2.0");
URLConnect.addHeader("Accept", "text/html");
URLConnect.addHeader("QUICKBASE-ACTION", action);
URLConnect.addHeader("Content-Type", "text/xml");
if (QDBticket == null || QDBticket.length() == 0)
{
addRequestParameter(qdbRequest, "username", QDBusername);
addRequestParameter(qdbRequest, "password", QDBpassword);
}else{
URLConnect.addCookie("TICKET="+QDBticket+"; path=/");
}
if (QDBappToken != null && QDBappToken.length() > 0)
{
addRequestParameter(qdbRequest, "apptoken", QDBappToken);
}
if (qdbRequest == null || qdbRequest.getDocumentElement() == null){
Element qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
StringWriter sw = new StringWriter ();
writeDoc(qdbRequest, sw);
sw.close();
String content = sw.toString();
System.out.println("----------------\n" + content + "\n----------------\n");
URLConnect.setBody(content);
URLConnect.doConnect();
qdbResp = URLConnect.getBody();
if (URLConnect.getHeaders().indexOf("/xml") > -1)
{
qdbResponse = xmlFromString(qdbResp);
NodeList nlTicket=null;
nlTicket = getNodeList(qdbResponse, "ticket");
if (nlTicket != null){
if (nlTicket.item(0) != null){
QDBticket = nlTicket.item(0).getChildNodes().item(0).getNodeValue();
}
}
QDBerrorcode = getNodeList(qdbResponse, "errcode").item(0).getChildNodes().item(0).getNodeValue();
QDBerrortext = getNodeList(qdbResponse, "errtext").item(0).getChildNodes().item(0).getNodeValue();
NodeList nlErrDetail=null;
nlErrDetail = getNodeList(qdbResponse, "errdetail");
if (nlErrDetail != null){
if (nlErrDetail.item(0) != null){
QDBerrortext = nlErrDetail.item(0).getChildNodes().item(0).getNodeValue();
}
}
if (!QDBerrorcode.equals("0")){
throw new QuickBaseException (QDBerrortext, QDBerrorcode);
}
}
else
{
throw new QuickBaseException("postApiXml did not return XML: " + qdbResp, QDBerrorcode);
}
return qdbResponse;
}
/**
* A helper method for getAttachmentUrl. If a this object has
* not acquired a ticket, the method is used to obtain one. The
* credentials presented are those that were used at construction
* time.
*/
Document getTicket() throws QuickBaseException, Exception {
final String action = "API_Authenticate";
Document qdbRequest = newXmlDocument();
return postApiXml("main", action, qdbRequest);
}
public void setTicket(String ticket) {
QDBticket = ticket;
}
public void setAppToken (String token) {
QDBappToken = token;
}
/**
* Retrieve a file attachment from QuickBase. This method retrieves
* the document given by <code>url</code>, returning the body as
* a byte array. <code>url</code> should be a location as given
* in the
*
* <pre>
* <url> ... </url>
* </pre>
* portion of a file attachment field value.
*
* <p>
* This method will take care of supplying authorization credentials. If a ticket
* is available from a previous request, that ticket will be used. Otherwise, the
* username/password combination will be used to acquire a ticket.
*
* <p>
* Note that the body of the response is returned verbatim; there is no
* attempt made to interpret it. It's entirely possible to supply a bad
* url, and receive an HTTP 200 response whose body consists of an error
* page. Thus, the caller is responsible for examining the contents of the
* response to ensure that what they expected was indeed what they ended up with.
*
* @param url the attachment url to retrieve.
* @return the body of the response given when <code>url</code> was
* requested.
*/
public byte[] getFile(String url) throws QuickBaseException, Exception {
if (QDBticket == null || QDBticket.length() == 0) {
getTicket();
}
// HTTPConnection URLConnect = new HTTPConnection(url);
HTTPConnection URLConnect = null;
try {
URLConnect = (HTTPConnection) Class.forName(httpConnectionClass).newInstance();
}
catch (Exception e) {
throw new
QuickBaseException("Could not instantiate HTTPConnection object of class "
+ httpConnectionClass );
}
URLConnect.setURL(url);
URLConnect.setTimeout( timeout );
URLConnect.setMethod("GET");
URLConnect.setFollowRedirects(false);
if (proxyHost != null && proxyHost.length() > 0) {
if (proxyPort <= 0) {
proxyPort = (QuickBaseUrlPrefix.indexOf("https") == 0) ? 443 : 80;
}
URLConnect.setProxy(proxyHost, proxyPort);
}
URLConnect.addHeader("UserAgent", "QuickBaseJavaAPI/2.0");
URLConnect.addHeader("Accept", "*/*");
URLConnect.addCookie("TICKET=" + QDBticket + "; path=/");
URLConnect.doConnect();
return URLConnect.getBodyBytes();
}
/**
* Retrieve String from a QuickBase HTTP API call that returns HTML, CSV, TSV data.
* For API calls that return XML please use postApiXml.
* You can use this method to call QuickBase API calls that are not already wrapped in this class for you.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on the XML input formats.
*
* @param dbid The unique identifier of a QuickBase database.
* @param action The name of the particular API call e.g. 'API_DoQuery'.
* @param qdbRequest The XML document defining the input parameters to the QuickBase API call.
* @return String containing the output of the QuickBase API call.
*
*/
public String postApi(String dbid, String action, Document qdbRequest)throws IOException, Exception
{
QDBerrorcode="";
QDBerrortext="";
//HTTPConnection URLConnect = new HTTPConnection(QuickBaseUrlPrefix+dbid+"?act="+action);
HTTPConnection URLConnect = null;
try {
URLConnect = (HTTPConnection) Class.forName(httpConnectionClass).newInstance();
}
catch (Exception e) {
throw new QuickBaseException( "Could not instantiate HTTPConnection object of class "
+ httpConnectionClass );
}
URLConnect.setURL(QuickBaseUrlPrefix+dbid+"?act="+action);
URLConnect.setTimeout( timeout );
URLConnect.enableSSL("","","","",false);
URLConnect.setMethod("POST");
URLConnect.setFollowRedirects(false);
URLConnect.addHeader("UserAgent", "QuickBaseJavaAPI/2.0");
URLConnect.addHeader("Accept", "text/html");
URLConnect.addHeader("QUICKBASE-ACTION", action);
URLConnect.addHeader("Content-Type", "text/xml");
if (proxyHost != null && proxyHost.length() > 0) {
if (proxyPort <= 0) proxyPort = (QuickBaseUrlPrefix.indexOf("https") == 0) ? 443 : 80;
URLConnect.setProxy(proxyHost, proxyPort);
}
if (QDBticket == null || QDBticket.length() == 0)
{
addRequestParameter(qdbRequest, "username", QDBusername);
addRequestParameter(qdbRequest, "password", QDBpassword);
}
if (qdbRequest == null){
Element qdbapiElement = qdbRequest.createElement("qdbapi");
qdbRequest.appendChild(qdbapiElement);
}
StringWriter sw = new StringWriter ();
writeDoc(qdbRequest, sw);
sw.close();
String content = sw.toString();
URLConnect.setBody(content);
if (QDBticket != null && QDBticket.length() > 0 )
{
URLConnect.addCookie("TICKET="+QDBticket+"; path=/");
}
URLConnect.doConnect();
return URLConnect.getBody();
}
/**
* Clone a QuickBase database and return the database id of the clone.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_CloneDatabase.
*
* @param dbid The unique identifier of a QuickBase database.
* @param Name The name for the new ('clone') database.
* @param Description for the the new ('clone') database.
* @return String containing the database ID of the new ('clone') database.
*
*/
public String cloneDatabase (String dbid, String Name, String Description) throws QuickBaseException, Exception
{
String newQuickBaseID;
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "newdbname", Name);
addRequestParameter(qdbRequest, "newdbdesc", Description);
Document qdbResponse = postApiXml (dbid,"API_CloneDatabase", qdbRequest);
return getNodeList(qdbResponse, "newdbid").item(0).getChildNodes().item(0).getNodeValue();
}
/**
* Add a new record to the QuickBase database and return the record ID of the new record.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_AddRecord.
*
* @param dbid The unique identifier of a QuickBase database.
* @param recorddata A HashMap of (field name or field identifier) and field value pairs containing the new recorded to be added.
* @return String containing the record ID of the new record.
* Note that this method will not work with field names that are composed of only numeric characters (0-9).
*
*/
public String addRecord(String dbid, HashMap recorddata) throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
Set keys = recorddata.keySet();
for (Iterator i = keys.iterator(); i.hasNext();)
{
String nextKey=(String)i.next();
String APIfieldName=nextKey;
if(isFid(APIfieldName))
{
Object data = recorddata.get(nextKey);
if (data instanceof FileAttachment) {
FileAttachment file = (FileAttachment) data;
addRequestParameter(qdbRequest, "field", file.getEncodedContents(), "fid", APIfieldName, file.getFilename() );
} else {
addRequestParameter(qdbRequest, "field", (String)data, "fid", APIfieldName );
}
}
else
{
APIfieldName=removeNonAlphaNumerics(APIfieldName);
Object data = recorddata.get(nextKey);
if (data instanceof FileAttachment) {
FileAttachment file = (FileAttachment) data;
addRequestParameter(qdbRequest, "field", file.getEncodedContents(), "name", APIfieldName, file.getFilename() );
} else {
addRequestParameter(qdbRequest, "field", (String)data, "name", APIfieldName );
}
}
}
Document qdbResponse = postApiXml(dbid, "API_AddRecord", qdbRequest);
return getNodeList(qdbResponse, "rid").item(0).getChildNodes().item(0).getNodeValue();
}
/**
* Add one or more records to the QuickBase database and return the record IDs of the new records.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_ImportFromCSV.
*
* @param dbid The unique identifier of a QuickBase database.
* @param qdbCSV A String containing the CSV comma separated values of new records to be added.
* @return Vector containing the record IDs of the new records.
*
*/
public Vector importFromCSV(String dbid, String qdbCSV, String clist, boolean skipfirst) throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
if(skipfirst)
{
addRequestParameter(qdbRequest, "skipfirst", "1");
}
else
{
addRequestParameter(qdbRequest, "skipfirst", "0");
}
addRequestParameter(qdbRequest, "clist", clist);
addCSVCdata(qdbRequest, qdbCSV);
Document qdbResponse = postApiXml(dbid, "API_ImportFromCSV", qdbRequest);
NodeList rids = qdbResponse.getElementsByTagName("rid");
Vector result = new Vector();
for(int i = 0; i < rids.getLength(); i++)
{
result.addElement(rids.item(i).getChildNodes().item(0).getNodeValue());
}
return result;
}
/**
* Change the permissions of a particular user or group on a particular QuickBase database.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_ChangePermission.
* The keys of the input HashMap, along with their valid values is as follows:
* view none, own, any
* modify none, own, any
* create false, true
* admin false, true
* If one or more of the four permission is not included in the input HashMap, its setting is not changed.
* If you just want to check on the permission settings for a user or group, just call changePermission with an empty HashMap.
* The return HashMap follows the same convention as the input HashMap except that every permission level is reported.
*
* @param dbid The unique identifier of a QuickBase database.
* @param usergroup A QuickBase screen name, or QuickBase group, or email address of a registered QuickBase user.
* @param permissions Contains the new permissions for the user or group.
* @return HashMap of the permissions after the change.
*
*/
public HashMap changePermission(String dbid, String usergroup, HashMap permissions)throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "uname",usergroup);
Set keys = permissions.keySet();
for (Iterator i = keys.iterator(); i.hasNext();){
String nextKey=(String)i.next();
String permission = (String)permissions.get(nextKey);
if(nextKey.toLowerCase().startsWith("modify"))
{
if(permission.equals("") | permission.toLowerCase().startsWith("no"))
{
addRequestParameter(qdbRequest, "modify","none");
}
else if(permission.toLowerCase().startsWith("own"))
{
addRequestParameter(qdbRequest, "modify","own");
}
else
{
addRequestParameter(qdbRequest, "modify","any");
}
}
else if(nextKey.toLowerCase().startsWith("read")|nextKey.toLowerCase().startsWith("view"))
{
if(permission.equals("") | permission.toLowerCase().startsWith("no") )
{
addRequestParameter(qdbRequest, "view","none");
}
else if(permission.toLowerCase().startsWith("own"))
{
addRequestParameter(qdbRequest, "view","own");
}
else
{
addRequestParameter(qdbRequest, "view","any");
}
}
else if(nextKey.toLowerCase().startsWith("create") | nextKey.toLowerCase().startsWith("add") | nextKey.toLowerCase().startsWith("new"))
{
if(permission.equals("") | permission.toLowerCase().startsWith("no") | permission.toLowerCase().startsWith("false"))
{
addRequestParameter(qdbRequest, "create","false");
}
else
{
addRequestParameter(qdbRequest, "create","true");
}
}
else if(nextKey.toLowerCase().startsWith("admin"))
{
if(permission.equals("") | permission.toLowerCase().startsWith("no") | permission.toLowerCase().startsWith("false"))
{
addRequestParameter(qdbRequest, "admin","false");
}
else
{
addRequestParameter(qdbRequest, "admin","true");
}
}
}
Document qdbResponse = postApiXml (dbid,"API_ChangePermission", qdbRequest);
HashMap newPermissions = new HashMap();
NodeList nl=null;
newPermissions.put ("view", "none");
nl = getNodeList(qdbResponse, "view");
if (nl.getLength() > 0){
newPermissions.put ("view",nl.item(0).getChildNodes().item(0).getNodeValue());
}
newPermissions.put ("modify", "none");
nl = getNodeList(qdbResponse, "modify");
if (nl.getLength() > 0){
newPermissions.put ("modify",nl.item(0).getChildNodes().item(0).getNodeValue());
}
newPermissions.put ("create", "false");
nl = getNodeList(qdbResponse, "create");
if (nl.getLength() > 0){
newPermissions.put ("create",nl.item(0).getChildNodes().item(0).getNodeValue());
}
newPermissions.put ("admin", "false");
nl = getNodeList(qdbResponse, "admin");
if (nl.getLength() > 0){
newPermissions.put ("admin",nl.item(0).getChildNodes().item(0).getNodeValue());
}
return newPermissions;
}
/**
* Edit a record in a QuickBase database and return the number of modified fields.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_EditRecord.
* Not all fields can be modified. Built-in and formula (virtual) fields cannot be modified.
* If you attempt to modify them with editRecord you will get an error and no part of the record will have been modified.
*
* @param dbid The unique identifier of a QuickBase database.
* @param recorddata A HashMap of (field name or field identifier) and field value pairs containing the fields to be modified.
* @param rid String containing the record ID of the record to be edited.
* @return String containing the record ID of the new record.
* Note that this method will not work with field names that are composed of only numeric characters (0-9).
*
*/
public String editRecord(String dbid, HashMap recorddata, String rid)throws QuickBaseException, Exception
{
Set keys = recorddata.keySet();
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "rid", rid);
for (Iterator i = keys.iterator(); i.hasNext();)
{
String nextKey=(String)i.next();
String APIfieldName=nextKey;
if(isFid(APIfieldName))
{
addRequestParameter(qdbRequest, "field", (String)recorddata.get(nextKey), "fid", APIfieldName );
}
else
{
APIfieldName=removeNonAlphaNumerics(APIfieldName);
addRequestParameter(qdbRequest, "field", (String)recorddata.get(nextKey), "name", APIfieldName );
}
}
Document qdbResponse = postApiXml(dbid, "API_EditRecord", qdbRequest);
try
{
return getNodeList(qdbResponse, "num_fields_changed").item(0).getChildNodes().item(0).getNodeValue();
}
catch(Exception e)
{
throw new NullPointerException("editRecord: "+QDBerrortext);
}
}
/**
* Delete a record from the QuickBase database.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_DeleteRecord.
*
* @param dbid The unique identifier of a QuickBase database.
* @param rid String containing the record ID of the record to be deleted.
*
*/
public void deleteRecord(String dbid, String rid)throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "rid", rid);
Document xmlResponse = postApiXml(dbid, "API_DeleteRecord",qdbRequest);
return;
}
/**
* Change the record owner of a record from the QuickBase database.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information on API_ChangeRecordOwner.
*
* @param dbid The unique identifier of a QuickBase database.
* @param rid String containing the record ID of the record to be deleted.
* @param newowner String containing the screenname or email address of the new record owner.
*
*/
public void changeRecordOwner(String dbid, String rid, String newowner)throws QuickBaseException, Exception
{
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "rid", rid);
addRequestParameter(qdbRequest, "newowner", newowner);
Document xmlResponse = postApiXml(dbid, "API_ChangeRecordOwner",qdbRequest);
return;
}
/**
* Removes characters from a field name
* The list of remaining characters are as follows:
* a-z
* A-Z
* 0-9
* _
*/
private String removeNonAlphaNumerics(String stringInput) {
StringBuffer buffer = new StringBuffer();
char character;
String fieldName = stringInput.toLowerCase();
for(int i = 0; i < fieldName.length(); i++) {
character = fieldName.charAt(i);
// Check for a-z
if((character >= 'a') && (character <= 'z')) {
buffer.append(character);
}
// Check for 0-9
else if((character >= '0') && (character <= '9')) {
buffer.append(character);
}
else{
buffer.append('_');
}
}
return buffer.toString();
}
private boolean isFid(String fieldName)
{
char character;
for(int i = 0; i < fieldName.length(); i++)
{
character = fieldName.charAt(i);
// Check for 0-9
if((character < '0') || (character > '9'))
{
return false;
}
}
return true;
}
protected Document newXmlDocument(){
Document document = null;
try{
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element qdbapiElement = document.createElement("qdbapi");
document.appendChild(qdbapiElement);
return document;
}catch(Exception e){
throw new RuntimeException(e.getMessage());
}
}
protected Document createXmlDocument(InputSource src, boolean needsValidation){
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(needsValidation);
return factory.newDocumentBuilder().parse(src);
}catch(Exception e){
throw new RuntimeException(e.getMessage());
}
}
protected void writeDoc(Node node, Writer out){
try{
Source src = new DOMSource(node);
Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(src, new StreamResult(out));
}catch(Exception e){
throw new RuntimeException(e.getMessage());
}
}
/**
* Excercise many of the methods of this class.
* Please refer to the
* <a href="https://www.quickbase.com/up/6mztyxu8/g/rc7/en/">
* QuickBase HTTP API</a> for more information.
*
* @param args Specify the username, password, and optionally a QuickBase domain as the first three command line arguments.
*
*/
static public void main(String args[])
throws Exception
{
QuickBaseClient qbc = new QuickBaseClient("username", "password");
System.out.println ("ticket="+qbc.getOneTimeTicket());
PrintWriter out = null;
String className = "QuickBaseClient";
try {
out = new PrintWriter(System.out);
out.println("Welcome to QuickBase\n");
if (args.length < 2 ){
out.println("Please specify the username, password as the first two command line arguments.");
out.println("You can include an optional third command line argument of the form: 'https://hostname.yourdomain.com/db/'");
out.println("This will set the QuickBase domain. The default domain is: 'https://www.quickbase.com/db/'");
return;
}
String username = args[0];
String password = args[1];
String strURL = "https://www.quickbase.com/db/";
QuickBaseClient qdb = null;
if(args.length > 2 && args[2].startsWith("http"))
{
strURL = args[2];
qdb = new QuickBaseClient(username, password, strURL);
}
else
{
qdb = new QuickBaseClient(username, password);
}
out.println(className + "\n" + strURL + ": Authenticating with " + username + " and <password>\n");
HashMap tables = (HashMap)qdb.grantedDBs(false, false, true);
if(tables == null)
{
out.println("No tables belong to this user.");
}
Set tableNames = tables.keySet();
String tableName = "";
String tableDbid = "";
for (Iterator i = tableNames.iterator(); i.hasNext();){
tableName = (String)i.next();
tableDbid = (String)tables.get(tableName);
out.println("Name: " + tableName + " DBID: " + tableDbid);
}
Document schema = qdb.getSchema(tableDbid);
NodeList childTables = schema.getElementsByTagName("chdbid");
out.println("The QuickBase application " + tableName + " has " + childTables.getLength() + " child tables.");
out.println("The table aliases and identifiers are listed below.");
for (int i = 0; i < childTables.getLength(); i++)
{
out.println("Child Table Alias: " + childTables.item(i).getAttributes().item(0).getNodeValue());
out.println("Child Table DBID: " + childTables.item(i).getChildNodes().item(0).getNodeValue());
}
} catch (QuickBaseException qdbe) {
System.err.println("Exception in main "+ qdbe.toString()+ " error code: "+qdbe.getErrorCode() );
qdbe.printStackTrace();
} catch (Exception e) {
System.err.println("Exception in main "+ e.toString() );
e.printStackTrace();
} finally {
if (null != out) {
out.flush();
out.close();
}
}
}
}