/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.services.persistence; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.persistence.Cache; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; import javax.persistence.PersistenceUnitUtil; import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.metamodel.Metamodel; import org.apache.commons.lang.NotImplementedException; import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.skalli.services.Services; import org.junit.Test; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceRegistration; import org.osgi.service.jpa.EntityManagerFactoryBuilder; @SuppressWarnings("nls") public class EntityManagerServiceImplTest { private static final String TEST_JPA_UNIT_NAME = "EntityManagerServiceImplTestJpaUnitName"; private static final Map<Object,Object> TEST_JPA_SERVICE_PROPS = Collections.singletonMap((Object)"osgi.unit.name", (Object)TEST_JPA_UNIT_NAME); private static final String TEST_DRIVER = "MyDriver"; private static final String TEST_URL = "MyUrl"; private static final String TEST_USER = "MyUser"; private static final String TEST_PWD = "MyPWD"; private static final String TEST_TARGET_DB = "MyTargetDB"; private class EntityManagerMock implements EntityManager { private Map<String, Object> properties; public EntityManagerMock(Map<String, Object> properties) { this.properties = properties; } @Override public void clear() { } @Override public void close() { } @Override public boolean contains(Object arg0) { return false; } @Override public Query createNamedQuery(String arg0) { throw new NotImplementedException(); } @Override public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1) { throw new NotImplementedException(); } @Override public Query createNativeQuery(String arg0) { throw new NotImplementedException(); } @Override public Query createNativeQuery(String arg0, Class arg1) { throw new NotImplementedException(); } @Override public Query createNativeQuery(String arg0, String arg1) { throw new NotImplementedException(); } @Override public Query createQuery(String arg0) { throw new NotImplementedException(); } @Override public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0) { throw new NotImplementedException(); } @Override public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1) { throw new NotImplementedException(); } @Override public void detach(Object arg0) { } @Override public <T> T find(Class<T> arg0, Object arg1) { throw new NotImplementedException(); } @Override public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) { throw new NotImplementedException(); } @Override public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2) { throw new NotImplementedException(); } @Override public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2, Map<String, Object> arg3) { throw new NotImplementedException(); } @Override public void flush() { } @Override public CriteriaBuilder getCriteriaBuilder() { throw new NotImplementedException(); } @Override public Object getDelegate() { throw new NotImplementedException(); } @Override public EntityManagerFactory getEntityManagerFactory() { throw new NotImplementedException(); } @Override public FlushModeType getFlushMode() { throw new NotImplementedException(); } @Override public LockModeType getLockMode(Object arg0) { throw new NotImplementedException(); } @Override public Metamodel getMetamodel() { throw new NotImplementedException(); } @Override public Map<String, Object> getProperties() { return this.properties; } @Override public <T> T getReference(Class<T> arg0, Object arg1) { throw new NotImplementedException(); } @Override public EntityTransaction getTransaction() { throw new NotImplementedException(); } @Override public boolean isOpen() { return false; } @Override public void joinTransaction() { } @Override public void lock(Object arg0, LockModeType arg1) { } @Override public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2) { } @Override public <T> T merge(T arg0) { throw new NotImplementedException(); } @Override public void persist(Object arg0) { } @Override public void refresh(Object arg0) { } @Override public void refresh(Object arg0, Map<String, Object> arg1) { } @Override public void refresh(Object arg0, LockModeType arg1) { } @Override public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2) { } @Override public void remove(Object arg0) { } @Override public void setFlushMode(FlushModeType arg0) { } @Override public void setProperty(String arg0, Object arg1) { } @Override public <T> T unwrap(Class<T> arg0) { return null; } } private class EntityManagerFactoryMock implements EntityManagerFactory { private Map<String, Object> props; public EntityManagerFactoryMock(Map props) { this.props = props; } @Override public boolean isOpen() { return false; } @Override public Map<String, Object> getProperties() { return props; } @Override public PersistenceUnitUtil getPersistenceUnitUtil() { return null; } @Override public Metamodel getMetamodel() { return null; } @Override public CriteriaBuilder getCriteriaBuilder() { return null; } @Override public Cache getCache() { return null; } @SuppressWarnings("unchecked") @Override public EntityManager createEntityManager(Map props) { this.props = props; return new EntityManagerMock(props); } @Override public EntityManager createEntityManager() { return new EntityManagerMock(props); } @Override public void close() { } } private class EntityManagerFactoryBuilderMock implements EntityManagerFactoryBuilder { private EntityManagerFactory emfMock; @Override public EntityManagerFactory createEntityManagerFactory(Map props) { this.emfMock = new EntityManagerFactoryMock(props); return emfMock; } } @Test public void testGetEntityManagerReturnsConfiguredProps() throws Exception { Map<String, Object> expectedProps = getDefaultPersistenceProperties(); Map<String, Object> aktualProps = getPropertiesOfGetEntityManagerCall(expectedProps); assertPersistenceProperties(aktualProps); } @Test public void testGetEntityManagerMissingConfigProperties() throws Exception { for (String property: EntityManagerServiceBase.REQUIRED_PROPERTIES) { Map<String, Object> expectedProps = getDefaultPersistenceProperties(); expectedProps.remove(property); try { getPropertiesOfGetEntityManagerCall(expectedProps); fail("StorageException expected due to missing property " + property); } catch (IOException e) { // expected } } } @Test public void testGetEntityManagerFromInjectedFactory() throws Exception { Hashtable<String, String> serviceProps = new Hashtable<String, String>(); serviceProps.put("osgi.unit.name", TEST_JPA_UNIT_NAME); BundleContext context = FrameworkUtil.getBundle(EntityManagerServiceImplTest.class).getBundleContext(); ServiceRegistration<EntityManagerFactory> registration = null; try { registration = context.registerService(EntityManagerFactory.class, new EntityManagerFactoryMock( getDefaultPersistenceProperties()), serviceProps); Map<String, Object> aktualProps = getPropertiesOfGetEntityManagerCall(null); assertPersistenceProperties(aktualProps); } finally { registration.unregister(); } } @Test public void testGetEntityManagerLazyFactoryAppearsWithinTimeout() throws Exception { // factory appears with delay of 250 milliseconds, client is willing to wait for 1 second => success assertLazyFactory(1000, 250, 1000); } @Test(expected=IOException.class) public void testGetEntityManagerLazyFactoryAppearsTooLate() throws Exception { // factory appears with delay of 250 millisecond, client is willing to wait for 100 millisecond only => failure assertLazyFactory(100, 250, 1000); } @Test(expected=IOException.class) public void testGetEntityManagerNoWait() throws Exception { // factory appears with delay of 10 milliseconds only, client is not willing to wait at all => failure assertLazyFactory(-1, 10, 1000); } private void assertLazyFactory(long timeout, long initialDelay, long awaitDone) throws Exception { CountDownLatch latchStart = new CountDownLatch(1); CountDownLatch latchDone = new CountDownLatch(1); // thread that registers the entity manager factory: waits on latchStart, then registers the service with // a delay of initialDelay millisecond, then waits on latchDone for max. awaitDone milliseconds; // asserts that latchDone has been triggered before the timeout elapsed; then it terminates RegisterRunnable registerRunnable = new RegisterRunnable(latchStart, latchDone, initialDelay, awaitDone); Thread registerThread = new Thread(registerRunnable); // thread that waits on a entity manager factory to appear within the timeout milliseconds; // if a factory appears, asserts that the entity manager has the expected // properties; then it triggers latchDone before terminating ClientRunnable clientRunnable = new ClientRunnable(latchStart, latchDone, timeout); Thread clientThread = new Thread(clientRunnable); registerThread.start(); clientThread.start(); registerThread.join(); clientThread.join(); if (registerRunnable.failure != null) { throw registerRunnable.failure; } if (clientRunnable.failure != null) { throw clientRunnable.failure; } assertTrue(registerRunnable.done); assertTrue(clientRunnable.done); } @Test public void testGetAllPersistenceProperties() { Set<String> allFieldValues = EntityManagerServiceBase.getPublicStaticFinalFieldValues(PersistenceUnitProperties.class); //check that the most relevant properties of PersistenceUnitProperties have not disappeared assertThat(allFieldValues, hasItems(PersistenceUnitProperties.JDBC_DRIVER, PersistenceUnitProperties.JDBC_URL, PersistenceUnitProperties.JDBC_USER, PersistenceUnitProperties.JDBC_PASSWORD, PersistenceUnitProperties.TARGET_DATABASE)); } private static Map<String, Object> getDefaultPersistenceProperties() { Map<String, Object> properties = new HashMap<String, Object>(5); properties.put(PersistenceUnitProperties.JDBC_DRIVER, TEST_DRIVER); properties.put(PersistenceUnitProperties.JDBC_URL, TEST_URL); properties.put(PersistenceUnitProperties.JDBC_USER, TEST_USER); properties.put(PersistenceUnitProperties.JDBC_PASSWORD, TEST_PWD); properties.put(PersistenceUnitProperties.TARGET_DATABASE, TEST_TARGET_DB); return properties; } private void assertPersistenceProperties(Map<String, Object> aktualProps) { assertThat(aktualProps.get(PersistenceUnitProperties.JDBC_DRIVER).toString(), is(TEST_DRIVER)); assertThat(aktualProps.get(PersistenceUnitProperties.JDBC_URL).toString(), is(TEST_URL)); assertThat(aktualProps.get(PersistenceUnitProperties.JDBC_USER).toString(), is(TEST_USER)); assertThat(aktualProps.get(PersistenceUnitProperties.JDBC_PASSWORD).toString(), is(TEST_PWD)); assertThat(aktualProps.get(PersistenceUnitProperties.TARGET_DATABASE).toString(), is(TEST_TARGET_DB)); assertThat(aktualProps.size(), is(5)); } private Map<String, Object> getPropertiesOfGetEntityManagerCall(Map<String, Object> expectedProps) throws IOException { EntityManagerServiceBase ems = new EntityManagerServiceBase() { @Override protected EntityManagerFactory locateEntityManagerFactory() { return Services.getService(EntityManagerFactory.class); } }; ems.bindEntityManagerFactoryBuilder(new EntityManagerFactoryBuilderMock(), TEST_JPA_SERVICE_PROPS); EntityManagerMock emMock = (EntityManagerMock) ems.getEntityManager(expectedProps); Map<String, Object> aktualProps = emMock.getProperties(); return aktualProps; } private class RegisterRunnable implements Runnable { public boolean done; public Exception failure; private CountDownLatch latchStart; private CountDownLatch latchDone; private long initialDelay; private long awaitDone; public RegisterRunnable(CountDownLatch latchStart, CountDownLatch latchDone, long initialDelay, long awaitDone) { this.latchStart = latchStart; this.latchDone = latchDone; this.initialDelay = initialDelay; this.awaitDone = awaitDone; } @Override public void run() { try { latchStart.await(); Thread.sleep(initialDelay); } catch (InterruptedException e) { failure = e; return; } Hashtable<String, String> serviceProps = new Hashtable<String, String>(); serviceProps.put("osgi.unit.name", TEST_JPA_UNIT_NAME); BundleContext context = FrameworkUtil.getBundle(EntityManagerServiceImplTest.class).getBundleContext(); ServiceRegistration<EntityManagerFactory> registration = null; try { registration = context.registerService(EntityManagerFactory.class, new EntityManagerFactoryMock( getDefaultPersistenceProperties()), serviceProps); done = latchDone.await(awaitDone, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { failure = e; } finally { registration.unregister(); } } } private class ClientRunnable implements Runnable { public boolean done; public Exception failure; private CountDownLatch latchStart; private CountDownLatch latchDone; private long timeout; public ClientRunnable(CountDownLatch latchStart, CountDownLatch latchDone, long timeout) { this.latchStart = latchStart; this.latchDone = latchDone; this.timeout = timeout; } @Override public void run() { try { System.setProperty(EntityManagerServiceBase.SKALLI_EMF_TIMEOUT, Long.toString(timeout)); latchStart.countDown(); Map<String, Object> aktualProps = getPropertiesOfGetEntityManagerCall(null); assertPersistenceProperties(aktualProps); latchDone.countDown(); done = true; } catch (IOException e) { failure = e; } finally { System.clearProperty(EntityManagerServiceBase.SKALLI_EMF_TIMEOUT); } } } }