Switched to use new project layout; updated build; moved files around

This commit is contained in:
Thomas Risberg
2010-10-07 12:14:39 -04:00
parent 06b5ceff15
commit 0f7984a331
67 changed files with 4000 additions and 168 deletions

View File

@@ -0,0 +1,24 @@
package org.springframework.data.support;
import java.util.Map;
import org.springframework.core.convert.ConversionService;
/**
* Interface representing the set of changes in an entity.
*
* @author Rod Johnson
* @author Thomas Risberg
*
*/
public interface ChangeSet {
<T> T get(String key, Class<T> requiredClass, ConversionService cs);
void set(String key, Object o);
Map<String, Object> getValues();
Object removeProperty(String k);
}

View File

@@ -0,0 +1,13 @@
package org.springframework.data.support;
/**
* Interface introduced to objects exposing ChangeSet information
* @author Rod Johnson
* @author Thomas Risberg
*/
public interface ChangeSetBacked {
ChangeSet getChangeSet();
}

View File

@@ -0,0 +1,47 @@
package org.springframework.data.support;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented by classes that can synchronize
* between data stores and ChangeSets.
* @author Rod Johnson
*
* @param <K> entity key
*/
public interface ChangeSetPersister<K> {
String ID_KEY = "_id";
String CLASS_KEY = "_class";
/**
* TODO how to tell when not found? throw exception?
*/
void getPersistentState(Class<? extends ChangeSetBacked> entityClass, K key, ChangeSet changeSet) throws DataAccessException, NotFoundException;
/**
* Return id
* @param cs
* @return
* @throws DataAccessException
*/
K getPersistentId(Class<? extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException;
/**
* Return key
* @param cs Key may be null if not persistent
* @return
* @throws DataAccessException
*/
K persistState(Class<? extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException;
/**
* Exception thrown in alternate control flow if getPersistentState
* finds no entity data.
*/
class NotFoundException extends Exception {
}
}

View File

@@ -0,0 +1,28 @@
package org.springframework.data.support;
import java.util.Map;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented by classes that can synchronize
* between entities and ChangeSets.
* @author Rod Johnson
*
* @param <E>
*/
public interface ChangeSetSynchronizer<E extends ChangeSetBacked> {
Map<String, Class<?>> persistentFields(Class<? extends E> entityClassClass);
/**
* Take all entity fields into a changeSet.
* @param entity
* @return
* @throws DataAccessException
*/
void populateChangeSet(ChangeSet changeSet, E entity) throws DataAccessException;
void populateEntity(ChangeSet changeSet, E entity) throws DataAccessException;
}

View File

@@ -0,0 +1,51 @@
package org.springframework.data.support;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.convert.ConversionService;
/**
* Simple ChangeSet implementation backed by a HashMap.
* @author Thomas Risberg
* @author Rod Johnson
*/
public class HashMapChangeSet implements ChangeSet {
private Map<String, Object> values;
public HashMapChangeSet(Map<String,Object> values) {
this.values = values;
}
public HashMapChangeSet() {
this(new HashMap<String, Object>());
}
@Override
public void set(String key, Object o) {
values.put(key, o);
}
@Override
public String toString() {
return "HashMapChangeSet: values=[" + values + "]";
}
@Override
public Map<String, Object> getValues() {
return Collections.unmodifiableMap(values);
}
@Override
public Object removeProperty(String k) {
return this.values.remove(k);
}
@Override
public <T> T get(String key, Class<T> requiredClass, ConversionService conversionService) {
return conversionService.convert(values.get(key), requiredClass);
}
}

View File

@@ -0,0 +1,69 @@
package org.springframework.data.transaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.support.ChangeSetBacked;
import org.springframework.data.support.ChangeSetPersister;
import org.springframework.transaction.support.TransactionSynchronization;
public class ChangeSetBackedTransactionSynchronization implements TransactionSynchronization {
protected final Log log = LogFactory.getLog(getClass());
private ChangeSetPersister<Object> changeSetPersister;
private ChangeSetBacked entity;
private int changeSetTxStatus = -1;
public ChangeSetBackedTransactionSynchronization(ChangeSetPersister<Object> changeSetPersister, ChangeSetBacked entity) {
this.changeSetPersister = changeSetPersister;
this.entity = entity;
}
@Override
public void afterCommit() {
log.debug("After Commit called for " + entity);
changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
changeSetTxStatus = 0;
}
@Override
public void afterCompletion(int status) {
log.debug("After Completion called with status = " + status);
if (changeSetTxStatus == 0) {
if (status == STATUS_COMMITTED) {
// this is good
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
}
else {
// this could be bad - TODO: compensate
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
}
}
}
@Override
public void beforeCommit(boolean readOnly) {
}
@Override
public void beforeCompletion() {
}
@Override
public void flush() {
}
@Override
public void resume() {
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
@Override
public void suspend() {
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
}

View File

@@ -0,0 +1,13 @@
package org.springframework.datastore.serialization;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SerializationStore {
}

View File

@@ -0,0 +1,32 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
/**
* Listener interface for asynchronous storage operations.
* Can be annotated with OnlyOnFailure as an optimization
* if the listener is only interested in compensating transactions
* in the event of write failure.
*
* @author Rod Johnson
*
* @param <V> new value type
*/
public interface AsynchStoreCompletionListener<V> {
/**
* Constant indicating no store completion action
*/
class NONE implements AsynchStoreCompletionListener<Object> {
public void onCompletion(AsynchStoreCompletionListener.StoreResult result, Object newValue, Field foreignStore) {}
}
enum StoreResult {
SUCCESS,
FAILURE,
INDETERMINATE
};
void onCompletion(StoreResult result, V newValue, Field foreignStore);
}

View File

@@ -0,0 +1,34 @@
package org.springframework.persistence;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Persistence policies that can be attached to entities or relationship
* fields.
* @author rodjohnson
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface PersistencePolicy {
enum LATENCY_SENSITIVITY { NONE, MEDIUM, HIGH };
boolean largeObject() default false;
boolean queryable() default true;
boolean immutable() default false;
boolean transactional() default true;
boolean lossAcceptable() default false;
// TODO freshness, or should this be handled separately in a caching annotation
LATENCY_SENSITIVITY latencySensitivity() default LATENCY_SENSITIVITY.HIGH;
}

View File

@@ -0,0 +1,65 @@
package org.springframework.persistence;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation indicating that a field may be stored in a foreign store
* and specifying the necessary guarantees. Conceptual rather than
* implementation-specific.
* @see ForeignStoreKeyManager
*
* @author Rod Johnson
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RelatedEntity {
/**
*
* Optional information as to how to compute or locate the key value.
* Some strategies may take this into account.
*/
String keyExpression() default "";
/**
* Should we use the key of the present entity
* @return
*/
boolean sameKey() default false;
/**
* Policies for persistence
* @return
*/
PersistencePolicy policy() default @PersistencePolicy();
/**
* Name for the preferred data store. Merely a hint. May not be followed.
* @return
*/
String preferredStore() default "";
/**
* Is asynchronous store OK?
* @return
*/
boolean asynchStore() default false;
// TODO - indicates if an asynchronous write should begin
// only after commit of a transaction
boolean afterCommit() default false;
/**
* Completion listener class. Only used if asynchStore is true.
* Must have a no-arg constructor.
* @return
*/
@SuppressWarnings("unchecked")
Class<? extends AsynchStoreCompletionListener> storeCompletionListenerClass() default AsynchStoreCompletionListener.NONE.class;
String storeCompletionListenerBeanName() default "";
}

View File

@@ -0,0 +1,73 @@
package org.springframework.persistence.support;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
/**
* Try for a constructor taking state: failing that, try a no-arg
* constructor and then setUnderlyingNode().
*
* @author Rod Johnson
*/
public abstract class AbstractConstructorEntityInstantiator<BACKING_INTERFACE, STATE> implements EntityInstantiator<BACKING_INTERFACE, STATE> {
private final Log log = LogFactory.getLog(getClass());
final public <T extends BACKING_INTERFACE> T createEntityFromState(STATE n, Class<T> c) {
try {
return fromStateInternal(n, c);
} catch (InstantiationException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
final private <T extends BACKING_INTERFACE> T fromStateInternal(STATE n, Class<T> c) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
// TODO this is fragile
Class<? extends STATE> stateInterface = (Class<? extends STATE>) n.getClass().getInterfaces()[0];
Constructor<T> nodeConstructor = ClassUtils.getConstructorIfAvailable(c, stateInterface);
if (nodeConstructor != null) {
// TODO is this the correct way to instantiate or does Spring have a preferred way?
log.info("Using " + c + " constructor taking " + stateInterface);
return nodeConstructor.newInstance(n);
}
Constructor<T> noArgConstructor = ClassUtils.getConstructorIfAvailable(c);
if (noArgConstructor == null) noArgConstructor = getDeclaredConstructor(c);
if (noArgConstructor != null) {
log.info("Using " + c + " no-arg constructor");
StateProvider.setUnderlyingState(n);
T t = noArgConstructor.newInstance();
setState(t, n);
return t;
}
throw new IllegalArgumentException(getClass().getSimpleName() + ": entity " + c + " must have either a constructor taking [" + stateInterface +
"] or a no-arg constructor and state set method");
}
private <T> Constructor<T> getDeclaredConstructor(Class<T> c) {
try {
final Constructor<T> declaredConstructor = c.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
return declaredConstructor;
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* Subclasses must implement to set state
* @param entity
* @param s
*/
protected abstract void setState(BACKING_INTERFACE entity, STATE s);
}

View File

@@ -0,0 +1,32 @@
package org.springframework.persistence.support;
/**
* Interface to be implemented by classes that can instantiate and
* configure entities.
* The framework must do this when creating objects resulting from finders,
* even when there may be no no-arg constructor supplied by the user.
*
* @author Rod Johnson
*/
public interface EntityInstantiator<BACKING_INTERFACE,STATE> {
/*
* The best solution if available is to add a constructor that takes Node
* to each GraphEntity. This means generating an aspect beside every
* class as Roo presently does.
*
* An alternative that does not require Roo
* is a user-authored constructor taking Node and calling setUnderlyingNode()
* but this is less elegant and pollutes the domain object.
*
* If the user supplies a no-arg constructor, instantiation can occur by invoking it
* prior to calling setUnderlyingNode().
*
* If the user does NOT supply a no-arg constructor, we must rely on Sun-specific
* code to instantiate entities without invoking a constructor.
*/
<T extends BACKING_INTERFACE> T createEntityFromState(STATE s, Class<T> c);
}

View File

@@ -0,0 +1,21 @@
package org.springframework.persistence.support;
/**
* @author Michael Hunger
* @since 24.09.2010
*/
public abstract class StateProvider {
private final static ThreadLocal stateHolder=new ThreadLocal();
private StateProvider() {}
public static <STATE> void setUnderlyingState(STATE state) {
if (stateHolder.get()!=null) throw new IllegalStateException("StateHolder already contains state "+stateHolder.get()+" in thread "+Thread.currentThread());
stateHolder.set(state);
}
public static <STATE> STATE retrieveState() {
STATE result= (STATE) stateHolder.get();
stateHolder.remove();
return result;
}
}

View File

@@ -0,0 +1,78 @@
package org.springframework.persistence.transaction;
import java.util.IdentityHashMap;
import java.util.Map;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionStatus;
public class NaiveDoubleTransactionManager implements PlatformTransactionManager {
Map<TransactionStatus,TransactionStatus> status=new IdentityHashMap<TransactionStatus, TransactionStatus>();
private final PlatformTransactionManager a;
private final PlatformTransactionManager b;
public NaiveDoubleTransactionManager(PlatformTransactionManager a, PlatformTransactionManager b) {
System.err.println("WARNING: Naive JTA/Neo4j Spring transaction manager--must implement properly");
this.a = a;
this.b = b;
}
@Override
public void commit(TransactionStatus ts) throws TransactionException {
try {
final TransactionStatus tsb = copyTransactionStatus(status.get(ts));
try {
a.commit(ts);
}
catch (Throwable t) {
System.err.println("Continuing to commit tx despite this:" + t);
}
try {
b.commit(tsb);
}
catch (Throwable t) {
System.err.println("Can't commit tx" + t);
throw new TransactionException(t.getMessage(), t) {};
}
} finally {
status.remove(ts);
}
}
private TransactionStatus copyTransactionStatus(TransactionStatus ts) {
Object t = (ts instanceof DefaultTransactionStatus) ? ((DefaultTransactionStatus) ts).getTransaction() : null;
return new DefaultTransactionStatus(t,ts.isNewTransaction(), false, false, false, null);
}
@Override
public TransactionStatus getTransaction(TransactionDefinition td)
throws TransactionException {
TransactionStatus atx = a.getTransaction(td);
TransactionStatus btx = b.getTransaction(td);
status.put(atx, btx);
return atx;
}
@Override
public void rollback(TransactionStatus ts) throws TransactionException {
final TransactionStatus tsb = copyTransactionStatus(status.remove(ts));
try {
a.rollback(ts);
}
catch (Throwable t) {
System.err.println("Continuing to rollback tx despite this:" + t);
}
try {
b.rollback(tsb);
}
catch (Throwable t) {
System.err.println("Can't rollback tx" + t);
throw new TransactionException(t.getMessage(), t) {};
}
}
}