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..ac5694a826 --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + elide-dbmanager-hibernate5 + 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 + 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 + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-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/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..b3a16f2bdb --- /dev/null +++ b/elide-dbmanager/elide-dbmanager-hibernate5/src/main/java/com/yahoo/elide/dbmanagers/hibernate5/PersistenceManager.java @@ -0,0 +1,104 @@ +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; +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) { + Preconditions.checkNotNull(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); + } + } +} 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..6784bf03e4 --- /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!"); + } +} 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