/**
* Copyright (C) 2008 - Abiquo Holdings S.L. All rights reserved.
*
* Please see /opt/abiquo/tomcat/webapps/legal/ on Abiquo server
* or contact contact@abiquo.com for licensing information.
*/
package com.abiquo.server.core.task;
import static java.lang.String.format;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import com.abiquo.server.core.task.enums.TaskOwnerType;
import com.abiquo.server.core.task.enums.TaskState;
import com.google.common.collect.Lists;
/**
* Repository to manage the redis-based DAOs {@link TaskDAO} and {@link JobDAO} and to manage the
* indexes of task system.
*
* @author eruiz@abiquo.com
*/
@Component
public class AsyncTaskRep
{
@Autowired
private JedisPool jedisPool;
@Autowired
private TaskDAO taskDao;
@Autowired
private JobDAO jobDao;
/**
* Saves the indicated task. Just insert their jobs if they have not been previously persisted
* (cascade on insert).
*
* @param task the task to save
* @return the saved task
*/
public Task save(final Task task)
{
Jedis jedis = jedisPool.getResource();
// Collect the not inserted jobs (insert cascade)
List<Job> jobsToInsert = Lists.newArrayList();
for (Job job : task.getJobs())
{
if (!jedis.exists(job.getEntityKey()))
{
jobsToInsert.add(job);
}
}
// Save logic
Transaction transaction = jedis.multi();
boolean discard = true;
try
{
// Persist not persisted Jobs
for (Job job : jobsToInsert)
{
job.setParentTaskId(task.getTaskId());
jobDao.save(job, transaction);
}
// Persist task
taskDao.save(task, transaction);
// Index task and owner
deindexTaskByOwner(task, transaction);
indexTaskByOwner(task, transaction);
indexTaskOwner(task, transaction);
transaction.exec();
discard = false;
}
finally
{
if (discard)
{
transaction.discard();
}
jedisPool.returnResource(jedis);
}
return task;
}
public void delete(final Task task)
{
Jedis jedis = jedisPool.getResource();
Transaction transaction = jedis.multi();
boolean discard = true;
try
{
delete(task, transaction);
transaction.exec();
discard = false;
}
finally
{
if (discard)
{
transaction.discard();
}
jedisPool.returnResource(jedis);
}
}
public void delete(final List<Task> tasks)
{
Jedis jedis = jedisPool.getResource();
Transaction transaction = jedis.multi();
boolean discard = true;
try
{
for (Task task : tasks)
{
delete(task, transaction);
}
transaction.exec();
discard = false;
}
finally
{
if (discard)
{
transaction.discard();
}
jedisPool.returnResource(jedis);
}
}
private void delete(final Task task, final Transaction transaction)
{
// Delete referenced jobs
for (Job job : task.getJobs())
{
jobDao.delete(job, transaction);
}
// Deindex task and owner
deindexTaskByOwner(task, transaction);
// Delete task
taskDao.delete(task, transaction);
}
public List<Task> findTasksByOwnerId(final TaskOwnerType type, final String ownerId)
{
Jedis jedis = jedisPool.getResource();
try
{
// Get the list of tasks indexed by owner type and id
List<String> taskKeys = jedis.lrange(taskOwnerKey(type, ownerId), 0, -1);
// Retrieve the whole Task entity
List<Task> tasks = new ArrayList<Task>();
for (String taskKey : taskKeys)
{
Task task = taskDao.find(taskKey, jedis);
if (task != null)
{
fillJobCollection(task, jedis);
tasks.add(task);
}
}
return tasks;
}
finally
{
jedisPool.returnResource(jedis);
}
}
public Task findTask(final String taskId)
{
Jedis jedis = jedisPool.getResource();
try
{
Task task = taskDao.findById(taskId, jedis);
fillJobCollection(task, jedis);
return task;
}
finally
{
jedisPool.returnResource(jedis);
}
}
public Task findNewestTask(final TaskOwnerType type, final String ownerId)
{
Jedis jedis = jedisPool.getResource();
try
{
List<String> result = jedis.lrange(taskOwnerKey(type, ownerId), 0, 0);
if (result.isEmpty())
{
return null;
}
Task task = taskDao.find(result.get(0), jedis);
fillJobCollection(task, jedis);
return task;
}
finally
{
jedisPool.returnResource(jedis);
}
}
public TaskState findNewestTaskState(final TaskOwnerType type, final String ownerId)
{
Jedis jedis = jedisPool.getResource();
try
{
List<String> result = jedis.lrange(taskOwnerKey(type, ownerId), 0, 0);
if (result.isEmpty())
{
return null;
}
String taskKey = result.get(0);
return taskDao.findTaskState(taskKey, jedis);
}
finally
{
jedisPool.returnResource(jedis);
}
}
public Task findTaskByJobId(final String jobId)
{
Jedis jedis = jedisPool.getResource();
try
{
Job job = findJob(jobId);
return job != null ? findTask(job.getParentTaskId()) : null;
}
finally
{
jedisPool.returnResource(jedis);
}
}
private Task fillJobCollection(final Task task, final Jedis jedis)
{
if (task != null)
{
task.getJobs().addAll(
jobDao.findJobs(taskDao.getTaskJobsEntityKey(task.getIdAsString()), jedis));
}
return task;
}
public Job save(final Job job)
{
Jedis jedis = jedisPool.getResource();
Transaction transaction = jedis.multi();
boolean discard = true;
try
{
jobDao.save(job, transaction);
transaction.exec();
discard = false;
}
finally
{
if (discard)
{
transaction.discard();
}
jedisPool.returnResource(jedis);
}
return job;
}
public Job findJob(final String jobId)
{
Jedis jedis = jedisPool.getResource();
try
{
return jobDao.findById(jobId, jedis);
}
finally
{
jedisPool.returnResource(jedis);
}
}
public void trimOwnersTasks(final TaskOwnerType type, final long numberOfTasks)
{
Jedis jedis = jedisPool.getResource();
Jedis jedisTx = jedisPool.getResource();
Transaction transaction = jedisTx.multi();
boolean discard = true;
try
{
Set<String> owners = jedis.smembers(ownerIndexKey(type));
for (String owner : owners)
{
for (String taskKey : jedis.lrange(owner, numberOfTasks, -1))
{
Task task = taskDao.find(taskKey, jedis);
delete(task, transaction);
}
}
transaction.exec();
discard = false;
}
finally
{
if (discard)
{
transaction.discard();
}
jedisPool.returnResource(jedisTx);
jedisPool.returnResource(jedis);
}
}
private void indexTaskByOwner(final Task task, final Transaction transaction)
{
transaction.lpush(taskOwnerKey(task), task.getEntityKey());
}
private void indexTaskOwner(final Task task, final Transaction transaction)
{
transaction.sadd(ownerIndexKey(task), taskOwnerKey(task));
}
private void deindexTaskByOwner(final Task task, final Transaction transaction)
{
transaction.lrem(taskOwnerKey(task), 0, task.getEntityKey());
}
private String taskOwnerKey(final Task task)
{
return taskOwnerKey(task.getType().getOwnerType(), task.getOwnerId());
}
private String taskOwnerKey(final TaskOwnerType type, final String ownerId)
{
return format("Owner:%s:%s", type.getName(), ownerId);
}
private String ownerIndexKey(final Task task)
{
return ownerIndexKey(task.getType().getOwnerType());
}
private String ownerIndexKey(final TaskOwnerType type)
{
return format("Owner:%s", type.getName());
}
}