Permalink
Browse files

continuing to work on #10

Signed-off-by: gburgett <gordon.burgett@gmail.com>
  • Loading branch information...
gburgett committed Jan 13, 2013
1 parent eb4fe8d commit d4b697917bd1e5050926bcea34991e1c5bd39f4c
@@ -4,7 +4,7 @@
*/
package org.gburgett.xflat.convert;
-import org.jdom2.Content;
+import org.jdom2.xpath.XPathExpression;
/**
* The interface for a PojoConverter. The implementation is loaded dynamically
@@ -22,4 +22,22 @@
* @return The new conversion service that the database should use.
*/
public ConversionService extend(ConversionService service);
+
+ /**
+ * Gets an XPath expression which will select the ID property
+ * of the converted data inside a row. Can be null if the class has no ID property.
+ * <p/>
+ * The context of the expression is the root of the row. For example, if your
+ * class "Foo" has a property "FooInt" which is your ID, and the class is converted
+ * to the following XML:<br/>
+ * {@code
+ * <foo>
+ * <fooInt>17</FooInt>
+ * </foo>
+ * }
+ * then the expression should be "foo/fooInt".
+ * @param clazz
+ * @return
+ */
+ public XPathExpression<?> idSelector(Class<?> clazz);
}
@@ -4,27 +4,34 @@
*/
package org.gburgett.xflat.convert.converters;
-import java.io.IOException;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
import org.gburgett.xflat.convert.ConversionException;
import org.gburgett.xflat.convert.ConversionNotSupportedException;
import org.gburgett.xflat.convert.ConversionService;
import org.gburgett.xflat.convert.Converter;
import org.gburgett.xflat.convert.PojoConverter;
+import org.gburgett.xflat.db.IdAccessor;
import org.gburgett.xflat.util.JDOMStreamReader;
import org.gburgett.xflat.util.JDOMStreamWriter;
import org.jdom2.Document;
import org.jdom2.Element;
+import org.jdom2.Namespace;
+import org.jdom2.filter.Filters;
import org.jdom2.output.XMLOutputter;
+import org.jdom2.xpath.XPathExpression;
+import org.jdom2.xpath.XPathFactory;
/**
* A PojoConverter that extends a ConversionService to convert all unknown
@@ -49,6 +56,65 @@ public ConversionService extend(ConversionService service) {
return new JAXBConversionService(service);
}
+
+ private Map<Class<?>, XPathExpression<?>> idSelectorCache = new ConcurrentHashMap<>();
+
+ @Override
+ public XPathExpression<?> idSelector(Class<?> clazz) {
+ XPathExpression<?> ret = idSelectorCache.get(clazz);
+ if(ret == null){
+ ret = makeIdSelector(clazz);
+ idSelectorCache.put(clazz, ret);
+ }
+ return ret;
+ }
+
+ private XPathExpression<?> makeIdSelector(Class<?> clazz){
+ IdAccessor accessor = IdAccessor.forClass(clazz);
+
+ if(!accessor.hasId()){
+ return null;
+ }
+
+ Namespace ns = null;
+ StringBuilder ret = new StringBuilder(clazz.getSimpleName());
+
+ XmlAttribute attribute = (XmlAttribute) accessor.getIdPropertyAnnotation(XmlAttribute.class);
+ if(attribute != null){
+ ret.append("/@");
+ if(attribute.namespace() != null){
+ ns = Namespace.getNamespace("id", attribute.namespace());
+ ret.append(ns.getPrefix()).append(":");
+ }
+ if(attribute.name() != null){
+ ret.append(attribute.name());
+ }
+ else{
+ ret.append(accessor.getIdPropertyName());
+ }
+ }
+ else{
+ ret.append("/");
+ XmlElement element = (XmlElement) accessor.getIdPropertyAnnotation(XmlElement.class);
+ if(element != null){
+ if(element.namespace() != null){
+ ns = Namespace.getNamespace("id", attribute.namespace());
+ ret.append(ns.getPrefix()).append(":");
+ }
+ if(element.name() != null){
+ ret.append(element.name());
+ }
+ else{
+ ret.append(accessor.getIdPropertyName());
+ }
+ }
+ }
+
+ if(ns == null){
+ return XPathFactory.instance().compile(ret.toString());
+ }
+ return XPathFactory.instance().compile(ret.toString(), Filters.fpassthrough(), null, ns);
+ }
private static class JAXBConversionService implements ConversionService{
@@ -0,0 +1,15 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gburgett.xflat.db;
+
+/**
+ * Represents an action that a table can perform with an engine.
+ * @param <T>
+ */
+public interface EngineAction<T> {
+
+ public T act(Engine engine);
+
+}
@@ -7,6 +7,7 @@
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -17,6 +18,7 @@
import java.util.concurrent.ConcurrentHashMap;
import org.gburgett.xflat.Id;
import org.gburgett.xflat.XflatException;
+import org.jdom2.Attribute;
/**
* A helper class that accesses the IDs of an object.
@@ -235,4 +237,28 @@ public void setIdValue(T pojo, Object id)
throw new UnsupportedOperationException("Cannot get ID value when object has no ID");
}
+
+ public String getIdPropertyName(){
+ if(this.idProperty != null){
+ return this.idProperty.getName();
+ }
+
+ if(this.idField != null){
+ return this.idField.getName();
+ }
+
+ throw new UnsupportedOperationException("Cannot get field name when object has no ID");
+ }
+
+ public <U extends Annotation> U getIdPropertyAnnotation(Class<U> annotationClass){
+ if(this.idProperty != null){
+ return this.idProperty.getReadMethod().getAnnotation(annotationClass);
+ }
+
+ if(this.idField != null){
+ return this.idField.getAnnotation(annotationClass);
+ }
+
+ throw new UnsupportedOperationException("Cannot get annotation when object has no ID");
+ }
}
@@ -9,14 +9,16 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
+import org.gburgett.xflat.Cursor;
+import org.gburgett.xflat.EngineStateException;
import org.gburgett.xflat.Range;
import org.gburgett.xflat.ShardsetConfig;
import org.gburgett.xflat.XflatException;
import org.gburgett.xflat.convert.ConversionException;
-import org.gburgett.xflat.engine.IdShardedEngine;
import org.jdom2.Element;
/**
@@ -29,6 +31,8 @@
//the engines that are spinning down while this engine spins down
private Map<Range<T>, EngineBase> spinningDownEngines = new HashMap<>();
+ private WeakHashMap<Cursor<Range<T>>, String> openTableCursors = new WeakHashMap<>();
+
private final Object spinDownSyncRoot = new Object();
protected File directory;
@@ -77,48 +81,55 @@ public ShardedEngineBase(File file, String tableName, ShardsetConfig<T> config){
return ret;
}
- protected EngineBase getEngine(Range<T> range){
-
- EngineState state = getState();
- if(state == EngineState.Uninitialized || state == EngineState.SpunDown){
- throw new XflatException("Attempt to read or write to an engine in an uninitialized state");
- }
-
-
- TableMetadata ret = openShards.get(range);
-
- if(ret == null){
+ private EngineBase getEngine(Range<T> range){
+ TableMetadata metadata = openShards.get(range);
+ if(metadata == null){
//definitely ensure we aren't spinning down before we start up a new engine
synchronized(spinDownSyncRoot){
- state = getState();
+ EngineState state = getState();
if(state == EngineState.SpunDown){
throw new XflatException("Engine has already spun down");
}
//build the new metadata element so we can use it to provide engines
String name = range.getName();
- ret = this.getMetadataFactory().makeTableMetadata(name, new File(directory, name + ".xml"));
- TableMetadata weWereLate = openShards.putIfAbsent(range, ret);
+ metadata = this.getMetadataFactory().makeTableMetadata(name, new File(directory, name + ".xml"));
+ TableMetadata weWereLate = openShards.putIfAbsent(range, metadata);
if(weWereLate != null){
//another thread put the new metadata already
- ret = weWereLate;
+ metadata = weWereLate;
}
if(state == EngineState.SpinningDown){
EngineBase eng = spinningDownEngines.get(range);
if(eng == null){
- //we're requesting a new engine for some kind of read, get it and immediately begin spinning it down.
- eng = ret.provideEngine();
+ //we're requesting a new engine for some kind of read, get it and let the task spin it down.
+ eng = metadata.provideEngine();
spinningDownEngines.put(range, eng);
- ret.spinDown();
+ return eng;
}
- return eng;
}
}
}
- return ret.provideEngine();
+ return metadata.provideEngine();
+ }
+
+ protected <U> U doWithEngine(Range<T> range, EngineAction<U> action){
+
+ EngineState state = getState();
+ if(state == EngineState.Uninitialized || state == EngineState.SpunDown){
+ throw new XflatException("Attempt to read or write to an engine in an uninitialized state");
+ }
+
+ try{
+ return action.act(getEngine(range));
+ }
+ catch(EngineStateException ex){
+ //try one more time with a potentially new engine, if we still fail then let it go
+ return action.act(getEngine(range));
+ }
}
@@ -70,12 +70,4 @@ void setEngineProvider(EngineProvider engine){
return action.act(this.engineProvider.provideEngine());
}
}
-
- /**
- * Represents an action that a table can perform with an engine.
- * @param <T>
- */
- protected interface EngineAction<T>{
- public T act(Engine engine);
- }
}
Oops, something went wrong.

0 comments on commit d4b6979

Please sign in to comment.