User Guide

2  Configuration

Jofti can be used in a number of ways. The following sections detail the configuration options available.

The currently supported structures and caches are:

  • Map type collections (non-name spaced)
  • Tangosol Coherence v3.1 and above (non-name spaced)
  • EHCache v0.7 and above (non-name spaced) (The recommended version is 1.2 and above due to the expiry callback absence in earlier versions)
  • OSCache v2.1 and above (non-name spaced)
  • JBossCache v1.2.4 and above (name spaced)

The two main usages of Jofti are: as a listener for a Cache or a wrapper for a Cache.

  • The Listener model.
    • This allows the continued usage of your Cache implementation using its own APIs, and the index is updated using event callbacks from the Cache. This is currently available for Coherence, OSCache and EHCache (only version 1.2 and above).
  • The wrapper model.
    • In this configuration, Jofti's own APIs act as an abstraction to the cache implementation allowing the use of different cache implementations without changing the calling code. This is currently available for all supported Cache implementations except Coherence. which is only currently supported in a listener configuration.

2.1  Built-in Classes

Jofti ships with a number of built-in Classes that it will index without configuration. Without setting any class mappings the adapters will only index these types. These are:

  • java.lang.String.class
  • java.lang.Integer.class
  • java.lang.Double.class
  • java.lang.Float.class
  • java.lang.Long.class
  • java.lang.Short.class
  • java.lang.Byte.class
  • java.lang.Character.class
  • java.math.BigInteger.class
  • java.math.BigDecimal.class
  • java.util.Date.class
  • java.sql.Timestamp.class
  • java.net.URI.class
The primitive classes:
  • int
  • long
  • float
  • double
  • short
  • char
And the single dimension array for each of these classes.
  • java.lang.String[].class
  • java.lang.Integer[].class
  • java.lang.Double[].class
  • java.lang.Float[].class
  • java.lang.Long[].class
  • java.lang.Short[].class
  • java.lang.Byte[].class
  • java.lang.Character[].class
  • java.math.BigInteger[].class
  • java.math.BigDecimal[].class
  • java.util.Date[].class
  • Timestamp[].class
  • java.net.URI[].class
  • ComparableBoolean[].class
  • java.lang.Boolean[].class
  • int[].class
  • long[].class
  • short[].class
  • byte[].class
  • boolean[].class
  • char[].class
  • float[].class
  • double[].class

2.2  Configuring Jofti as a Cache listener

The listener model is perhaps the simplest way to use Jofti, as it requires no configuration file to instantiate the Index. In fact you cannot pre-configure a listener adapter using the Jofti config mechanism, as this type of adapter does not attempt to instantiate a Cache instance and therefore there is no way to set the Cache implementation at configuration time. This must be done programmatically. It is also currently the only supported mechanism for Tangosol Coherence.

If you just want a simple way to add an index to a Cache that is already used in your code, or for simple usage with no or very little change to the existing code, the listener adapters are the easiest and best way to deal with this.

To use an Index in this manner, you must first create an instance of the IndexManager, with which you can then create indexes as listeners for a Cache.

This can be demonstrated in the following code fragment.


IndexManager manager = new IndexManagerImpl();
manager.init();

The code above creates an instance of the IndexManager. As you can see, it is not a singleton and does not have to be used as if it were one (although you can make it a singleton in your code if you wish).

The init method is required to tell the manager that it should load its configuration file (in this case there is no config file) and initialise itself.

You should see a logging message similar to the message below.


09:39:28,599 INFO  No config file configured - not loading any caches

As a Cache was not configured at initialisation, it is necessary to add a cache to the manager programmatically.

For listener addapters this is done via the addIndex method on the manager.

For example, to add a listener for EHCache to the manager we would write the following:


// assume we have an already existing EHCache Manager instance
net.sf.ehcache.Cache cache = ehManager.getCache("default");

DefaultIndexConfig config = new DefaultIndexConfig();
config.setCacheAdapter("com.jofti.cache.adapter.EhCacheListenerAdapter");

Index index =  manager.addIndex(config,cache);

You should see logging messages similar to the message below.


10:10:47,713 INFO loading cache 'default' with adapter
com.jofti.cache.adapter.EhCacheListenerAdapter
10:10:47,835 INFO Configuring cache 'default'
10:10:48,014 INFO Starting cache 'default'
10:10:48,049 INFO Added cache 'default' to loaded caches

From the above message we can see that a EhCacheListenerAdapter has been added to the manager with the name 'default'.

This is the default setting in the DefaultConfig object. The DefaultConfig object has a series of attributes that are used by the manager to work out what adapter to use, and what type of class parser and index type is to be used.
The settings are:

  • name: default
  • adapter: com.jofti.cache.adapter.MapAdapter
  • parserType: com.jofti.introspect.JavaBeanClassIntrospector
  • indexType: com.jofti.tree.TreeIndex
  • ClassMappings: [ ]

From the above settings it should be obvious that the adapter attribute was set to the EhCacheListenerAdapter, over-riding the default Map adapter.
The config object can also supply a Map of classes and attributes that the index should take notice of when added to the cache in the classMappings attribute.

The listener adaptors available in Jofti are:

  • com.jofti.cache.adapter.CoherenceListenerAdapter
  • com.jofti.cache.adapter.EhCacheListenerAdapter
  • com.jofti.cache.adapter.OSCacheListenerAdapter

We shall not go into the parser and index type yet (see later sections for details).

In addition, without configuration the Index will only pick up data types that match the built in Class types. The built in indexing only applies to top level objects (those that are not properties of another class). So the adapter will automatically index a String value put under its own key, but not a String attribute of another class unless it has additional configuration instructing it to do so (see section adding classes to an index without pre-configuration for details).

It is worth noting at this point that each Cache implementation is required to be set up in a different way and if the caches are added in this manner it is the responsibility of the cache creator to correctly configure and initialise the caches before creating an index with them. Please refer to the individual adapter's javadocs for further information.

The call to addIndex() results in a listener being registered with the Cache implementation. To add an entry to the index you can just carry on using the Cache as per normal and the listener callbacks will populate the Index.

The initial examples deal with the built in object types as top level objects, indexing attributes will be discussed later. To add a value to the index we would write the following:


//Element is a wrapper required by EHCache only
cache.put(new Element("key", "test"));

This puts a String value 'test' under a key called 'key'. The adapter will transparently add the value to the Index.

To remove a value from the index we would write the following:


cache.remove("key");

This removes any object with the key 'key' from the index. The adapter will transparently remove the value from the Index.

The Index does not require that objects placed in the Cache are of the same type. So it is fine to do the following:


cache.put(new Element("testString", "test"));
cache.put(new Element("testInteger", new Integer(20)));
cache.put(new Element("testInteger", new Integer(20)));
cache.put(new Element("testDate", new Date()));
cache.put(new Element("testDate", new Date()));

Unlike the native Cache, the Index allows retrieval of objects by data elements other than the key. To retrieve information from the cache without using the key you must use a Query object.

A number of Query convenience classes come with Jofti for simple queries, or, alternatively you can use either an EJB3 QL based language or a general SQL-like query language for more complex queries (this is discussed in the Querying section).

To retrieve a simple object classes we have placed in the index, using the convenience query, we would write something like:


// assume we have added an index using the addIndex() method
cache.put(new Element("testInteger", new Integer(20)));

Map results = index.query(new MatchQuery(new Integer(20));

The query above attempts to match the exact property and returns a map of results, where the key in the results is the key in the Cache and the value is the value for that key in the Cache.

So, as a slightly more complex example we can enter a few values and retrieve a subset.


cache.put(new Element("testInteger1", new Integer(20)));
cache.put(new Element("testInteger2", new Integer(21)));
cache.put(new Element("testInteger3", new Integer(22)));
cache.put(new Element("testInteger4", new Integer(23)));
cache.put(new Element("testInteger5", new Integer(24)));

Map results = index.query(new MatchLargerQuery(new Integer(22),true));

This query convenience class is used to return any values larger than Integer(22), the boolean tells the query to be inclusive of the value we have passed in (this is equivalent to >=). In this case 3 entries are returned in the results.

It is worth pointing out at this stage that because we are only dealing with the built in types we do not need to tell the Index what the field name is. It uses the type of the object passed in to work out which entries it should examine.

So for example if we repeated the above code but added some String elements, these would be ignored in the search below:


cache.put(new Element("testInteger1", new Integer(20)))
cache.put(new Element("testInteger2", new Integer(21)));
cache.put(new Element("testInteger3", new Integer(22)));
cache.put(new Element("testInteger4", new Integer(23)));
cache.put(new Element("testInteger5", new Integer(24)));
cache.put(new Element("testString1", "22"));
cache.put(new Element("testString1", "23"));
cache.put(new Element("testString1", "24"));

Map col = index.query(new MatchLargerQuery(new Integer(22),true));

The result map would still contain 3 items as the Strings would be excluded from the search.

The query convenience classes available for the Index interface are:

  • com.jofti.query.MatchQuery
  • com.jofti.query.MatchLargerQuery
  • com.jofti.query.MatchNotQuery
  • com.jofti.query.MatchSmallerQuery
  • com.jofti.query.MatchRangeQuery
  • com.jofti.query.MatchInQuery
See the class javadocs for more details on usage.

2.3  Adding Classes to Index with No Configuration

Until now the examples have dealt with simple built in classes. In order to enable other types to be indexed we need to tell the Index which classes and fields are to be included. For listener adapters, this is done by passing in an xml definition of the classes and fields or setting the values programmatically into the Configuration object.

xml definition

DefaultindexConfig config = new DefaultIndexConfig();
config.setCacheAdapter("com.jofti.cache.adapter.EhCacheListenerAdapter");

Index index = manager.addIndex(config,cache,
    "class-definitions.xml");

The class-definitions file has the following structure:

<classes>
    <class name="com.test.SimpleTestClass">
        <property>privateField</property>
    </class>
    <class name="com.test.NestedTestClass">
        <property>contained.containedField</property>
    </class>   
</classes>

 

programmatic definition
To achieve the same with the configuration object we would write:

DefaultIndexConfig config = new DefaultIndexConfig();

List simpleProperties = new ArrayList();
simpleProperties.add("privateField");
config.addMapping("com.test.SimpleTestClass", 
	simpleProperties);


List containedProperties = new ArrayList();
containedProperties.add("contained.containedField");
config.addMapping("com.test.NestedTestClass",
	containedProperties);


Index index = manager.addIndex(config,cache);

Whichever method you use, the property attribute in the class definition is a property name that must match the JavaBeans conventions. i.e. it must have a getter method - so for the property privateField there must be a method called getPrivateField() on the class.

Additionally, the type of the property must implement Comparable.

Fields nested within classes that are themselves a property can be referred to as shown in the contained.containedField example. This is the equivalent of calling NestedTestClass.getContained().getContainedField().

Note: in order to reference nested attributes you cannot set separate entries for each of the attribute Classes, it must be set within the confines of the containing parent Object. This is because each Class entry is actually a root for its attributes, including nested objects, not just a list of the attributes at its own level.

So, assuming that NestedTestClass has an attribute of type Contained which you are interested in indexing, you cannot do this:


<classes>
    <class name="com.test.SimpleTestClass">
        <property>privateField</property>
    </class>
    <class name="com.test.NestedTestClass">
        <property>someField</property>
    </class>  
    <class name="com.test.Contained">
        <property>containedField</property>
    </class>  
</classes>

The index will not recognize that the Contained class is a member of the NestedTestClass, so when you put a NestedTextClass into the cache Jofti will ignore the Contained attribute. For a more detailed explanation of the configuration for types please see the Data Types in Jofti section.

So having created the index with the classes and attributes we want indexing, we can now use the index for our declared classes:


// set up the test class
NestedTestClass temp = new NestedTestClass();
Contained contained = new Contained();
contained.setContainedField("test");
temp.setContained(contained);

// add it into the index
cache.put(new Element("testContained", temp));

// try and retrieve it

Map results = index.query(
	new MatchQuery( "com.test.NestedTestClass",
	"contained.containedField","test"));
	

Notice that because our newly added classes have attributes that are indexed, unlike the built in types which are indexed as a single entity, we have to use a different method on the query convenience objects where we specify the class and the attribute to query.

Interestingly we could have performed the String search detailed earlier using this method as shown below.


Map results = index.query(
	new MatchQuery( "java.lang.String", null,"test"));
	
Although, obviously the earlier convenience method is far simpler.

2.3  Configuring Jofti as a wrapper for a Cache.

The use of Jofti as an abstraction for a Cache is achieved either programmatically using no pre-configuration, as shown in the previous section (although using different methods on the Manager), or by using Jofti's configuration to instantiate and manage the Cache implementations.

This usage idiom is supported for all Caches and the Map interface except Tangosol Coherence. Indeed, for Maps and JBossCache this is the only option available, as the callbacks for these two implementations either do not exists or are not expressive enough to support the listener model.

We shall deal with the programmatic method with no pre-configuration first.

The mechanism to start the Index is the same as detailed previously, the difference is that in order to obtain a reference to the wrapper you can either use the addIndex methods e.g.


// assume we are using the default config settings for a Map adapter

DefaultIndexConfig config = new DefaultIndexConfig();

Map map = new HashMap();
IndexedCache cache =  (IndexedCache)manager.addIndex(config,map);

Unlike the previous example, the return from the addIndex method is cast to an IndexedCache type. This is to allow us to do the following.

// assume we are using the default config settings for a Map adapter

DefaultIndexConfig config = new DefaultIndexConfig();

Map map = new HashMap();
IndexedCache cache =  (IndexedCache)manager.addIndex(config,map);

cache.put("test", "testVal");

cache.query(new MatchQuery("testVal");

Or the addIndexedCache methods. e.g

// assume we are using the default config settings for a Map adapter

DefaultIndexConfig config = new DefaultIndexConfig();

IndexedCache cache =  (IndexedCache)manager.addIndexedCache
	(config,"class-definitions.xml");
cache.put("test", "testVal");

cache.query(new MatchQuery("testVal");

The addIndexedCache methods differ from the addIndex methods in that they will try to instantiate a Cache instance directly, instead of relying on an instance passed into the add method. As you can see we do not actually pass a Cache into the addIndexedCache method.

The usage of the addIndex() method with no Cache (or a null value) will result in an incorrectly configured Index. You cannot use the listener adpaters in this way as they will not attempt to instantiate a Cache.

The IndexedCache type is the wrapper Jofti provides to manipulate the Cache (or Map) implementation. It supports the same get(),put(),remove() idiom as the java.util.Map interface and is used in the same manner. In addition, this interface normalises the different Cache implementations, so for instance the requirement that EHCache imposes in using the Element class for the put() method is not required in the interface as it adapts the inserted objects internally.

So, the usage for EHCache would be identical to the Map apart from the adapter specification.


// assume we are using the default config settings for a Map adapter

DefaultIndexConfig config = new DefaultIndexConfig();
config.setCacheAdapter("com.jofti.cache.adapter.EhCacheAdapter");

IndexedCache cache =  (IndexedCache)manager.addIndex(config,
	"class-definitions.xml");

cache.put("test", "testVal");

cache.query(new MatchQuery("testVal");

Notice that we use com.jofti.cache.adapter.EhCacheAdapter not com.jofti.cache.adapter.EhCacheListenerAdapter.

It is important to point out that if you use the non-listener adapters, changes to the Cache directly will not be picked up by the index as there is no callback mechanism. So, this does not work:


// assume we are using the default config settings for a Map adapter

DefaultIndexConfig config = new DefaultIndexConfig();

Map map = new HashMap();
IndexedCache cache =  (IndexedCache)manager.addIndex(config,map);

map.put("test", "testVal");

cache.query(new MatchQuery("testVal");

This will not return any matches as the value will not be present in the index.

 

Using Jofti in a pre-configured mode

In order to use the index in its preconfigured form it is necessary to first have a look at the configuration file format (some of which will be familiar).

An example configuration file called index-config.xml can be found in the Jofti/resources directory.
The section below details the entries in the example file.

 
[?xml version="1.0"?]
[!DOCTYPE cache-config SYSTEM "cache-config.dtd"]

<indexes>
    <!--  An instance of an index -->
    <index name="test" init-method="init" destroy-method="destroy">
    	
    	<!--  The type of index to use -->
        <type>
           com.jofti.tree.TreeIndex
        </type>
        
        <!--  The type of parser to use -->
        <parser>
           com.jofti.introspect.JavaBeanClassIntrospector
        </parser>
        
        <!--  The cache to use -->
        <cache>
            <!--  The adapter for this particular index -->
            <adapter init-method="init" destroy-method="destroy">
            <type>
            	com.jofti.cache.adapter.EhCacheAdapter
            <type>
            
            <!--  The configuration properties for
             the cache implemetation -->
            <init-properties>
                <property name="file">
                 cache-configs/ehcache.xml
                </property>
            </init-properties>
            </adapter>
        </cache>

        <!-- 
        Classes to be indexed
        -->
        <classes>
            <class name="com.test.TestClass">
            	<property>privateField</property>
            </class>
            <class name="com.test.TestClass">
            	<property>contained.containedField</property>
            </class>   
        </classes>
    </index>
</indexes>

The example file specifies only one index which has the name 'test'. It is obvious from examining the file that the configuration specifies the same entries as the configuration object used in the earlier example.

Although there are a couple of differences.

The section for the cache details an init and destroy method that will be used to start and stop the cache. These are optional and depend on the type of Cache used.

It also, in the adapter entry, shows a config file. This is the configuration file required by EHCache (in this case). The adapter is responsible for passing this file to the cache implementation, and initialising the cache.

To use the index in this way you would write something similar to:


IndexManager manager = new IndexManagerImpl();
manager.init("example-config-file.xml");

You should see logging messages similar to the message below.


21:16:42,813 INFO Loading config from file 'example-config-file.xml'
21:16:43,440 INFO Loaded config from file 'example-config-file.xml'
21:16:43,441 INFO Initialising indexes and caches
21:16:43,446 INFO instantiating cache 'test'
21:16:43,746 INFO initialising cache 'test'
21:16:44,073 INFO initialised cache 'test'
21:16:44,074 INFO instantiated cache 'test'
21:16:44,074 INFO Configuring index for cache 'test'
21:16:44,650 INFO Index Configured for cache 'test'

To obtain a reference to the index you would write something similar to:

IndexCache index = (IndexCache)manager.getIndex("test");

p> If the name used to retrieve the index is incorrect the getIndex() method will not return an error instead the index reference will be NULL.

The config file can easily be used to instantiate multiple caches by adding extra index entries. In addition, the indexes need not be of the same type. So, for instance, if there was a requirement to have one cache that was local and in memory you might choose EHCache and another cache to be distributed for a different type of data you might choose JBossCache.

The resulting file would look like:

[?xml version="1.0"?]
[!DOCTYPE cache-config SYSTEM "cache-config.dtd"]

<indexes>
    <!--  An instance of an index -->
    <index name="local" init-method="init" destroy-method="destroy">
    	
    	<!--  The type of index to use -->
        <type>
           com.jofti.tree.TreeIndex
        </type>
        
        <!--  The type of parser to use -->
        <parser>
           com.jofti.introspect.JavaBeanClassIntrospector
        </parser>
        
        <!--  The cache to use -->
        <cache>
            <!--  The adapter for this particular index -->
            <adapter init-method="init" destroy-method="destroy">
            <type>
            	com.jofti.cache.adapter.EhCacheAdapter
            <type>
            
            <!--  The configuration properties for
             the cache implemetation -->
            <init-properties>
                <property name="file">
                 cache-configs/ehcache.xml
                </property>
            </init-properties>
            </adapter>
        </cache>

        <!-- 
        Classes to be indexed
        -->
        <classes>
            <class name="com.test.TestClass">
            	<property>privateField</property>
            </class>
            <class name="com.test.TestClass">
            	<property>contained.containedField</property>
            </class>   
        </classes>
    </index>
    
    <index name="distributed" init-method="init" destroy-method="destroy">
    	
    	<!--  The type of index to use -->
        <type>
           com.jofti.tree.NameSpacedTreeIndex
        </type>
        
        <!--  The type of parser to use -->
        <parser>
           com.jofti.introspect.JavaBeanClassIntrospector
        </parser>
        
        <!--  The cache to use -->
        <cache>
            <!--  The adapter for this particular index -->
            <adapter init-method="init" destroy-method="destroy">
            <type>
            	com.jofti.cache.adapter.JBossCacheAdapter
            <type>
            
            <!--  The configuration properties for
             the cache implemetation -->
            <init-properties>
                <property name="file">
                    treecache.xml
                </property>
            </init-properties>
            </adapter>
        </cache>

        <!-- 
        Classes to be indexed
        -->
        <classes>
            <class name="com.test.TestClass">
            	<property>privateField</property>
            </class>
            <class name="com.foo.bar.SomeClass">
            	<property>bahField</property>
            </class>   
        </classes>
    </index>
</indexes>

Having two caches in the manager does not mean that the caches share an index. Indeed, each cache instance has its own index. It is not possible to query across the indexes . For further examples, look in the test-configs directory which has an example config for each type of cache.

One point to pick up on is that JBossCache is a name spaced cache and as such cannot be used in the same way as non-name space cache. The next section deals with the differences.

2.5  Configuring Name-Spaced Caches

A name space cache is similar to the normal cache type refered to in the earlier part of the document, with the obvious exception that all mutator,accessor methods, indexes and queries must take into account a name space.

The only currently supported name spaced cache is JBossCache.

So to follow our earlier example we can create a non-configured IndexManager and add a name spaced cache to it.


IndexManager manager = new IndexManagerImpl();
manager.init();
DefaultIndexConfig config = new DefaultIndexConfig();
config.setName("test");
config.setCacheAdapter("com.jofti.cache.adapter.JBossCacheAdapter");

// set the type of tree index for JBossCache
config.setIndexType("com.jofti.tree.NameSpacedTreeIndex");

// assume we have obtained a configured JBoss Cache

NameSpacedIndex index = (NameSpacedIndex)
    manager.addIndexedCache(config,jbossCache);
    

It should be immediately obvious that the Index type has become a NameSpacedIndex type. These are not part of the same class hierarchy and so an attempt to cast NameSpacedIndex to IndexCache will cause a ClassCastException.

Similarly in order to obtain the distributed cache specified in the config file in the previous section you must use:


NameSpacedIndex index = manager.getNameSpacedIndex("distributed");

An attempt to retrieve a name spaced index using the getIndex() method (or vice versa) will result in an exception.

The NameSpacedIndex interface has similar sematics to the IndexCache interface but with the addition that every method takes an extra parameter which is a name space (see the JBossCache documentation for namespace restrictions and details).

To add a value to the index in the name space "/namespace/one" we would write the following:


index.put("/namespace/one","test" ,"test");

This puts a String value 'test' under a key called 'test' in the nameSpace "/namespace/one". The adapter will transparently add the value to the Index and put the value into the Cache.

To remove a value from the index we would write the following:


index.remove("/namespace/one","test");

This removes any object with the key 'test' from the index. The adapter will transparently remove the value from the Index and the Cache under the name space "/namespace/one".

To get a value from the index we would write the following:


Object obj = index.get("/namespace/one","test");

note: the name space parameter above is the String format of the JBosscache name space object.

The simple query objects shown earlier also require a name space to be used. To this end there are a series of equivalent query objects that also take a namespace parameter. An example match query using the above entry would look like:


Map results = index.query(new MatchNSQuery("/namespace/one","test"));

The namespace format of the query says get all values of String type that match "test" under the name space "/namespace/one". As the JBossCache namespace is hierarchical - the query searches all namespaces under the one specified, not just at that level.

The second difference in the use of name spaced caches is that the results are not in the same format as the normal cache queries.

The Map entries in the results above are of the format <NameSpaceKey><value>. The NameSpaceKey interface contains two methods, getNameSpace() and getKey().

The JBossCache adapter will always return the name space object in the cache native format (called an Fqn).

The query convenience classes available for the NameSpacedIndex interface are:

  • com.jofti.query.namespace.MatchNSQuery
  • com.jofti.query.namespace.MatchNSLargerQuery
  • com.jofti.query.namespace.MatchNSNotQuery
  • com.jofti.query.namespace.MatchNSSmallerQuery
  • com.jofti.query.namespace.MatchNSRangeQuery
  • com.jofti.query.namespace.MatchNSInQuery
See the class javadocs for more details on usage.

2.6  Indexing already populated Caches

Sometimes you may have a Cache that is already populated and you want to create an index for this Cache. When a Cache is inserted into Jofti it will attempt to index all the values it can in the Cache by looping through the keys. For caches where it is not possible to get a list of keys this is not supported (OSCache does not support this).

It is important to realise that use of keys that are not comparable that are inserted directly into the Cache without going through Jofti's API will be ignored by Jofti when loading up the index.

The way to achieve this is similar to the mechanism for creating a Cache with no preconfiguration.

Below is an example for the MapAdaptor:

IndexManager manager = new IndexManagerImpl();
manager.init();
DefaultIndexConfig config = new DefaultIndexConfig();
config.setName("test");

// populate some values into the Map
Map temp = new HashMap();
		
for (int i =0;i<1000;i++){
    temp.put("key"+i,"test"+i);
}
		
temp.put(new Object(),"some other value");
		
manager.addIndexedCache(config,temp, "some-config.xml");
		
Index index = manager.getIndex("test");
		
Map results  =index.query(new MatchQuery("test1"));

This example above shows that that values already in the HashMap when it was inserted into the Index are available to be queried once the addIndexedCache() method has returned (The time for this will be dependent on the number of entries in the Map/Cache).

It is also worth noting that the value put into the Map under the new Object() key will be ignored as the key is not Comparable.

2.7  Configuring Disk Overflow

The disk overflow feature allows us to keep a certain number of pages in memory at any one time and the pages that we cannot fit into memory are be stored onto and read from disk as required.

Configuration of the disk overflow startegies can be done either using the configuration file, or programmatically.
The configuration file section has the following format:
 

<indexes>
    <!--  An instance of an index -->
    <index name="test" init-method="init" destroy-method="destroy">
    	
    	<!--  ...other index properties -->
       
        
       <!--  The disk overflow strategy-->
        <disk-overflow>
            <provider>
                com.jofti.store.LRUStoreManager
            </provider>
            <init-properties>
                <!--  defaults to 3000 -->
                <property name="max-nodes">3000</property>
                
                <!--  defaults to ../tmp -->
                <property name="directory">/tmp/</property>
                
                <!--  defaults to 10 -->
                <property name="max-files">20</property>
                
                <!--  defaults to 300mb -->
                <property name="file-size">50485760</property>
                
                <!--  defaults to 4096 -->
                <property name="block-size">4096</property>
            </init-properties>
         </disk-overflow>
       
       
    </index>
</indexes>

All the init-properties for the disk-overflow section are optional.

To configure the diskOverflow strategy programmatically it is simply a matter of adding the appropriate properties to the Index properties.


IndexManager manager = new IndexManagerImpl();
manager.init();
DefaultIndexConfig config =
 new DefaultIndexConfig();
config.setName("test");

// set up the disk overflow
Properties props = new Properties();
props.setProperty("provider","com.jofti.store.HashedStoremanager");
props.setProperty("directory",System.getProperty("java.io.tmpdir"));
props.setProperty("file-size",""+5048570);
props.setProperty("max-nodes",""+3000);
props.setProperty("max-files",""+20);

config.setIndexProperties(props);

manager.addIndexedCache(config,temp, "some-config.xml");
		
Index index = manager.getIndex("test");
		

A detailed discussion of the performance characteristics of each strategy is covered in Chapter 5 Disk Overflow Performance

Features

  • Multi Cache support
  • Transaction support
  • Type aware searching
  • Configurable property indexing
  • Indexing/searching by interfaces
  • Support for Dynamic Proxies
  • Support for primitve attributes
  • Support for Collections and Arrays
  • String prefix searching
  • Simple query language