9.2.14. Hosted Foreign Objects in Virtuoso
Java VM Hosted Objects
A special build of Virtuoso hosts a Java VM and allows manipulation of Java classes through the SQL user defined types.
In order to access the Java class instances they have to be defined as Virtuoso types using CREATE TYPE and specifying LANGUAGE JAVA. Java classes have to be in the CLASSPATH of the hosted Java VM.
Example 9.13. Hosted Java Objects
Java (Point.java):
public class Point implements java.io.Serializable { public double x = 0; public double y = 0; public Point (double new_x, double new_y) { x = new_x; y = new_y; } public double distance (Point p) { return Math.sqrt ((p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y)); } }
This Java class should be compiled and the corresponding Point.class should be placed in the hosted VM's classpath. Then a Virtuoso user defined type should be created as follows:
create type Point language java external name 'Point' as ( x double precision external name 'x', y double precision external name 'y' ) constructor method Point (new_x double precision, new_y double precision), method distance (Point p) returns double precision external name 'distance';
From now on the SQL Point type can be used to create instances of the
Java Point class, access it's members, call it's methods and store it into
tables (since the Java Point class implements the
java.io.Serializable
interface).
For the hosted Java objects a LANGUAGE JAVA should be specified. The format of EXTERNAL NAME is:
the full name of the Java class for classes (ex. 'java.lang.Class') |
the name of the methods/instance members |
Since Java has static members and the Virtuoso SQL types do not, Virtuoso allows read-only access to static members through static observer functions with EXTERNAL VARIABLE NAME instead of EXTERNAL NAME.
Example 9.14. Static Member Access
java (stat.java) : public class stat { static stat_m double; }
Virtuoso SQL:
create type stat language java external name 'stat' static method observe_stat_m () returns double precision external variable name 'stat_m';
Virtuoso does automatic mapping between the Virtuoso SQL data types and the Java data types. Since Java data types are much more primitive than Virtuoso types it is safe to explicitly specify the Java type of an instance member, method parameter or method return value. This is done by using the Type Signatures format described in the Java Native Interface Specification (chapter 3 : JNI Types and Data Structures : Table 3.2). The signatures are supplied as string values to EXTERNAL TYPE clause.
To facilitate the creation of the wrapper SQL types Virtuoso uses
the Java Reflection API to get the description of the class in XML form.
This XML is then transformed using an XSL stylesheet to makes the
CREATE TYPE statements required automatically. In the process it preserves
the superclass/subclass relationships of the specified Java classes and
represents them as a supertypes/subtypes in SQL. The
jvm_ref_import()
procedure is used to create the XML by calling the Java Reflection API.
The function import_jar()
takes the same parameters as jvm_ref_import()
but will
automatically create and execute the create type statements within the Virtuoso
server.
Table 9.2. Java Type to Virtuoso Type Conversions
Java Type/Class | Virtuoso Internal Type |
---|---|
boolean | smallint |
byte | smallint |
char | smallint |
short | integer |
int | integer |
long | integer |
float | real |
double | double precision |
byte[] | binary |
java.lang.String | NVARCHAR |
java.util.Date | DATETIME |
[] | vector |
Table 9.3. Virtuoso Type to Java Type Conversions
Virtuoso Internal Type | Java Type/Class |
---|---|
smallint | short |
integer | integer |
real | float |
double precision | double |
varchar | java.lang.String |
nvarchar | java.lang.String |
datetime | java.util.Date |
timestamp | java.util.Date |
binary | byte[] |
For all the other types encountered in the signatures of the Java methods/members it makes a forward reference to a Virtuoso/PL user defined type based on the java class name, replacing the dot ('.') with the underscore ('_') character.
For example:
'java.lang.System'
becomes 'java_lang_System'
In order to correctly map a java superclass/subclass relationship between
class A and class B when importing, it is necessary to include A, B and all
the intermediate classes in the superclass/subclass chain in a single
import_jar()
call.
To implement serialization/deserialization for Java object the Virtuoso needs the __virt_helper Java class. This class contains utility functions implementing serialization/deserialization. This class must be in the CLASSPATH.
The Java VM hosted inside the Virtuoso binary is not started at startup,
but when first needed. It's startup is marked by a message in the Virtuoso
log file. An application can control the initialization of the Java VM by
explicitly initializing the Java VM (preferably on server startup) by
calling the VSE: java_vm_attach()
CLR Hosted Objects
A special virtuoso build is available to allow SQL types integration with the CLR (Common Language Runtime) on Windows. This is achieved by providing COM server in C# (virtclr.dll) that is called from the native code through COM.
The virtclr.dll library should be registered into the CLR's Global assembly cache.
The semantics of CLR hosted objects are largely the same as those described for Java hosted objects. As before, native objects need SQL Type wrappers, but with LANGUAGE CLR clause specified.
To automatically create the SQL Type wrappers based on the CLR Reflection
API the Virtuoso CLR binary has a system stored procedure:
import_clr()
There are three forms for specifying the EXTERNAL NAME of a CLR class:
-
a)
<Assembly public name>/<namespace-prefixed-class-name>
Here the Virtuoso CLR host issues Assembly.Load with<Assembly public name>
to find the assembly. After finding it, it looks for<namespace-prefixed-class-name>
in it. -
b)
<namespace-prefixed-class-name>
Here the Virtuoso CLR host issues Assembly.Load with<namespace-prefixed-class-name>
. After finding it, it looks for<namespace-prefixed-class-name>
in it. -
c)
<path-to-the-assembly-binary>/<namespace-prefixed-class-name>
Here the Virtuoso CLR host issues Assembly.LoadFrom with<path-to-the-assembly-binary>
. After finding it, it looks for<namespace-prefixed-class-name>
in it.
The Virtuoso CLR host does the above when creating an instance of the type, accessing static methods or properties.
However when it deserializes an serialized CLR instance it calls the CLR deserialization class BinaryFormatter. The BinaryFormatter.Deserialize calls internally Assembly.Load to find the serialized class description. So although the classes defined with EXTERNAL name as in c) above are otherwise accessible (and serializable) they will possibly not deserialize correctly (as the assembly binary may not be findable through the Assembly.Load). To avoid that CLR limitation it is advisable to use the EXTERNAL NAME forms a) and b) wherever possible.
The Assembly.Load process of finding Assemblies is very well documented on the MSDN . Note that if an assembly was loaded through Assembly.LoadFrom it is not considered as "already loaded" by the Assembly.Load. The Virtuoso CLR is a CLR runtime host. As such it can use the normal CLR configuration files. It also is able of using private assemblies.
See Also: | |
---|---|
The Create Assembly Syntax |
ASPX Hosting Using the Hosted CLR
Virtuoso CLR hosting allows ASPX pages to be executed through the Virtuoso HTTP server inside the hosted CLR Virtual machine. To enable this support an additional library (virt_http.dll) needs to be registered with the Global Assembly cache. Having achieve this and copying ASPX project files under the Virtuoso HTTP server's root allows direct execution of the ASPX page. See the sample ASPX pages in the Virtuoso distribution.
If the ASPX project files reside in a WebDAV directory they are copied into a temporary file system directory under a special temporary directory (configurable by the TempASPXDir INI parameter in [HTTPServer] section of virtuoso ini file) before executed. In order to be execute correctly from WebDAV the ASPX files should have Execute WebDAV permission set them. The execution of ASPX is also controlled by the EnableDavVSP INI parameter in the [HTTPServer] as with any active content within WebDAV.
See Also: | |
---|---|
Deploying ASP.Net Web Applications Runtime Hosting |
Expressing Hosted Language Supertype/Subtype Dependencies With Virtuoso/PL User-Defined-Types
It is also possible to represent the tree or in-part of Java or the CLR's superclass/subclass hierarchy with Virtuoso user defined type mappings.
Consider the following sample Java code:
class g1 { public int g1_value; public int mtd_g1 (int x) { return g1_value; } }; class g2 extends g1 { public int g2_value; public int mtd_g2 (int x) { return g2_value; } }; class g3 extends g2 { public int g3_value; }; class g2_sib extends g1 { public int g2_sib_value; }; class uses_types { public static g3 mtd (int x) { return new g3 (); } }
One can create SQL user defined types for
g1
, g2
and g3
to represent the
g1
/g2
/
g3
Java class hierarchy if calling
mtd_g1
and
mtd_g2
is needed:
create type sql_g1 language java external name 'g1' as ( g1_value int) method mtd_g1 (x integer) returns integer; create type sql_g2 under sql_g1 language java external name 'g2' as ( g2_value int) method mtd_g2 (x integer) returns integer; create type sql_g3 under sql_g2 language java external name 'g3' as ( g3_value int) method mtd_g3 (x integer) returns integer; create type uses_types language java external name 'uses_types' static method mtd (x integer) returns sql_g3;
provided with the above, one can call
uses_types.g3 ()
method and
call mtd_g1 ()
on the returned instance in
Virtuoso/PL as follows:
select uses_types::mtd (12).mtd_g1 (10);
Sometimes it is not desirable or necessary to mirror the full supertype/subtype hierarchy from Java to Virtuoso/PL.
For the above example only sql_g2
and sql_g3
can be defined if the goal was
to call mtd_g2()
instead of
mtd_g1()
.
When creating instances of the Virtuoso/PL user defined types to represent the data returned by the hosted code, Virtuoso tries to find the closest common ancestor of the hosted instance's class and the ones defined in Virtuoso as user defined types.
For example if in the above example a Java function returns an instance of
g3
and there is a
sql_g3
defined inside virtuoso the
g3
Java instance will be wrapped into an
sql_g3
Virtuoso/PL instance. Note that
that will not depend on the presence or absence of
sql_g1
and
sql_g2
definitions - i.e. Virtuoso will favor
the exact match.
If, however sql_g3
is not defined, but
sql_g2
and sql_g1
are, then the g3
instance will be wrapped
up in an sql_g2
instance when returned.
Similarly, if an instance of the g2_sib
is to be returned in Virtuoso/PL and sql_g1
to sql_g3
are defined, Virtuoso will wrap the
g2_sib
Java instance into an
sql_g1
SQL instance.