Added before/after link delete events and fixed a bug with deleting links where the updated entity was never saved back to the DB.

This commit is contained in:
Jon Brisbin
2012-07-30 13:53:13 -05:00
parent 1cb9307b1c
commit 2f6650c846
22 changed files with 497 additions and 85 deletions

View File

@@ -1,10 +1,12 @@
package org.springframework.data.rest.repository;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.rest.repository.invoke.CrudMethod;
import org.springframework.data.rest.repository.invoke.RepositoryQueryMethod;
/**
@@ -74,4 +76,13 @@ public interface RepositoryMetadata<E extends EntityMetadata<? extends Attribute
*/
Map<String, RepositoryQueryMethod> queryMethods();
/**
* Does this Repository all this method to be exported?
*
* @param method
*
* @return
*/
Boolean exportsMethod(CrudMethod method);
}

View File

@@ -0,0 +1,19 @@
package org.springframework.data.rest.repository.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HandleAfterLinkDelete {
Class<?>[] value() default {};
}

View File

@@ -0,0 +1,19 @@
package org.springframework.data.rest.repository.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HandleBeforeLinkDelete {
Class<?>[] value() default {};
}

View File

@@ -42,6 +42,10 @@ public abstract class AbstractRepositoryEventListener<T extends AbstractReposito
onBeforeLinkSave(event.getSource(), ((BeforeLinkSaveEvent)event).getLinked());
} else if(event instanceof AfterLinkSaveEvent) {
onAfterLinkSave(event.getSource(), ((AfterLinkSaveEvent)event).getLinked());
} else if(event instanceof BeforeLinkDeleteEvent) {
onBeforeLinkDelete(event.getSource(), ((BeforeLinkDeleteEvent)event).getLinked());
} else if(event instanceof AfterLinkDeleteEvent) {
onAfterLinkDelete(event.getSource(), ((BeforeLinkDeleteEvent)event).getLinked());
} else if(event instanceof BeforeDeleteEvent) {
onBeforeDelete(event.getSource());
} else if(event instanceof AfterDeleteEvent) {
@@ -83,6 +87,24 @@ public abstract class AbstractRepositoryEventListener<T extends AbstractReposito
protected void onAfterLinkSave(Object parent, Object linked) {
}
/**
* Override this method if you are interested in {@literal beforeLinkDelete} events.
*
* @param parent
* @param linked
*/
protected void onBeforeLinkDelete(Object parent, Object linked) {
}
/**
* Override this method if you are interested in {@literal afterLinkDelete} events.
*
* @param parent
* @param linked
*/
protected void onAfterLinkDelete(Object parent, Object linked) {
}
/**
* Override this method if you are interested in {@literal beforeDelete} events.
*

View File

@@ -0,0 +1,10 @@
package org.springframework.data.rest.repository.context;
/**
* @author Jon Brisbin
*/
public class AfterLinkDeleteEvent extends LinkSaveEvent {
public AfterLinkDeleteEvent(Object source, Object linked) {
super(source, linked);
}
}

View File

@@ -5,8 +5,7 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class BeforeDeleteEvent
extends RepositoryEvent {
public class BeforeDeleteEvent extends RepositoryEvent {
public BeforeDeleteEvent(Object source) {
super(source);
}

View File

@@ -0,0 +1,10 @@
package org.springframework.data.rest.repository.context;
/**
* @author Jon Brisbin
*/
public class BeforeLinkDeleteEvent extends LinkSaveEvent{
public BeforeLinkDeleteEvent(Object source, Object linked) {
super(source, linked);
}
}

View File

@@ -0,0 +1,63 @@
package org.springframework.data.rest.repository.invoke;
import java.lang.reflect.Method;
/**
* @author Jon Brisbin
*/
public enum CrudMethod {
COUNT,
DELETE_ALL,
DELETE_ONE,
DELETE_SOME,
FIND_ALL,
FIND_ONE,
FIND_SOME,
SAVE_ONE,
SAVE_SOME;
public static CrudMethod fromMethod(Method m) {
String s = m.getName();
Class<?>[] paramTypes = m.getParameterTypes();
boolean some = (paramTypes.length > 0 && Iterable.class.isAssignableFrom(paramTypes[0]));
if("count".equals(s)) {
return COUNT;
} else if("delete".equals(s)) {
return (some ? DELETE_SOME : DELETE_ONE);
} else if("deleteAll".equals(s)) {
return DELETE_ALL;
} else if("findAll".equals(s)) {
return (some ? FIND_SOME : FIND_ALL);
} else if("findOne".equals(s)) {
return FIND_ONE;
} else if("save".equals(s)) {
return (some ? SAVE_SOME : SAVE_ONE);
} else {
return null;
}
}
public String toMethodName() {
switch(this) {
case COUNT:
return "count";
case DELETE_ALL:
return "deleteAll";
case DELETE_ONE:
case DELETE_SOME:
return "delete";
case FIND_ALL:
case FIND_SOME:
return "findAll";
case FIND_ONE:
return "findOne";
case SAVE_ONE:
case SAVE_SOME:
return "save";
default:
return null;
}
}
}

View File

@@ -3,70 +3,16 @@ package org.springframework.data.rest.repository.invoke;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.util.ReflectionUtils;
import org.springframework.data.rest.repository.support.Methods;
/**
* @author Jon Brisbin
*/
public class RepositoryMethod {
public enum Type {
COUNT,
CUSTOM,
DELETE,
FIND_ALL,
FIND_ONE,
SAVE;
public static Type fromMethodName(String s) {
if("count".equals(s)) {
return COUNT;
} else if("delete".equals(s)) {
return DELETE;
} else if("findAll".equals(s)) {
return FIND_ALL;
} else if("findOne".equals(s)) {
return FIND_ONE;
} else if("save".equals(s)) {
return SAVE;
} else {
return CUSTOM;
}
}
public String toMethodName() {
switch(this) {
case COUNT:
return "count";
case DELETE:
return "delete";
case FIND_ALL:
return "findAll";
case FIND_ONE:
return "findOne";
case SAVE:
return "save";
default:
return null;
}
}
}
public static final ReflectionUtils.MethodFilter USER_METHODS = new ReflectionUtils.MethodFilter() {
@Override public boolean matches(Method method) {
return (!method.isSynthetic()
&& !method.isBridge()
&& method.getDeclaringClass() != Object.class
&& !method.getName().contains("$"));
}
};
public static final LocalVariableTableParameterNameDiscoverer NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
private Method method;
private Class<?>[] paramTypes;
private String[] paramNames;
@@ -84,7 +30,7 @@ public class RepositoryMethod {
sortable = true;
}
}
paramNames = NAME_DISCOVERER.getParameterNames(method);
paramNames = Methods.NAME_DISCOVERER.getParameterNames(method);
if(null == paramNames) {
paramNames = new String[paramTypes.length];
}

View File

@@ -17,8 +17,7 @@ import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class JpaAttributeMetadata
implements AttributeMetadata {
public class JpaAttributeMetadata implements AttributeMetadata {
private String name;
private Attribute attribute;

View File

@@ -12,6 +12,7 @@ import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.repository.EntityMetadata;
import org.springframework.data.rest.repository.annotation.RestResource;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
@@ -36,22 +37,28 @@ public class JpaEntityMetadata
for(Attribute attr : entityType.getAttributes()) {
boolean exported = true;
Field field = ReflectionUtils.findField(type, attr.getJavaMember().getName());
if(null != field) {
RestResource fieldResourceAnno = field.getAnnotation(RestResource.class);
if(null != fieldResourceAnno) {
exported = fieldResourceAnno.exported();
}
if(null == field) {
continue;
}
RestResource fieldResourceAnno = field.getAnnotation(RestResource.class);
if(null != fieldResourceAnno) {
exported = fieldResourceAnno.exported();
}
if(exported) {
String name = attr.getName();
if(null != fieldResourceAnno && StringUtils.hasText(fieldResourceAnno.path())) {
name = fieldResourceAnno.path();
}
Class<?> attrType = (attr instanceof PluralAttribute
? ((PluralAttribute)attr).getElementType().getJavaType()
: attr.getJavaType());
if(repositories.hasRepositoryFor(attrType)) {
linkedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
linkedAttributes.put(name, new JpaAttributeMetadata(entityType, attr));
} else {
if(!(attr instanceof SingularAttribute && ((SingularAttribute)attr).isId())
&& !(attr instanceof SingularAttribute && ((SingularAttribute)attr).isVersion())) {
embeddedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
embeddedAttributes.put(name, new JpaAttributeMetadata(entityType, attr));
}
}
}

View File

@@ -13,21 +13,23 @@ import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.repository.RepositoryMetadata;
import org.springframework.data.rest.repository.annotation.RestResource;
import org.springframework.data.rest.repository.invoke.CrudMethod;
import org.springframework.data.rest.repository.invoke.RepositoryQueryMethod;
import org.springframework.data.rest.repository.support.Methods;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class JpaRepositoryMetadata
implements RepositoryMetadata<JpaEntityMetadata> {
public class JpaRepositoryMetadata implements RepositoryMetadata<JpaEntityMetadata> {
private final String name;
private final Class<?> repoClass;
private final CrudRepository<Object, Serializable> repository;
private final EntityInformation entityInfo;
private final Map<String, RepositoryQueryMethod> queryMethods = new HashMap<String, RepositoryQueryMethod>();
private final Map<CrudMethod, Boolean> crudMethodExposed = new HashMap<CrudMethod, Boolean>();
private final Map<String, RepositoryQueryMethod> queryMethods = new HashMap<String, RepositoryQueryMethod>();
private String rel;
private JpaEntityMetadata entityMetadata;
@@ -66,6 +68,24 @@ public class JpaRepositoryMetadata
}
}
ReflectionUtils.doWithMethods(
repoClass,
new ReflectionUtils.MethodCallback() {
@Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
CrudMethod cr = CrudMethod.fromMethod(method);
RestResource rr = method.getAnnotation(RestResource.class);
if(null != rr) {
crudMethodExposed.put(cr, rr.exported());
}
}
},
new ReflectionUtils.MethodFilter() {
@Override public boolean matches(Method method) {
return (null != CrudMethod.fromMethod(method) && Methods.USER_METHODS.matches(method));
}
}
);
Metamodel metamodel = entityManager.getMetamodel();
entityMetadata = new JpaEntityMetadata(repositories, metamodel.entity(entityInfo.getJavaType()));
}
@@ -102,6 +122,15 @@ public class JpaRepositoryMetadata
return Collections.unmodifiableMap(queryMethods);
}
@Override public Boolean exportsMethod(CrudMethod method) {
Boolean b = crudMethodExposed.get(method);
if(null == b) {
return true;
} else {
return b;
}
}
@Override public String toString() {
return "JpaRepositoryMetadata{" +
"name='" + name + '\'' +

View File

@@ -0,0 +1,28 @@
package org.springframework.data.rest.repository.support;
import java.lang.reflect.Method;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin
*/
public abstract class Methods {
private Methods() {
}
public static final ReflectionUtils.MethodFilter USER_METHODS =
new ReflectionUtils.MethodFilter() {
@Override public boolean matches(Method method) {
return (!method.isSynthetic()
&& !method.isBridge()
&& method.getDeclaringClass() != Object.class
&& !method.getName().contains("$"));
}
};
public static final LocalVariableTableParameterNameDiscoverer NAME_DISCOVERER =
new LocalVariableTableParameterNameDiscoverer();
}