/* * This is a common dao with basic CRUD operations and is not limited to any * persistent layer implementation * * Copyright (C) 2010 Imran M Yousuf (imyousuf@smartitengineering.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.smartitengineering.dao.impl.hbase; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.PrivateModule; import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.util.Providers; import com.smartitengineering.dao.hbase.ddl.HBaseTableConfiguration; import com.smartitengineering.dao.hbase.ddl.HBaseTableGenerator; import com.smartitengineering.dao.hbase.ddl.config.json.ConfigurationJsonParser; import com.smartitengineering.dao.impl.hbase.data.SampleDomain; import com.smartitengineering.dao.impl.hbase.data.SampleDomainObjectCoverter; import com.smartitengineering.dao.impl.hbase.spi.AsyncExecutorService; import com.smartitengineering.dao.impl.hbase.spi.CellConfig; import com.smartitengineering.dao.impl.hbase.spi.DomainIdInstanceProvider; import com.smartitengineering.dao.impl.hbase.spi.FilterConfigs; import com.smartitengineering.dao.impl.hbase.spi.LockAttainer; import com.smartitengineering.dao.impl.hbase.spi.LockType; import com.smartitengineering.dao.impl.hbase.spi.MergeService; import com.smartitengineering.dao.impl.hbase.spi.ObjectRowConverter; import com.smartitengineering.dao.impl.hbase.spi.RowCellIncrementor; import com.smartitengineering.dao.impl.hbase.spi.SchemaInfoProvider; import com.smartitengineering.dao.impl.hbase.spi.impl.CellConfigImpl; import com.smartitengineering.dao.impl.hbase.spi.impl.LockAttainerImpl; import com.smartitengineering.dao.impl.hbase.spi.impl.MixedExecutorServiceImpl; import com.smartitengineering.dao.impl.hbase.spi.impl.RowCellIncrementorImpl; import com.smartitengineering.dao.impl.hbase.spi.impl.SchemaInfoProviderBaseConfig; import com.smartitengineering.dao.impl.hbase.spi.impl.SchemaInfoProviderImpl; import com.smartitengineering.dao.impl.hbase.spi.impl.guice.GenericBaseConfigProvider; import com.smartitengineering.dao.impl.hbase.spi.impl.guice.GenericFilterConfigsProvider; import com.smartitengineering.util.bean.guice.GuiceUtil; import java.io.InputStream; import java.util.Collection; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author imyousuf */ public class CommonDaoWriteTest { private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static final Logger LOGGER = LoggerFactory.getLogger(CommonDaoWriteTest.class); private static Injector injector; private static CommonDaos commonDaos; private static RowCellIncrementor<SampleDomain, SampleDomain, Long> incrementor; @BeforeClass public static void globalSetup() throws Exception { /* * Start HBase and initialize tables */ //-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); try { TEST_UTIL.startMiniCluster(); } catch (Exception ex) { LOGGER.error(ex.getMessage(), ex); } //Create table for testing InputStream classpathResource = CommonDaoWriteTest.class.getClassLoader().getResourceAsStream( "com/smartitengineering/dao/impl/hbase/domain/ddl-config-sample.json"); Collection<HBaseTableConfiguration> configs = ConfigurationJsonParser.getConfigurations(classpathResource); try { new HBaseTableGenerator(configs, TEST_UTIL.getConfiguration(), true).generateTables(); } catch (Exception ex) { LOGGER.error("Could not create table!", ex); Assert.fail(ex.getMessage()); } Properties properties = new Properties(); properties.setProperty(GuiceUtil.CONTEXT_NAME_PROP, "com.smartitengineering.dao.impl.hbase"); properties.setProperty(GuiceUtil.IGNORE_MISSING_DEP_PROP, Boolean.TRUE.toString()); properties.setProperty(GuiceUtil.MODULES_LIST_PROP, TestModule.class.getName()); GuiceUtil.getInstance(properties).register(); /* * Perform injection for different types of common dao */ injector = Guice.createInjector(new TestModule(), new TestNoModule(), new TestOpModule(), new TestPesModule()); commonDaos = injector.getInstance(CommonDaos.class); incrementor = injector.getInstance(Key.get(new TypeLiteral<RowCellIncrementor<SampleDomain, SampleDomain, Long>>() { })); Assert.assertNotNull("Common Daos not initialized properly!", commonDaos); } @AfterClass public static void globalTearDown() { try { TEST_UTIL.shutdownMiniCluster(); } catch (Exception ex) { LOGGER.warn("Error shutting down!", ex); } } @Test public void testOptimisticPersist() { com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> dao = commonDaos.optimisticDao; SampleDomain domain = new SampleDomain(); domain.setName("Name 1"); domain.setId(1l); dao.save(domain); domain.setName("Name 1 `2"); try { dao.update(domain); Assert.fail("Should have failed!"); } catch (Exception ex) { LOGGER.info(ex.getMessage(), ex); } domain.setVersion(1l); dao.update(domain); domain.setVersion(3l); try { dao.delete(domain); Assert.fail("Should have failed!"); } catch (Exception ex) { LOGGER.info(ex.getMessage(), ex); } domain.setVersion(0l); try { dao.delete(domain); Assert.fail("Should have failed!"); } catch (Exception ex) { LOGGER.info(ex.getMessage(), ex); } domain.setVersion(2l); dao.delete(domain); } @Test public void testPessimisticPersist() { com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> dao = commonDaos.pessimisticDao; SampleDomain domain = new SampleDomain(); domain.setName("Name 2"); domain.setId(2l); dao.save(domain); domain.setName("Name 2 `2"); try { dao.update(domain); } catch (Exception ex) { LOGGER.info(ex.getMessage(), ex); Assert.fail("Should not have failed!"); } domain.setVersion(1l); dao.update(domain); dao.delete(domain); } @Test public void testRowCellAutoIncrement() { Assert.assertNotNull(incrementor); Assert.assertEquals(1l, incrementor.incrementAndGet(1l, 1l)); Assert.assertNotNull(incrementor); Assert.assertEquals(2l, incrementor.incrementAndGet(1l, 1l)); Assert.assertNotNull(incrementor); Assert.assertEquals(3l, incrementor.incrementAndGet(1l, 1l)); Assert.assertNotNull(incrementor); Assert.assertEquals(13l, incrementor.incrementAndGet(1l, 10l)); Assert.assertNotNull(incrementor); Assert.assertEquals(8l, incrementor.incrementAndGet(1l, -5l)); } private static class CommonDaos { public static final String NO_LOCK = "noLockDao"; public static final String PESSIMISTIC_LOCK = "pessimisticDao"; public static final String OPTIMISTIC_LOCK = "optimistic"; @Inject @Named(OPTIMISTIC_LOCK) private com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> optimisticDao; @Inject @Named(NO_LOCK) private com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> noLockDao; @Inject @Named(PESSIMISTIC_LOCK) private com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> pessimisticDao; public com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> getNoLockDao() { return noLockDao; } public com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> getOptimisticDao() { return optimisticDao; } public com.smartitengineering.dao.common.CommonDao<SampleDomain, Long> getPessimisticDao() { return pessimisticDao; } } private static final TypeLiteral<SchemaInfoProviderImpl<SampleDomain, Long>> vTypeLiteral = new TypeLiteral<SchemaInfoProviderImpl<SampleDomain, Long>>() { }; private static final TypeLiteral<com.smartitengineering.dao.common.CommonDao<SampleDomain, Long>> l = new TypeLiteral<com.smartitengineering.dao.common.CommonDao<SampleDomain, Long>>() { }; private static final TypeLiteral<CommonDao<SampleDomain, Long>> p = new TypeLiteral<CommonDao<SampleDomain, Long>>() { }; public static class TestModule extends AbstractModule { @Override protected void configure() { bind(new TypeLiteral<FilterConfigs<SampleDomain>>() { }).toProvider(new GenericFilterConfigsProvider<SampleDomain>( "com/smartitengineering/dao/impl/hbase/domain/Configs.json")).in(Scopes.SINGLETON); bind(AsyncExecutorService.class).to(MixedExecutorServiceImpl.class).in(Singleton.class); bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool()); bind(Integer.class).annotatedWith(Names.named("maxRows")).toInstance(new Integer(100)); bind(Long.class).annotatedWith(Names.named("waitTime")).toInstance(new Long(10)); bind(TimeUnit.class).annotatedWith(Names.named("unit")).toInstance(TimeUnit.SECONDS); bind(Boolean.class).annotatedWith(Names.named("mergeEnabled")).toInstance(Boolean.FALSE); bind(DomainIdInstanceProvider.class).toProvider(Providers.<DomainIdInstanceProvider>of(null)); bind(new TypeLiteral<MergeService<SampleDomain, Long>>() { }).toProvider(Providers.<MergeService<SampleDomain, Long>>of(null)); bind(new TypeLiteral<Class<Long>>() { }).toInstance(Long.class); bind(Configuration.class).toInstance(TEST_UTIL.getConfiguration()); } } private static class TestOpModule extends PrivateModule { @Override protected void configure() { bind(new TypeLiteral<ObjectRowConverter<SampleDomain>>() { }).to(SampleDomainObjectCoverter.class).in(Singleton.class); bind(new TypeLiteral<LockAttainer<SampleDomain, Long>>() { }).to(new TypeLiteral<LockAttainerImpl<SampleDomain, Long>>() { }).in(Scopes.SINGLETON); bind(new TypeLiteral<SchemaInfoProviderBaseConfig<SampleDomain>>() { }).toProvider(new GenericBaseConfigProvider<SampleDomain>( "com/smartitengineering/dao/impl/hbase/domain/BaseConfigOptimistic.json")).in(Scopes.SINGLETON); bind(LockType.class).toInstance(LockType.OPTIMISTIC); bind(new TypeLiteral<SchemaInfoProvider<SampleDomain, Long>>() { }).to(vTypeLiteral).in(Singleton.class); bind(l).annotatedWith(Names.named(CommonDaos.OPTIMISTIC_LOCK)).to(p).in(Scopes.SINGLETON); binder().expose(l).annotatedWith(Names.named(CommonDaos.OPTIMISTIC_LOCK)); bind(new TypeLiteral<RowCellIncrementor<SampleDomain, SampleDomain, Long>>() { }).to(new TypeLiteral<RowCellIncrementorImpl<SampleDomain, SampleDomain, Long>>() { }); binder().expose(new TypeLiteral<RowCellIncrementor<SampleDomain, SampleDomain, Long>>() { }); CellConfigImpl<SampleDomain> configImpl = new CellConfigImpl<SampleDomain>(); configImpl.setFamily("family"); configImpl.setQualifier("someRowId"); bind(new TypeLiteral<CellConfig<SampleDomain>>() { }).toInstance(configImpl); } } private static class TestNoModule extends PrivateModule { @Override protected void configure() { bind(new TypeLiteral<ObjectRowConverter<SampleDomain>>() { }).to(SampleDomainObjectCoverter.class).in(Singleton.class); bind(new TypeLiteral<LockAttainer<SampleDomain, Long>>() { }).to(new TypeLiteral<LockAttainerImpl<SampleDomain, Long>>() { }).in(Scopes.SINGLETON); bind(new TypeLiteral<SchemaInfoProvider<SampleDomain, Long>>() { }).to(vTypeLiteral).in(Singleton.class); bind(new TypeLiteral<SchemaInfoProviderBaseConfig<SampleDomain>>() { }).toProvider(new GenericBaseConfigProvider<SampleDomain>( "com/smartitengineering/dao/impl/hbase/domain/BaseConfigNonOptimistic.json")).in(Scopes.SINGLETON); bind(LockType.class).toInstance(LockType.NONE); bind(l).annotatedWith(Names.named(CommonDaos.NO_LOCK)).to(p).in(Scopes.SINGLETON); binder().expose(l).annotatedWith(Names.named(CommonDaos.NO_LOCK)); } } private static class TestPesModule extends PrivateModule { @Override protected void configure() { bind(new TypeLiteral<ObjectRowConverter<SampleDomain>>() { }).to(SampleDomainObjectCoverter.class).in(Singleton.class); bind(new TypeLiteral<LockAttainer<SampleDomain, Long>>() { }).to(new TypeLiteral<LockAttainerImpl<SampleDomain, Long>>() { }).in(Scopes.SINGLETON); bind(new TypeLiteral<SchemaInfoProvider<SampleDomain, Long>>() { }).to(vTypeLiteral).in(Singleton.class); bind(new TypeLiteral<SchemaInfoProviderBaseConfig<SampleDomain>>() { }).toProvider(new GenericBaseConfigProvider<SampleDomain>( "com/smartitengineering/dao/impl/hbase/domain/BaseConfigNonOptimistic.json")).in(Scopes.SINGLETON); bind(LockType.class).toInstance(LockType.PESSIMISTIC); bind(l).annotatedWith(Names.named(CommonDaos.PESSIMISTIC_LOCK)).to(p).in(Scopes.SINGLETON); binder().expose(l).annotatedWith(Names.named(CommonDaos.PESSIMISTIC_LOCK)); } } }