Using Java Functions
DataDirect XQuery supports Java static methods, Java instance methods, and Java constructors.
The DataDirect XQuery type ddtek:javaObject allows you to invoke Java methods. The predeclared DataDirect XQuery namespace prefix is ddtek, and is bound to http://www.datadirect.com/xquery. The examples in this section demonstrate the usage of ddtek:javaObject.
Declaring Java Functions
Two steps are required to declare Java functions:
1. Importing the Class
Before you can declare a Java class, you must import it into the XQuery environment. To do this, you must declare a namespace with a specific URL. The syntax of an import is:
where:
prefixis a namespace prefix to associate with the Java class.
java class namecontains the complete package and class name of the Java class to import. The specified Java class must be accessed using the Java class loader in the environment in which the query is executed. Typically, this means that the CLASSPATH must contain a reference to the directory or jar file where the Java class can be found. In J2EE server environments, other requirements may exist such as the jar or class file must be stored in a specific directory.For example:
If DataDirect XQuery cannot find the Java class, it generates a static error.
2. Declaring the Function
Before you can invoke a Java function in a query, you must declare it. How you declare it depends on whether the Java function is a static method or an instance method.
Static Method
To declare a static method, use the following syntax:
declare namespace file = "ddtekjava:java.io.File"; declare functionnamespace:function name(argument list) asreturn typeexternal;For example:
declare function file:createTempFile($prefix as xs:string,$suffix as xs:string) as ddtek:javaObject external;See also "Example: Java Function (Static Method)".
Instance method
To declare an instance method, follow these steps:
- Import the class to a namespace (see "1. Importing the Class").
- In the cases where you need to explicitly create a new Java object instance, declare a function mapping to a Java constructor unless the Java object type you are declaring has a defined XQuery mapping. A class instance can be an XML type for which a mapping is defined.
- Declare a function mapping to an instance method declaring ddtek:javaObject as the value for the first parameter if Step 2 is required. Otherwise, the value of the first parameter is the appropriate XQuery data type.
- Invoke the function using the class instance on which the instance method is invoked as the first argument.
The numbers in the following example correspond to the preceding steps:
declare namespace BigInteger = "ddtekjava:java.math.BigInteger"; 1 declare function BigInteger:BigInteger($v as xs:string) 2 as ddtek:javaObject external; declare function BigInteger:gcd( $this as ddtek:javaObject, $val as xs:integer) as xs:integer external; 3 BigInteger:gcd(BigInteger:BigInteger("12"),4) 4The following example does not include Step 2 because the Java object to be declared has a defined XQuery mapping.
declare namespace BigInteger = "ddtekjava:java.math.BigInteger"; 1 declare function BigInteger:gcd( $this as xs:integer, $val as xs:integer) as xs:integer external; 3 BigInteger:gcd(12,4) 4Mapping Types Between Java and XQuery for Java External Functions
Before DataDirect XQuery passes XQuery arguments to a Java method, it converts the arguments from the XQuery data type to a Java type using the SequenceType specified for the external function when it was declared.
Table 13-1 shows how to map arguments and return types from the Java function declaration to XQuery SequenceTypes to be used in the XQuery function declaration.
Table 13-1. Mapping Types Between Java and XQuery Java XQuery boolean xs:boolean byte xs:byte byte[] ddtek:javaObject
xs:base64binary
xs:hexBinary double xs:double float xs:float int xs:int long xs:long short xs:short javax.xml.transform.Source1 ddtek:javaObject
document-node( ) java.lang.Boolean ddtek:javaObject
xs:boolean[?] java.math.BigDecimal ddtek:javaObject
xs:decimal[?] java.math.BigInteger ddtek:javaObject
xs:integer[?] java.lang.Byte ddtek:javaObject
xs:byte[?] java.lang.Double ddtek:javaObject
xs:double[?] java.lang.Short ddtek:javaObject
xs:short[?] java.lang.Long ddtek:javaObject
xs:long[?] java.lang.Integer ddtek:javaObject
xs:int[?] java.lang.Float ddtek:javaObject
xs:float[?] java.lang.String ddtek:javaObject
xs:untypedAtomic[?]
xs:string[?] java.net.URI ddtek:javaObject
xs:anyURI[?] java.xml.namespace.QName ddtek:javaObject
xs:QName[?] org.w3c.dom.Attr ddtek:javaObject
attribute() [?] org.w3c.dom.Comment ddtek:javaObject
comment() [?] org.w3c.dom.Document ddtek:javaObject
document-node() [?] org.w3c.dom.Element ddtek:javaObject
element() [?] org.w3c.dom.Node ddtek:javaObject
document-node() [?]
element() [?]
attribute() [?]
comment() [?]
text() [?]
processing-instruction() [?] org.w3c.doc.ProcessingInstruction ddtek:javaObject
processing-instruction() [?] org.w3c.dom.Text ddtek:javaObject
text() [?] java.lang.Object 2 ddtek:javaObject void empty-sequence() 3 boolean, byte, double, float, int, long, short 4 xs:anyAtomicType? 4 byte[], java.math.BigDecimal, java.math.BigInteger, java.lang.Byte, java.lang.Double, java.lang.Float, java.lang.Integer, java.lang.Long, java.lang.Short, java.lang.String, java.net.URI, java.xml.namespace.QName xs:anyAtomicType? 4 Any Java data type in this table item() 4 Any Java object type in this table item() ? 4 Any Java type in this table java type [] 5 ddtek:javaObject
XQuery type of this table(*|+)
1 Must be one of the following interfaces: javax.xml.transform.stream.StreamSource, javax.xml.transform.sax.SAXSource, javax.xml.transform.dom.DOMSource, javax.xml.transform.stax.StAXSource, or com.ddtek.xquery.StAXSource.2 java.lang.Object or another Java class not listed in this table.3 Only for return types.4 Useful to declare Java methods overloaded on argument type. See "Resolving Function Calls".5 When the Java parameter type or method return type is an array, either declare the XQuery function to receive or return one of the matching types from this table and add a * or + quantifier.You can also use ddtek:javaObject with a * or + quantifier.
In cases where Table 13-1 lists multiple mapping options, consider the following information:
- ddtek:javaObject cannot be combined with other XQuery types. This means that when the expression you want to pass to a Java function is the result of calling another Java method that returns a ddtek:javaObject, you must declare the function as receiving a ddtek:javaObject expression. Also, when you declare the return type of a Java function as ddtek:javaObject, the returned value only can be passed into another Java method. The returned value cannot be used with any other XQuery expression.
- To map a Java method to an XQuery type when the method is overloaded on parameter type and no common XQuery type can be found for that parameter, you must use a less specific SequenceType. Typically, in this case, you would use a less specific SequenceType such as item(), xs:anyAtomicType, or ddtek:javaObject depending on the details of the set of overloaded methods. DataDirect XQuery attempts to map a given XQuery function invocation to the correct Java function using the static types of the function call arguments instead of the SequenceType of the arguments. See the next section "Resolving Function Calls".
Resolving Function Calls
Before DataDirect XQuery attempts to resolve the Java method to invoke for a given XQuery function call, DataDirect XQuery identifies the external function declaration.
Then, like any other XQuery function call, the static types of the argument expressions are matched with the SequenceType of the function declaration parameters. Static type errors are detected and reported.
Finally, the Java method must be resolved. Sometimes, DataDirect XQuery cannot determine how to map an XQuery function declaration and the associated function call to a Java instance method. Ambiguity can occur for the following reasons:
- Java supports method overloading on argument type; however, XQuery does not support function overloading on argument type.
- Invoking Java instance methods from DataDirect XQuery requires a first artificial "this" argument. This requirement creates a potential conflict with static methods of the same class that accept a true Java object as their first argument.
DataDirect XQuery uses the following steps to determine the Java method to invoke for a given function call. Except for the first step, each of the consecutive steps reduces the number of Java functions that are potential candidates to which to map the XQuery function call.
- Add all public (static and instance) methods and public constructors of the Java class identified through the function’s namespace.
- Use a public constructor if the name of the function equals the class name. In the absence of matching public constructors, DataDirect XQuery considers Java methods with the same name.
- Retain only Java methods whose names match the name of the function if the name of the function being invoked does not equal the class name.
- Remove all instance methods, unless the first argument in the function call is typed as ddtek:javaObject and the Java class associated with the ddtek:javaObject equals the class identified by the namespace of the function (see "Notes About Using Java Instance Methods").
- Remove all static methods and constructors whose number of parameters does not match the number of parameters in the XQuery function declaration.
- Remove all instance methods whose number of parameters does not equal the number of parameters of the XQuery function declaration minus one (this takes into account the first artificial "this" argument that is required when invoking an instance method from XQuery).
- Remove all Java methods and constructors for which the SequenceType of the argument as specified in the function declaration does not match the type of the Java method. The type matching requires a mapping from an XQuery SequenceType to a Java type. This mapping is documented in Table 13-1. Note that when the function argument is declared as item(), xs:anyAtomicType, or ddtek:javaObject, DataDirect uses the static type of the argument expression of the function call instead of the SequenceType of the function declaration. This typically is more accurate and allows correct method resolution in more scenarios.
Unless exactly one method remains after the previously described procedure, one of the following static errors is generated:
Static error during resolving of external Java function. Ambiguous call to Java external function '<function>'or
Static error during resolving of external Java function. No matching Java external function found for '<function>'Note that at execution time:
- Standard Java late binding applies when invoking instance methods.
- If the XQuery external function resolves to an instance method and at runtime the "this" argument evaluates to an empty sequence, the following error is generated:
Runtime error. Value of this pointer is null in call to external Java instance method.
NOTE: Generic methods introduced with J2SE 5.0 are not supported.
Notes About Using Java Instance Methods
- ddtek:javaObject can only be used when declaring a SequenceType and cannot be used in any other XQuery expression where a QName bound to a type is allowed such as cast, treat as, and so on.
- External variables of type ddtek:javaObject are not supported.
- When possible, DataDirect XQuery keeps track of the exact Java class that is associated with a given ddtek:javaObject-typed expression. This can be useful when trying to map a given function call to a Java method.
When tracking of the underlying Java class is not possible, you can use the DataDirect XQuery proprietary function, ddtek:javaCast, to resolve this issue (see "ddtek:javaCast()"). DataDirect XQuery cannot track the underlying Java class in the following situations:
- The ddtek:javaObject expression is the result of executing a recursive function, for example:
declare namespace c1 = "ddtekjava:c1";
declare namespace c2 = "ddtekjava:c2";
declare namespace c3 = "ddtekjava:c3";
declare variable $e as xs:integer external;
declare function c1:c1() external;
declare function c2:c2() external;
declare function c3:f($this as ddtek:javaObject) external;
declare function local:recursive($p as xs:integer) as ddtek:javaObject {
if($p le 1) then c1:c1()
else if($p eq 2) then c2:c2()
else local:recursive($p - 2)
};
c3:f(local:recursive($e))
Assuming that there are two static c3:f methods, the first taking an instance of c1 and the second taking an instance of c2, DataDirect XQuery is unable to determine statically which one to invoke, so generates a static error.
- The static type of an expression is a sequence (or union) of different ddtek:javaObject types, for example:
declare namespace c1 = "ddtekjava:c1";
declare namespace c2 = "ddtekjava:c2";
declare namespace c3 = "ddtekjava:c3";
declare function c1:c1() external;
declare function c2:c2() external;
declare function c3:f($this as ddtek:javaObject) external;
for $x in (c1:c1(),c2:c2()) return c3:f($x)
Assuming that there are two static c3:f methods, the first taking an instance of c1 and the second taking an instance of c2, DataDirect XQuery is unable to determine statically which one to invoke, so generates a static error.
- Keeping the preceding information about tracking the extract Java class in mind, it is possible to return a ddtek:javaObject value from a query. A Java program containing the query can use the following ways to retrieve the result:
- Serialize the result by using one of the following XQSequence methods: getSequenceAsString, writeSequence, writeSequenceToSAX, or writeSequenceToStream (see also "Serialization Support").
- Use the XQSequence getObject method, which returns the Java object.
- Use the XQSequence getLexicalValue method.
NOTE: Using any other XQJ method to retrieve a ddtek:javaObject value generates an error.
See also "Resolving Function Calls".
Disabling Java Functions
You can disable the ability to invoke Java functions, for example, for security reasons, by specifying the AllowJavaFunctions property of DDXQDataSource. See "DDXQDataSource and DDXQJDBCConnection Properties".