These are the advanced topics of release 6.0.
The list of advanced topics for release 4.0 and before can be found at
Advanced topics release 4.0.
Copyright © 2007
Rights to the content of this publication
This publication contains proprietary information belonging to
Trademarks
1 Introduction
2 How to program servlets with
2.1 Classpath Issues
2.1.1 Common exceptions from Tomcat
2.2 Session Pooling
3 How to use external (XML-)editors to edit
3.1 Introduction and usage
3.2 Dealing with DTDs, example
3.3 Dealing with DTDs, explained
3.4 Other Caveats
4 XSL(T) Transformations without our interface
4.1 XhiveFormatterIf.formatAsPDF
4.2 XhiveTransformerIf.transform...
5 Replication tutorial
5.1 Replication setup
5.2 Replication application code
This documentation contains several concise topics concerning specific
usages and details of
The information in these articles are supposed to give detailed information for specific circumstances, for more detailed information please refer to the manual. Most of this information was compiled based on questions from customers.
If the information given on these pages seems incomplete, or incorrect, do not hesitate to send an email to support@x-hive.com.
Programming a servlet is not really different from programming other applications. We do provide a sample servlet, which shows how to combine them programmatically. In this article two common issues that often come up with servlets are dealt with; using a session pool and classpath issues.
The information in this section only applies to
Summary of this article:
When possible, place all
tomcatDir/common/lib (and not in WEB-INF/lib of
individual servlets), replacing the Xerces jar with the one
included with
tomcatDir/common/endorsed directory, it is best
to place xercesImpl.jar and xml-apis.jar
and xalan.jar in the common/endorsed
directory instead of the common/lib directory. In fact,
because of a bug in Tomcat (see below) it is obligatory currently to
put xml-apis.jar there, to use the endorsed overriding scheme.
In concurrent situations, it gives a great performance boost to use sessions in read-only mode where possible. Use a session-pool to share sessions between requests in different threads, to limit the number of sessions created. A sample session pool is included with the sample servlet.
Don't use Tomcat 4.0.x, use later versions. Tomcat is a fast evolving program, different versions have different properties, especially in the area of classloading.
The information in this section applies mostly to the Tomcat servlet engine. It is about how to how to set up a classpath, specifically how Tomcat deals with this. It does not explain fully how the Tomcat classloader system works, please see the Tomcat documentation for this.
In Tomcat, like other servlet engines, the proper way to deal with the classpath is to create a
WEB-INF/lib directory for your servlet and copy all the jars to
this directory.
This is what is advised in the servlet specification. It is also where jars are placed if
an application gets unpacked from a .war file. However, there some serious
downsides to placing the
WEB-INF/lib:
The classloader that loads classes for jars located in the WEB-INF/lib
directory has certain special conditions. Specifically, the specification indicates
it may not load classes from certain packages. This can give problems in that it might
cause classes which are available in multiple jars to be loaded partially from one jar
and partially from another jar. In other words, if you place the
WEB-INF/lib of specific servlets, you should ensure not to place the
same or conflicting jars in common/lib.
If you have multiple servlets running within one Tomcat environment. There
will be one classloader per servlet running.
This will lead to multiple drivers being initialized when you connect, which means
that you can only do this if you have an external (dedicated) server running (in simpler terms,
when all the servlets connect to the datbase through an xhive://hostname:portnumer
URL). If you do decide to deploy xhive.jar in multiple directories, you must also
ensure that they all use different session-pools, no objects may be shared between servlets.
The requirement of not having a dedicated server can be important, because as described in the performance chapter of the manual, running without a dedicated (external) server will give a great performance boost to your applications.
If you want Tomcat to automatically reload servlets when they are changed (recompiled), you can turn on 'reloadable=true' on the servlet context in server.xml (consult the Tomcat documentation for this) (or you can also redeploy servlets). However, for each reloading/ redeploying action a new classloader is created, and this will thus has the same issues as described before.
tomcatDir/common/lib.
Jars in this directory are shared between all servlets. The classloader
which loads the classes from this directory does not unload classes, which means
the JNI library will not be loaded multiple times. Here follows a list of jar placement
tips:
When you want to run a servlet using the
lib subdirectory of
your
tomcatDir/common/lib.
If your Tomcat version also has a tomcatDir/common/endorsed directory,
it is best to place the following
common/lib if you are using JDK 1.4: xercesImpl.jar,
xalan.jar and xml-apis.jar. The reason for this is
that putting the jars there will mean they override the XML-classes that come with
JDK 1.4. For Xalan a lot of bug-fixes were applied since the version
that ships default with the JDK. For Xerces it means that the DOM Level 3 interfaces
are used. For almost every serious
Tomcat comes with its own version of Xerces (in xercesImpl.jar or
xerces.jar). You should remove those jars and replace them with ours. The reason
to use our jars instead of Tomcat is that we make more demanding use of those jars (Tomcat
only really uses them to load some configuration files, which will work with just about
any parser implementation).
One thing to make sure of is that the same jar is not duplicated in multiple directories
of Tomcat. Specifically, if you place the
When using Tomcat 4.1.x, download/ use the LE version when possible. The LE editions
of Tomcat only work with JDK 1.4 and ship with fewer jars, specifically xercesImpl.jar
is not included. This means one less jar to worry about, and JDK 1.4 is a requirement
for
Because the classloading issues described can sometimes lead to confusing exceptions and
error messages, we will list some common ones and their causes here. These errors
may be shown in your webbrowser accessing Tomcat, on the console of the running
Tomcat process, or in the file tomcatDir/logs/localhost_log.<date>.txt:
Load of library: oojava failed java.lang.UnsatisfiedLinkError
You are using
java.lang.VerifyError: xmlDecl signature: Illegal use of nonvirtual function call
Means that there is an older version of Xerces somewhere in
the classpath. Be sure to remove the existing xerces.jar from
tomcatDir/common/lib when you copy the
java.lang.ClassNotFoundError: some org.w3.dom class not found
Means that you have not placed our xml-apis.jar in the endorsed directory, and the DOM Level 3 interfaces cannot be loaded (because of a bug in Tomcat).
org.apache.xml.utils.WrappedRuntimeException: The output format must have a '{http://xml.apache.org/xslt}content-handler' property!
This error is usually caused by the Xalan included with JDK 1.4. Make sure xalan.jar
is placed in tomcatDir/common/endorsed (if you run into this error and there
is no such directory, we advise you to upgrade to a newer version of Tomcat).
It should be noted that although the creation of sessions in
In the sample servlet we create a stack of sessions,
from which sessions are taken and returned when needed.
Each time a session is used a connection is made on it
and a transaction is opened. In this way the session
pool can be used in a generic way. You can use this mechanism too
in your applications. As the goal of the
pool is to increase the responsiveness, we describe
three possible ways to increase the reaction speed and explain the merits
of each one. Only the first
tip on read-only sessions can lead to great improvements of
the performance of your system with
When you can determine at the start of a request that that request does not have to make changes in the database (e.g. you can make out from the request-URI that it is a request that will involve an XQuery and stylesheet transformation), you can use a readonly transaction. These have the advantage that A) the request itself does not have to wait for locks of other concurrent transactions and B) that other transactions do not have to wait for locks taken by this transaction. Because of this double effect, the performance gain of using read-only transactions when possible should not be underestimated. When using a pool of sessions, you can still set the readonlymode of the session before each transaction begin.
If you always connect to the same database as
the same user in your application, you can
choose to already connect on each session
when you create the pool (our session pool
has the method connectAs for
this purpose). The overhead of a connect is not
very large though, roughly comparable to the retrieval
of one tiny document through an index.
In previous releases we sometimes recommended to not commit
the transaction for every request, but for
This article describes the unsupported FTP-and DAV services that can be used to handle the documents that reside in the database with FTP or DAV aware programs.
After an introduction, we describe some issues with using these services.
In certain cases you may want to edit XML documents in the database with an external editor. We provide two services for this:
An FTP-server, which is included since
xhive-ant run-ftpserver -Ddbname=<DatabaseName>
Starting with
Windows Network Places. Under the 'Windows Network Places' folder, you can add network places for both FTP-sites and DAV-urls. After you have started one of the above described servers, you can view the contents of the database as a folder, and use drag and drop to place documents in the database. You can even open items from such folders in editors, but unfortunately not save them.
XML Spy can read/write documents on both FTP- and DAV-servers.
XMetaL 3.0 supports WebDAV, but their implementation is incompatible with Apache's WebDAV implementation on which our integration is based and most other WebDAV servers.
XMetaL 4.0's WebDAV support does work okay with our WebDAV server. You can use XMetaL 4 with release 1.5 or later of our WebDAV server.
Ultraedit supports FTP-editing of documents.
MS Word can read and write (binary) files from a DAV-server.
WebDrive is a program with which both FTP- and DAV-servers can be mapped as drive letters. Succes with this tool in combination with the X-Hive/services is mixed, it helps to turn off 'Cache Files' in the program settings.
ftp://localhost/ and for accessing a DAV
server (with standard Tomcat settings) would be http://localhost:8080/xhivedav
(where localhost is the hostname of the machine running the server).
This section and the next are only interesting if you want to use the combination of WebDAV (or FTP) and DTDs.
In some cases, you will want to use DTDs connected to the documents in the database
from the external editor. This is especially the case with an editor like XMetaL,
where a DTD is required to be able to edit the document properly. In
The next section will go into detail about how we solved this problem. However, we will start with a usage example (the sample will use WebDAV, references to XMetaL imply XMetaL 4.0 or better):
Make sure the WebDAV server is properly installed and running.
If you want to use DTDs stored in
http://localhost:8080/xhivedav)
and log on.
You can not store DTDs in any folder, only in folders representing catalogs (in a default
configuration, a folder named xhive-catalog). You can place a DTD through
WebDAV by placing (dragging) a DTD file in that folder.
(If you add a DTD in this way, the name of the file will be set as the public id of
that DTD in the catalog (this is unusual, but it is best if the item has a public id. In the
end the public id as used within
Next, launch your WebDAV aware editor and create a new file. Since you want to use a DTD, you have to reference it in a document type declaration. This is the general format of such a document type declaration:
<!DOCTYPE rootElem PUBLIC "publicId" "systemId">
Set the publicId string to match a public id of a DTD stored
in the catalog of the library where you are going to store the document. If
you loaded up the DTD through WebDAV as explained before, the public id would
be the same as the filename of the DTD (so for instance play.dtd).
An alternative (introduced in our WebDAV support version 1.5) is to set the
systemId to the full URL to the DTD as accessible through
WebDAV. An example of such an id would be
http://localhost:8080/xhivedav/xhive-catalog/play.dtd.
The next step is to save the document to the WebDAV server, which is
an editor dependent action. For instance, for XMLSpy you have to use the menu
File|Save to URL..., and in XMetaL you have to select the
WebDAV folder in the 'Network Places' section and save it there as a normal
file (note that this only works for WebDAV aware editors).
One final thing to note, is that when you open the file again later from the WebDAV server, the document type declaration will be changed to always have this format:
<!DOCTYPE rootElem PUBLIC "X-Hive/DB public id" "xhive-catalog/filename.dtd">
Note:
The information in this section is mostly targeted towards
This section describes how we worked around the problems of using DTDs that are stored within
In
XhiveLibraryIf.getCatalog()).
That catalog holds a list of stored DTDs. Documents may refer to DTDs in this catalog.
This referring is through the the public id in the document type declaration, and the system id
in the document type is not used for this.
When parsing with validation, a DTD will also be stored in the catalog, unless a DTD with the same public-id is already in the catalog (if that is the case, the document is validated against that DTD). You can also store a DTD explicitly (through the Abstract Schema interfaces).
With File-based editors, DTD resolution is done based on the file-location specified in the system id of the document type declaration. This can be a relative path. When you present the database as files and folders, and you want the DTD be found, it must be found as a file in some folder, you cannot simply follow the link made in the database. The remainder of this section describes what solution we have taken to implement this, but that solution can best be described as 'a hack'. It could be that in your specific situation another method could work better, in which case you can change the sources. Also, this section describes the general technique, the actual implementation may be different in different versions of the servers.
When you store a file, it parses the document without validation (because
some documents may not have a DTD specified for it, and then validation is not
possible), which means a DTD is not stored nor is a DTD attached to the document.
However, when there is a doctype definition the DTD is still read, as it may
contain entity definitions. Therefore, if you have a doctype definition in a document
you want to save, or (for WebDAV) you must specify the full location of the DTD as
the system id.
After the XML document is parsed, the newer versions of the servers will try to find
a DTD in the catalog linked to this document based on the public id (or the full system
id if it was found), and if one can be found it will be linked to the document in the
database. This is done with DocumentAS.setActiveASModel.
When you retrieve a document from the database later, and the DTD was linked to the document as described above, the public id and system id of the document type declaration may have to be changed to now point to the local path.
What we have done is made the catalog available as a folder in each library (whether that library
has a local catalog or not). This folder
is virtual, in the sense that it is simply added by the server process to the list of
file- and directory-names. The name is therefore also arbitrary (xhive-catalog),
and can be changed in the
sources. When a document is loaded from the database to a DAV/ FTP client, that
client may use the system id to look up the DTD. To be able to find the DTD
in the database, the system id must then be a relative path, with a format like
xhive-catalog/fileName.dtd. In that case the client will
request the server for a file fileName.dtd in a folder xhive-catalog
relative to the library where the document is found. When the server gets such a request,
it will know to look in the catalog, and send the DTD to the client over the network.
To get the system id to hold a string of the described structure, you can for instance set
it correctly when you first parse the document. When you retrieve the document, the method
XhiveDocumentIf.fixupDoctypeIds is used to set this information correctly
for the server (so the data is changed!).
So in short, with the default way we provide these servers the following is the case:
Each library represented as a folder has a virtual folder to represent the catalog.
When you store new documents using FTP or DAV, the DTD is not stored.
When you store a new document and want to use a DTD, you must set the public id to something that is available in the catalog or pass a full system id. Otherwise, parsing will fail (some versions of the FTP-server try to fix this problem, by attempting to look up the public id based on the relative system id). If the public id matches something in the catalog, the document will also be linked with this DTD.
When receiving documents which have a DTD assigned to them in the database,
they will be looked up in the database by 'normal file resolution' if they
have a system id specified like xhive-catalog/fileName.dtd.
The API-call XhiveDocumentIf.fixupDoctypeIds in
If a choice can be made between FTP and WebDAV for communication, we advise to use FTP (it is a simpler protocol, and the full implementation is in one classfile not depending on third party solutions so is easier to debug).
The WebDAV protocol requires that the 'file'-size of each document is known at all times. The file-size of XML documents can only be determined through serializing the file, so this is currently slow, especially when a directory listing is retrieved (which would show filesizes). Therefor, we employ caching of previously determined file-sizes, and show file-size as a default of 2 KB when we expect file-size is not important.
When you parse documents into the database, or create new libraries from a remote client, they are created/ parsed with some default parameters. You may want to check the source code of these servers to check whether they match what you want (and change them otherwise).
When storing documents on the FTP/ WebDAV-server, new documents are created during parsing
and existing documents are replaced. When using non-live indexes like
Context Conditioned Indexes (and pre-
When documents or DTDs are stored in the database over FTP/ DAV, the client sends the document contents as a stream to the server. This means that if that document contains local references to external entities (like DTDs), local in the sense that they contain relative paths to files on the client system, these will not be found on the server, and thus parsing might fail.
The FTP and DAV servers can store both XML documents and BLOBs. When data comes in, whether it is stored as XML or a BLOB is determined based on the file-extension. You can edit what extensions are seen as XML-files in the sources.
On systems that already have a FTP-server running, you will not be able to run the
port-constant to a number other than 21, and connect to
another port (the hostname could then for instance be localhost:2100).
With DAV, you should not rename folders, as with the current DAV-server implementation
this operation is defined as 'create a new folder, copy all the
documents to this folder, and delete the other folder'.
Instead, use the Adminclient or the FTP-server for this function
(where the command is simply 'setName').
When using the combination WebDrive, XMetaL and DTDs, you will run into
the problem that XMetaL wants to store BLOBs (dtdname.rlx)
in the catalog. We have chosen not to allow this. XMetaL 4's internal WebDAV
connection does work directly with
The XhiveTransformerIf and XhiveFormatterIf
interfaces in com.xhive.util.interfaces are really convenient
access methods to Xalan and FOP of Apache project respectively.
In some cases you may want to use the more advanced options of these packages.
This is why we give you our implementation of these interfaces here,
to give you a starting point on how to use the interfaces of those
packages.
import org.apache.fop.apps.Driver;
import org.apache.fop.apps.Version;
public void formatAsPDFToStream(Document foSource, OutputStream os) throws XhiveException {
try {
Driver driver = new Driver();
driver.setRenderer("org.apache.fop.render.pdf.PDFRenderer", Version.getVersion());
driver.addElementMapping("org.apache.fop.fo.StandardElementMapping");
driver.addElementMapping("org.apache.fop.svg.SVGElementMapping");
driver.addPropertyList("org.apache.fop.fo.StandardPropertyListMapping");
driver.addPropertyList("org.apache.fop.svg.SVGPropertyListMapping");
driver.setOutputStream(os);
driver.buildFOTree(foSource);
driver.format();
driver.render();
}
catch(Exception e) {
throw new XhiveException(XhiveException.FORMAT_EXCEPTION, e);
}
}
The sample code above is for a quite an old version (0.20) of FOP, shipped with
import org.apache.avalon.framework.logger.ConsoleLogger;
import org.apache.avalon.framework.logger.Logger;
import org.apache.fop.apps.Driver;
import org.apache.fop.messaging.MessageHandler;
public static void formatAsPDFToStream(DOMImplementation docCreator, Document xmlSource, Document xslSource, OutputStream os)
throws XhiveException {
try {
// XSLT part
XhiveTransformerIf transformer = XhiveDriverFactory.getDriver().getTransformer();
Document foDocument = transformer.transformToDocument(docCreator, xmlSource, xslSource);
// FOP part
Driver driver = new Driver();
Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_ERROR);
MessageHandler.setScreenLogger(logger);
driver.setLogger(logger);
driver.setRenderer(Driver.RENDER_PDF);
driver.setOutputStream(os);
driver.render(foDocument);
} catch (Exception e) {
//e.printStackTrace();
throw new XhiveException(XhiveException.FORMAT_EXCEPTION, e);
}
}
import com.xhive.core.interfaces.XhiveSessionIf;
import com.xhive.error.XhiveException;
import com.xhive.dom.*;
import org.w3c.dom.*;
import javax.xml.transform.*;
import java.io.*;
import java.util.Iterator;
public Document transformToDocument(DOMImplementation docCreator, Node xmlSource, Document xslSource)
throws XhiveException {
Document result = docCreator.createDocument("", "Result", null);
Node rootElem = result.getDocumentElement();
result.removeChild(rootElem);
transform(xmlSource, xslSource, new DOMResult(result));
return result;
}
public void transformToStream(Node xmlSource, Document xslSource, Writer writer)
throws XhiveException {
transform(xmlSource, xslSource, new StreamResult(writer));
}
public String transformToString(Node xmlSource, Document xslSource)
throws XhiveException {
StringWriter result = new StringWriter();
transformToStream(xmlSource, xslSource, result);
return result.toString();
}
private void transform(Node xmlSource, Document xslSource, Result result) {
try {
// Try to initialize URI resolver
URIResolver myResolver;
try {
XhiveSessionIf session = ...; // Left as an exercise to the reader
if (session != null) {
myResolver = new XhiveURIResolver(session);
} else {
myResolver = null;
}
} catch (Exception e) {
// (No session?) Fine, then we don't use a URIResolver
myResolver = null;
}
TransformerFactory tFactory = TransformerFactory.newInstance();
if (myResolver != null) {
// Factory resolver must be set before transformer is created
tFactory.setURIResolver(myResolver);
}
Transformer transformer = tFactory.newTransformer(new DOMSource(xslSource));
if (myResolver != null) {
transformer.setURIResolver(myResolver);
}
transformer.transform(new DOMSource(xmlSource), result);
} catch (Exception e) {
throw new XhiveException(XhiveException.TRANSFORM_EXCEPTION, e);
}
}
/**
* URI resolver that translates
* xhive:path#query
* into an xquery run on path, e.g.
* xhive:/plays#//TITLE
*/
private class XhiveURIResolver implements URIResolver {
private static final String XHIVE_PREFIX = "xhive:";
private static final String SEPARATOR = "#";
private XhiveSessionIf session;
public XhiveURIResolver(XhiveSessionIf session) {
this.session = session;
}
public Source resolve(String href, String base) throws TransformerException {
// Do we need to do anything?
if (((base == null) || (!base.startsWith(XHIVE_PREFIX))) &&
(!href.startsWith(XHIVE_PREFIX))) {
return null;
} else {
// Process href
// Up to us to come up with a result
if (!href.startsWith(XHIVE_PREFIX)) {
// Create href with base
href = base + href;
}
// Strip xhive: from href
href = href.substring(XHIVE_PREFIX.length());
if (!href.startsWith("/")) {
href = "/" + href;
}
// Separate in path and query
String path = null;
String query = null;
if (href.indexOf(SEPARATOR) != -1) {
path = href.substring(0, href.indexOf(SEPARATOR));
query = href.substring(href.indexOf(SEPARATOR) + 1);
} else {
path = href;
}
// Get query context
XhiveLibraryChildIf contextNode = session.getDatabase().getRoot().getByPath(path);
if (contextNode == null) {
// Nothing found, error or null?
throw new TransformerException("XhiveXalanTransfomer: Could not resolve " + href);
//return null;
} else {
if (query == null) {
if (contextNode instanceof XhiveDocumentIf) {
return new DOMSource(contextNode);
} else if (contextNode instanceof XhiveBlobNodeIf) {
return new StreamSource(((XhiveBlobNodeIf) contextNode).getContents());
} else {
throw new TransformerException("XhiveXalanTransfomer: " + href + " is not a document");
}
} else {
return getExecuteQuerySource(contextNode, query, href);
}
}
}
}
private Source getExecuteQuerySource(XhiveLibraryChildIf contextNode, String query, String href) throws TransformerException {
if (query.startsWith("xpointer(") && query.endsWith(")")) {
query = query.substring("xpointer(".length(), query.length() - 1);
}
Iterator queryResult = null;
try {
queryResult = contextNode.executeXQuery(query);
} catch (XhiveException e) {
throw new TransformerException("XhiveXalanTransformer: Problem with query " + href + ": " + e.getMessage(), e);
}
// We will only use the first result here (otherwise we would have to include a new top-element)
if (queryResult.hasNext()) {
XhiveXQueryValueIf queryValue = (XhiveXQueryValueIf) queryResult.next();
// Is it a node?
try {
return new DOMSource(queryValue.asNode());
} catch (XhiveException e) {
// must be XQUERY_ERROR_VALUE, so interpret it as a string
return new StreamSource(new StringReader(queryValue.asString()));
}
} else {
throw new TransformerException("XhiveXalanTransformer: Query " + href + ": " + " has no results");
}
}
}
For improved Xalan XSLT performance, it may be wise to create Templates
objects using the newTemplates call on TransformerFactory.
What follows is an example of this, see the Xalan documentation for details.
TransformerFactory tFactory = TransformerFactory.newInstance(); tFactory. translet = tFactory.newTemplates(new DOMSource(xslSource)); // Now keep this translet cached somewhere, and for transforming do: Transformer transformer = translet.newTransformer(); transformer.transform( new DOMSource(xmlSource), new StreamResult(writer)) ) ;
This article uses the replication information given in the manual to give an example on how to set up a simple replicating system based on an existing single server application. In this example use case the specific goal of the replicating is to improve performance by duplicating the application (+ federation) on different hosts.
This article only applies to
The first step is to prepare the databases for replication. We create a temporary segment in each database, so that temporary results can be created in the replica-databases. This can be done in the adminclient, by connecting to a database and:
// Creating a temporary segment in Java code
session.getDatabase().createTemporaryDataSegment("tempSegmentName", null, 0);
session.getDatabase().setTemporaryDataSegment("tempSegmentName");
Next, the actual replica is made, assuming the master server is running a page server accessible from
other hosts, you can run the following command on the replica host (assuming
XHCreateReplica -debug -federation xhive://masterhost:1235 -replicaid newOrExistingReplicaId
-replicabootstrappath /path/to/new/replicadata/XhiveDatabase.bootstrap SuperUserPasswordXhiveFederationIf.registerReplicator(...)
and XhiveFederationIf.replicateFully(...) API calls).
This will register the replica in the master federation, and copy the complete federation over
to the replica host. You can now connect to the federation copy on the replica host, although it
is not wise to do so directly (since it should only be accessed as a replica). If something would
happen to your replica for whatever reason, you can always remove the files on the replica host
and issue this command again.
On the master host, you can access the federation in the normal way, as long as you make sure it acts as a page server to the replicating host. So assuming that for performance you access the database files directly the driver initialization code in your application would be:
XhiveDriverIf driver = XhiveDriverFactory.getDriver("/path/to/XhiveDatabase.bootstrap");
driver.init(numCachePages);
driver.startListenerThread(new ServerSocket(1235));
The driver configuration code on the replica host depends on the application. If you only
want to run read-only transactions on your replica federation only an extra call to
XhiveDriverIf.configureReplicator(...) is needed. However, for our example we
want the replica host to act as a full copy of the application, including allowing
for read-write transactions. Since
In code, this means you have to create two driver objects, one that acts on the replica and one that connects to the master-server directly:
// On replica-host, use two drivers
XhiveDriverIf masterDriver = XhiveDriverFactory.getDriver("xhive://masterhost:1235");
masterDriver.init(numCachePages / 2);
XhiveDriverIf replicaDriver = XhiveDriverFactory.getDriver("/path/to/replicadir/XhiveDatabase.bootstrap");
driver.configureReplicator("xhive://masterhost:1235", "previouslySetupId");
replicaDriver.init(numCachePages / 2);
In your session pooling code, you decide which driver to get the session from based on whether
it is a readonly transaction or not. For performance, it is advisable to have your session pool code
(and the code that uses the session pool) already make a distinction between readonly and readwrite
transactions (using XhiveSessionIf.setReadOnlyMode(...)) for concurrency performance. Once
you have that, this could be the getSession code:
synchronized XhiveSessionIf getSession(boolean readOnlyTransaction) {
if (readOnlyTransaction) {
return waitForUpdates(replicaDriver.createSession());
} else {
return masterDriver.createSession();
}
}waitForUpdates will be explained shortly, the createSession call
should be replaced by code that pools the sessions. Since there are two drivers you also need to
set up two collections of sessions.
With the setup above you will be able to perform both readonly and update transactions, with
the readonly transactions having improved performance since they use the replica. However,
XhiveSessionIf.TimeStamp currentWaitTimeStamp = null;
synchronized returnSession(XhiveSessionIf session) {
// Regular session pool code
....
if (session.getDriver().equals(masterDriver)) {
currentWaitTimeStamp = session.getUpdateTimeStamp();
}
}getSession call a routine that waits for the updates to be available
on the replica before continuing:
XhiveSessionIf waitForUpdates(XhiveSessionIf session) {
if (currentWaitTimeStamp != null) {
session.waitForTimeStamp(currentWaitTimeStamp);
currentWaitTimeStamp = null;
}
return session;
}XhiveSessionIf.waitForTimeStamp(...) should complete immediately or quickly).