@XmlRootElement
public class Stage {
    private final Definition definition;
    private final WorkerRanker workerRanker;
    private final TaskAllocator taskAllocator;
    private final AnswerAggregator answerAggregator;

    @Inject
    public Stage(Definition definition, WorkerRanker workerRanker, TaskAllocator taskAllocator, AnswerAggregator answerAggregator) {
        this.definition = requireNonNull(definition);
        this.workerRanker = requireNonNull(workerRanker);
        this.taskAllocator = requireNonNull(taskAllocator);
        this.answerAggregator = requireNonNull(answerAggregator);
    }

    @JsonProperty
    public String getId() {
        return definition.getId();
    }

    @JsonProperty
    public String getDescription() {
        return definition.getDescription();
    }

    @JsonProperty
    public Map<String, String> getOptions() {
        return definition.getOptions();
    }

    @JsonIgnore
    public WorkerRanker getWorkerRanker() {
        return workerRanker;
    }

    @JsonIgnore
    public TaskAllocator getTaskAllocator() {
        return taskAllocator;
    }

    @JsonIgnore
    public AnswerAggregator getAnswerAggregator() {
        return answerAggregator;
    }

    @JsonProperty("workerRanker")
    @SuppressWarnings("unused")
    public String getWorkerRankerName() {
        return definition.getWorkerRanker();
    }

    @JsonProperty("taskAllocator")
    @SuppressWarnings("unused")
    public String getTaskAllocatorName() {
        return definition.getTaskAllocator();
    }

    @JsonProperty("answerAggregator")
    @SuppressWarnings("unused")
    public String getAnswerAggregatorName() {
        return definition.getAnswerAggregator();
    }

    @FreeBuilder
    @XmlRootElement
    @JsonDeserialize(builder = Definition.Builder.class)
    public interface Definition {
        @JsonProperty
        String getId();

        @JsonProperty
        Timestamp getDateTime();

        @JsonProperty
        String getDescription();

        @JsonProperty()
        String getWorkerRanker();

        @JsonProperty()
        String getTaskAllocator();

        @JsonProperty()
        String getAnswerAggregator();

        @JsonProperty
        Map<String, String> getOptions();

        @JsonIgnore
        String getOptionsJSON();

        @JsonPOJOBuilder(withPrefix = "set")
        class Builder extends Stage_Definition_Builder {
            public Builder() {
                setDateTime(DateTimeUtils.now());
            }

            public Builder setOptions(String json) {
                return putAllOptions(PostgresUtils.parseJSONString(json));
            }

            @Override
            public Definition build() {
                setOptionsJSON(PostgresUtils.buildJSONString(getOptions()));
                return super.build();
            }
        }
    }
} @UseStringTemplate3StatementLocator
@RegisterMapper(AnswerDAO.Mapper.class)
public interface AnswerDAO {
    String ANSWER_TYPE_ANSWER = "answer";
    String ANSWER_TYPE_DEFAULT = ANSWER_TYPE_ANSWER;
    String ANSWER_TYPE_SKIP = "skip";

    @SqlQuery("select * from answers where stage = :stage")
    List<Answer> listForStage(@Bind("stage") String stage);

    @SqlQuery("select * from answers where task_id = :taskId and stage = :stage")
    List<Answer> listForTask(@Bind("taskId") Integer taskId, @Bind("stage") String stage);

    @SqlQuery("select * from answers where worker_id = :workerId and stage = :stage")
    List<Answer> listForWorker(@Bind("workerId") Integer workerId, @Bind("stage") String stage);

    @SqlQuery("select * from answers where id = :id and stage = :stage limit 1")
    Answer find(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlQuery("select * from answers where stage = :stage and worker_id = :worker_id and task_id = :task_id limit 1")
    Answer findByWorkerAndTask(@Bind("stage") String stage, @Bind("worker_id") Integer workerId, @Bind("task_id") Integer taskId);

    @SqlQuery("insert into answers (stage, datetime, tags, type, worker_id, task_id, answers) values (:stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[]), cast(:type as answer_type), :workerId, :taskId, cast(:answersTextArray as text[])) returning id")
    int insert(@BindBean Answer a);

    @SqlBatch("insert into answers (id, stage, datetime, tags, type, worker_id, task_id, answers) values (coalesce(:id, nextval('answers_id_seq')), :stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[]), cast(:type as answer_type), :workerId, :taskId, cast(:answersTextArray as text[]))")
    @BatchChunkSize(1000)
    int[] insert(@BindBean Iterator<Answer> answers);

    /*
     * This is a slow method for inserting the given collection of answers and returning all the inserted objects.
     */
    static List<Answer> insert(AnswerDAO dao, Collection<Answer> answers) {
        return answers.stream().map(answer -> dao.find(dao.insert(answer), answer.getStage())).collect(Collectors.toList());
    }

    @SqlQuery("select count(*) from answers")
    int count();

    @SqlQuery("select count(*) from answers where stage = :stage")
    int count(@Bind("stage") String stage);

    @SqlUpdate("delete from answers where id = :id and stage = :stage")
    void delete(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlUpdate("delete from answers where stage = :stage")
    void deleteAll(@Bind("stage") String stage);

    @SqlUpdate("delete from answers")
    void deleteAll();

    @SqlUpdate("select setval('answers_id_seq', coalesce((select max(id) + 1 from answers), 1), false)")
    void resetSequence();

    void close();

    class Mapper implements ResultSetMapper<Answer> {
        @Override
        public Answer map(int index, ResultSet r, StatementContext ctx) throws SQLException {
            return new Answer.Builder().
                    setId(r.getInt("id")).
                    setStage(r.getString("stage")).
                    setDateTime(r.getTimestamp("datetime")).
                    addAllTags(Arrays.asList((String[]) r.getArray("tags").getArray())).
                    setMetadata(r.getString("metadata")).
                    setType(r.getString("type")).
                    setWorkerId(r.getInt("worker_id")).
                    setTaskId(r.getInt("task_id")).
                    addAllAnswers(Arrays.asList((String[]) r.getArray("answers").getArray())).
                    build();
        }
    }
} @RegisterMapper(StageDAO.Mapper.class)
public interface StageDAO {
    @SqlQuery("select * from stages where id = :id limit 1")
    Stage.Definition find(@Bind("id") String id);

    @SqlQuery("select * from stages order by datetime")
    List<Stage.Definition> select();

    @SqlQuery("select count(*) from stages")
    int count();

    @SqlQuery("insert into stages (id, description, worker_ranker, task_allocator, answer_aggregator, options, datetime) values (:id, :description, :workerRanker, :taskAllocator, :answerAggregator, cast(:optionsJSON as jsonb), coalesce(:dateTime, localtimestamp)) returning id")
    String insert(@BindBean Stage.Definition definition);

    @SqlUpdate("update stages set description=:description, worker_ranker=:workerRanker, task_allocator=:taskAllocator, answer_aggregator=:answerAggregator, options=cast(:optionsJSON as jsonb) where id=:id")
    void update(@BindBean Stage.Definition definition);

    @SqlUpdate("delete from stages where id = :id")
    void delete(@Bind("id") String id);

    @SqlUpdate("delete from stages")
    void deleteAll();

    void close();

    class Mapper implements ResultSetMapper<Stage.Definition> {
        @Override
        public Stage.Definition map(int index, ResultSet r, StatementContext ctx) throws SQLException {
            final Stage.Definition.Builder builder = new Stage.Definition.Builder().
                    setId(r.getString("id")).
                    setDescription(r.getString("description")).
                    setWorkerRanker(r.getString("worker_ranker")).
                    setTaskAllocator(r.getString("task_allocator")).
                    setAnswerAggregator(r.getString("answer_aggregator")).
                    setOptions(r.getString("options")).
                    setDateTime(r.getTimestamp("datetime"));
            return builder.build();
        }
    }
} @UseStringTemplate3StatementLocator
@RegisterMapper(TaskDAO.Mapper.class)
public interface TaskDAO {
    String TASK_TYPE_SINGLE = "single";

    @SqlQuery("select * from tasks where stage = :stage")
    List<Task> listForStage(@Bind("stage") String stage);

    @SqlQuery("select * from tasks where id = :id and stage = :stage limit 1")
    Task find(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlQuery("select * from tasks where id in (<ids>) and stage = :stage")
    List<Task> select(@BindIn("ids") List<Integer> ids, @Bind("stage") String stage);

    @SqlQuery("select * from tasks where stage = :stage order by random() limit 1")
    Task random(@Bind("stage") String stage);

    @SqlQuery("select count(*) from tasks")
    int count();

    @SqlQuery("select count(*) from tasks where stage = :stage")
    int count(@Bind("stage") String stage);

    /**
     * Provides the upper bound of unanswered tasks of the given stage by the given worker.
     *
     * @param stage    the stage name.
     * @param workerId the worker identifier.
     * @return the number of unanswered tasks.
     */
    @SqlQuery("select count(distinct tasks.id) from tasks left join answers on answers.task_id = tasks.id and answers.stage = tasks.stage and answers.worker_id = :worker_id where tasks.stage = :stage and answers.id is null")
    int remaining(@Bind("stage") String stage, @Bind("worker_id") Integer workerId);

    @SqlQuery("insert into tasks (stage, datetime, tags, type, description, answers) values (:stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[]), cast(:type as task_type), :description, cast(:answersTextArray as text[])) returning id")
    int insert(@BindBean Task t);

    @SqlBatch("insert into tasks (id, stage, datetime, tags, type, description, answers) values (coalesce(:id, nextval('tasks_id_seq')), :stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[]), cast(:type as task_type), :description, cast(:answersTextArray as text[]))")
    @BatchChunkSize(1000)
    void insert(@BindBean Iterator<Task> tasks);

    @SqlUpdate("begin transaction; delete from answers where task_id = :id and stage = :stage; delete from tasks where id = :id and stage = :stage; commit")
    void delete(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlUpdate("delete from tasks where stage = :stage")
    void deleteAll(@Bind("stage") String stage);

    @SqlUpdate("delete from tasks")
    void deleteAll();

    @SqlUpdate("select setval('tasks_id_seq', coalesce((select max(id) + 1 from tasks), 1), false)")
    void resetSequence();

    void close();

    class Mapper implements ResultSetMapper<Task> {
        @Override
        public Task map(int index, ResultSet r, StatementContext ctx) throws SQLException {
            return new Task.Builder().
                    setId(r.getInt("id")).
                    setStage(r.getString("stage")).
                    setDateTime(r.getTimestamp("datetime")).
                    addAllTags(Arrays.asList((String[]) r.getArray("tags").getArray())).
                    setMetadata(r.getString("metadata")).
                    setType(r.getString("type")).
                    setDescription(r.getString("description")).
                    addAllAnswers(Arrays.asList((String[]) r.getArray("answers").getArray())).
                    build();
        }
    }
} @UseStringTemplate3StatementLocator
@RegisterMapper(WorkerDAO.Mapper.class)
public interface WorkerDAO {
    @SqlQuery("select * from workers where stage = :stage")
    List<Worker> listForStage(@Bind("stage") String stage);

    @SqlQuery("select * from workers where id = :id and stage = :stage limit 1")
    Worker find(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlQuery("select * from workers where stage = :stage and tags @\\> ARRAY[<tags>]\\:\\:text[] limit 1")
    Worker findByTags(@Bind("stage") String stage, @BindIn("tags") List<String> tags);

    @SqlQuery("insert into workers (stage, datetime, tags) values (:stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[])) returning id")
    int insert(@BindBean Worker t);

    @SqlBatch("insert into workers (id, stage, datetime, tags) values (coalesce(:id, nextval('workers_id_seq')), :stage, coalesce(:dateTime, localtimestamp), cast(:tagsTextArray as text[]))")
    @BatchChunkSize(1000)
    void insert(@BindBean Iterator<Worker> tasks);

    @SqlQuery("select count(*) from workers")
    int count();

    @SqlQuery("select count(*) from workers where stage = :stage")
    int count(@Bind("stage") String stage);

    @Transaction
    @SqlUpdate("delete from answers where worker_id = :id and stage = :stage; delete from workers where id = :id and stage = :stage")
    void delete(@Bind("id") Integer id, @Bind("stage") String stage);

    @SqlUpdate("delete from workers where stage = :stage")
    void deleteAll(@Bind("stage") String stage);

    @SqlUpdate("delete from workers")
    void deleteAll();

    @SqlUpdate("select setval('workers_id_seq', coalesce((select max(id) + 1 from workers), 1), false)")
    void resetSequence();

    void close();

    class Mapper implements ResultSetMapper<Worker> {
        @Override
        public Worker map(int index, ResultSet r, StatementContext ctx) throws SQLException {
            return new Worker.Builder().
                    setId(r.getInt("id")).
                    setStage(r.getString("stage")).
                    setDateTime(r.getTimestamp("datetime")).
                    addAllTags(Arrays.asList((String[]) r.getArray("tags").getArray())).
                    setMetadata(r.getString("metadata")).
                    build();
        }
    }
} public class StageService {
    private final Logger logger;
    private final Map<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();
    private final StageDAO stageDAO;
    private final ServiceLocator locator;

    @Inject
    public StageService(StageDAO stageDAO, ServiceLocator locator) {
        this.logger = LoggerFactory.getLogger(getClass());
        this.stageDAO = stageDAO;
        this.locator = locator;
    }

    public Map<String, Stage> getStages() {
        final Map<String, Stage> map = new LinkedHashMap<>();
        final List<Stage.Definition> definitions = stageDAO.select();

        for (final Stage.Definition definition : definitions) {
            try {
                map.put(definition.getId(), getStage(definition));
            } catch (IllegalStateException e) {
                logger.error("Error initiating stage definition with id {}", definition.getId(), e);
            }
        }

        return map;
    }

    private static void initialize(ServiceLocator locator, Object bean) {
        locator.inject(bean);
        locator.postConstruct(bean);
    }

    private Stage getStage(Stage.Definition definition) {
        final Class<? extends WorkerRanker> workerRankerClass = getClass(definition.getWorkerRanker(), WorkerRanker.class);
        final Class<? extends TaskAllocator> taskAllocatorClass = getClass(definition.getTaskAllocator(), TaskAllocator.class);
        final Class<? extends AnswerAggregator> answerAggregatorClass = getClass(definition.getAnswerAggregator(), AnswerAggregator.class);

        final WorkerRanker workerRanker = locator.create(workerRankerClass);
        final TaskAllocator taskAllocator = locator.create(taskAllocatorClass);
        final AnswerAggregator answerAggregator = locator.create(answerAggregatorClass);
        final Stage stage = new Stage(definition, workerRanker, taskAllocator, answerAggregator);

        final ServiceLocator localLocator = Injections.createLocator(locator, new AbstractBinder() {
            @Override
            protected void configure() {
                bind(definition).to(Stage.Definition.class);
                bind(stage).to(Stage.class);
                bind(workerRanker).to(WorkerRanker.class);
                bind(taskAllocator).to(TaskAllocator.class);
                bind(answerAggregator).to(AnswerAggregator.class);
            }
        });

        initialize(localLocator, workerRanker);
        initialize(localLocator, taskAllocator);
        initialize(localLocator, answerAggregator);

        return localLocator.getService(Stage.class);
    }

    private <T> Class<? extends T> getClass(String name, Class<? extends T> tClass) {
        final Class<?> clazz = CLASS_CACHE.computeIfAbsent(name, n -> {
            try {
                return Class.forName(name);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException("No class with name " + name, e);
            }
        });
        return clazz.asSubclass(tClass);
    }
} public interface AnswerAggregator {
    /**
     * Given a collection of tasks, an aggregator maps these tasks to the aggregated answers.
     *
     * @param tasks tasks.
     * @return Aggregated answers.
     */
    @Nonnull
    Map<Integer, AnswerAggregation> aggregate(@Nonnull Collection<Task> tasks);

    /**
     * Given a task, an aggregator returns either an aggregated answer, or nothing.
     * This is an alias for the method accepting the task collection.
     *
     * @param task task.
     * @return Aggregated answer.
     */
    @Nonnull
    default Optional<AnswerAggregation> aggregate(@Nonnull Task task) {
        final Map<Integer, AnswerAggregation> aggregations = aggregate(Collections.singletonList(task));
        if (aggregations.isEmpty()) return Optional.empty();
        return Optional.of(aggregations.get(task.getId()));
    }
} public interface TaskAllocator {
    /**
     * Given a worker, an allocator returns either an allocated task, or nothing.
     *
     * @param worker worker.
     * @param n      maximum number of tasks to be allocated.
     * @return Allocated tasks.
     */
    @Nonnull
    Optional<TaskAllocation> allocate(@Nonnull Worker worker, @Nonnegative int n);

    /**
     * Given a worker, an allocator returns either an allocated task, or nothing.
     * This is an alias for the method accepting the number of allocated tasks.
     *
     * @param worker worker.
     * @return Allocated task.
     */
    @Nonnull
    default Optional<TaskAllocation> allocate(@Nonnull Worker worker) {
        return allocate(worker, 1);
    }
} public interface WorkerRanker {
    /**
     * Given a collection of workers, estimate their performance.
     *
     * @param workers workers.
     * @return Worker rankings.
     */
    @Nonnull
    Map<Integer, WorkerRanking> rank(@Nonnull Collection<Worker> workers);

    /**
     * Given a worker, a ranker returns either a worker ranking, or nothing.
     * This is an alias for the method accepting the worker collection.
     *
     * @param worker worker.
     * @return Worker ranking.
     */
    @Nonnull
    default Optional<WorkerRanking> rank(@Nonnull Worker worker) {
        final Map<Integer, WorkerRanking> rankings = rank(Collections.singletonList(worker));
        if (rankings.isEmpty()) return Optional.empty();
        return Optional.of(rankings.get(worker.getId()));
    }
} @Singleton
@Path("/stages")
@Produces(mtsar.util.MediaType.APPLICATION_JSON)
public class StageResource {
    private final TaskDAO taskDAO;
    private final WorkerDAO workerDAO;
    private final AnswerDAO answerDAO;
    private final StageDAO stageDAO;
    private final StageService stageService;

    @Inject
    public StageResource(StageService stageService, TaskDAO taskDAO, WorkerDAO workerDAO, AnswerDAO answerDAO, StageDAO stageDAO) {
        this.stageService = stageService;
        this.taskDAO = taskDAO;
        this.workerDAO = workerDAO;
        this.answerDAO = answerDAO;
        this.stageDAO = stageDAO;
    }

    @GET
    public Collection<Stage> getStages() {
        return getStagesMap().values();
    }

    private Map<String, Stage> getStagesMap() {
        return stageService.getStages();
    }

    @GET
    @Produces(MediaType.TEXT_HTML)
    public StagesView getStagesView() {
        return new StagesView(getStagesMap(), taskDAO, workerDAO, answerDAO);
    }

    @GET
    @Path("{stage}")
    public Stage getStage(@PathParam("stage") String id) {
        return fetchStage(id);
    }

    @GET
    @Path("{stage}")
    @Produces(MediaType.TEXT_HTML)
    public StageView getStageView(@PathParam("stage") String id) {
        return new StageView(fetchStage(id), taskDAO, workerDAO, answerDAO);
    }

    @POST
    public Response createStage(@Context UriInfo uriInfo, @FormParam("id") String id, @FormParam("description") String description, @FormParam("workerRanker") String workerRanker, @FormParam("taskAllocator") String taskAllocator, @FormParam("answerAggregator") String answerAggregator) {
        if (stageDAO.find(id) != null) throw new WebApplicationException(Response.Status.CONFLICT);
        final String stageId = stageDAO.insert(new Stage.Definition.Builder().
                setId(id).
                setDescription(description).
                setWorkerRanker(workerRanker).
                setTaskAllocator(taskAllocator).
                setAnswerAggregator(answerAggregator).
                build());
        return Response.created(getStageURI(uriInfo, stageId)).build();
    }

    @PATCH
    @Path("{stage}")
    public Response updateStage(@Context UriInfo uriInfo, @PathParam("stage") String id, @FormParam("description") String description, @FormParam("workerRanker") String workerRanker, @FormParam("taskAllocator") String taskAllocator, @FormParam("answerAggregator") String answerAggregator) {
        final Stage.Definition definition = stageDAO.find(id);
        if (definition == null) throw new WebApplicationException(Response.Status.NOT_FOUND);
        stageDAO.update(Stage.Definition.Builder.from(definition).
                setDescription(description).
                setWorkerRanker(workerRanker).
                setTaskAllocator(taskAllocator).
                setAnswerAggregator(answerAggregator).
                build());
        return Response.seeOther(getStageURI(uriInfo, definition.getId())).build();
    }

    @Path("{stage}/workers")
    public WorkerResource getWorkers(@PathParam("stage") String id) {
        return new WorkerResource(fetchStage(id), taskDAO, workerDAO, answerDAO);
    }

    @GET
    @Path("{stage}/workers.csv")
    @Produces(mtsar.util.MediaType.TEXT_CSV)
    public StreamingOutput getWorkersCSV(@PathParam("stage") String id) {
        final List<Worker> workers = workerDAO.listForStage(fetchStage(id).getId());
        return output -> WorkerCSV.write(workers, output);
    }

    @Path("{stage}/tasks")
    public TaskResource getTasks(@PathParam("stage") String id) {
        return new TaskResource(fetchStage(id), taskDAO, workerDAO, answerDAO);
    }

    @GET
    @Path("{stage}/tasks.csv")
    @Produces(mtsar.util.MediaType.TEXT_CSV)
    public StreamingOutput getTasksCSV(@PathParam("stage") String id) {
        final List<Task> tasks = taskDAO.listForStage(fetchStage(id).getId());
        return output -> TaskCSV.write(tasks, output);
    }

    @Path("{stage}/answers")
    public AnswerResource getAnswers(@PathParam("stage") String id) {
        return new AnswerResource(fetchStage(id), taskDAO, workerDAO, answerDAO);
    }

    @GET
    @Path("{stage}/answers.csv")
    @Produces(mtsar.util.MediaType.TEXT_CSV)
    public StreamingOutput getAnswersCSV(@PathParam("stage") String id) {
        final List<Answer> answers = answerDAO.listForStage(fetchStage(id).getId());
        return output -> AnswerCSV.write(answers, output);
    }

    private URI getStageURI(UriInfo uriInfo, String id) {
        return uriInfo.getBaseUriBuilder().
                path("stages").path(id).
                build();
    }

    private Stage fetchStage(String id) {
        if (!getStagesMap().containsKey(id)) throw new WebApplicationException(Response.Status.NOT_FOUND);
        return getStagesMap().get(id);
    }
}
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.Maps;
import io.dropwizard.testing.junit.ResourceTestRule;
import io.dropwizard.views.ViewMessageBodyWriter;
import io.dropwizard.views.mustache.MustacheViewRenderer;
import mtsar.api.Stage;
import mtsar.api.sql.AnswerDAO;
import mtsar.api.sql.StageDAO;
import mtsar.api.sql.TaskDAO;
import mtsar.api.sql.WorkerDAO;
import mtsar.dropwizard.hk2.StageService;
import mtsar.processors.AnswerAggregator;
import mtsar.processors.TaskAllocator;
import mtsar.processors.WorkerRanker;
import mtsar.resources.StageResource;
import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.mockito.internal.util.collections.Sets;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
/*
 * Copyright 2015 Dmitry Ustalov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mtsar;




public class StageResourceTest {
    private static final GenericType<Collection<Map>> MAP_COLLECTION = new GenericType<Collection<Map>>() {
    };

    private static final Stage.Definition definition = mock(Stage.Definition.class);
    private static final WorkerRanker workerRanker = mock(WorkerRanker.class);
    private static final TaskAllocator taskAllocator = mock(TaskAllocator.class);
    private static final AnswerAggregator answerAggregator = mock(AnswerAggregator.class);
    private static final Stage stage = new Stage(definition, workerRanker, taskAllocator, answerAggregator);
    private static final WorkerDAO workerDAO = mock(WorkerDAO.class);
    private static final TaskDAO taskDAO = mock(TaskDAO.class);
    private static final AnswerDAO answerDAO = mock(AnswerDAO.class);