package com.googlecode.objectify.test.dataloader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import org.milyn.Smooks;
import org.milyn.container.ExecutionContext;
import org.milyn.persistence.util.PersistenceUtil;
import org.milyn.scribe.register.MapDaoRegister;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.testng.util.Strings;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.appengine.tools.development.testing.LocalTaskQueueTestConfig;
import com.googlecode.objectify.ObjectifyOpts;
import com.googlecode.objectify.cache.TriggerFutureHook;
import com.googlecode.objectify.test.dataloader.annotation.Dataloader;
import com.googlecode.objectify.test.dataloader.annotation.EntitiesList;
import com.googlecode.objectify.test.dataloader.annotation.NotAnObjectifiedDataloaderTest;
import com.googlecode.objectify.util.DAOBase;
/**
* The base class for all dataloader unit tests. Also works as a generic unit
* test skeleton for all Objectify unit tests.
* @author Shakil Siraj
*/
@Test
public class ObjectifiedTest {
/**
* Log4J logger for the class.
*/
private final Logger LOGGER = Logger.getLogger(ObjectifiedTest.class
.getName());
/**
* Name of the test group for which the beforeMethod should be executed.
*/
protected static final String OFY_DATALOADER_GROUP = "ofyDataloaderGroup";
/**
* Name of the Objectify Helper Bean class that will be added to the
* Smooks execution context.
*/
protected static final String OFY_HELPER = "ofyHelper";
/**
* AppEngine test helper class.
*/
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig(),
new LocalMemcacheServiceTestConfig(),
new LocalTaskQueueTestConfig());
/**
* Refernce to Objectify DAOBase.
*/
private final DAOBase daoBase;
/**
* Default constructor.
*/
public ObjectifiedTest() {
daoBase = getObjectifyDAOBase();
}
/**
* This intercepter registers and loads up the Smooks configurations before
* and method gets tested. This also checks if there was any Dataloader and
* EnititiesList annotation defined. If not then it will use the class level
* annotations to pre-populate the datastore for the particular unit test.
* @param method
* the method that is about to be unit-tested
* @throws Exception
* from the method under microscope
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@BeforeMethod(alwaysRun = true, groups = { OFY_DATALOADER_GROUP })
public final void beforeMethod(final Method method) throws Exception {
if (!method.isAnnotationPresent(NotAnObjectifiedDataloaderTest.class)) {
this.helper.setUp();
if (method.isAnnotationPresent(EntitiesList.class)) {
LOGGER.fine("Using EntitiesList for the method "
+ method.getName());
registerObjectifyEntityType(method
.getAnnotation(EntitiesList.class));
} else {
if (getClass().isAnnotationPresent(EntitiesList.class)) {
LOGGER.fine("Using class level EntitiesList for the method "
+ method.getName());
registerObjectifyEntityType((EntitiesList) getClass()
.getAnnotation(EntitiesList.class));
}
}
if (method.isAnnotationPresent(Dataloader.class)) {
LOGGER.fine("Using Dataloader for the method "
+ method.getName());
setupSmooksDataLoader(method.getAnnotation(Dataloader.class));
} else if (getClass().isAnnotationPresent(Dataloader.class)) {
LOGGER.fine("Using class level dataloader for the method "
+ method.getName());
setupSmooksDataLoader((Dataloader) getClass().getAnnotation(
Dataloader.class));
}
}
}
/**
* Registers the entities list with Objectify. It also checks if the entity
* was already registered or not (via some other method).
* @param entitiesList
* The specified entities list
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void registerObjectifyEntityType(final EntitiesList entitiesList) {
for (int i = 0; i < entitiesList.classes().length; i++) {
Class clazz = entitiesList.classes()[i];
if (!EntitiesRegister.INSTANCE.contains(clazz)) {
LOGGER.fine("Registering entity type " + clazz.getName());
daoBase.fact().register(clazz);
EntitiesRegister.INSTANCE.add(clazz);
} else {
LOGGER.info("Ignoring registering entity type "
+ clazz.getName() + " as it is already registered");
}
}
}
/**
* Setup the dataloader so smooks can load data. If the dataloader is not
* present at method level, use the dataloader defined at class level.
* @param dataLoader
* the {@link Dataloader} definition
* @throws Exception
* Any thrown while loading data
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void setupSmooksDataLoader(final Dataloader dataLoader)
throws Exception {
if (!Strings.isNullOrEmpty(dataLoader.file()[0])) {
String configFileName = dataLoader.config();
LOGGER.fine("Loading smooks configuration file " + configFileName);
Smooks smooks = new Smooks(getClass().getResourceAsStream(
configFileName));
ExecutionContext executionContext = smooks.createExecutionContext();
executionContext.getBeanContext().addBean(OFY_HELPER,
getOfyHelperBean());
MapDaoRegister<Object> register = MapDaoRegister
.newInstance(getDAOMap());
PersistenceUtil.setDAORegister(executionContext, register);
for (int i = 0; i < dataLoader.file().length; i++) {
StreamSource streamSource = new StreamSource(this.getClass()
.getResourceAsStream(dataLoader.file()[i]));
smooks.filterSource(executionContext, streamSource,
new Result[] {null});
}
} else {
LOGGER.info("Skipping loading smooks configuration "
+ "file as there is no StreamSource " + "file defined");
}
}
/**
* Creates an instance of {@link ObjectifyHelper}.
* NOTE:If you need to set more options simply override
* @return Objectify Helper class
*/
protected final Object getOfyHelperBean() {
return new ObjectifyHelper();
}
/**
* Returns a Map of smooks DAOs that will be used by smooks to persist the
* data.
* NOTE: if you want to add more of your DAOs, simply override.
* @return A {@link Map} containing all application DAOs
*/
protected final Map<String, ? extends Object> getDAOMap() {
HashMap<String, Object> daoMap = new HashMap<String, Object>();
daoMap.put("genericDAO", new ObjectifySmooksDAO(daoBase));
return daoMap;
}
/**
* After each test method execution, clean up the persistence store.
* @param method
* the method that has just finished execution
*/
@AfterMethod(alwaysRun = true)
public final void afterMethod(final Method method) {
if (!method.isAnnotationPresent(NotAnObjectifiedDataloaderTest.class)) {
TriggerFutureHook.completeAllPendingFutures();
this.helper.tearDown();
}
}
/**
* Creates an instance of {@link DAOBase} with the specified options. NOTE:
* If you need to set more options simply override
* @return The Objectify DAOBase implementation
*/
protected final DAOBase getObjectifyDAOBase() {
ObjectifyOpts opts = new ObjectifyOpts();
opts.setGlobalCache(true);
opts.setSessionCache(true);
opts.setBeginTransaction(true);
return new DAOBase(opts);
}
/**
* Getter method for daoBase.
* @return the daoBase
*/
protected final DAOBase getDaoBase() {
return daoBase;
}
}