Advanced k42 Developer Guide
This document provides an introduction to developing k42 applications in Java. The complete reference to the API may be found here. The major aspects of k42 are introduced gradually using simple examples. The approach to topicmap development and manipulation is shown.
The K42_HOME/docs/files directory contains a Java file with the code used in this guide, and an XTM compliant file containing the topicmap produced.
Getting Started
See the Installation Guide for information on installing and setting up the k42 application server.
To begin developing a k42 client application you will need to use the com.empolis.topicmaps.client.K42Client class to obtain a handle to the topicmap.
K42Client
Using an instance of this class will make it easy to get going, the code below shows a Java source file that hooks into the k42 system.
import com.empolis.topicmaps.client.K42Client; 
import com.empolis.topicmaps.ik42.*; 
import java.io.*; 

class K42Developer { 
	public static void main(String[] args) { 
		K42Client client = new K42Client("C:\\k42\\guide.props"); 
		
		// new code to follow here
	}
}
Ensure that the property file, referred to, exists and is available on your system. This file can be used to store a number of properties, all of which have default values. The first time you run the program, you will find a new file named base.ps, created in the same directory as the property file. The property file reference may be found in Appendix A of this document.
ITopicMap
The top-level interface in a k42 system is thecom.empolis.topicmaps.ik42.ITopicMap. The K42Clientmakes it easy to retrieve a ITopicMap object:
ITopicMap tm = client.getTopicMap();
// To clear any existing topicmap data call tm.clear();
tm.clear();
The ITopicMap object will enable you to create various topicmap constructs, such as: topics, topic associations, names, and occurrences.
As an example: subject, music, and specifically The Beatles, will be used throughout this document. Start by creating a topic to represent the group within the topicmap.
ITopic theBeatles = tm.createTopic(); 
theBeatles.addName("The Beatles");
This shows the use of the ITopicMap interface to create a topic, then the ITopic interface to add a name to that topic.
What Can You Do Now?
Now is a good time to get familiar with the k42 iterators. Many methods of the IK42 API return typed iterators. That is, the standard Javajava.util.Iterator class is not returned (although all k42 iterators extend this class), and the requirement to cast object types is significantly reduced.
The code below shows two typed iterators in use:
ITopicIterator topics = tm.getTopics(); 
while (topics.hasNext()) {
	INameIterator names = topics.nextTopic().getNames(); 
	while (names.hasNext()) {
		System.out.println("Topic found with name : " + names.nextName().getValue()); 
	}
}
You can also retrieve your named topic from the topicmap directly:
ITopic topic = tm.getTopicByName("The Beatles");
Topic Classes
With a k42 topicmap, you can create connections between the k42 objects. There are many different types of associations within topicmaps. One of the predefined association types is the instanceof association. This used to enable object oriented techniques to be applied within topicmaps. In the example below, The Beatles topic is to be made an instance of the topic class:Group.
ITopic group = tm.createTopic();
group.addName("class:Group");
theBeatles.addInstanceOf(group); 
An object can be an instance of more than one class. More to the point, any topic can be referred to as a class. A useful convention to follow, is to use a name like "class:xxxx" to identify topics that are only be to used as classes, or to make all topics used as classes instances of a topic called "Class". Also, to avoid confusion, it is useful to restrict topics to be instances of single classes.
Using this technique further, class topics can be added to help organise the topicmap.
ITopic class_human = tm.createTopic(); 
class_human.addName("class:Human");
ITopic class_record = tm.createTopic(); 
class_record.addName("class:Record");
ITopic class_song = tm.createTopic();
class_song.addName("class:Song");
More Topics
More relevant topics may now be added to the map:
// The group members
ITopic john = tm.createTopic(); 
john.addName("John Lennon"); 
john.addInstanceOf(class_human); 

ITopic paul = tm.createTopic();
paul.addName("Paul McCartney"); 
paul.addInstanceOf(class_human); 

ITopic george = tm.createTopic(); 
george.addName("George Harrison");
george.addInstanceOf(class_human); 

ITopic ringo = tm.createTopic();
ringo.addName("Ringo Star"); 
ringo.addName("Richard Starkey");
ringo.addInstanceOf(class_human); 

// A couple of albums
ITopic letItBe = tm.createTopic(); 
letItBe.addName("Let It Be");
letItBe.addInstanceOf(class_record); 

ITopic abbeyRoad = tm.createTopic();
abbeyRoad.addName("Abbey Road");
abbeyRoad.addInstanceOf(class_record);
Class Hierarchy
Topics that act as classes can have superclass topics to further support the object model, by making use of theaddSuperClass method on the ITopic interface.
ITopic class_media = tm.createTopic(); 
class_media.addName("class:Media");

ITopic class_mammal = tm.createTopic(); 
class_mammal.addName("class:Mammal");

ITopic class_primate = tm.createTopic();
class_primate.addName("class:Primate");
class_primate.addSuperClass(class_mammal);
class_human.addSuperClass(class_primate);
class_record.addSuperClass(class_media);
Querying class topics
Class topics can be used to find groups of topics that are instances of that class.
ITopicIterator humans = class_human.getInstances(); 
ITopicIterator records = class_record.getInstances(); 
ITopicIterator classes = class_class.getInstances();
You can also investigate the superclass/subclass structures usinggetSuperClasses() andgetSubClasses().
Topic Associations
ITopicAssociation instances are the k42 objects that link topics together. In a topic association, a number of topics each play a role within an association instance. For example, a "Loves" association, would have roles for "Lover" and "Loved One". A role is also a topic.
k42 simplifies the association creation mechanisms to an extent through the support of association templates, and these are used in the code examples below. The explicit requirement for role topics also encourages consideration of the differences between roles and classes.
ITopic bandMember = tm.createTopic(); 
bandMember.addName("role:Band Member");
ITopic band = tm.createTopic(); 
band.addName("role:Band"); 
ITopicAssociation bandMembership = tm.createAssociationTemplate(bandMember, class_human, band, class_group); 
bandMembership.addName("Band Membership");
That is all that is necessary to create an association template. It is useful to be able to read the association information and present the data in an intuitive way. It is now possible to create association instances and retrieve data.
bandMembership.creat
"Paul McCartney" plays the role "role:Band Member" in association "Band Membership" with "The Beatles" playing the role "role:Band".
Whereas what you would really like to say, in addition to the above, is:
Paul McCartney is a member of The Beatles.
So, what can you do to get that kind of data from the association?
Names in Scopes
In a topicmap, the names of topics, and topic associations can be given a scope. A scope is defined by a set of topics. In k42, this set of topics is given an identity, and is called a scopeset. ITopicMapprovides a method to retrieve a IScopeSet instance when passed an array of topics.
Scopesets may be used to define the arc labels between two roles within the association template:
IScopeSet ss1 = tm.getScopeSet(new ITopic[] {bandMembership, band});
bandMember.addName("has member", ss1); 
IScopeSet ss2 = tm.getScopeSet(new ITopic[] {bandMembership, bandMember}); 
band.addName("is a member of", ss2);
As this process is consistent for creating all association templates, utility methods have been provided for use in the com.empolis.topicmaps.helper.Builder class, specifically the createAssociationTemplatemethod. You can now use this method to create another template:
ITopic role_recordMaker = tm.createTopic();
role_recordMaker.addName("role:Record Maker"); 
ITopic role_madeRecord = tm.createTopic(); 
role_madeRecord.addName("role:Made Record");
ITopicAssociation makesRecord = builder.createAssociationTemplate(
       "Makes Record", class_group, role_recordMaker, "makes", class_record, role_madeRecord, "made by");
Using these templates, association instances can be created for the primary topics:
bandMembership.createInstance(band, theBeatles, bandMember, john);
bandMembership.createInstance(band, theBeatles, bandMember, paul);
bandMembership.createInstance(band, theBeatles, bandMember, george);
bandMembership.createInstance(band, theBeatles, bandMember, ringo);
makesRecord.createInstance(role_recordMaker, theBeatles, role_madeRecord, letItBe); 
makesRecord.createInstance(role_recordMaker, theBeatles, role_madeRecord, abbeyRoad);
In order to retrieve the arc labels that have been defined for the roles, theIAssociationArc interface provides access to the arc string and the target Topic. IAssociationArcs are retrieved fromIAssociationEnd objects, contained within ITopicAssociation objects. Consider the following code:
IAssociationEnd end = bandMembership.getEnd(bandMember);
IAssociationArcIterator templateArcs = end.getArcs();

IAssociationArcIterator instanceArcs = theBeatles.getAssociationEnds().getAssociationArcs();
where instanceArcs represents all the arc objects that connect theBeatles topic to other topics.
Generic Roles
The specification of the role topic can sometimes appear difficult. Although, well chosen role topics can add significantly to the semantic content of the topicmap, a simpler pattern is available for the majority of associations, and that is to specify more generic topics for the roles:
ITopic role_agent = tm.createTopic(); 
role_agent.addName("role:Agent"); 
ITopic role_object = tm.createTopic();
role_object.addName("role:Object");
The two templates above could then be created as follows:
ITopicAssociation bandMembership = createTemplate(tm, "Band Membership", class_group, role_agent, "has member", class_record, role_object, "is a member of"); 
ITopicAssociation makesRecord = createTemplate(tm, "Makes Record", class_group, role_agent, "makes", class_record, role_object, "made by");
This example indicates a problem; some associations fit more comfortably into agent-object roles than others. The distinction between the roles in any association, should always be clear.
Examining Associations
There are many methods within k42 that allow you to examine associations. Possible calls are:
ITopicAssociationIterator templates = tm.getAssociationTemplates();
ITopicAssociation template = tm.getAssociationByName("Band Membership");
ITopicAssociationIterator assocs = template.getTemplateChildren(); 
assocs = theBeatles.getAssociations(); 
ITopicIterator topics = theBeatles.getAssociatedTopics();
Topic Occurrences
In k42 a resource, as specified by a URI, is represented by a topic. These resources can be created explicitly or implicitly, as shown in the following code:
ITopic resource = tm.createResource("http://www.theBeatles.com");
ITopicOccurrence occur1 = theBeatles.createOccurrence(resource);
ITopicOccurrence occur2 = john.createOccurrence("http://www.theBeatles.com");
if (resource == occur2.getResource()) { 
	System.out.println("The resource has identity!"); 
}
ITopicOccurrence occur3 = ringo.createOccurrence("http://wwww.theBeatles.com/ringo.html");
What Next?
A good approach is to experiment with the k42 API. Not all k42 concepts have been introduced in this document, but you have seen enough to be able to get started and produce useful topicmaps.
The k42 Concepts Guide and k42 API Documentation should now become your main reference documents.
Appendix A: Property File Reference
Property NameDefaultValues
empolis.persistentstore base.ps
The name of the file on the local filesystem that k42 will use to store topicmap data.
empolis.serverdispatchernameNone
The name that k42 remote clients must use to contact the k42 server. The full RMI URL to the server is as follows:rmi:// <k42 host IP/FQDN> /<value of empolis.serverdispatchername >
empolis.plugin.<plugin name>.class None
The name of the class that launches a particular plug-in. This class must be a subclass of thecom.empolis.topicmaps.k42.k42PlugIn class. If a plug-in name has been specified, then this property must be present.
Note that plugins are only processed on the initialization of an RMI service.
empolis.plugin.<plugin name>.<plugin property>None
A naming convention that allows custom properties for the plugin indicated.
empolis.rmiserverNone
The name that RMI remote clients must use to contact the k42 server, as previously named with RMIempolis.serverdispatchername property for the RMI server.
empolis.k42.mapname default
The name given to the k42 topicmap object. A single empolis object store can contain more than one k42 topicmap object.
empolis.ssl.keystorepassphraseNone This is the only mandatory property required to enable SSL support in k42. For information on installing and deploying SSL support, please refer to the Enabling SSL document.
empolis.ssl.keymanagerfactory SunX509The key manager factory to use, which governs how keys and certificates are managed within the store.
empolis.ssl.keystoretype JKSThe format of the keystore for keystores set up using the keytool utility.
empolis.ssl.keystorefilename ${user.home}/.keystoreSpecifies the full path of the keystore to use. The value of the user.homeJava environment variable may be discovered using the method callSystem.getProperty("user.home");.
empolis.ssl.protocol SSLSpecifies the SSL protocol to use.
empolis.ssl.keypassphraseValue of keystorepassphraseThis is the passphrase required to extract the key, used by the application to identify itself.
The property file can be used to override defaults for a number of purposes; the syntax used, is for a standard Java property file. When processing the property file, the K42Client will first look for a specified RMI server; if one is found, then an attempt is made to connect to the server. The Installation Guide gives information on how to setup an RMI Server. If no server is specified, the K42Client will attempt to initialise a local environment, using the specified storage file, if present. The K42Client will then retrieve the named topicmap object, using a default name, if none is specified.
Appendix B: Plugins
Plugins enable instances of classes derived from thecom.empolis.topicmaps.k42.K42PlugIn class to be loaded on start-up of a k42 RMI Server VM. Each K42PlugIn must have a default constructor to support instance creation from the provided classname, and must provide an implementation for the following method:
void initialize(ITopicMap tm, Properties properties)
A plugin could be used for any number of reasons. For example: a plugin could carry out some validity check, or ensure some minimum data initialisation.
In k42, plugins are used to implement services for TopicMap Viewer and WebAuthor. The technique used here is for the plugins to provide custom socket interfaces directly into the server VM, avoiding the overhead of repeated communication over RMI.
Appendix C: RMI Clients
This is simply a case of defining the correct properties in the property file. All that is necessary is to provide a value for theempolis.rmiserver property. The RMI client will start-up and try to connect to the named RMI server.
Appendix D: TopicMap Query Language (TMQL) User Guide
TMQL is a querying mechanism for topicmaps. It allows the user to select single or multiple topics, occurrences, associations, roles etc. based on an SQL-like syntax.
Using TMQL
TMQL can be used through the k42 API for topicmaps. In order to use TMQL in this way, you need to import the QueryMgr class:
import com.empolis.topicmaps.tmql.QueryMgr;
You can then create an instance of the QueryMgr class which can be used to execute queries. The constructor for the QueryMgr class must be passed anITopicMap instance which is the topicmap to make queries on.
// tm is an ITopicMap instance of the topic map to make queries on
QueryMgr tmqlParser = new QueryMgr(tm);
The QueryMgr can then be used to execute queries. The results of queries are stored in an IQueryResult object. The IQueryResult class is in the same package as QueryMgr.
There are various ways that a QueryMgr can be used to execute TMQL queries. They all return IQueryResult objects. The main method takes a string parameter which is the TMQL query. For example: IQueryResult res = tmqlParser.executeQuery("SELECT topic x WHERE x in assoc");
The QueryMgr object can also hold the current query string in a StringBuffer. This means that you can build up the query before executing it. To execute the currently stored query, the executeQuery method of QueryMgr is called without arguments. For example:
tmqlParser.setCurrentQuery("SELECT topic x WHERE x in assoc");
tmqlParser.getCurrentQuery().append(" AND x named 'myTopic'"); 
IQueryResult res = tmqlParser.executeQuery();
It is also possible to execute queries using the results of previous queries. If, for example, we wanted to find all of the topics with a particular name and then find the subset of those topics that were in associations, we could do the following:
IQueryResult res1 = tmql.executeQuery("SELECT topic x WHERE x named name is 'myName'"); 
IQueryResult res2 = tmql.executeQuery("SELECT topic x WHERE x in assoc", res1);
By passing the results of the first query into the second, we are telling the TMQL query manager to only use values of x that are in the first query, rather than all possible topics. It is in effect a way of ANDing the two queries.