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:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
}
|
||||
@@ -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 {};
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + '\'' +
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user