/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* 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 org.constellation.rest.api;
import com.google.common.base.Optional;
import org.apache.sis.parameter.ParameterBuilder;
import org.constellation.admin.dto.ServiceDTO;
import org.constellation.admin.dto.TaskStatusDTO;
import org.constellation.admin.exception.ConstellationException;
import org.constellation.business.IDatasetBusiness;
import org.constellation.business.IProcessBusiness;
import org.constellation.business.IServiceBusiness;
import org.constellation.configuration.AcknowlegementType;
import org.constellation.configuration.ConfigurationException;
import org.constellation.configuration.StringList;
import org.constellation.configuration.StringMap;
import org.constellation.database.api.TaskParameterWithOwnerName;
import org.constellation.database.api.jooq.tables.pojos.*;
import org.constellation.database.api.repository.StyleRepository;
import org.constellation.database.api.repository.TaskParameterRepository;
import org.constellation.database.api.repository.UserRepository;
import org.constellation.process.DatasetProcessReference;
import org.constellation.process.ServiceProcessReference;
import org.constellation.process.StyleProcessReference;
import org.constellation.process.UserProcessReference;
import org.constellation.util.ParamUtilities;
import org.geotoolkit.process.ProcessDescriptor;
import org.geotoolkit.process.ProcessFinder;
import org.geotoolkit.processing.chain.model.Chain;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.InvalidParameterValueException;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.util.NoSuchIdentifierException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* RestFull API for task management/operations.
*
* @author Johann Sorel (Geomatys)
*/
@Component
@Path("/1/task")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class TaskRest {
private static final DateFormat TASK_DATE = new SimpleDateFormat("yyyy/MM/dd HH:mm");
@Inject
private IProcessBusiness processBusiness;
@Inject
private TaskParameterRepository taskParameterRepository;
@Inject
private UserRepository userRepository;
/**
* DatasetBusiness used for provider GUI editors data
*/
@Inject
private IDatasetBusiness datasetBusiness;
/**
* ServiceBusiness used for provider GUI editors data
*/
@Inject
private IServiceBusiness serviceBusiness;
/**
* StyleRepository used for provider GUI editors data
*/
@Inject
private StyleRepository styleRepository;
// <editor-fold defaultstate="collapsed" desc="GeotkProcessAPI">
/**
* Returns a list of all process available in the current factories.
*/
@GET
@Path("listProcesses")
public Response listProcess(){
final Map<String, Set<String>> registryMap = processBusiness.listProcess();
final List<String> result = new ArrayList<>();
for (Map.Entry<String, Set<String>> registry : registryMap.entrySet()) {
final Set<String> processes = registry.getValue();
for (String process : processes) {
result.add(registry.getKey() +":"+ process);
}
}
final Map<String, List<String>> wrapper = Collections.singletonMap("list", result);
return Response.ok(wrapper).build();
}
/**
* Returns a list of all process available in the current factories.
*/
@GET
@Path("countProcesses")
public Response countAvailableProcess(){
final Map<String, Set<String>> registryMap = processBusiness.listProcess();
int count = 0;
for (Set<String> strings : registryMap.values()) {
count += strings.size();
}
StringMap stringMap = new StringMap();
stringMap.getMap().put("value",""+count);
return Response.ok(stringMap).build();
}
/**
* Returns a list of all process available for the specified factory.
*
* @param authorityCode
*/
@GET
@Path("process/factory/{authorityCode}")
public Response listProcessForFactory(final @PathParam("authorityCode") String authorityCode) {
return Response.ok(new StringList(processBusiness.listProcessForFactory(authorityCode))).build();
}
/**
* Returns a list of all process available in the current factories.
*/
@Path("listProcessFactories")
public Response listProcessFactories(){
final List<String> names = processBusiness.listProcessFactory();
return Response.ok(new StringList(names)).build();
}
/**
* Returns a description of the process parameters.
*/
@GET
@Path("process/descriptor/{authority}/{code}")
public Response getProcessDescriptor(final @PathParam("authority") String authority, final @PathParam("code") String code)
throws ConfigurationException {
final ParameterDescriptorGroup idesc;
try {
idesc = getDescriptor(authority, code);
} catch (ConfigurationException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(new AcknowlegementType("Failure", "Could not find process description for given authority/code.")).build();
}
try {
final String jsonString = ParamUtilities.writeParameterDescriptorJSON(idesc);
return Response.ok(jsonString, MediaType.APPLICATION_JSON_TYPE).build();
} catch (IOException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new AcknowlegementType("Failure", "Could not find chain for given authority/code.")).build();
}
}
private ParameterDescriptorGroup getDescriptor(final String authority, final String code) throws ConfigurationException {
final ProcessDescriptor desc;
try {
desc = ProcessFinder.getProcessDescriptor(authority,code);
} catch (NoSuchIdentifierException ex) {
throw new ConfigurationException("No Process for id : {" + authority + "}"+code+" has been found");
} catch (InvalidParameterValueException ex) {
throw new ConfigurationException(ex);
}
if(desc == null){
throw new ConfigurationException("No Process for id : {" + authority + "}"+code+" has been found");
}
//change the description, always encapsulate in the same namespace and name
//jaxb object factory can not reconize changing names without a namespace
ParameterDescriptorGroup idesc = desc.getInputDescriptor();
idesc = new ParameterBuilder().addName("input").setRequired(true)
.createGroup(idesc.descriptors().toArray(new GeneralParameterDescriptor[0]));
return idesc;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="ChainAPI">
@POST
@Path("chain")
public Response createChain(final Chain chain) throws ConfigurationException {
processBusiness.createChainProcess(chain);
return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build();
}
@DELETE
@Path("chain/{authority}/{code}")
public Response deleteChain(final @PathParam("authority") String authority, final @PathParam("code") String code) {
if (processBusiness.deleteChainProcess(authority, code)) {
return Response.ok(new AcknowlegementType("Success", "The chain has been deleted")).build();
} else {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new AcknowlegementType("Failure", "Could not find chain for given authority/code.")).build();
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="TaskParameterAPI">
@POST
@Path("params/create")
public Response createParamsTask(final TaskParameter taskParameter, @Context HttpServletRequest req) throws ConfigurationException {
final Optional<CstlUser> cstlUser = userRepository.findOne(req.getUserPrincipal().getName());
try {
processBusiness.testTaskParameter(taskParameter);
if (cstlUser.isPresent()) {
taskParameter.setOwner(cstlUser.get().getId());
taskParameter.setDate(System.currentTimeMillis());
processBusiness.addTaskParameter(taskParameter);
return Response.ok().status(Response.Status.CREATED).type(MediaType.TEXT_PLAIN_TYPE).build();
} else {
return Response.status(Response.Status.EXPECTATION_FAILED).build();
}
} catch (ConfigurationException ex) {
final AcknowlegementType failure = new AcknowlegementType("Failure", "Failed to create task : " + ex.getMessage());
return Response.status(Response.Status.NOT_ACCEPTABLE).entity(failure).build();
}
}
@POST
@Path("params/update")
public Response updateParamsTask(final TaskParameter taskParameter) throws ConfigurationException {
try {
processBusiness.testTaskParameter(taskParameter);
processBusiness.updateTaskParameter(taskParameter);
return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build();
} catch (ConfigurationException ex) {
final AcknowlegementType failure = new AcknowlegementType("Failure", "Failed to create task : " + ex.getMessage());
return Response.status(Response.Status.NOT_ACCEPTABLE).entity(failure).build();
}
}
@GET
@Path("params/get/{id}")
public Response getParamsTask(final @PathParam("id") Integer id) {
final TaskParameter taskParameter = processBusiness.getTaskParameterById(id);
if (taskParameter != null) {
return Response.ok(taskParameter).build();
}
return Response.status(Response.Status.BAD_REQUEST).build();
}
@GET
@Path("params/delete/{id}")
public Response deleteParamsTask(final @PathParam("id") Integer id) {
final TaskParameter taskParameter = processBusiness.getTaskParameterById(id);
if (taskParameter != null) {
processBusiness.deleteTaskParameter(taskParameter);
return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build();
}
return Response.status(Response.Status.BAD_REQUEST).build();
}
@GET
@Path("params/duplicate/{id}")
@Transactional
public Response duplicateParamsTask(final @PathParam("id") Integer taskParameterIdForTemplate, @Context HttpServletRequest req) throws ConfigurationException {
final Optional<CstlUser> cstlUser = userRepository.findOne(req.getUserPrincipal().getName());
if (cstlUser.isPresent()) {
final TaskParameter taskParameter = processBusiness.getTaskParameterById(taskParameterIdForTemplate);
taskParameter.setId(null);
taskParameter.setName(taskParameter.getName()+"(COPY)");
taskParameter.setOwner(cstlUser.get().getId());
taskParameterRepository.create(taskParameter);
return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build();
} else {
return Response.status(Response.Status.EXPECTATION_FAILED).build();
}
}
@GET
@Path("params/execute/{id}")
public Response executeParamsTask(final @PathParam("id") Integer id, @Context HttpServletRequest req)
throws ConfigurationException {
final Optional<CstlUser> cstlUser = userRepository.findOne(req.getUserPrincipal().getName());
if (!cstlUser.isPresent()) {
throw new ConstellationException("operation not allowed without login");
}
final TaskParameter taskParameter = processBusiness.getTaskParameterById(id);
final String title = taskParameter.getName()+" "+TASK_DATE.format(new Date());
try {
processBusiness.executeTaskParameter(taskParameter, title, cstlUser.get().getId());
} catch (ConstellationException | ConfigurationException ex) {
final AcknowlegementType failure = new AcknowlegementType("Failure", "Failed to run task : " + ex.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(failure).build();
}
return Response.ok("The task has been created").build();
}
@GET
@Path("params/schedule/start/{id}")
public Response startScheduleParamsTask(final @PathParam("id") Integer id, @Context HttpServletRequest req) throws ConfigurationException {
final Optional<CstlUser> cstlUser = userRepository.findOne(req.getUserPrincipal().getName());
if (!cstlUser.isPresent()) {
throw new ConstellationException("operation not allowed without login");
}
final Date now = new Date();
final TaskParameter taskParameter = processBusiness.getTaskParameterById(id);
final String title = taskParameter.getName()+" "+TASK_DATE.format(now);
try {
processBusiness.scheduleTaskParameter(taskParameter, title, cstlUser.get().getId(), true);
} catch (ConstellationException ex) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new AcknowlegementType("Failure", "Failed to schedule task : "+ex.getMessage())).build();
}
return Response.ok(new AcknowlegementType("Success", "The task has been schedule")).build();
}
@GET
@Path("params/schedule/stop/{id}")
public Response stopScheduleParamsTask(final @PathParam("id") Integer id, @Context HttpServletRequest req) throws ConfigurationException {
final Optional<CstlUser> cstlUser = userRepository.findOne(req.getUserPrincipal().getName());
if (!cstlUser.isPresent()) {
throw new ConstellationException("operation not allowed without login");
}
final TaskParameter taskParameter = processBusiness.getTaskParameterById(id);
try {
processBusiness.stopScheduleTaskParameter(taskParameter, cstlUser.get().getId());
} catch (ConstellationException ex) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new AcknowlegementType("Failure", "Failed to un-schedule task: "+ex.getMessage())).build();
}
return Response.ok(new AcknowlegementType("Success", "The task has been un-schedule")).build();
}
@GET
@Path("params/list")
public Response listParamsTask() throws ConfigurationException {
final List<? extends TaskParameter> all = taskParameterRepository.findAll();
final List<TaskParameterWithOwnerName> ltpwon = convertTaskParameter(all);
return Response.ok(ltpwon).build();
}
@GET
@Path("params/list/{type}")
public Response listParamsTaskByType(final @PathParam("type") String type) throws ConfigurationException {
final List<? extends TaskParameter> all = taskParameterRepository.findAllByType(type);
final List<TaskParameterWithOwnerName> ltpwon = convertTaskParameter(all);
return Response.ok(ltpwon).build();
}
private List<TaskParameterWithOwnerName> convertTaskParameter(List<? extends TaskParameter> all) {
final List<TaskParameterWithOwnerName> ltpwon = new ArrayList<>();
for (TaskParameter tp : all){
TaskParameterWithOwnerName tpwon = new TaskParameterWithOwnerName();
tpwon.setId(tp.getId());
tpwon.setName(tp.getName());
tpwon.setDate(tp.getDate());
tpwon.setOwner(tp.getOwner());
tpwon.setProcessAuthority(tp.getProcessAuthority());
tpwon.setProcessCode(tp.getProcessCode());
tpwon.setInputs(tp.getInputs());
tpwon.setType(tp.getType());
final Optional<CstlUser> byId = userRepository.findById(tp.getOwner());
if (byId.isPresent()) {
tpwon.setOwnerName(byId.get().getFirstname()+" "+byId.get().getLastname());
}
ltpwon.add(tpwon);
}
return ltpwon;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="EditorsDataProvidersAPI">
/**
* List all Datasets as DatasetProcessReference to provider GUI editors.
* @return list of DatasetProcessReference
*/
@GET
@Path("/list/datasetRef")
@Produces({MediaType.APPLICATION_JSON})
public Response getDatasetProcessReferenceList() {
final List<DatasetProcessReference> datasetPRef = new ArrayList<>();
final List<Dataset> datasets = datasetBusiness.getAllDataset();
if(datasets!=null){
for(final Dataset ds : datasets){
final DatasetProcessReference ref = new DatasetProcessReference();
ref.setId(ds.getId());
ref.setIdentifier(ds.getIdentifier());
datasetPRef.add(ref);
}
}
return Response.ok(datasetPRef).build();
}
/**
* List all Services as ServiceProcessReference to provider GUI editors.
* @param domainId
* @return list of ServiceProcessReference
*/
@GET
@Path("/list/serviceRef/domain/{domainId}")
@Produces({MediaType.APPLICATION_JSON})
public Response getServiceProcessReferenceList(@PathParam("domainId") Integer domainId) throws ConfigurationException {
final List<ServiceProcessReference> servicePRef = new ArrayList<>();
final List<ServiceDTO> services = serviceBusiness.getAllServices(null);
if(services!=null){
for(final ServiceDTO service : services){
final ServiceProcessReference ref = new ServiceProcessReference();
ref.setId(service.getId());
ref.setType(service.getType());
ref.setName(service.getTitle());
servicePRef.add(ref);
}
}
return Response.ok(servicePRef).build();
}
/**
* List all Style as StyleProcessReference to provider GUI editors.
* @return list of ServiceProcessReference
*/
@GET
@Path("/list/styleRef")
@Produces({MediaType.APPLICATION_JSON})
public Response getStyleProcessReferenceList() throws ConfigurationException {
final List<StyleProcessReference> servicePRef = new ArrayList<>();
final List<Style> styles = styleRepository.findAll();
if(styles!=null){
for(final Style style : styles){
final StyleProcessReference ref = new StyleProcessReference();
ref.setId(style.getId());
ref.setType(style.getType());
ref.setName(style.getName());
ref.setProvider(style.getProvider());
servicePRef.add(ref);
}
}
return Response.ok(servicePRef).build();
}
/**
* List all User as UserProcessReference to provider GUI editors.
* @return list of UserProcessReference
* @throws org.constellation.configuration.ConfigurationException
*/
@GET
@Path("/list/userRef")
@Produces({MediaType.APPLICATION_JSON})
public Response getUserProcessReferenceList() throws ConfigurationException {
final List<UserProcessReference> userPRef = new ArrayList<>();
final List<CstlUser> users = userRepository.findAll();
if(users!=null){
for(final CstlUser user : users){
final UserProcessReference ref = new UserProcessReference();
ref.setId(user.getId());
ref.setIdentifier(user.getLogin());
userPRef.add(ref);
}
}
return Response.ok(userPRef).build();
}
// </editor-fold>
/**
* List running tasks.
*/
@GET
@Path("listTasks")
public Response listTasks() {
final List<Task> tasks = processBusiness.listRunningTasks();
Map<Integer, List<TaskStatusDTO>> map = new HashMap<>();
for (Task task : tasks) {
Integer taskParameterId = task.getTaskParameterId();
if (!map.containsKey(taskParameterId)) {
map.put(taskParameterId, new ArrayList<TaskStatusDTO>());
}
map.get(taskParameterId).add(toTaskStatus(task));
}
return Response.ok(map).build();
}
/**
* List running tasks.
*/
@GET
@Path("listRunningTasks/{id}/{limit}")
public Response listRunningTaskForTaskParameter(final @PathParam("id") Integer id, final @PathParam("limit") Integer limit) {
final List<Task> tasks = processBusiness.listRunningTasks(id, 0, limit);
List<TaskStatusDTO> lst = new ArrayList<>();
for(Task task : tasks) {
lst.add(toTaskStatus(task));
}
return Response.ok(lst).build();
}
/**
* List running tasks.
*/
@GET
@Path("taskHistory/{id}/{limit}")
public Response listHistoryForTaskParameter(final @PathParam("id") Integer id, final @PathParam("limit") Integer limit) {
final List<Task> tasks = processBusiness.listTaskHistory(id, 0, limit);
List<TaskStatusDTO> lst = new ArrayList<>();
for(Task task : tasks) {
lst.add(toTaskStatus(task));
}
return Response.ok(lst).build();
}
private TaskStatusDTO toTaskStatus(Task task) {
final TaskStatusDTO status = new TaskStatusDTO();
status.setId(task.getIdentifier());
status.setTaskId(task.getTaskParameterId());
status.setMessage(task.getMessage());
status.setPercent(task.getProgress() != null ? task.getProgress().floatValue() : 0f);
status.setStatus(task.getState());
status.setStart(task.getDateStart());
status.setEnd(task.getDateEnd());
status.setOutput(task.getTaskOutput());
final TaskParameter taskParameter = processBusiness.getTaskParameterById(task.getTaskParameterId());
status.setTitle(taskParameter.getName());
return status;
}
}