Author: rmannibucau
Date: Wed Aug 3 12:58:30 2011
New Revision: 1153468
URL: http://svn.apache.org/viewvc?rev=1153468&view=rev
Log:
adding support for save/delete/update in QueryProxy
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/BeanContext.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/QueryProxy.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/DynamicEJBImplTest.java
openejb/trunk/openejb3/examples/rest-on-ejb/src/main/java/org/superbiz/rest/User.java
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/BeanContext.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/BeanContext.java?rev=1153468&r1=1153467&r2=1153468&view=diff
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/BeanContext.java
(original)
+++
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/BeanContext.java
Wed Aug 3 12:58:30 2011
@@ -1204,7 +1204,7 @@ public class BeanContext extends Deploym
WebBeansContext webBeansContext = null;
AbstractInjectionTargetBean<Object> beanDefinition = null;
ConstructorInjectionBean<Object> beanConstructor = null;
- if (!beanClass.equals(localInterface)) { // not a dynamic proxy
implementation
+ if (!isDynamicallyImplemented()) { // not a dynamic proxy
implementation
webBeansContext =
getModuleContext().getAppContext().getWebBeansContext();
beanDefinition = get(CdiEjbBean.class);
@@ -1221,13 +1221,13 @@ public class BeanContext extends Deploym
final Class beanClass = this.getBeanClass();
CreationalContext<Object> creationalContext =
get(CreationalContext.class);
- if (webBeansContext != null && creationalContext == null) {
+ if (creationalContext == null && !isDynamicallyImplemented()) {
creationalContext =
webBeansContext.getBeanManagerImpl().createCreationalContext(beanDefinition);
}
// Create bean instance
final Object beanInstance;
- if (beanConstructor != null) {
+ if (!isDynamicallyImplemented()) {
final InjectionProcessor injectionProcessor = new
InjectionProcessor(beanConstructor.create(creationalContext),
this.getInjections(), InjectionProcessor.unwrap(ctx));
beanInstance = injectionProcessor.createInstance();
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java?rev=1153468&r1=1153467&r2=1153468&view=diff
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
(original)
+++
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
Wed Aug 3 12:58:30 2011
@@ -132,9 +132,8 @@ public abstract class EjbHomeProxyHandle
EjbObjectProxyHandler handler =
newEjbObjectHandler(getBeanContext(), primaryKey, objectInterfaceType,
this.getInterfaces(), mainInterface);
// TODO Is it correct for ManagedBean injection via managed bean
class?
- if (getBeanContext().isDynamicallyImplemented()) { // dynamic
proxy implementation
- return DynamicProxyImplFactory.newProxy(getBeanContext());
- } if (InterfaceType.LOCALBEAN.equals(objectInterfaceType) ||
getBeanContext().getComponentType().equals(BeanType.MANAGED)) {
+ if ((InterfaceType.LOCALBEAN.equals(objectInterfaceType) ||
getBeanContext().getComponentType().equals(BeanType.MANAGED))
+ &&! getBeanContext().isDynamicallyImplemented()) {
return
LocalBeanProxyFactory.newProxyInstance(handler.getBeanContext().getClassLoader(),
handler.getBeanContext().getBeanClass(), handler);
} else {
List<Class> proxyInterfaces = new
ArrayList<Class>(handler.getInterfaces().size() + 1);
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/QueryProxy.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/QueryProxy.java?rev=1153468&r1=1153467&r2=1153468&view=diff
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/QueryProxy.java
(original)
+++
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/QueryProxy.java
Wed Aug 3 12:58:30 2011
@@ -1,19 +1,24 @@
package org.apache.openejb.util.proxy;
+import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import javax.persistence.EntityManager;
import javax.persistence.Query;
+import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
+import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -21,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -30,11 +36,14 @@ public class QueryProxy implements Invoc
private static final Logger LOGGER =
Logger.getInstance(LogCategory.OPENEJB, QueryProxy.class);
// keywords
+ public static final String PERSIST_NAME = "save";
+ public static final String MERGE_NAME = "update";
+ public static final String REMOVE_NAME = "delete";
public static final String FIND_PREFIX = "find";
public static final String BY = "By";
public static final String AND = "And";
- // cache for current instance
+ // cache for finders of the current instance
private final Map<String, Class<?>> RETURN_TYPES = new
ConcurrentHashMap<String, Class<?>>();
private final Map<String, List<String>> CONDITIONS = new
ConcurrentHashMap<String, List<String>>();
@@ -49,24 +58,113 @@ public class QueryProxy implements Invoc
return method.invoke(this, args);
}
- if (!method.getName().startsWith(FIND_PREFIX)) {
- throw new IllegalArgumentException("finder should start with
find");
+ final String methodName = method.getName();
+ final Class<?> returnType = method.getReturnType();
+
+ // simple cases
+ if (PERSIST_NAME.equals(methodName)) {
+ persist(args, returnType);
+ return null; // void
+ }
+
+ if (MERGE_NAME.equals(methodName)) {
+ return merge(args, returnType);
+ }
+
+ if (REMOVE_NAME.equals(methodName)) {
+ remove(args, returnType);
+ return null; // void
}
+ // other cases (finders)
+ if (methodName.startsWith(FIND_PREFIX)) {
+ return find(method, args);
+ }
+
+ throw new IllegalArgumentException("method not yet managed");
+ }
+
+ private Object find(Method method, Object[] args) {
final String methodName = method.getName();
+
Class<?> type;
if (RETURN_TYPES.containsKey(methodName)) {
type = RETURN_TYPES.get(methodName);
} else {
type = getGenericType(method.getGenericReturnType());
+ RETURN_TYPES.put(methodName, type);
}
- Query query = getQuery(em, methodName, type, args);
+
+ Query query = createFinderQuery(em, methodName, type, args);
+
if (Collection.class.isAssignableFrom(method.getReturnType())) {
return query.getResultList();
}
return query.getSingleResult();
}
+ private void remove(Object[] args, Class<?> returnType) {
+ if (args != null && args.length == 1 && returnType.equals(Void.TYPE)) {
+ Object entity = args[0];
+ if (!em.contains(entity)) { // should not be done (that's why it
is not cached) but can be easier...
+ final Class<?> entityClass = entity.getClass();
+ final EntityType<? extends Object> et =
em.getMetamodel().entity(entity.getClass());
+
+ if (!et.hasSingleIdAttribute()) {
+ throw new IllegalArgumentException("Dynamic EJB doesn't
manage IdClass yet");
+ }
+
+ SingularAttribute<?, ?> id = null; // = et.getId(entityClass);
doesn't work with openJPA
+ for (SingularAttribute<?, ?> sa : et.getSingularAttributes()) {
+ if (sa.isId()) {
+ id = sa;
+ break;
+ }
+ }
+ if (id == null) {
+ throw new IllegalArgumentException("id field not found");
+ }
+ final String idName = id.getName();
+
+ Object idValue;
+ try {
+ idValue = BeanUtils.getProperty(entity, idName);;
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException("can't invoke to get
entity id");
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("can't find the method
to get entity id");
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("can't access
field/method to get entity id");
+ }
+
+ entity = em.find(et.getJavaType(), idValue);
+ if (entity == null) {
+ throw new IllegalArgumentException("entity " + entity + "
is not managed and can't be found.");
+ }
+ }
+ em.remove(entity);
+ } else {
+ throw new IllegalArgumentException(REMOVE_NAME + " should have
only one parameter and return void");
+ }
+ }
+
+ private Object merge(Object[] args, Class<?> returnType) {
+ if (args != null && args.length == 1 &&
returnType.equals(args[0].getClass())) {
+ return em.merge(args[0]);
+ } else {
+ throw new IllegalArgumentException(MERGE_NAME + " should have only
one parameter and return the same" +
+ " type than the parameter type");
+ }
+ }
+
+ private void persist(Object[] args, Class<?> returnType) {
+ if (args != null && args.length == 1 && returnType.equals(Void.TYPE)) {
+ em.persist(args[0]);
+ } else {
+ throw new IllegalArgumentException(PERSIST_NAME + " should have
only one parameter and return void");
+ }
+ }
+
private Class<?> getGenericType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
@@ -74,11 +172,12 @@ public class QueryProxy implements Invoc
return (Class<?>) pt.getActualTypeArguments()[0];
}
}
- return (Class<?>) type;
+ return Class.class.cast(type);
}
- private <T> Query getQuery(EntityManager entityManager, String methodName,
Class<T> entityType, Object[] args) {
+ private <T> Query createFinderQuery(EntityManager entityManager, String
methodName, Class<T> entityType, Object[] args) {
final List<String> conditions = parseMethodName(methodName);
+
final EntityType<T> et =
entityManager.getMetamodel().entity(entityType);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -114,7 +213,22 @@ public class QueryProxy implements Invoc
query = query.where(where);
}
- return entityManager.createQuery(query);
+ // pagination
+ TypedQuery<?> emQuery = entityManager.createQuery(query);
+ if (args != null && args.length == conditions.size() + 2
+ && isInt(args[args.length - 2].getClass()) &&
isInt(args[args.length - 1].getClass())) {
+ int first = (Integer) args[args.length - 2];
+ int max = (Integer) args[args.length - 1];
+
+ emQuery.setFirstResult(first);
+ emQuery.setMaxResults(max);
+ }
+
+ return emQuery;
+ }
+
+ private boolean isInt(Class<?> aClass) {
+ return Integer.TYPE.equals(aClass) || Integer.class.equals(aClass);
}
private List<String> parseMethodName(final String methodName) {
Modified:
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/DynamicEJBImplTest.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/DynamicEJBImplTest.java?rev=1153468&r1=1153467&r2=1153468&view=diff
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/DynamicEJBImplTest.java
(original)
+++
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/DynamicEJBImplTest.java
Wed Aug 3 12:58:30 2011
@@ -25,23 +25,29 @@ import org.apache.openejb.jee.jpa.unit.P
import org.apache.openejb.junit.ApplicationComposer;
import org.apache.openejb.junit.Configuration;
import org.apache.openejb.junit.Module;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.ejb.EJB;
+import javax.ejb.EJBException;
import javax.ejb.Singleton;
import javax.ejb.Stateless;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import java.util.Collection;
import java.util.Properties;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
/**
* @author rmannibucau
@@ -60,6 +66,11 @@ public class DynamicEJBImplTest {
}
}
+ @After public void checkAll() {
+ Collection<User> users = dao.findAll();
+ assertEquals(10, users.size());
+ }
+
@Test public void simple() {
User user = dao.findById(1);
assertNotNull(user);
@@ -71,6 +82,49 @@ public class DynamicEJBImplTest {
assertEquals(10, users.size());
}
+ @Test public void pagination() {
+ Collection<User> users = dao.findAll(0, 5);
+ assertEquals(5, users.size());
+
+ users = dao.findAll(6, 1);
+ assertEquals(1, users.size());
+ assertEquals(7, users.iterator().next().getId());
+ }
+
+ @Test public void persist() {
+ User u = new User();
+ dao.save(u);
+ assertNotNull(u.getId());
+ util.remove(u);
+ }
+
+ @Test public void remove() {
+ User u = new User();
+ dao.save(u);
+ assertNotNull(u.getId());
+ dao.delete(u);
+ try {
+ dao.findById(u.getId());
+ fail();
+ } catch (EJBException ee) {
+ assertTrue(ee.getCause() instanceof NoResultException);
+ }
+ }
+
+ @Test public void merge() {
+ User u = new User();
+ u.setInfo("one");
+ dao.save(u);
+ assertEquals("one", u.getInfo());
+ assertNotNull(u.getId());
+
+ u.setInfo("another one");
+ dao.update(u);
+ assertEquals("another one", u.getInfo());
+
+ dao.delete(u);
+ }
+
@Test public void oneCriteria() {
Collection<User> users = dao.findByName("foo");
assertEquals(4, users.size());
@@ -102,6 +156,13 @@ public class DynamicEJBImplTest {
Collection<User> findByNameAndInfo(String name, String info);
Collection<User> findAll();
+ Collection<User> findAll(int first, int max);
+
+ void save(User u);
+
+ void delete(User u);
+
+ User update(User u);
}
@Entity public static class User {
@@ -168,6 +229,10 @@ public class DynamicEJBImplTest {
public UserDAO getDao() {
return dao;
}
+
+ public void remove(User u) {
+ em.remove(em.find(u.getClass(), u.getId()));
+ }
}
@Configuration public Properties config() {
Modified:
openejb/trunk/openejb3/examples/rest-on-ejb/src/main/java/org/superbiz/rest/User.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/examples/rest-on-ejb/src/main/java/org/superbiz/rest/User.java?rev=1153468&r1=1153467&r2=1153468&view=diff
==============================================================================
---
openejb/trunk/openejb3/examples/rest-on-ejb/src/main/java/org/superbiz/rest/User.java
(original)
+++
openejb/trunk/openejb3/examples/rest-on-ejb/src/main/java/org/superbiz/rest/User.java
Wed Aug 3 12:58:30 2011
@@ -83,7 +83,7 @@ public class User implements Cloneable {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (o == null || !User.class.isAssignableFrom(o.getClass())) {
return false;
}