Skip to content

Commit

Permalink
[ISPN-287] ( Querying to support arbitrary key types (not just String) )
Browse files Browse the repository at this point in the history
  • Loading branch information
maniksurtani committed Dec 9, 2009
1 parent aa96feb commit e3f92dd
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 127 deletions.
47 changes: 7 additions & 40 deletions query/src/main/java/org/infinispan/query/DefaultTransformer.java
@@ -1,13 +1,8 @@
package org.infinispan.query;

import org.infinispan.CacheException;
import org.jboss.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.io.Serializable;

/**
* Warning, slow as a dog, uses serialization to get a byte representation of a class. Implement your own!
Expand All @@ -19,43 +14,15 @@
public class DefaultTransformer implements Transformer {
@Override
public Object fromString(String s) {
//"sz:[b1, b2, b3, b4, ... ]"
String sz = s.substring(0, s.indexOf(":"));
byte[] buf = new byte[Integer.parseInt(sz)];
String sub = s.substring(s.indexOf(":") + 1);
String tokens = sub.replace("[", "").replace("]", "");
StringTokenizer st = new StringTokenizer(tokens, ",");
int i = 0;
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
byte b = Byte.parseByte(token);
buf[i++] = b;
}

ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new ByteArrayInputStream(buf));
Object o = ois.readObject();
ois.close();
return o;
} catch (Exception e) {
throw new CacheException (e);
}

return Base64.decodeToObject(s);
}

@Override
public String toString(Object customType) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(customType);
oos.close();
baos.close();
byte[] b = baos.toByteArray();
return b.length + ":" + Arrays.toString(b);
} catch (Exception e) {
throw new CacheException(e);
if (customType instanceof Serializable) {
return Base64.encodeObject((Serializable) customType);
} else {
throw new IllegalArgumentException("Expected " + customType.getClass() + " to be Serializable!");
}
}
}
@@ -1,9 +1,10 @@
package org.infinispan.query.backend;

import org.infinispan.util.Util;
import org.infinispan.CacheException;
import org.infinispan.query.Transformer;
import org.infinispan.query.Transformable;
import org.infinispan.query.Transformer;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;

/**
* This transforms arbitrary keys to a String which can be used by Lucene as a document identifier, and vice versa.
Expand All @@ -22,6 +23,8 @@
* @since 4.0
*/
public class KeyTransformationHandler {
private static Log log = LogFactory.getLog(KeyTransformationHandler.class);

public static Object stringToKey(String s) {
char type = s.charAt(0);
switch (type) {
Expand Down Expand Up @@ -55,17 +58,18 @@ public static Object stringToKey(String s) {
case 'T':
// this is a custom transformable.
int indexOfSecondDelimiter = s.indexOf(":", 2);
String transformerClassName = s.substring(2, indexOfSecondDelimiter);
String keyClassName = s.substring(2, indexOfSecondDelimiter);
String keyAsString = s.substring(indexOfSecondDelimiter + 1);
Transformer t;
// try and locate class
Class keyClass = null;
try {
t = (Transformer) Util.getInstance(transformerClassName);
} catch (Exception e) {
// uh oh, cannot load this class! What now?
throw new CacheException(e);
keyClass = Thread.currentThread().getContextClassLoader().loadClass(keyClassName);
} catch (ClassNotFoundException e) {
log.error("Could not locate class " + keyClass, e);
}

t = getTransformer(keyClass);
if (t == null) throw new CacheException("Cannot find an appropriate Transformer for key type " + keyClass);
return t.fromString(keyAsString);
}
throw new CacheException("Unknown type metadata " + type);
Expand All @@ -86,6 +90,8 @@ public static String keyToString(Object key) {
// First going to check if the key is a primitive or a String. Otherwise, check if it's a transformable.
// If none of those conditions are satisfied, we'll throw an Exception.

Transformer tf = null;

if (isStringOrPrimitive(key)) {
// Using 'X' for Shorts and 'Y' for Bytes because 'S' is used for Strings and 'B' is being used for Booleans.

Expand All @@ -111,32 +117,9 @@ else if (key instanceof Character)

return prefix + ":" + key;

} else if (isTransformable(key)) {
} else if ((tf = getTransformer(key.getClass())) != null) {
// There is a bit more work to do for this case.
prefix = 'T';

System.out.println("key class is: - " + key.getClass());
// Do the transformer casting

// Try and get the @Transformable annotation.
Transformable transformableAnnotation = key.getClass().getAnnotation(Transformable.class);

// Use that to find the class that is being used as the transformer.
Class<? extends Transformer> transformerClass = transformableAnnotation.transformer();
Transformer t;
try {
t = Util.getInstance(transformerClass);
}
catch (Exception e) {
throw new CacheException(e);
}
//Get the name of the Class that has been used. Add it to the toString() method that has to be defined
// in the Transformer implementation
String subKey = key.getClass().getName() + ":" + t.toString(key);
// We've built the second part of the String and now need to add that bit to the prefix for our complete keyString
// for lucene.
return prefix + ":" + subKey;

return "T:" + key.getClass().getName() + ":" + tf.toString(key);
} else
throw new IllegalArgumentException("Indexing only works with entries keyed on Strings, primitives " +
"and classes that have the @Transformable annotation - you passed in a " + key.getClass().toString());
Expand All @@ -160,10 +143,23 @@ private static boolean isStringOrPrimitive(Object key) {
return false;
}

private static Boolean isTransformable(Object key) {
// returns true if the Transformable annotation is present on the custom key class.
return key.getClass().isAnnotationPresent(Transformable.class);

/**
* Retrieves a {@link org.infinispan.query.Transformer} instance for this {@link org.infinispan.query.Transformable}
* type key. If the key is not {@link org.infinispan.query.Transformable}, a null is returned.
*
* @param keyClass key class to analyze
* @return a Transformer for this key, or null if the key type is not properly annotated.
* @throws IllegalAccessException if a Transformer instance cannot be created vai reflection.
* @throws InstantiationException if a Transformer instance cannot be created vai reflection.
*/
private static Transformer getTransformer(Class<?> keyClass) {
Transformable t = keyClass.getAnnotation(Transformable.class);
Transformer tf = null;
if (t != null) try {
tf = t.transformer().newInstance();
} catch (Exception e) {
log.error("Cannot instantiate an instance of Transformer class " + t.transformer() + "!", e);
}
return tf;
}

}
@@ -1,7 +1,8 @@
package org.infinispan.query.backend;

import org.testng.annotations.Test;
import org.infinispan.query.test.CustomKey;
import org.infinispan.query.test.CustomKey2;
import org.testng.annotations.Test;

/**
* This is the test class for {@link org.infinispan.query.backend.KeyTransformationHandler}
Expand Down Expand Up @@ -84,7 +85,14 @@ public void testStringToKeyWithStringAndPrimitives() {
}

public void testStringToKeyWithCustomTransformable(){
CustomKey customKey = new CustomKey("hello", 5);
System.out.println(customKey.getClass().getName());
CustomKey customKey = new CustomKey(88, 8800, 12889976);
String strRep = KeyTransformationHandler.keyToString(customKey);
assert customKey.equals(KeyTransformationHandler.stringToKey(strRep));
}

public void testStringToKeyWithDefaultTransformer() {
CustomKey2 ck2 = new CustomKey2(Integer.MAX_VALUE, Integer.MIN_VALUE, 0);
String strRep = KeyTransformationHandler.keyToString(ck2);
assert ck2.equals(KeyTransformationHandler.stringToKey(strRep));
}
}
Expand Up @@ -101,9 +101,9 @@ public void testPrimitiveAndStringKeys(){
}

public void testCustomKeys(){
CustomKey key1 = new CustomKey("Kim", 3);
CustomKey key2 = new CustomKey("Jong", 4);
CustomKey key3 = new CustomKey("Il", 2);
CustomKey key1 = new CustomKey(1, 2, 3);
CustomKey key2 = new CustomKey(900, 800, 700);
CustomKey key3 = new CustomKey(1024, 2048, 4096);

cache.put(key1, person1);
cache.put(key2, person1);
Expand Down
68 changes: 49 additions & 19 deletions query/src/test/java/org/infinispan/query/test/CustomKey.java
@@ -1,35 +1,65 @@
package org.infinispan.query.test;

import org.infinispan.query.Transformable;

/**
* Test class used as a transformable. Used to test the code in {@link org.infinispan.query.backend.KeyTransformationHandler}
* through the {@link org.infinispan.query.backend.KeyTransformationHandlerTest}
*
* @author Navin Surtani
*/
import org.infinispan.query.backend.KeyTransformationHandlerTest;

@Transformable(transformer = CustomTransformer.class)
public class CustomKey {

private String name;
private int aNumber;
int i, j, k;

public CustomKey(int i, int j, int k) {
this.i = i;
this.j = j;
this.k = k;
}

public CustomKey() {
}

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

public CustomKey(String name, int aNumber){
this.name = name;
this.aNumber = aNumber;
public int getJ() {
return j;
}

public String getName(){
return name;
public void setJ(int j) {
this.j = j;
}

public int getANumber(){
return aNumber;
public int getK() {
return k;
}

public void setK(int k) {
this.k = k;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

CustomKey customKey = (CustomKey) o;

if (i != customKey.i) return false;
if (j != customKey.j) return false;
if (k != customKey.k) return false;

return true;
}

@Override
public String toString(){
return "aNumber=" + aNumber + ";name=" + name;
public int hashCode() {
int result = i;
result = 31 * result + j;
result = 31 * result + k;
return result;
}
}
}
65 changes: 65 additions & 0 deletions query/src/test/java/org/infinispan/query/test/CustomKey2.java
@@ -0,0 +1,65 @@
package org.infinispan.query.test;

import org.infinispan.query.Transformable;

import java.io.Serializable;

@Transformable
public class CustomKey2 implements Serializable {
int i, j, k;

public CustomKey2(int i, int j, int k) {
this.i = i;
this.j = j;
this.k = k;
}

public CustomKey2() {
}

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

public int getJ() {
return j;
}

public void setJ(int j) {
this.j = j;
}

public int getK() {
return k;
}

public void setK(int k) {
this.k = k;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

CustomKey2 that = (CustomKey2) o;

if (i != that.i) return false;
if (j != that.j) return false;
if (k != that.k) return false;

return true;
}

@Override
public int hashCode() {
int result = i;
result = 31 * result + j;
result = 31 * result + k;
return result;
}
}

0 comments on commit e3f92dd

Please sign in to comment.