/*
* This file is part of rasdaman community.
*
* Rasdaman community is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rasdaman community is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with rasdaman community. If not, see .
*
* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann /
rasdaman GmbH.
*
* For more information please see
* or contact Peter Baumann via .
*/
/*************************************************************
*
*
* PURPOSE:
*
*
* COMMENTS:
* - return type complex not yet supported.
*
*********************************************************** */
package rasj.clientcommhttp;
import rasj.*;
import org.odmg.*;
import rasj.odmg.*;
import rasj.global.*;
import java.io.*;
import java.net.*;
import java.lang.*;
import java.util.*;
/**
* This class handles a HTTP-request to the RasDaMan server.
* The specified RasDaMan server is contacted, the specified command is sent to
* the server, and the result of the query is retrieved and stored in a byte array.
* The specification of the communication protocol is given below.
*
*
* @version $Revision: 1.35 $
*
*
* Request structure
* The rasj HTTP request to the rasdaman server uses the HTTP POST-Format with the following
* parameters:
*
*
* Parameter: | Description: | Required for |
* Command |
* Integer value specifying the desired action (e.g. OpenDB, BT, CT, CloseDB ...) |
* all requests |
* Database |
* String value specifying the database |
* OpenDB, CloseDB |
* ClientType |
* Integer value defining the type of the client (at the moment always RasClient) |
* all requests |
* ClientID |
* Integer value specifying the ClientID (currently set to 1 for every client) |
* all requests |
* QueryString |
* String value containing the RasQL query |
* executeQuery |
* Endianess |
* Integer value containing the Client Endianess |
* only insert queries |
* NumberOfQueryParameters |
* Integer value specifying the number of query parameters |
* only insert queries |
* QueryParameters |
* Byte Array containing the query parameters (MDDs) using the following format:
*
* Integer | String | String | Integer | String | String |
* String | Long | Byte[] |
*
* objectType | objectTypeName | typeStructure | typeLength |
* domain | storageLayout | OID | dataSize | binary data |
*
* |
* only insert queries |
*
*
*
*
* Result formats / internal representation:
* The result of a HTTP request has one of the following forms:
* MDD Collections:
*
Byte | Byte |
* String | Long(4Bytes) |
* resultElement 1 | ... |
* String | String | String | Long(4Bytes) | Byte[] |
* Result type 1=MDDCollection |
* Endianess |
* Collection type |
* Number of results |
* BaseType description |
* Spatial domain |
* OID |
* Size of the Binary Data Block |
* Binary Data Block |
*
*
* Skalar Collections:
*
Byte | Byte |
* String | Long(4Bytes) |
* resultElement 1 | ... |
* String | Long(4Bytes) | Byte[] |
* Result type 2=SkalarCollection |
* Endianess |
* Collection type |
* Number of results |
* ElementType description |
* Size of the Binary Data Block |
* Binary Data Block |
*
*
* Errors:
*
Byte | Byte |
* Long(4Bytes) | Long(4Bytes) | Long(4Bytes) |
* String |
* Result type 0=Error |
* Endianess |
* Error number |
* Line number |
* Column number |
* Token |
*
*
* Single Integer Value:
*
Byte | Integer |
* Result type 3=Integer | Value |
*
*
* OID:
*
Byte | String | String | Double |
* Result type 4=OID | system | basename | localOID |
*
*
* Acknowledgement:
*
Byte |
* Result type 99=OK |
*
*
*
*
*/
public class RasHttpRequest implements RasCommDefs, RasGlobalDefs
{
static final String rcsid = "@(#)Package rasj.clientcommhttp, class RasRequest: $Header: /home/rasdev/CVS-repository/rasdaman/java/rasj/clientcommhttp/RasHttpRequest.java,v 1.35 2003/12/19 15:36:43 rasdev Exp $";
/**
* The type of this client */
private String client = "RASCLIENT";
/**
* The result type ( MDD Collection, Skalar Collection, Error, Integer ). This field is set
* automatically when a query has been executed, so there's no setResultType method.
*/
private byte resultType = 0;
/**
* The result of the query
**/
private Object result = null;
/**
* This method sends a query to the RasDaMan Server and retrieves the results.
*
* @param con server connection
* @param parameters the parameters for the request as name/value pairs (for example "clientID=4354351&queryString=select img from test")
*/
public void execute( String serverURL, String parameters )
throws RasQueryExecutionFailedException, RasConnectionFailedException
{
Debug.enterVerbose( "RasHttpRequest.execute: start. serverURL=" + serverURL + ", parameters=" + parameters );
BenchmarkTimer httpTimer = new BenchmarkTimer("httpRequest");
BenchmarkTimer rcvTimer = new BenchmarkTimer("receive");
try
{
URL url = new URL( serverURL );
Debug.talkVerbose( "RasHttpRequest.execute: sending to " + url + " POST request=" + parameters );
httpTimer.startTimer();
BenchmarkTimer sendTimer = new BenchmarkTimer("send");
sendTimer.startTimer();
// Send the query
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Content-type","application/octet-stream");
con.setRequestProperty("User-Agent","RasDaMan Java Client");
con.setRequestProperty("Version","1.0");
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream(),"8859_1");
out.write(parameters,0,parameters.length());
out.flush();
out.close();
sendTimer.stopTimer();
sendTimer.print();
rcvTimer.startTimer();
// Read response
BenchmarkTimer getInputTimer = new BenchmarkTimer("getInputStream");
getInputTimer.startTimer();
DataInputStream in = new DataInputStream(con.getInputStream());
// int BuffSize = con.getContentLength(); // not used -- PB 2003-jun-14
// Debug.talkVerbose("RasHttpRequest.execute: buffSize=" + BuffSize);
getInputTimer.stopTimer();
getInputTimer.print();
/* variables for later use */
byte[] b1 = new byte[1];
byte[] b4 = new byte[4];
byte endianess = 0;
String collType = null;
int numberOfResults = 0;
int dataSize = 0;
byte[] binData = null;
int readBytes = 0;
int readBytesTmp = 0;
DBag resultBag;
RasGMArray res = null;
in.read(b1);
resultType = b1[0];
Debug.talkVerbose("RasHttpRequest.execute: resultType=" + resultType );
switch( resultType )
{
case RESPONSE_OK:
case RESPONSE_OK_NEGATIVE:
//Nothing todo
break;
// +++++++++++++++++++++++++++++++++++++++++++++++++
case RESPONSE_MDDS:
Debug.talkVerbose("RasHttpRequest.execute: result type is MDD." );
// read Endianess
while(in.read(b1) == 0)
;
endianess = b1[0];
// read Collection Type
collType = RasUtils.readString(in);
Debug.talkVerbose("RasHttpRequest.execute: colltype=" + collType);
// read NumberOfResults
while(in.available() < 4)
;
in.read(b4);
numberOfResults = RasUtils.ubytesToInt(b4,endianess);
Debug.talkVerbose("RasHttpRequest.execute: number of results: " + numberOfResults);
// Initialize return-set and parameters
resultBag = new RasBag();
String mddBaseType = null;
String domain = null;
String oid = "";
RasOID roid = null;
// do this for each result
for(int x = 0; x < numberOfResults; x++)
{
Debug.talkVerbose("RasHttpRequest.execute: handling result #" + (x+1) );
//read mddBaseType
mddBaseType = RasUtils.readString(in);
// read spatialDomain
domain = RasUtils.readString(in);
// read OID
oid = RasUtils.readString(in);
//System.err.println("OID is " + oid);
roid = new RasOID(oid);
// read size of binData
while(in.available() < 4)
;
in.read(b4);
dataSize = RasUtils.ubytesToInt(b4,endianess);
Debug.talkVerbose("RasHttpRequest.execute: mddBaseType is " + mddBaseType + ", spatialDomain=" + domain + ", size of BinData=" + dataSize );
// read binData
binData = new byte[dataSize];
readBytes = 0;
readBytesTmp = 0;
while( (readBytesTmp != -1) && (readBytes < dataSize) )
{
readBytesTmp = in.read(binData,readBytes,dataSize-readBytes);
readBytes += readBytesTmp;
}
Debug.talkVerbose("RasHttpRequest.execute: read " + readBytes + " bytes.");
RasType rType = RasType.getAnyType(mddBaseType);
//System.out.println(rType);
RasBaseType rb = null;
if(rType.getClass().getName().equals("rasj.RasMArrayType"))
{
RasMArrayType tmp = (RasMArrayType)rType;
rb = tmp.getBaseType();
}
else
{
Debug.talkCritical("RasHttpRequest.execute: exception: element of MDD Collection is no MArray" );
throw new RasClientInternalException("RasHttpRequest","execute()","element of MDD Collection is no MArray");
}
if(rb.isBaseType())
{
if(rb.isStructType())
{
// It is a structType
//System.err.println("It is a structType");
RasStructureType sType = (RasStructureType)rb;
//System.out.println(sType);
res = new RasGMArray(new RasMInterval(domain), 0);
res.setTypeLength(rb.getSize());
res.setArraySize(dataSize);
res.setArray(binData);
//insert into result set
resultBag.add(res);
break;
} else
{
// It is a primitiveType
RasPrimitiveType pType = (RasPrimitiveType)rb;
//System.err.println("It's a primitive type: " + pType);
switch(pType.getTypeID())
{
case RAS_BOOLEAN:
case RAS_BYTE:
case RAS_CHAR:
//System.err.println("It's a byte array!");
res = new RasMArrayByte(new RasMInterval(domain));
break;
case RAS_SHORT:
//System.err.println("It's a short array!");
res = new RasMArrayShort(new RasMInterval(domain));
break;
case RAS_USHORT:
//System.err.println("It's a ushort array!");
byte[] tmData = new byte[dataSize*2];
for(int i=0;i
* Future versions will support other types like, for example, "BROWSER", where the
* results will be coded as standard HTTP-responses of certain mime types (for example
* "image/gif").
*
* @param clientType currently only "RASCLIENT" supported
*/
public void setClientType ( String clientType )
{
client = clientType;
}
/**
* main program for testing purposes
*/
/* BEGIN experimental ***********************
public static void main( String[] args )
{
String server = "localhost";
String port = "7001";
String base = "RASBASE";
String user = "rasguest";
String passwd = "rasguest";
String query = "select r from RAS_COLLECTIONNAMES as r";
int count = 1;
System.out.println( "Query test started." );
for (int i=args.length-1; i>=0; i--)
{
if (args[i].equals("--server"))
server = args[i+1];
if (args[i].equals("--port"))
port = args[i+1];
if (args[i].equals("--database"))
base = args[i+1];
if (args[i].equals("--user"))
user = args[i+1];
if (args[i].equals("--passwd"))
passwd = args[i+1];
if (args[i].equals("--query"))
query = args[i+1];
if (args[i].equals("--count"))
count = Integer.parseInt(args[i+1]);
}
try
{
RasImplementation myApp = new RasImplementation("http://"+server+":"+port);
myApp.setUserIdentification(user, passwd);
System.out.println( "opening database..." );
Database myDb = myApp.newDatabase();
myDb.open( base, Database.OPEN_READ_ONLY );
System.out.println( "starting transaction..." );
Transaction myTa = myApp.newTransaction();
myTa.begin();
String parameters = "Command=8&ClientID=1&QueryString=" + query;
String serverUrl = "http://" + server + ":" + 7102; // port;
for (int i = 0; i < count; i++)
{
System.out.println( "sending query #" + i + "..." );
execute( serverUrl, parameters );
}
System.out.println( "closing transaction..." );
myTa.abort();
System.out.println( "closing database..." );
myDb.close();
System.out.println( "all done." );
}
catch(Exception e)
{
System.err.println( e.getMessage() );
}
System.out.println( "Query test done." );
} // main()
END experimental ***********************/
}