From 7db97e96f24ca9ca5e00f7a9fa80e3cd22107e19 Mon Sep 17 00:00:00 2001 From: Shad Sharma Date: Fri, 16 Oct 2015 16:32:13 -0500 Subject: [PATCH 1/5] initial commit for hibernate5 support --- .../elide-dbmanager-hibernate5/.gitignore | 1 + .../elide-dbmanager-hibernate5/pom.xml | 73 +++++ .../hibernate5/HibernateManager.java | 293 ++++++++++++++++++ .../yahoo/elide/security/CriteriaCheck.java | 21 ++ .../hibernate5/HibernateManagerTest.java | 13 + elide-dbmanager/pom.xml | 1 + 6 files changed, 402 insertions(+) create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/.gitignore create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/pom.xml create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManager.java create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/security/CriteriaCheck.java create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/.gitignore b/elide-dbmanager/elide-dbmanager-hibernate5/.gitignore new file mode 100644 index 0000000000..2f7896d1d1 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml new file mode 100644 index 0000000000..a9ae0f66b1 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + elide-dbmanager-hibernate5 + jar + Elide Hibernate5 Database Manager + Elide database manager for Hibernate5 support + + com.yahoo.elide + elide-dbmanager-parent-pom + 0.0.1.0-SNAPSHOT + + + + + com.yahoo.elide + elide-core + + + org.hibernate + hibernate-core + 5.0.2.Final + + + slf4j-api + org.slf4j + + + xml-apis + xml-apis + + + jasper-runtime + tomcat + + + xerces + xerces + + + jasper-compiler + tomcat + + + + + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate5 + 2.6.3 + + + + org.testng + testng + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManager.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManager.java new file mode 100644 index 0000000000..60e50edda0 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManager.java @@ -0,0 +1,293 @@ +/* + * Copyright 2015, Yahoo Inc. + * Licensed under the Apache License, Version 2.0 + * See LICENSE file in project root for terms. + */ +package com.yahoo.elide.dbmanagers.hibernate5; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterators; +import com.yahoo.elide.core.DatabaseManager; +import com.yahoo.elide.core.DatabaseTransaction; +import com.yahoo.elide.core.EntityDictionary; +import com.yahoo.elide.core.FilterScope; +import com.yahoo.elide.core.RequestScope; +import com.yahoo.elide.core.exceptions.TransactionException; +import com.yahoo.elide.security.Check; +import com.yahoo.elide.security.CriteriaCheck; +import com.yahoo.elide.security.User; + +import org.hibernate.EntityMode; +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.ObjectNotFoundException; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.hibernate.metadata.ClassMetadata; + +import java.io.IOException; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; + +import lombok.NonNull; +import org.hibernate.resource.transaction.spi.TransactionStatus; + +/** + * Hibernate interface library + */ +public class HibernateManager extends DatabaseManager { + + /** + * Wraps ScrollableResult as Iterator + * @param type of return object + */ + public static class ScrollableIterator implements Iterable, Iterator { + + final private ScrollableResults scroll; + private boolean inUse = false; + private boolean hasNext; + + public ScrollableIterator(ScrollableResults scroll) { + this.scroll = scroll; + } + + @Override + public Iterator iterator() { + if (inUse) { + throw new ConcurrentModificationException(); + } + + if (!scroll.first()) { + return Collections.emptyListIterator(); + } + + inUse = true; + hasNext = true; + return Iterators.unmodifiableIterator(this); + } + + @Override + public boolean hasNext() { + return hasNext; + + } + + @Override + public @NonNull T next() { + @SuppressWarnings("unchecked") + @NonNull T row = (T) scroll.get()[0]; + Preconditions.checkNotNull(row); + hasNext = scroll.next(); + return row; + } + } + + /** + * Hibernate Transaction implementation + */ + public class HibernateTransaction implements DatabaseTransaction { + + private Transaction transaction; + private LinkedHashSet deferredTasks = new LinkedHashSet<>(); + + /** + * Instantiates a new Hibernate transaction. + * + * @param transaction the transaction + */ + public HibernateTransaction(Transaction transaction) { + this.transaction = transaction; + } + + @Override + public void delete(Object object) { + deferredTasks.add(() -> getSession().delete(object)); + } + + @Override + public void save(Object object) { + deferredTasks.add(() -> getSession().saveOrUpdate(object)); + } + + @Override + public void flush() { + try { + for (Runnable task : deferredTasks) { + task.run(); + } + deferredTasks.clear(); + getSession().flush(); + } catch (HibernateException e) { + throw new TransactionException(e); + } + } + + @Override + public void commit() { + try { + this.flush(); + this.transaction.commit(); + } catch (HibernateException e) { + throw new TransactionException(e); + } + } + + @Override + public T createObject(Class entityClass) { + try { + T object = entityClass.newInstance(); + deferredTasks.add(() -> getSession().persist(object)); + return object; + } catch (InstantiationException | IllegalAccessException e) { + return null; + } + } + + @Override + public T loadObject(Class loadClass, String id) { + @SuppressWarnings("unchecked") + T record = (T) getSession().load(loadClass, Long.valueOf(id)); + try { + Hibernate.initialize(record); + } catch (ObjectNotFoundException e) { + return null; + } + return record; + } + + @Override + public Iterable loadObjects(Class loadClass) { + @SuppressWarnings("unchecked") + Iterable list = new ScrollableIterator(getSession().createCriteria(loadClass) + .scroll(ScrollMode.FORWARD_ONLY)); + return list; + } + @Override + public Iterable loadObjects(Class loadClass, FilterScope filterScope) { + Criterion criterion = buildCriterion(filterScope); + + // if no criterion then return all objects + if (criterion == null) { + return loadObjects(loadClass); + } + + @SuppressWarnings("unchecked") + Iterable list = new ScrollableIterator(getSession().createCriteria(loadClass) + .add(criterion) + .scroll(ScrollMode.FORWARD_ONLY)); + return list; + } + + /** + * builds criterion if all checks implement CriteriaCheck + * @param Filter type + * @param filterScope Filter Scope + */ + public Criterion buildCriterion(FilterScope filterScope) { + Criterion compositeCriterion = null; + List> checks = filterScope.getChecks(); + RequestScope requestScope = filterScope.getRequestScope(); + for (Check check : checks) { + Criterion criterion; + if (check instanceof CriteriaCheck) { + criterion = ((CriteriaCheck) check).getCriterion(requestScope); + } else { + criterion = null; + } + + // if no criterion, examine userPermission and ANY state + if (criterion == null) { + switch (filterScope.getRequestScope().getUser().checkUserPermission(check)) { + // ALLOW and ALL try more criteria + case ALLOW: + if (!filterScope.isAny()) { + continue; + } + break; + + // DENY and ANY check try more criteria + case DENY: + if (filterScope.isAny()) { + continue; + } + break; + } + + // Otherwise no criteria filtering possible + return null; + } else if (compositeCriterion == null) { + compositeCriterion = criterion; + } else if (filterScope.isAny()) { + compositeCriterion = Restrictions.or(compositeCriterion, criterion); + } else { + compositeCriterion = Restrictions.and(compositeCriterion, criterion); + } + } + return compositeCriterion; + } + + @Override + public void close() throws IOException { + if (this.transaction.getStatus() == TransactionStatus.ACTIVE) { + transaction.rollback(); + throw new IOException("Transaction not closed"); + } + } + + @Override + public User accessUser(Object opaqueUser) { + return new User(opaqueUser); + } + } + + private final SessionFactory sessionFactory; + + /** + * Initialize HibernateManager and dictionaries + * + * @param aSessionFactory the a session factory + */ + public HibernateManager(SessionFactory aSessionFactory) { + this.sessionFactory = aSessionFactory; + } + + @Override + public void populateEntityDictionary(EntityDictionary dictionary) { + /* bind all entities */ + for (ClassMetadata meta : sessionFactory.getAllClassMetadata().values()) { + dictionary.bindEntity(meta.getMappedClass()); + } + } + + /** + * Get current Hibernate session + * + * @return session + */ + public Session getSession() { + Session session = sessionFactory.getCurrentSession(); + Preconditions.checkNotNull(session); + Preconditions.checkArgument(session.isConnected()); + return session; + } + + /** + * Start Hibernate transaction + * + * @return transaction + */ + @Override + public DatabaseTransaction beginTransaction() { + Session session = sessionFactory.getCurrentSession(); + Preconditions.checkNotNull(session); + return new HibernateTransaction(session.beginTransaction()); + } +} diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/security/CriteriaCheck.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/security/CriteriaCheck.java new file mode 100644 index 0000000000..ad53699bf1 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/security/CriteriaCheck.java @@ -0,0 +1,21 @@ +/* + * Copyright 2015, Yahoo Inc. + * Licensed under the Apache License, Version 2.0 + * See LICENSE file in project root for terms. + */ +package com.yahoo.elide.security; + +import com.yahoo.elide.core.RequestScope; + +import org.hibernate.criterion.Criterion; + +/** + * Extends Check to support Hibernate Criteria to limit SQL query responses. + * @param Type of record for Check + */ +public interface CriteriaCheck extends Check { + /** + * @see org.hibernate.criterion.Restrictions + */ + Criterion getCriterion(RequestScope requestScope); +} diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java new file mode 100644 index 0000000000..b860740069 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java @@ -0,0 +1,13 @@ +package com.yahoo.elide.dbmanagers.hibernate5; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class HibernateManagerTest { + // TODO: Create tests for our manager + @Test + public void todo() { + System.err.println("Still need to write tests for hibernate3 manager!"); + } +} \ No newline at end of file diff --git a/elide-dbmanager/pom.xml b/elide-dbmanager/pom.xml index 408464283c..45b8c2f287 100644 --- a/elide-dbmanager/pom.xml +++ b/elide-dbmanager/pom.xml @@ -31,6 +31,7 @@ + elide-dbmanager-hibernate5 elide-dbmanager-hibernate3 elide-dbmanager-inmemorydb From 53f412c2bfaa4c982cee854ec5d362b4a43bb8a4 Mon Sep 17 00:00:00 2001 From: Shad Sharma Date: Sun, 18 Oct 2015 16:43:18 -0500 Subject: [PATCH 2/5] add PersistenceManager --- .../hibernate5/PersistenceManager.java | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java new file mode 100644 index 0000000000..f835ca4074 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java @@ -0,0 +1,102 @@ +package com.yahoo.elide.dbmanagers.hibernate5; + +import com.yahoo.elide.core.DatabaseManager; +import com.yahoo.elide.core.DatabaseTransaction; +import com.yahoo.elide.core.EntityDictionary; +import com.yahoo.elide.security.User; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.metamodel.EntityType; +import java.io.IOException; + +/** + * Manager for javax.persistence compatible db resource + */ +public class PersistenceManager extends DatabaseManager { + private final EntityManagerFactory entityManagerFactory; + + public PersistenceManager(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + @Override + public void populateEntityDictionary(EntityDictionary dictionary) { + for (EntityType entity : entityManagerFactory.getMetamodel().getEntities()) { + dictionary.bindEntity(entity.getBindableJavaType()); + } + } + + @Override + public DatabaseTransaction beginTransaction() { + return new PersistenceTransaction(entityManagerFactory.createEntityManager()); + } + + public class PersistenceTransaction implements DatabaseTransaction { + private final EntityManager entityManager; + + public PersistenceTransaction(EntityManager entityManager) { + this.entityManager = entityManager; + this.entityManager.getTransaction().begin(); + } + + @Override + public void save(Object entity) { + entityManager.persist(entity); + } + + @Override + public void delete(Object entity) { + entityManager.remove(entity); + } + + @Override + public void flush() { + entityManager.flush(); + } + + @Override + public void commit() { + flush(); + entityManager.getTransaction().commit(); + } + + @Override + public T createObject(Class entityClass) { + try { + T entity = entityClass.newInstance(); + entityManager.persist(entity); + return entity; + } catch (InstantiationException | IllegalAccessException e) { + return null; + } + } + + @Override + public T loadObject(Class entityClass, String id) { + return entityManager.find(entityClass, Long.valueOf(id)); + } + + @Override + public Iterable loadObjects(Class entityClass) { + return entityManager.createQuery("from " + entityClass.getName(), entityClass).getResultList(); + } + + @Override + public void close() throws IOException { + try { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + throw new IOException("Transaction not closed"); + } + } finally { + entityManager.close(); + } + } + + @Override + public User accessUser(Object opaqueUser) { + return new User(opaqueUser); + } + } +} From a4f1861913f3605f7207e266b04c335f0173a63b Mon Sep 17 00:00:00 2001 From: Shad Sharma Date: Sun, 18 Oct 2015 17:05:29 -0500 Subject: [PATCH 3/5] fix parent pom version --- elide-dbmanager/elide-dbmanager-hibernate5/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml index a9ae0f66b1..6cdfa1f135 100644 --- a/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml +++ b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml @@ -13,7 +13,7 @@ com.yahoo.elide elide-dbmanager-parent-pom - 0.0.1.0-SNAPSHOT + 1.0.0.1-SNAPSHOT From 4e3e89a8dc70b74f18a201309297feadbe87500f Mon Sep 17 00:00:00 2001 From: Shad Sharma Date: Mon, 19 Oct 2015 10:35:02 -0500 Subject: [PATCH 4/5] address @deathbytape comments --- .../elide-dbmanager-hibernate5/pom.xml | 30 +++++++++++++++++++ .../hibernate5/HibernateManagerTest.java | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml index 6cdfa1f135..ac5694a826 100644 --- a/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml +++ b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml @@ -10,12 +10,34 @@ jar Elide Hibernate5 Database Manager Elide database manager for Hibernate5 support + https://github.com/yahoo/elide com.yahoo.elide elide-dbmanager-parent-pom 1.0.0.1-SNAPSHOT + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Yahoo Inc. + https://github.com/yahoo + + + + + scm:git:ssh://git@github.com/yahoo/elide.git + https://github.com/yahoo/elide.git + HEAD + + com.yahoo.elide @@ -68,6 +90,14 @@ org.apache.maven.plugins maven-compiler-plugin + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java index b860740069..6784bf03e4 100644 --- a/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/test/java/com/yahoo/elide/dbmanagers/hibernate5/HibernateManagerTest.java @@ -10,4 +10,4 @@ public class HibernateManagerTest { public void todo() { System.err.println("Still need to write tests for hibernate3 manager!"); } -} \ No newline at end of file +} From 4adf5c54167cd8aba6f09120707537daeb2fd611 Mon Sep 17 00:00:00 2001 From: Shad Sharma Date: Mon, 19 Oct 2015 12:27:26 -0500 Subject: [PATCH 5/5] address @DeathByTape comments --- .../yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java index f835ca4074..b3a16f2bdb 100644 --- a/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java @@ -1,5 +1,6 @@ package com.yahoo.elide.dbmanagers.hibernate5; +import com.google.common.base.Preconditions; import com.yahoo.elide.core.DatabaseManager; import com.yahoo.elide.core.DatabaseTransaction; import com.yahoo.elide.core.EntityDictionary; @@ -17,6 +18,7 @@ public class PersistenceManager extends DatabaseManager { private final EntityManagerFactory entityManagerFactory; public PersistenceManager(EntityManagerFactory entityManagerFactory) { + Preconditions.checkNotNull(entityManagerFactory); this.entityManagerFactory = entityManagerFactory; }