Developed at the

Polish-Japanese Institute of Information Technology

Chair of Software Engineering

SBA and SBQL home pages

© Copyright by ODRA team, © Copyright by PJIIT

 

 

ODRA – Object Database for Rapid Application development

Description and Programmer Manual

 

 

by Radosław Adamus, Tomasz Wardziak and the ODRA team

 

15. Interoperability with Java

15.1 Accessing Java Libraries via Reflection

ODRA supports calling external Java code directly from SBQL programs. You can invoke any arbitrary code written in Java, pass parameters to it and utilize its return value. The method is based on the reflexive capabilities of Java.

The following example creates a new java.util.Random object and invokes the nextInt method:

rndref:integer; //1

rndref:=external load_class("java.util.Random"); //2

external new_object(rndref); //3

external init_parameters(rndref); //4

external add_parameters(rndref, 5); //5

external invoke_integer(rndref, "nextInt"); //6


Operations defined above will return random number between 0 and 5.

Line 1 is responsible for creation of rndref variable that will store the reference to an external Java object.

Line 2 loads the java.util.Random class and stores its reference on the rndref variable. In this way any Java class can be loaded.

Line 3 creates a new instance of Java Random object.

Line 4 is always mandatory. It initializes an internal collection of parameters that will be used to invoke a method from an object.

Line 5 adds one parameter that will be used as an argument for the nextInt method. In this case number 5 is loaded as a first and only argument of the method. You can load as many arguments as the Java method requires. Consult Java documentation for method signatures.

Line 6 performs execution of external call and returns value to the SBQL stack (unless the return value is of type void). In this case invoke_integer method is executed, but you should choose one of the following methods to invoke, depending of a return type of Java method:

·        Invoke_integer – method’s return type is integer

·        Invoke_void – method’s return type is void

·        Invoke_library – method’s return type is reference

·        Invoke_string – method’s return type is string

·        Invoke_boolean – method’s return type is boolean

·        Invoke_real – method’s return type is double

At this moment Java arrays are not supported and methods returning Java arrays should be wrapped with methods returning Java collections.

Sample external invocations:

data:integer;

data:=external load_class("java.util.Date");

external new_object(data);

return external invoke_string(data, "toString");

Returns current date as a string.

ToUpper(val:string):string

{

 string_lib:=external 

         load_class("odra.sbql.external.lib.StringLib");

 external new_object(string_lib);

 init_string();

   

 result:string;

 external init_parameters(string_lib);

 external add_parameters(string_lib, val);

 result:=external invoke_string(string_lib, "ToUpper");

 return result;

}

The method ToUpper takes string as a parameter and returns this string in uppercase. It uses ToUpper method defined in odra.sbql.external.lib.StringLib.

SBQL code creating standard SBQL library can be found in res/standard_library folder.

15.2 Java Object Base Connectivity (JOBC)

ODRA JOBC (Java Object Base Connectivity) is defined and implemented according to the idea, syntax and semantics of JDBC (Java Data Base Connectivity). The differences concerns:

  • Input: we assume SBQL queries rather than SQL queries,
  • Output: we assume serialized ODRA objects rather than serialized relational tables.

In this section we introduce the ideas and usage of the ODRA JOBC API. In particular, we explain how to configure and connect an application to an ODRA database, how to prepare a query and how to execute it and how to process its result.

The implemented ODRA JOBC API driver is compatible with Java 1.4+.

15.2.1 Configuring connection

Connection configuration is accomplished during instantiating an JOBC class instance. Available constructor signatures are the following:

public JOBC(String user, String password, String host, int port);

public JOBC(String user, String password, String host);

where:

user – the name of the database user

password – the user password

host – the IP/DNS address of the ODRA server.

port – an optional  ODRA database instance port number (if not provided the default is assumed which is 1521).

Example

Connection to local host on the default port as user ‘admin’ with password ‘admin’.

JOBC db = new JOBC("admin", "admin", "localhost");

 

15.2.2 Connecting to and disconnecting from an ODRA database

Connection to a database is performed by the connect method. If connection cannot be established, an JOBC exception is thrown. The connect method signature is as follows:

void connect() throws IOException;

An opened connection can be explicitly closed by calling the close() method.

Example

db.connect();

 . . . . . . //execute queries

db.close();

 

15.2.3 Setting up a working module

An ODRA data is stored in a hierarchical structure of modules. Each user has an own root environment that is named with the username. A root module can contain any number of sub-modules storing programs and data. After connecting to the database the current module indicator is set to the user root module. To switch the current module the programmer can call the following method in the JOBC class:

void setCurrentModule(String moduleGlobalName) throws JOBCException;

where:

moduleGlobalName – the global name of the requested module. The global name starts with the username and contains zero or more sub-module names separated by dots.

Example

Switch to the module ‘reports’ that is a sub-module of ‘current’ module owns by the user ‘admin’:

db.setCurrentModule(“admin.current.reports”);

 

15.2.4 Executing SBQL queries

Executing SBQL queries requires the following actions:

  1. Call the execute method on the JOBC class instance that takes the SBQL query string as a single  parameter. In case the query without parameters this is the simplest way.

public Result execute(String query) throws JOBCException;

  1. If a query has parameters (described later), an instance of the SBQLQuery class has to be obtained from the JOBC class instance through the getSBQLQuery method call:

SBQLQuery getSBQLQuery(String query);

where ‘query’ is a string containing the SBQL query.

A query stored in an SBQLQuery class instance can be performed through a call to the overloaded execute method of the JOBC class instance.

public Result execute(SBQLQuery query) throws JOBCException

The execute method returns an instance of the Result class, as described below. The same method is used to read and update the data stored in the database.

Examples

Result result = db.execute(“2+2”);

 

Result result = db.execute(“startService()”);

 

Result result = db.execute(“(Employee where lName=\”York\” and worksIn.Dept.name = \“IT\”).salary := 2000”);

 

SBQLQuery query = db.getSBQLQuery(“Person where lName=\”York\””);

Result result = db.execute(query);

 

15.2.5 Queries with parameters

Parameters in a query string are represented by names enclosed in curly brackets. To set a parameter value, first the SBQLQuery class instance has to be obtained from a JOBC instance (as described in the previous sub-section). Setting actual values for query parameters of different types can be performed through the following SBQLQuery class instance methods:

void addIntegerParam(String name, int param) throws JOBCException;

void addBooleanParam(String name, boolean param) throws JOBCException;

void addStringParam(String name, String param) throws JOBCException;

void addRealParam(String name, double param) throws JOBCException;

where:

name – the parameter name

param – the actual value for the parameter

A parameter with a given name can occur in a query more than once. All the parameter occurrences will be substituted by the actual value set for it. An instance of JOBCException is thrown if the name does not match the parameter name in the target query.

Examples

SBQLQuery query = db.getSBQLQuery(“Person where name = {pname}”);

query.addString(“pname”, “Smith”);

 

SBQLQuery query = db.getSBQLQuery(“Person where name = {pname} and age > {page}”);

query.addStringParam(“pname”, “Smith”);

query.addIntegerParam(“page”, 30);

 

SBQLQuery query = db.getSBQLQuery(“Book where (pagesNo >= {pnumber} – {range} and pagesNo <= {pnumber} + {range}).(title, genre)”);

query.addIntegerParam(“pnumber”, 150);

query.addIntegerParam(“range”, 20);

 

15.2.6 Processing query results

Results of SBQL queries are represented by the Result class instance. It can represent different types of SBQL query results. The following types of results are supported:

1.      primitive value of the following types:

a.            integer

b.           real

c.            string

d.           boolean

e.            date

2.      object reference

3.      structure of results

4.      bag of results

5.      named result (i.e. a binder) – any of the result kinds equipped with a name.

Getting a primitive value

To check if the result is primitive the following method is to be used:

boolean isPrimitive();

If the result is primitive, its value can be obtained with the use of the following methods in the Result class that maps an ODRA result to a value of a Java equivalent type:

int getInteger() throws JOBCException;

double getReal() throws JOBCException;

String getString() throws JOBCException;

boolean getBoolean() throws JOBCException;

Date getDate() throws JOBCException;

Examples

Result result = db.execute(“2+2”);

int value = result.getInteger();

 

Result result = db.execute(“avg(Employee.salary)”);

double value = result.getReal();

 

Result result = db.execute(“forall(Person) address.city = \“Warsaw\””);

boolean result = result.getBoolean();

Object references

SBQL queries can return object references. Because in the context of JOBC calls object references are currently not supported, they are represented as string constant values - “object reference”.

Complex results

An SBQL query can return a structure, which can be the result of operators used in a query or can be returned by the dereference acting on an identifier of a complex object. Fields of a structure can be named or unnamed. To check if the result is complex the following method is to be used:

boolean isComplex();

Fields of a complex result can be accessed in the following ways:

  1. By calling the fields() method that returns a bag of results representing structure fields. The bag can be processed by iteration.
  2. If a field is named,  it can be accessed with the use of getByName(String name) method.

Example

The query returns two elements structure ( {integer, string} ) containing the Smith’s age and home address. We assume that there is only one Person with that name and the cardinality of fields ‘age’ and ‘address’ is [1..1].

Result result = db.execute(“(Person where lName = \“Smith\”).(deref(age), deref(address))”);

 

Result fields = result.fields();

System.out.println(fields.get(0) + “,” + fields.get(1));

Bag of results and empty results

SBQL queries can return a bag of results (primitive, structures, object references or named). The JOBC API allows for iteration over the results in a bag result. The Result class implements the Iterable<Result> interface.

To check if the result is complex the following method is to be used:

boolean isBag();

An empty result has the following properties:

result.isBag();  // returns true

result.size(); // returns 0

result.isEmpty(); // returns true

Examples

Result result = db.execute(“(Person where age > 20).lName”);

 

//java 1.5+ foreach loop (in java1.4 use result.iterator())

for(Result res : result.toArray()){

    System.out.println(res.getString());

}

Named results

Results returns by SBQL queries can be named. A name can be given explicitly with the SBQL auxiliary names operators (‘as’ and ‘groupas’) or can be a consequence of dereference operation on an identifier of a complex object. The difference between ‘as’ and ‘groupas’ SBQL operators is that the first names each element in the result bag and the latter names the entire bag.

In JOBC the names are to be used to navigate in the result searching for a sub-result with a given name. In other situations the result name is transparent.

Example:

Result result = db.execute(“2 + 2 as result”);

int ires = result.getInteger();

Result result = db.execute(“(Person where age > 21).(name as personName, age as personAge) as adult”);

Filtering results by name

A result name can be used to search for a sub-result with a particular name. The search can be performed with the use of Result class instance method getByName taking the string representing a result name as a parameter:

Result getByName(String name);

Result returned by the getByName method call is a Result class instance representing a sub-result that was named and the name is equal to the parameter. If nothing was found the empty result is returned. The search is performed to the first occurrence of a named result, thus if the searched named result is a part of the other named result it will not be returned.

Examples:

Result result = db.execute(“(2 + 2) as result”);

Result intresult = result.getByName(“result”);

 

Result namedAdults = db.execute(“(Person where age > 21).name as personName, age as personAge) as adult”);

      //we assume that there is more than one person

      //satisfying the condition

namedAdults.isNamed(); //returns true

namedAdults.isBag(); //returns true (the names are transparent)

 

//java 1.5+ foreach loop

for(Result adult : namedAdults.toArray()) {

   adult.isNamed(); //returns true (due to operator as used in

                    //the query)

   adult.isComplex(); //return true (names are transparent)

}

 

//java 1.4

for(Iterator iter = unnamedResult.iterator();iter.hasNext();) {

   Result adult = (Result)iter.next();

   adult.isNamed(); //returns true (due to operator as used in

                    //the query)

   adult.isComplex(); //returns true (names are transparent)

}

 

Result unnamedAdults = namedAdults.getByName(“adult”);

unnamedAdults.isNamed(); //returns false

unnamedAdults.isBag(); //returns true

 

//java 1.5+ foreach loop

for(Result adult : unnamedAdults.toArray()) {

   adult.isNamed(); //returns false

   adult.isComplex(); //returns true

}

 

//java 1.4

for(Iterator iter = unnamedResult.iterator();iter.hasNext();) {

   Result adult = (Result)iter.next();

   adult.isNamed(); //returns false

   adult.isComplex(); //return true

}

 

Result names = result.getByName(“adult”).getByName(“name”);

names.isNamed(); //returns false

names.isBag();returns true

 

Result ages = result.getByName(“adult”).getByName(“age”);

ages.isNamed(); //returns false

ages.isBag(); //returns true

 

 

Last modified: July 9, 2008