From 57105ce3fd6a922ae5dc0fbce5285622de73cf43 Mon Sep 17 00:00:00 2001 From: enricovianello Date: Sun, 10 May 2020 12:27:14 +0200 Subject: [PATCH 1/4] Recall final status transitions handled more gracefully Fix for STOR-1177 --- .../tape/recalltable/TapeRecallCatalog.java | 8 +- .../tape/recalltable/resources/README.md | 44 ++-- .../recalltable/resources/TaskResource.java | 216 ++++++++++-------- .../resources/TaskResourceTest.java | 94 ++++++++ 4 files changed, 250 insertions(+), 112 deletions(-) diff --git a/src/main/java/it/grid/storm/tape/recalltable/TapeRecallCatalog.java b/src/main/java/it/grid/storm/tape/recalltable/TapeRecallCatalog.java index 648055c17..2d35dac66 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/TapeRecallCatalog.java +++ b/src/main/java/it/grid/storm/tape/recalltable/TapeRecallCatalog.java @@ -64,13 +64,9 @@ public TapeRecallCatalog() { * * @param groupTaskId @param newValue */ - public void changeGroupTaskRetryValue(UUID groupTaskId, int newValue) { + public void changeGroupTaskRetryValue(UUID groupTaskId, int newValue) throws DataAccessException { - try { - tapeRecallDAO.setGroupTaskRetryValue(groupTaskId, newValue); - } catch (DataAccessException e) { - log.error("Unable to takeover a task", e); - } + tapeRecallDAO.setGroupTaskRetryValue(groupTaskId, newValue); } /** diff --git a/src/main/java/it/grid/storm/tape/recalltable/resources/README.md b/src/main/java/it/grid/storm/tape/recalltable/resources/README.md index 54169be91..6fe2be7d5 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/resources/README.md +++ b/src/main/java/it/grid/storm/tape/recalltable/resources/README.md @@ -5,18 +5,18 @@ This interface expose operations on recall tasks. Recall tasks have the following fields -- groupTaskId -- insertionInstant the time at which the task was created -- requestType the type of request that originated the recall task, either bol or ptg -- fileName the local path of the file to recall -- voName the virtual organization -- userID -- retryAttempt the number of times GEMMS will retry to recall the file in case of failure -- status the status of the recall task, either 0 (success), 1 (queued), 2 (in-progress), 3 (error), 4 (aborted), 5 (undefined) -- deferredRecallInstant -- pinLifetime -- requestToken the token of the request (the bol or ptg) that originated the recall task -- inProgressInstant the time at which the task status was changed to in progress +- `groupTaskId`, each recall task that points to the same resource (same `fileName`) has the same `groupTaskId`; +- `insertionInstant`, the time at which the task was created; +- `requestType`, the type of request that originated the recall task: bol or ptg; +- `fileName`, the local path of the file to recall; +- `voName`, the virtual organization name; +- `userID`, the DN of the user that has submitted the task; +- `retryAttempt`, the number of times GEMMS will retry to recall the file in case of failure; +- `status`, the status of the recall task (see the available values into [status table](#recall-task-statuses) in appendix; +- `deferredRecallInstant` +- `pinLifetime` +- `requestToken`, the token of the request (bol or ptg) that originated the recall task; +- `inProgressInstant`, the time at which the task status was changed to in progress. Recall tasks are transferred using a text/plain representation. Fields are separated by a tab, recall tasks are separated by a #, and all tasks are included by curly brackets as in @@ -165,9 +165,9 @@ and the corresponding response would be ## PUT /recalltable/task/{groupTaskId} -Update a recall task status. +Update status of all the recall tasks of the same resources, grouped by groupTaskId. -The status is passed in the body of the PUT request encoded as follows +The status is passed in the body of the PUT request encoded as follow: status=0 @@ -251,4 +251,18 @@ In your `storm.properties` set: ``` rest.token.enabled = true rest.token.value = your-secret-token -``` \ No newline at end of file +``` + +## Recall Task statuses + +Recall tasks status table: + +| code | status | +|:----:|:-----------:| +| 0 | success | +| 1 | queued | +| 2 | in-progress | +| 3 | error | +| 4 | aborted | +| 5 | undefined | + diff --git a/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java b/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java index 7a2196f63..701bfa477 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java +++ b/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java @@ -19,40 +19,26 @@ package it.grid.storm.tape.recalltable.resources; import static it.grid.storm.persistence.model.TapeRecallTO.RecallTaskType.RCLL; +import static it.grid.storm.tape.recalltable.model.TapeRecallStatus.getRecallTaskStatus; +import static java.lang.String.format; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static javax.ws.rs.core.Response.Status.NOT_FOUND; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import it.grid.storm.config.Configuration; -import it.grid.storm.namespace.NamespaceDirector; -import it.grid.storm.namespace.NamespaceException; -import it.grid.storm.namespace.NamespaceInterface; -import it.grid.storm.namespace.StoRI; -import it.grid.storm.persistence.exceptions.DataAccessException; -import it.grid.storm.persistence.model.TapeRecallTO; -import it.grid.storm.rest.metadata.service.ResourceNotFoundException; -import it.grid.storm.rest.metadata.service.ResourceService; -import it.grid.storm.tape.recalltable.TapeRecallCatalog; -import it.grid.storm.tape.recalltable.TapeRecallException; -import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusLogic; -import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusValidator; -import it.grid.storm.tape.recalltable.model.TapeRecallStatus; -import it.grid.storm.tape.recalltable.model.TaskInsertRequestValidator; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; +import java.util.AbstractMap.SimpleEntry; import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -65,6 +51,30 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import it.grid.storm.config.Configuration; +import it.grid.storm.namespace.NamespaceDirector; +import it.grid.storm.namespace.NamespaceException; +import it.grid.storm.namespace.NamespaceInterface; +import it.grid.storm.namespace.StoRI; +import it.grid.storm.persistence.exceptions.DataAccessException; +import it.grid.storm.persistence.model.TapeRecallTO; +import it.grid.storm.rest.metadata.service.ResourceNotFoundException; +import it.grid.storm.rest.metadata.service.ResourceService; +import it.grid.storm.tape.recalltable.TapeRecallCatalog; +import it.grid.storm.tape.recalltable.TapeRecallException; +import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusLogic; +import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusValidator; +import it.grid.storm.tape.recalltable.model.TapeRecallStatus; +import it.grid.storm.tape.recalltable.model.TaskInsertRequestValidator; +import jersey.repackaged.com.google.common.collect.Lists; /** * @author Riccardo Zappi @@ -169,112 +179,94 @@ public Response putTaskStatus(InputStream input) { /** * Updates the status or retry value of a recall task. Called by GEMSS after a recall tasks is * finished. - * */ @PUT @Path("/{groupTaskId}") @Consumes("text/plain") - public void putNewTaskStatusOrRetryValue(@PathParam("groupTaskId") UUID groupTaskId, - InputStream input) throws TapeRecallException { + public Response putNewTaskStatusOrRetryValue(@PathParam("groupTaskId") UUID groupTaskId, + InputStream input) { + + log.debug("Requested to change recall table value for groupTaskId {}", groupTaskId); + + List lines; + try { + lines = getAllLines(input); + } catch (IOException e) { + String message = format("Unable to read entity lines: %s", e.getMessage()); + log.error(message, e); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); + } - log.debug("Requested to change recall table value for taskId {}", groupTaskId); + if (lines.isEmpty() || lines.size() > 1) { - String inputStr = buildInputString(input); + String message = format("Invalid body '%s'", lines); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.BAD_REQUEST).build(); + } - log.debug("@PUT (input string) = '{}'", inputStr); + Optional> keyValue = getKeyValue(lines.get(0)); - // Retrieve Tasks corresponding to taskId - // - the relationship between groupTaskId and entries within the DB is - // one-to-many + if (!keyValue.isPresent()) { - String errorStr = null; + String message = format("Invalid body '%s'", lines); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.BAD_REQUEST).build(); + } try { if (!recallCatalog.existsGroupTask(groupTaskId)) { - log.info( - "Received a tape recall status update but no Recall Group Task found with ID = '{}'", - groupTaskId); - - throw new TapeRecallException("No Recall Group Task found with ID = '" + groupTaskId + "'"); + String message = format("No Recall Group Task found with ID = '%s'", groupTaskId); + log.info(message); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.NOT_FOUND).build(); } } catch (DataAccessException e) { - log.error("Unable to retrieve Recall Group Task with ID = '{}' DataAccessException: {}", - groupTaskId, e.getMessage(), e); - - throw new TapeRecallException("Unable to retrieve recall group task " + "with ID = '" - + groupTaskId + "' " + e.getMessage()); + String message = format("Unable to retrieve Recall Group Task with ID = '%s' %s", groupTaskId, + e.getMessage()); + log.error(message, e); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); } - String keyRetryValue = config.getRetryValueKey(); String keyStatus = config.getStatusKey(); + String key = keyValue.get().getKey(); + Integer value = keyValue.get().getValue(); - int eqIndex = inputStr.indexOf('='); - - String value = null; - String key = null; - - if (eqIndex > 0) { - - value = inputStr.substring(eqIndex); - key = inputStr.substring(0, eqIndex); - - } else { - - errorStr = "Body '" + inputStr + "'is wrong"; - throw new TapeRecallException(errorStr); - } - - int intValue; - - try { - - // trim out the '\n' end. - intValue = Integer.valueOf(value.substring(1, value.length() - 1)); - - } catch (NumberFormatException e) { - - errorStr = "Unable to understand the number value = '" + value + "'"; - throw new TapeRecallException(errorStr); - } - - if (key.equals(keyRetryValue)) { // **** Set the Retry value + if (key.equalsIgnoreCase(keyStatus)) { // **** Set the Status - log.debug("Changing retry attempt of task {} to {}", groupTaskId, intValue); + log.debug("Changing status of task {} to {}", groupTaskId, value); - recallCatalog.changeGroupTaskRetryValue(groupTaskId, intValue); + try { - } else { + TapeRecallStatus newStatus = getRecallTaskStatus(value); + recallCatalog.changeGroupTaskStatus(groupTaskId, newStatus, new Date()); - if (key.equals(keyStatus)) { // **** Set the Status - log.debug("Changing status of task {} to {}", groupTaskId, intValue); + } catch (DataAccessException e) { - try { + String message = format( + "Unable to change the status for group task id %s to status %s DataAccessException : %s", + groupTaskId, value, e.getMessage()); + log.error(message, e); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); + } - recallCatalog.changeGroupTaskStatus(groupTaskId, - TapeRecallStatus.getRecallTaskStatus(intValue), new Date()); + } else { // inputMap.containsKey(keyRetryValue)) **** Set the Retry value - } catch (DataAccessException e) { + log.debug("Changing retry attempt of task {} to {}", groupTaskId, value); - log.error( - "Unable to change the status for group task id {} to status {} DataAccessException : {}", - groupTaskId, intValue, e.getMessage(), e); + try { - throw new TapeRecallException( - "Unable to change the status for group task id " + groupTaskId + " to status " - + intValue + " . DataAccessException : " + e.getMessage()); - } + recallCatalog.changeGroupTaskRetryValue(groupTaskId, value); - } else { + } catch (DataAccessException e) { - errorStr = "Unable to understand the key = '" + key + "' in @PUT request."; + String message = format("Unable to takeover a task: %s", e.getMessage()); + return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); - throw new TapeRecallException(errorStr); } } + + return Response.noContent().build(); } /** @@ -451,4 +443,46 @@ private String buildInputString(InputStream input) { return sb.toString(); } + private Optional> getKeyValue(String line) { + + Pattern pStatus = Pattern.compile(config.getStatusKey() + "=[0-9]+"); + Pattern pAttempt = Pattern.compile(config.getRetryValueKey() + "=[0-9]+"); + + SimpleEntry entry = null; + + Matcher mStatus = pStatus.matcher(line); + Matcher mAttempt = pAttempt.matcher(line); + + if (mStatus.matches() || mAttempt.matches()) { + String key = line.split("=")[0]; + Integer value = Integer.valueOf(line.split("=")[1]); + entry = new SimpleEntry(key, value); + } else { + log.warn("got invalid key-value data = {}", line); + } + + return Optional.ofNullable(entry); + } + + + public List getAllLines(InputStream input) throws IOException { + + List lines = Lists.newArrayList(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) { + String line = null; + while ((line = br.readLine()) != null) { + lines.add(line); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } finally { + try { + input.close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + return lines; + } + } diff --git a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java index e944e5682..f8030e2d1 100644 --- a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java +++ b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java @@ -6,6 +6,7 @@ import static javax.ws.rs.core.Response.Status.CREATED; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static javax.ws.rs.core.Response.Status.NO_CONTENT; import static javax.ws.rs.core.Response.Status.OK; import static junit.framework.Assert.assertNotNull; import static org.hamcrest.Matchers.equalTo; @@ -14,6 +15,7 @@ import static org.junit.Assert.fail; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Date; @@ -23,6 +25,7 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; +import org.apache.commons.io.IOUtils; import org.junit.Test; import org.mockito.Mockito; @@ -389,4 +392,95 @@ public void testGETTasksInProgressEmpty() } + private TapeRecallCatalog getTapeRecallCatalogGroupTaskId(UUID groupTaskId, + boolean existsResponse) throws DataAccessException { + + TapeRecallCatalog catalog = Mockito.mock(TapeRecallCatalog.class); + Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(existsResponse); + return catalog; + } + + @Test + public void testPUTTaskStatusToSuccessWorks() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "status=0"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, true)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(NO_CONTENT.getStatusCode())); + } + + @Test + public void testPUTTaskRetryValueWorks() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "retry-value=0"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, true)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(NO_CONTENT.getStatusCode())); + } + + @Test + public void testPUTTaskStatusOnTaskIdNotFound() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "status=0"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, false)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(NOT_FOUND.getStatusCode())); + assertThat(res.getEntity().toString(), equalTo("No Recall Group Task found with ID = '" + groupTaskId + "'")); + } + + @Test + public void testPUTTaskStatusWithWrongKeyInBody() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "wrong=0"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, true)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); + assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODY + "]'")); + } + + @Test + public void testPUTTaskStatusWithWrongValueInBody() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "status=queued"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, true)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); + assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODY + "]'")); + } + + @Test + public void testPUTTaskStatusWithBothStatusAndRetryValuesInBody() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "status=0\nretry-value=2"; + final String BODYSTR = "status=0, retry-value=2"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskId(groupTaskId, true)); + Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); + assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODYSTR + "]'")); + + } } From 3c43ca134c1bca2caf2c5a1be9b4fe5aa8e8061f Mon Sep 17 00:00:00 2001 From: enricovianello Date: Thu, 14 May 2020 11:13:48 +0200 Subject: [PATCH 2/4] Added Validator More refactoring --- .../it/grid/storm/config/Configuration.java | 20 - .../recalltable/model/PropertiesParser.java | 16 + .../model/PutTapeRecallStatusLogic.java | 1 - .../model/PutTapeRecallStatusValidator.java | 198 +++---- .../recalltable/model/PutUpdateTaskLogic.java | 5 + .../model/PutUpdateTaskValidator.java | 135 +++++ .../recalltable/model/RequestDataParser.java | 9 + .../{ => model}/TapeRecallException.java | 2 +- .../recalltable/persistence/PropertiesDB.java | 236 --------- .../TapeRecallTOListMessageBodyWriter.java | 4 +- .../recalltable/resources/TaskResource.java | 158 +----- .../resources/TasksCardinality.java | 2 +- .../recalltable/resources/TasksResource.java | 491 +++++++++--------- .../resources/TaskResourceTest.java | 90 +++- 14 files changed, 612 insertions(+), 755 deletions(-) create mode 100644 src/main/java/it/grid/storm/tape/recalltable/model/PropertiesParser.java create mode 100644 src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskLogic.java create mode 100644 src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskValidator.java create mode 100644 src/main/java/it/grid/storm/tape/recalltable/model/RequestDataParser.java rename src/main/java/it/grid/storm/tape/recalltable/{ => model}/TapeRecallException.java (95%) delete mode 100644 src/main/java/it/grid/storm/tape/recalltable/persistence/PropertiesDB.java diff --git a/src/main/java/it/grid/storm/config/Configuration.java b/src/main/java/it/grid/storm/config/Configuration.java index d18006117..7f4208ff9 100644 --- a/src/main/java/it/grid/storm/config/Configuration.java +++ b/src/main/java/it/grid/storm/config/Configuration.java @@ -172,8 +172,6 @@ public class Configuration { private static final String REST_SERVICES_PORT_KEY = "storm.rest.services.port"; private static final String REST_SERVICES_MAX_THREAD = "storm.rest.services.maxthread"; private static final String REST_SERVICES_MAX_QUEUE_SIZE = "storm.rest.services.max_queue_size"; - private static final String RETRY_VALUE_KEY_KEY = "tape.recalltable.service.param.retry-value"; - private static final String STATUS_KEY_KEY = "tape.recalltable.service.param.status"; private static final String TASKOVER_KEY_KEY = "tape.recalltable.service.param.takeover"; private static final String STORM_PROPERTIES_VERSION_KEY = "storm.properties.version"; private static final String TAPE_SUPPORT_ENABLED_KEY = "tape.support.enabled"; @@ -1389,24 +1387,6 @@ public int getRestServicesMaxQueueSize() { .getInt(REST_SERVICES_MAX_QUEUE_SIZE, RestService.DEFAULT_MAX_QUEUE_SIZE); } - /** - * Method used to retrieve the key string used to pass RETRY-VALUE parameter to Recall Table - * service key="tape.recalltable.service.param.retry-value"; - */ - public String getRetryValueKey() { - - return cr.getConfiguration().getString(RETRY_VALUE_KEY_KEY, "retry-value"); - } - - /** - * Method used to retrieve the key string used to pass RETRY-VALUE parameter to Recall Table - * service key="tape.recalltable.service.param.status"; - */ - public String getStatusKey() { - - return cr.getConfiguration().getString(STATUS_KEY_KEY, "status"); - } - /** * Method used to retrieve the key string used to pass RETRY-VALUE parameter to Recall Table * service key="tape.recalltable.service.param.takeover"; diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/PropertiesParser.java b/src/main/java/it/grid/storm/tape/recalltable/model/PropertiesParser.java new file mode 100644 index 000000000..f8fb56ea9 --- /dev/null +++ b/src/main/java/it/grid/storm/tape/recalltable/model/PropertiesParser.java @@ -0,0 +1,16 @@ +package it.grid.storm.tape.recalltable.model; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class PropertiesParser implements RequestDataParser { + + @Override + public Properties parse(InputStream input) throws IOException { + + Properties props = new Properties(); + props.load(input); + return props; + } +} diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusLogic.java b/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusLogic.java index 54523b253..0ce745d30 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusLogic.java +++ b/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusLogic.java @@ -20,7 +20,6 @@ import it.grid.storm.persistence.exceptions.DataAccessException; import it.grid.storm.persistence.model.TapeRecallTO; import it.grid.storm.tape.recalltable.TapeRecallCatalog; -import it.grid.storm.tape.recalltable.TapeRecallException; import java.util.Date; import java.util.UUID; diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusValidator.java b/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusValidator.java index 547f1cbfc..a1a9ecb97 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusValidator.java +++ b/src/main/java/it/grid/storm/tape/recalltable/model/PutTapeRecallStatusValidator.java @@ -17,136 +17,144 @@ package it.grid.storm.tape.recalltable.model; -import it.grid.storm.namespace.NamespaceDirector; -import it.grid.storm.namespace.StoRI; -import it.grid.storm.srm.types.InvalidTSURLAttributesException; -import it.grid.storm.srm.types.TSURL; -import it.grid.storm.util.SURLValidator; -import it.grid.storm.util.TokenValidator; +import static java.lang.String.format; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import java.util.StringTokenizer; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PutTapeRecallStatusValidator implements RequestValidator { - - private static final Logger log = LoggerFactory - .getLogger(PutTapeRecallStatusValidator.class); - - private String requestToken = null; - private StoRI stori = null; - private String inputString = null; - private Response validationResponse = null; - - public PutTapeRecallStatusValidator(String inputString) { +import it.grid.storm.namespace.NamespaceDirector; +import it.grid.storm.namespace.StoRI; +import it.grid.storm.srm.types.InvalidTSURLAttributesException; +import it.grid.storm.srm.types.TSURL; +import it.grid.storm.util.SURLValidator; +import it.grid.storm.util.TokenValidator; - this.inputString = inputString; - } +public class PutTapeRecallStatusValidator implements RequestValidator { - /** - * Parse and validate input. - *

- * If this method returns true the input data can be retrieved - * with the methods: {@link #getRequestToken()} and {@link #getStoRI()}. - *

- * If this method returns false the response can be retrieved - * with the method {@link #getResponse()}. - * - * @return true for successful validation process, - * false otherwise. - */ - public boolean validate() { + private static final Logger log = LoggerFactory.getLogger(PutTapeRecallStatusValidator.class); - StringTokenizer tokenizer = new StringTokenizer(inputString, "\n"); + public static final String REQUEST_TOKEN_KEY = "requestToken"; + public static final String SURL_KEY = "surl"; - if (tokenizer.countTokens() != 2) { + public static final String NOT_FOUND_PROPERTY = "Invalid body. Not found property %s."; + public static final String INVALID_TOKEN = "Invalid token: %s."; + public static final String INVALID_SURL = "Invalid SURL: %s."; - log.trace("putTaskStatus() - input error"); + private InputStream input = null; - validationResponse = Response.status(400).build(); - return false; + private Response validationResponse = null; - } + private String requestToken = null; + private StoRI stori = null; - String requestTokenInput = tokenizer.nextToken(); - String surlInput = tokenizer.nextToken(); + public PutTapeRecallStatusValidator(InputStream input) { - if ((!requestTokenInput.startsWith("requestToken=")) - || (!surlInput.startsWith("surl="))) { + this.input = input; + } - log.trace("putTaskStatus() - input error"); + /** + * Parse and validate input. + *

+ * If this method returns true the input data can be retrieved with the methods: + * {@link #getRequestToken()} and {@link #getStoRI()}. + *

+ * If this method returns false the response can be retrieved with the method + * {@link #getResponse()}. + * + * @return true for successful validation process, false otherwise. + */ + public boolean validate() { - validationResponse = Response.status(400).build(); - return false; + Properties props; + PropertiesParser parser = new PropertiesParser(); + try { + props = parser.parse(input); + } catch (IOException e) { + log.error(e.getMessage(), e); + validationResponse = Response.status(INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + return false; + } - } + if (!props.containsKey(REQUEST_TOKEN_KEY)) { + validationResponse = Response.status(BAD_REQUEST).entity(format(NOT_FOUND_PROPERTY, REQUEST_TOKEN_KEY)).build(); + return false; + } - requestToken = requestTokenInput - .substring(requestTokenInput.indexOf('=') + 1); - String surlString = surlInput.substring(surlInput.indexOf('=') + 1); + if (!props.containsKey(SURL_KEY)) { + validationResponse = Response.status(BAD_REQUEST).entity(format(NOT_FOUND_PROPERTY, SURL_KEY)).build(); + return false; + } - if ((requestToken.length() == 0) || (surlString.length() == 0)) { + requestToken = props.getProperty(REQUEST_TOKEN_KEY); - log.trace("putTaskStatus() - input error"); + if (requestToken.length() == 0 || !TokenValidator.valid(requestToken)) { + validationResponse = Response.status(BAD_REQUEST).entity(format(INVALID_TOKEN, requestToken)).build(); + return false; + } - validationResponse = Response.status(400).build(); - return false; + String surlString = props.getProperty(SURL_KEY); - } + if (surlString.length() == 0) { + validationResponse = Response.status(BAD_REQUEST).entity(format(INVALID_SURL, surlString)).build(); + return false; + } - if(!TokenValidator.valid(requestToken)){ - validationResponse = Response.status(400).entity("Invalid token: " + requestToken +" \n\n").build(); - return false; - } - - if (!validateSurl(surlString)) { - return false; - } + if (!validateSurl(surlString)) { + validationResponse = Response.status(BAD_REQUEST).entity(format(INVALID_SURL, surlString)).build(); + return false; + } - return true; - } + return true; + } - public String getRequestToken() { + public String getRequestToken() { - return requestToken; - } + return requestToken; + } - public StoRI getStoRI() { + public StoRI getStoRI() { - return stori; - } + return stori; + } - public Response getResponse() { + public Response getResponse() { - return validationResponse; - } + return validationResponse; + } - private boolean validateSurl(String surlString) { + private boolean validateSurl(String surlString) { - TSURL surl; + TSURL surl; - if(!SURLValidator.valid(surlString)){ - validationResponse = Response.status(400).entity("Invalid surl: " + surlString + "\n\n").build(); - return false; - } - - try { + if (!SURLValidator.valid(surlString)) { + validationResponse = + Response.status(400).entity("Invalid surl: " + surlString + "\n\n").build(); + return false; + } - surl = TSURL.makeFromStringValidate(surlString); + try { + + surl = TSURL.makeFromStringValidate(surlString); - } catch (InvalidTSURLAttributesException e) { - validationResponse = Response.status(400).build(); - return false; - } - try { - stori = NamespaceDirector.getNamespace().resolveStoRIbySURL(surl); - } catch (Exception e) { - log.warn("Unable to build a stori for surl {} UnapprochableSurlException: {}" , surl , e.getMessage(),e); - return false; - } - return true; - } + } catch (InvalidTSURLAttributesException e) { + validationResponse = Response.status(400).build(); + return false; + } + try { + stori = NamespaceDirector.getNamespace().resolveStoRIbySURL(surl); + } catch (Exception e) { + log.warn("Unable to build a stori for surl {} UnapprochableSurlException: {}", surl, + e.getMessage(), e); + return false; + } + return true; + } } diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskLogic.java b/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskLogic.java new file mode 100644 index 000000000..bebca13bf --- /dev/null +++ b/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskLogic.java @@ -0,0 +1,5 @@ +package it.grid.storm.tape.recalltable.model; + +public class PutUpdateTaskLogic { + +} diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskValidator.java b/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskValidator.java new file mode 100644 index 000000000..fffe84a87 --- /dev/null +++ b/src/main/java/it/grid/storm/tape/recalltable/model/PutUpdateTaskValidator.java @@ -0,0 +1,135 @@ +/* + * + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2010. + * + * 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 it.grid.storm.tape.recalltable.model; + +import static java.lang.String.format; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PutUpdateTaskValidator implements RequestValidator { + + public static final String INVALID_BODY = "Invalid body. Expected [status|retry-value]={value}."; + + public static final String STATUS_KEY = "status"; + public static final String RETRY_VALUE_KEY = "retry-value"; + + public static final String NOT_FOUND_PROPERTY = "Invalid body. Not found property %s."; + public static final String EMPTY_BODY = "No properties found in body."; + public static final String WRONG_SIZE = "Expected one property. Found %d."; + public static final String NOT_INT_VALUE = "Invalid integer value '%s'."; + + private static final Logger LOG = LoggerFactory.getLogger(PutUpdateTaskValidator.class); + + private InputStream input = null; + + private Response validationResponse = null; + + private Integer value = null; + private boolean isStatus = false; + private boolean isRetryValue = false; + + public PutUpdateTaskValidator(InputStream input) { + + this.input = input; + } + + public boolean validate() { + + Properties props; + PropertiesParser parser = new PropertiesParser(); + try { + props = parser.parse(input); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + validationResponse = Response.status(INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + return false; + } + + if (props.size() == 0) { + + LOG.debug(EMPTY_BODY); + validationResponse = Response.status(BAD_REQUEST).entity(EMPTY_BODY).build(); + return false; + + } + + if (props.size() > 1) { + + String message = format(WRONG_SIZE, props.size()); + LOG.debug(message); + validationResponse = Response.status(BAD_REQUEST).entity(message).build(); + return false; + + } + + isStatus = props.containsKey(STATUS_KEY); + isRetryValue = props.containsKey(RETRY_VALUE_KEY); + + if (!isStatus && !isRetryValue) { + + LOG.debug(INVALID_BODY); + validationResponse = Response.status(BAD_REQUEST).entity(INVALID_BODY).build(); + return false; + } + + String valueStr = null; + try { + + valueStr = isStatus ? props.getProperty(STATUS_KEY) : props.getProperty(RETRY_VALUE_KEY); + value = Integer.valueOf(valueStr); + + } catch (NumberFormatException e) { + + String message = format(NOT_INT_VALUE, valueStr); + LOG.error(message); + validationResponse = Response.status(BAD_REQUEST).entity(message).build(); + return false; + } + + return true; + } + + public Response getResponse() { + + return validationResponse; + } + + public Integer getValue() { + + return value; + } + + public boolean isStatusRequest() { + + return isStatus; + } + + public boolean isRetryValueRequest() { + + return isRetryValue; + } +} diff --git a/src/main/java/it/grid/storm/tape/recalltable/model/RequestDataParser.java b/src/main/java/it/grid/storm/tape/recalltable/model/RequestDataParser.java new file mode 100644 index 000000000..2b24f7c8d --- /dev/null +++ b/src/main/java/it/grid/storm/tape/recalltable/model/RequestDataParser.java @@ -0,0 +1,9 @@ +package it.grid.storm.tape.recalltable.model; + +import java.io.IOException; +import java.io.InputStream; + +public interface RequestDataParser { + + T parse(InputStream input) throws IOException; +} diff --git a/src/main/java/it/grid/storm/tape/recalltable/TapeRecallException.java b/src/main/java/it/grid/storm/tape/recalltable/model/TapeRecallException.java similarity index 95% rename from src/main/java/it/grid/storm/tape/recalltable/TapeRecallException.java rename to src/main/java/it/grid/storm/tape/recalltable/model/TapeRecallException.java index a9ada4586..98f37bb3c 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/TapeRecallException.java +++ b/src/main/java/it/grid/storm/tape/recalltable/model/TapeRecallException.java @@ -18,7 +18,7 @@ /** * */ -package it.grid.storm.tape.recalltable; +package it.grid.storm.tape.recalltable.model; /** * @author ritz diff --git a/src/main/java/it/grid/storm/tape/recalltable/persistence/PropertiesDB.java b/src/main/java/it/grid/storm/tape/recalltable/persistence/PropertiesDB.java deleted file mode 100644 index 373f9f9c7..000000000 --- a/src/main/java/it/grid/storm/tape/recalltable/persistence/PropertiesDB.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * - * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2010. - * - * 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 it.grid.storm.tape.recalltable.persistence; - -import it.grid.storm.config.Configuration; -import it.grid.storm.persistence.exceptions.DataAccessException; -import it.grid.storm.persistence.model.TapeRecallTO; -import it.grid.storm.srm.types.TRequestToken; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Properties; -import java.util.UUID; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author zappi - * - */ -public class PropertiesDB { - - private static final Logger log = LoggerFactory.getLogger(PropertiesDB.class); - private static Configuration config = Configuration.getInstance(); - private final String dataFileName = "recall-table.txt"; - private final String propertiesDBName; - - public PropertiesDB() { - - String configurationDir = PropertiesDB.config.configurationDir(); - char sep = File.separatorChar; - propertiesDBName = configurationDir + sep + "etc" + sep + "db" + sep - + dataFileName; - log.debug("Properties RecallTable-DB = {}" , propertiesDBName); - } - - public PropertiesDB(boolean test) { - - String configurationDir; - if (test) { - configurationDir = System.getProperty("user.dir"); - } else { - configurationDir = PropertiesDB.config.configurationDir(); - } - char sep = File.separatorChar; - propertiesDBName = configurationDir + sep + "etc" + sep + "db" + sep - + dataFileName; - // log.debug("Properties RecallTable-DB = " + propertiesDBName); - File tasksDBfile = new File(propertiesDBName); - boolean success = false; - try { - success = tasksDBfile.createNewFile(); - } catch (IOException e) { - log.error("Error while trying to check : {}" , propertiesDBName,e); - } - if (success) { - log.debug("TaskDB = '{}' exists ? {}" , propertiesDBName , success); - } - } - - // *************** PERSISTENCE METHODS **************** - - /** - * - * @param task - * @throws FileNotFoundException - * @throws IOException - * @throws DataAccessException - */ - public void addRecallTask(TapeRecallTO task) throws FileNotFoundException, - IOException, DataAccessException { - - Properties properties = new Properties(); - properties.load(new FileInputStream(propertiesDBName)); - - // Retrieve the Request-Token (unique-key) - TRequestToken taskToken = task.getRequestToken(); - if (taskToken == null) { - log.error("You are trying to store a Task without a task-id."); - throw new DataAccessException( - "You are trying to store a Task without a task-id."); - } - // Build the String related to Task-id - String taskStr = task.toString(); - // Insert the new property entry - properties.setProperty(taskToken.getValue(), taskStr); - // Store the properties into disk - properties.store(new FileOutputStream(propertiesDBName), null); - } - - public void setRecallTask(List listTasks) - throws FileNotFoundException, IOException, DataAccessException { - - Properties properties = new Properties(); - properties.load(new FileInputStream(propertiesDBName)); - - TRequestToken taskToken = null; - String taskStr = null; - for (TapeRecallTO TapeRecallTO : listTasks) { - // Retrieve the Task-id (unique-key) - taskToken = TapeRecallTO.getRequestToken(); - if (taskToken == null) { - log.error("You are trying to store a Task without a RequestToken."); - throw new DataAccessException( - "You are trying to store a Task without a Request-Token."); - } - // Build the String related to Task-id - taskStr = TapeRecallTO.toString(); - // Insert the new property entry - properties.setProperty(taskToken.getValue(), taskStr); - taskToken = null; - } - // Store the properties into disk - properties.store(new FileOutputStream(propertiesDBName), null); - } - - // public List getRecallTask(UUID taskId) throws - // FileNotFoundException, IOException, DataAccessException { - // ArrayList result = new ArrayList(); - // Properties properties = new Properties(); - // properties.load(new FileInputStream(propertiesDBName)); - // - // for (Object values : properties.values()) { - // String v = (String)values; - // TapeRecallTO task = TapeRecallBuilder.build(v); - // if (task.getTaskId().equals(taskId)) { - // result.add(task); - // } - // } - // if (result.isEmpty()) { - // log.error("Unable to retrieve the task with ID = " + taskId); - // throw new DataAccessException("Unable to find the task with ID = " + - // taskId); - // } - // return result; - // } - - public void updateRecallTask(TapeRecallTO task) throws FileNotFoundException, - IOException, DataAccessException { - - Properties properties = new Properties(); - properties.load(new FileInputStream(propertiesDBName)); - - UUID taskId = task.getTaskId(); - - // Check if the Task exists within the Properties DB - boolean taskExist = properties.containsKey(taskId.toString()); - if (!(taskExist)) { - log.error("Unable to find the task with ID = {}" , taskId); - throw new DataAccessException("Unable to find the task with ID = " - + taskId); - } else { - // Build the String related to Task-id - String taskStr = task.toString(); - // Insert the new property entry - properties.setProperty(taskId.toString(), taskStr); - log.debug("Removed tasks '{}'" , taskId); - } - - // Store the properties into disk - properties.store(new FileOutputStream(propertiesDBName), null); - } - - public void deleteRecallTask(UUID taskId) throws FileNotFoundException, - IOException, DataAccessException { - - Properties properties = new Properties(); - properties.load(new FileInputStream(propertiesDBName)); - - // Retrieve the Task from taskId - String task = properties.getProperty(taskId.toString()); - if (task == null) { - log.error("Unable to find the task with ID = {}" , taskId); - throw new DataAccessException("Unable to find the task with ID = " - + taskId); - } else { - properties.remove(taskId); - log.debug("Removed tasks '{}'" , taskId); - } - - // Store the properties into disk - properties.store(new FileOutputStream(propertiesDBName), null); - } - - // public LinkedHashMap getAll() throws - // FileNotFoundException, IOException, DataAccessException { - // - // LinkedHashMap tasksDBmem = new - // LinkedHashMap(); - // ArrayList tasksList = new ArrayList(); - // Properties properties = new Properties(); - // properties.load(new FileInputStream(propertiesDBName)); - // Collection values = properties.values(); - // for (Object element : values) { - // String line = (String) element; - // TapeRecallTO task = TapeRecallBuilder.build(line); - // tasksList.add(task); - // } - // TapeRecallTO[] tasksArray = tasksList.toArray(new - // TapeRecallTO[tasksList.size()]); - // Arrays.sort(tasksArray); - // // Create the ordered LinkedHashMap - // for (TapeRecallTO element : tasksArray) { - // tasksDBmem.put(element.getRequestToken(), element); - // } - // return tasksDBmem; - // } - -} diff --git a/src/main/java/it/grid/storm/tape/recalltable/providers/TapeRecallTOListMessageBodyWriter.java b/src/main/java/it/grid/storm/tape/recalltable/providers/TapeRecallTOListMessageBodyWriter.java index 79c19f153..5334faa97 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/providers/TapeRecallTOListMessageBodyWriter.java +++ b/src/main/java/it/grid/storm/tape/recalltable/providers/TapeRecallTOListMessageBodyWriter.java @@ -59,7 +59,7 @@ public void writeTo(List tasks, Class type, StringBuilder sb = new StringBuilder(); - sb.append("{"); + sb.append(TapeRecallBuilder.TASK_START); for (TapeRecallTO t : tasks) { @@ -67,7 +67,7 @@ public void writeTo(List tasks, Class type, sb.append(TapeRecallBuilder.ELEMENT_SEP); } - sb.append("}"); + sb.append(TapeRecallBuilder.TASK_END); entityStream.write(sb.toString().getBytes()); } diff --git a/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java b/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java index 701bfa477..1aaec61d8 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java +++ b/src/main/java/it/grid/storm/tape/recalltable/resources/TaskResource.java @@ -22,23 +22,15 @@ import static it.grid.storm.tape.recalltable.model.TapeRecallStatus.getRecallTaskStatus; import static java.lang.String.format; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static javax.ws.rs.core.Response.Status.NOT_FOUND; -import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URI; -import java.util.AbstractMap.SimpleEntry; import java.util.Date; import java.util.List; -import java.util.Optional; import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -51,7 +43,6 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +50,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import it.grid.storm.config.Configuration; import it.grid.storm.namespace.NamespaceDirector; import it.grid.storm.namespace.NamespaceException; import it.grid.storm.namespace.NamespaceInterface; @@ -69,12 +59,12 @@ import it.grid.storm.rest.metadata.service.ResourceNotFoundException; import it.grid.storm.rest.metadata.service.ResourceService; import it.grid.storm.tape.recalltable.TapeRecallCatalog; -import it.grid.storm.tape.recalltable.TapeRecallException; import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusLogic; import it.grid.storm.tape.recalltable.model.PutTapeRecallStatusValidator; +import it.grid.storm.tape.recalltable.model.PutUpdateTaskValidator; +import it.grid.storm.tape.recalltable.model.TapeRecallException; import it.grid.storm.tape.recalltable.model.TapeRecallStatus; import it.grid.storm.tape.recalltable.model.TaskInsertRequestValidator; -import jersey.repackaged.com.google.common.collect.Lists; /** * @author Riccardo Zappi @@ -85,8 +75,6 @@ public class TaskResource { private static final Logger log = LoggerFactory.getLogger(TaskResource.class); - private static Configuration config = Configuration.getInstance(); - private ResourceService service; private TapeRecallCatalog recallCatalog; @@ -113,7 +101,7 @@ public TaskResource(ResourceService service, TapeRecallCatalog recallCatalog) { * a 500 if something went wrong */ @GET - public Response getTasks(@QueryParam("maxResults") Integer maxResults) { + public Response getTasksInProgress(@QueryParam("maxResults") Integer maxResults) { List tasks = recallCatalog.getAllInProgressTasks(maxResults); @@ -145,16 +133,15 @@ public Response getTasks(@QueryParam("maxResults") Integer maxResults) { */ @PUT @Consumes("text/plain") - public Response putTaskStatus(InputStream input) { - - String inputString = buildInputString(input); + public Response updateTaskStatusIfOnline(InputStream input) { - log.debug("putTaskStatus() - Input: {}", inputString); + log.debug("PUT /recalltable/task "); - PutTapeRecallStatusValidator validator = new PutTapeRecallStatusValidator(inputString); + PutTapeRecallStatusValidator validator = new PutTapeRecallStatusValidator(input); if (!validator.validate()) { + log.info("PUT /recalltable/task - {} - {}", validator.getResponse().getStatus(), validator.getResponse().getEntity()); return validator.getResponse(); } @@ -183,33 +170,20 @@ public Response putTaskStatus(InputStream input) { @PUT @Path("/{groupTaskId}") @Consumes("text/plain") - public Response putNewTaskStatusOrRetryValue(@PathParam("groupTaskId") UUID groupTaskId, + public Response updateGroupTasks(@PathParam("groupTaskId") UUID groupTaskId, InputStream input) { log.debug("Requested to change recall table value for groupTaskId {}", groupTaskId); - List lines; - try { - lines = getAllLines(input); - } catch (IOException e) { - String message = format("Unable to read entity lines: %s", e.getMessage()); - log.error(message, e); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); - } + PutUpdateTaskValidator validator = new PutUpdateTaskValidator(input); - if (lines.isEmpty() || lines.size() > 1) { + if (!validator.validate()) { - String message = format("Invalid body '%s'", lines); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.BAD_REQUEST).build(); + log.info("PUT /recalltable/task - {} - {}", validator.getResponse().getStatus(), validator.getResponse().getEntity()); + return validator.getResponse(); } - Optional> keyValue = getKeyValue(lines.get(0)); - - if (!keyValue.isPresent()) { - - String message = format("Invalid body '%s'", lines); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.BAD_REQUEST).build(); - } + /* Business logic */ try { @@ -217,7 +191,7 @@ public Response putNewTaskStatusOrRetryValue(@PathParam("groupTaskId") UUID grou String message = format("No Recall Group Task found with ID = '%s'", groupTaskId); log.info(message); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.NOT_FOUND).build(); + return Response.status(NOT_FOUND).entity(message).build(); } } catch (DataAccessException e) { @@ -225,43 +199,39 @@ public Response putNewTaskStatusOrRetryValue(@PathParam("groupTaskId") UUID grou String message = format("Unable to retrieve Recall Group Task with ID = '%s' %s", groupTaskId, e.getMessage()); log.error(message, e); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(INTERNAL_SERVER_ERROR).entity(message).build(); } - String keyStatus = config.getStatusKey(); - String key = keyValue.get().getKey(); - Integer value = keyValue.get().getValue(); + if (validator.isStatusRequest()) { // **** Set the Status - if (key.equalsIgnoreCase(keyStatus)) { // **** Set the Status - - log.debug("Changing status of task {} to {}", groupTaskId, value); + log.debug("Changing status of task {} to {}", groupTaskId, validator.getValue()); try { - TapeRecallStatus newStatus = getRecallTaskStatus(value); + TapeRecallStatus newStatus = getRecallTaskStatus(validator.getValue()); recallCatalog.changeGroupTaskStatus(groupTaskId, newStatus, new Date()); } catch (DataAccessException e) { String message = format( "Unable to change the status for group task id %s to status %s DataAccessException : %s", - groupTaskId, value, e.getMessage()); + groupTaskId, validator.getValue(), e.getMessage()); log.error(message, e); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(INTERNAL_SERVER_ERROR).entity(message).build(); } - } else { // inputMap.containsKey(keyRetryValue)) **** Set the Retry value + } else { // **** Set the Retry value - log.debug("Changing retry attempt of task {} to {}", groupTaskId, value); + log.debug("Changing retry attempt of task {} to {}", groupTaskId, validator.getValue()); try { - recallCatalog.changeGroupTaskRetryValue(groupTaskId, value); + recallCatalog.changeGroupTaskRetryValue(groupTaskId, validator.getValue()); } catch (DataAccessException e) { String message = format("Unable to takeover a task: %s", e.getMessage()); - return Response.ok(message, TEXT_PLAIN_TYPE).status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(INTERNAL_SERVER_ERROR).entity(message).build(); } } @@ -405,84 +375,4 @@ public Response getGroupTaskInfo(@PathParam("groupTaskId") String groupTaskId, return Response.ok(jsonString).build(); } - /** - * Utility method. - * - */ - private String buildInputString(InputStream input) { - - BufferedReader reader = new BufferedReader(new InputStreamReader(input)); - - StringBuilder sb = new StringBuilder(); - - String line = null; - - try { - - while ((line = reader.readLine()) != null) { - - sb.append(line + "\n"); - } - - } catch (IOException e) { - - log.error(e.getMessage(), e); - - } finally { - - try { - - input.close(); - - } catch (IOException e) { - - log.error(e.getMessage(), e); - } - } - - return sb.toString(); - } - - private Optional> getKeyValue(String line) { - - Pattern pStatus = Pattern.compile(config.getStatusKey() + "=[0-9]+"); - Pattern pAttempt = Pattern.compile(config.getRetryValueKey() + "=[0-9]+"); - - SimpleEntry entry = null; - - Matcher mStatus = pStatus.matcher(line); - Matcher mAttempt = pAttempt.matcher(line); - - if (mStatus.matches() || mAttempt.matches()) { - String key = line.split("=")[0]; - Integer value = Integer.valueOf(line.split("=")[1]); - entry = new SimpleEntry(key, value); - } else { - log.warn("got invalid key-value data = {}", line); - } - - return Optional.ofNullable(entry); - } - - - public List getAllLines(InputStream input) throws IOException { - - List lines = Lists.newArrayList(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) { - String line = null; - while ((line = br.readLine()) != null) { - lines.add(line); - } - } catch (IOException e) { - log.error(e.getMessage(), e); - } finally { - try { - input.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - return lines; - } - } diff --git a/src/main/java/it/grid/storm/tape/recalltable/resources/TasksCardinality.java b/src/main/java/it/grid/storm/tape/recalltable/resources/TasksCardinality.java index c27d9b6f6..3a894d3c9 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/resources/TasksCardinality.java +++ b/src/main/java/it/grid/storm/tape/recalltable/resources/TasksCardinality.java @@ -20,7 +20,7 @@ import it.grid.storm.persistence.exceptions.DataAccessException; import it.grid.storm.tape.recalltable.TapeRecallCatalog; -import it.grid.storm.tape.recalltable.TapeRecallException; +import it.grid.storm.tape.recalltable.model.TapeRecallException; import javax.ws.rs.GET; import javax.ws.rs.Path; diff --git a/src/main/java/it/grid/storm/tape/recalltable/resources/TasksResource.java b/src/main/java/it/grid/storm/tape/recalltable/resources/TasksResource.java index ece65213d..0364866da 100644 --- a/src/main/java/it/grid/storm/tape/recalltable/resources/TasksResource.java +++ b/src/main/java/it/grid/storm/tape/recalltable/resources/TasksResource.java @@ -26,12 +26,15 @@ import it.grid.storm.config.Configuration; import it.grid.storm.persistence.model.TapeRecallTO; import it.grid.storm.tape.recalltable.TapeRecallCatalog; -import it.grid.storm.tape.recalltable.TapeRecallException; +import it.grid.storm.tape.recalltable.model.TapeRecallException; import it.grid.storm.tape.recalltable.model.TapeRecallStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.lang.String.format; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -55,246 +58,246 @@ @Path("/recalltable/tasks") public class TasksResource { - private static final Logger log = LoggerFactory.getLogger(TasksResource.class); - - private static Configuration config = Configuration.getInstance(); - - /** - * Return recall tasks for being taken over. The status of the tasks that - * are returned is set to in progress. - * - * @param input a key value pair in which the value is the number - * of results to be returned in the - * @return the tasks ready to takeover - * @throws TapeRecallException - */ - @PUT - @Consumes("text/plain") - @Produces("text/plain") - public Response putTakeoverTasks(InputStream input) throws TapeRecallException { - - // retrieve the Input String - String inputStr = buildInputString(input); - - log.debug("@PUT (input string) = '{}'" , inputStr); - - // retrieve the number of tasks to takeover (default = 1) - int numbOfTask = 1; - - // retrieve value from Body param - String keyTakeover = config.getTaskoverKey(); - - int eqIndex = inputStr.indexOf('='); - - if (eqIndex > 0) { - - String value = inputStr.substring(eqIndex); - String key = inputStr.substring(0, eqIndex); - - if (key.equals(keyTakeover)) { - - try { - - // trim out the '\n' end. - numbOfTask = Integer.valueOf(value.substring(1, value.length() - 1)); - - } catch (NumberFormatException e) { - - throw new TapeRecallException("Unable to understand " + - "the number value = '" + value + "'"); - } - } - } - - // retrieve the tasks - List tasks = new TapeRecallCatalog().takeoverNTasksWithDoubles(numbOfTask); - - HashMap> groupTaskMap = buildGroupTaskMap(tasks); - - List groupTasks = Lists.newArrayList(); - - for (List groupTaskList : groupTaskMap.values()) { - - try { - - groupTasks.add(makeOne(groupTaskList)); - - } catch (IllegalArgumentException e) { - - log.error("Unable to makeOne the task list . IllegalArgumentException : {}" , e.getMessage() , e); - log.error("Erroneous task list (long output): {}" , groupTaskList.toString()); - log.error("Skip the erroneous task list and go on...Please contact StoRM support"); - } - } - - if (tasks.size() > groupTasks.size()) { - - log.debug("Taking over some multy-group tasks"); - } - - log.debug("Number of tasks recalled : <{}> over <{}> tasks requested" , groupTasks.size() , tasks.size()); - - // need a generic entity - GenericEntity> entity = - new GenericEntity>(tasks) {}; - - return Response.ok(entity).build(); - } - - /** - * Creates a map with the taskIds as keys and the list of tasks related to - * each taskId (key) as value - * - * @param tasks - * @return - */ - private HashMap> buildGroupTaskMap(List tasks) { - - HashMap> groupTaskMap = Maps.newHashMap(); - - for (TapeRecallTO task : tasks) { - - List taskList = - groupTaskMap.get(task.getGroupTaskId()); - - if (taskList == null) { - - taskList = Lists.newArrayList(); - groupTaskMap.put(task.getGroupTaskId(), taskList); - } - - taskList.add(task); - } - - return groupTaskMap; - } - - /** - * Given a list of tasks with the same taskId produces a single task merging - * the list members - * - * @param recallTasks - * @return - */ - private TapeRecallTO makeOne(List recallTasks) { - - TapeRecallTO taskTO = new TapeRecallTO(); - - UUID taskId = recallTasks.get(0).getTaskId(); - - // verify that all have the same task id - for (TapeRecallTO recallTask : recallTasks) { - - if (!recallTask.getTaskId().equals(taskId)) { - - log.error("Received a list of not omogeneous tasks, the taskid '{}' is not matched by : {}" , taskId , recallTask); - - throw new IllegalArgumentException( - "Received a list of not omogeneous tasks"); - } - } - - for (TapeRecallTO recallTask : recallTasks) { - - // set common fields from any of the tasks - taskTO.setTaskId(recallTask.getTaskId()); - taskTO.setGroupTaskId(recallTask.getGroupTaskId()); - taskTO.setRequestToken(recallTask.getRequestToken()); - taskTO.setRequestType(recallTask.getRequestType()); - taskTO.setFileName(recallTask.getFileName()); - taskTO.setUserID(recallTask.getUserID()); - taskTO.setVoName(recallTask.getVoName()); - taskTO.setStatus(TapeRecallStatus.QUEUED); - - break; - } - - /* - * merge task on recall related fields to have a pin that starts as soon as - * requested and last as long as needed - */ - - int maxRetryAttempt = 0; - - Date minInsertionInstant = null; - Date minDeferredRecallInstant = null; - Date maxPinExpirationInstant = null; - - for (TapeRecallTO recallTask : recallTasks) { - - if (recallTask.getRetryAttempt() > maxRetryAttempt) { - maxRetryAttempt = recallTask.getRetryAttempt(); - } - - if (minInsertionInstant == null - || recallTask.getInsertionInstant().before(minInsertionInstant)) { - - minInsertionInstant = recallTask.getInsertionInstant(); - } - - if (minDeferredRecallInstant == null - || recallTask.getDeferredRecallInstant().before(minDeferredRecallInstant)) { - - minDeferredRecallInstant = recallTask.getDeferredRecallInstant(); - } - - Date currentPinExpirationInstant = - new Date(recallTask.getDeferredRecallInstant().getTime() + (recallTask.getPinLifetime() * 1000)); - - if (maxPinExpirationInstant == null - || currentPinExpirationInstant.after(maxPinExpirationInstant)) { - - maxPinExpirationInstant = currentPinExpirationInstant; - } - } - - taskTO.setRetryAttempt(maxRetryAttempt); - taskTO.setInsertionInstant(minInsertionInstant); - taskTO.setDeferredRecallInstant(minDeferredRecallInstant); - - int pinLifeTime = (int) (maxPinExpirationInstant.getTime() - minDeferredRecallInstant.getTime()) / 1000; - - taskTO.setPinLifetime(pinLifeTime); - - return taskTO; - } - - /** - * Utility method. - * - */ - private String buildInputString(InputStream input) { - - BufferedReader reader = new BufferedReader(new InputStreamReader(input)); - - StringBuilder sb = new StringBuilder(); - - String line = null; - - try { - - while ((line = reader.readLine()) != null) { - - sb.append(line + "\n"); - } - - } catch (IOException e) { - - log.error(e.getMessage(), e); - - } finally { - - try { - - input.close(); - - } catch (IOException e) { - - log.error(e.getMessage(), e); - } - } - - return sb.toString(); - } - -} \ No newline at end of file + private static final Logger log = LoggerFactory.getLogger(TasksResource.class); + + private static Configuration config = Configuration.getInstance(); + + /** + * Return recall tasks for being taken over. The status of the tasks that are returned is set to + * in progress. + * + * @param input a key value pair in which the value is the number of results to be returned in the + * @return the tasks ready to takeover + * @throws TapeRecallException + */ + @PUT + @Consumes("text/plain") + @Produces("text/plain") + public Response putTakeoverTasks(InputStream input) { + + // retrieve the Input String + String inputStr = buildInputString(input); + + log.debug("@PUT (input string) = '{}'", inputStr); + + // retrieve the number of tasks to takeover (default = 1) + int numbOfTask = 1; + + // retrieve value from Body param + String keyTakeover = config.getTaskoverKey(); + + int eqIndex = inputStr.indexOf('='); + + if (eqIndex > 0) { + + String value = inputStr.substring(eqIndex); + String key = inputStr.substring(0, eqIndex); + + if (key.equals(keyTakeover)) { + + try { + + // trim out the '\n' end. + numbOfTask = Integer.valueOf(value.substring(1, value.length() - 1)); + + } catch (NumberFormatException e) { + + String message = format("Unable to understand the number value = '%s'", value); + log.error(message, e); + return Response.status(INTERNAL_SERVER_ERROR).entity(message).build(); + } + } + } + + // retrieve the tasks + List tasks = new TapeRecallCatalog().takeoverNTasksWithDoubles(numbOfTask); + + HashMap> groupTaskMap = buildGroupTaskMap(tasks); + + List groupTasks = Lists.newArrayList(); + + for (List groupTaskList : groupTaskMap.values()) { + + try { + + groupTasks.add(makeOne(groupTaskList)); + + } catch (IllegalArgumentException e) { + + log.error("Unable to makeOne the task list . IllegalArgumentException : {}", e.getMessage(), + e); + log.error("Erroneous task list (long output): {}", groupTaskList.toString()); + log.error("Skip the erroneous task list and go on...Please contact StoRM support"); + } + } + + if (tasks.size() > groupTasks.size()) { + + log.debug("Taking over some multy-group tasks"); + } + + log.debug("Number of tasks recalled : <{}> over <{}> tasks requested", groupTasks.size(), + tasks.size()); + + // need a generic entity + GenericEntity> entity = new GenericEntity>(tasks) {}; + + return Response.ok(entity).build(); + } + + /** + * Creates a map with the taskIds as keys and the list of tasks related to each taskId (key) as + * value + * + * @param tasks + * @return + */ + private HashMap> buildGroupTaskMap(List tasks) { + + HashMap> groupTaskMap = Maps.newHashMap(); + + for (TapeRecallTO task : tasks) { + + List taskList = groupTaskMap.get(task.getGroupTaskId()); + + if (taskList == null) { + + taskList = Lists.newArrayList(); + groupTaskMap.put(task.getGroupTaskId(), taskList); + } + + taskList.add(task); + } + + return groupTaskMap; + } + + /** + * Given a list of tasks with the same taskId produces a single task merging the list members + * + * @param recallTasks + * @return + */ + private TapeRecallTO makeOne(List recallTasks) { + + TapeRecallTO taskTO = new TapeRecallTO(); + + UUID taskId = recallTasks.get(0).getTaskId(); + + // verify that all have the same task id + for (TapeRecallTO recallTask : recallTasks) { + + if (!recallTask.getTaskId().equals(taskId)) { + + log.error("Received a list of not omogeneous tasks, the taskid '{}' is not matched by : {}", + taskId, recallTask); + + throw new IllegalArgumentException("Received a list of not omogeneous tasks"); + } + } + + for (TapeRecallTO recallTask : recallTasks) { + + // set common fields from any of the tasks + taskTO.setTaskId(recallTask.getTaskId()); + taskTO.setGroupTaskId(recallTask.getGroupTaskId()); + taskTO.setRequestToken(recallTask.getRequestToken()); + taskTO.setRequestType(recallTask.getRequestType()); + taskTO.setFileName(recallTask.getFileName()); + taskTO.setUserID(recallTask.getUserID()); + taskTO.setVoName(recallTask.getVoName()); + taskTO.setStatus(TapeRecallStatus.QUEUED); + + break; + } + + /* + * merge task on recall related fields to have a pin that starts as soon as requested and last + * as long as needed + */ + + int maxRetryAttempt = 0; + + Date minInsertionInstant = null; + Date minDeferredRecallInstant = null; + Date maxPinExpirationInstant = null; + + for (TapeRecallTO recallTask : recallTasks) { + + if (recallTask.getRetryAttempt() > maxRetryAttempt) { + maxRetryAttempt = recallTask.getRetryAttempt(); + } + + if (minInsertionInstant == null + || recallTask.getInsertionInstant().before(minInsertionInstant)) { + + minInsertionInstant = recallTask.getInsertionInstant(); + } + + if (minDeferredRecallInstant == null + || recallTask.getDeferredRecallInstant().before(minDeferredRecallInstant)) { + + minDeferredRecallInstant = recallTask.getDeferredRecallInstant(); + } + + Date currentPinExpirationInstant = new Date( + recallTask.getDeferredRecallInstant().getTime() + (recallTask.getPinLifetime() * 1000)); + + if (maxPinExpirationInstant == null + || currentPinExpirationInstant.after(maxPinExpirationInstant)) { + + maxPinExpirationInstant = currentPinExpirationInstant; + } + } + + taskTO.setRetryAttempt(maxRetryAttempt); + taskTO.setInsertionInstant(minInsertionInstant); + taskTO.setDeferredRecallInstant(minDeferredRecallInstant); + + int pinLifeTime = + (int) (maxPinExpirationInstant.getTime() - minDeferredRecallInstant.getTime()) / 1000; + + taskTO.setPinLifetime(pinLifeTime); + + return taskTO; + } + + /** + * Utility method. + * + */ + private String buildInputString(InputStream input) { + + BufferedReader reader = new BufferedReader(new InputStreamReader(input)); + + StringBuilder sb = new StringBuilder(); + + String line = null; + + try { + + while ((line = reader.readLine()) != null) { + + sb.append(line + "\n"); + } + + } catch (IOException e) { + + log.error(e.getMessage(), e); + + } finally { + + try { + + input.close(); + + } catch (IOException e) { + + log.error(e.getMessage(), e); + } + } + + return sb.toString(); + } + +} diff --git a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java index f8030e2d1..4c7a88c2b 100644 --- a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java +++ b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java @@ -1,7 +1,10 @@ package it.grid.storm.tape.recalltable.resources; import static it.grid.storm.config.Configuration.CONFIG_FILE_PATH; +import static it.grid.storm.tape.recalltable.model.PutUpdateTaskValidator.INVALID_BODY; +import static it.grid.storm.tape.recalltable.model.PutUpdateTaskValidator.NOT_INT_VALUE; import static it.grid.storm.tape.recalltable.resources.TaskInsertRequest.MAX_RETRY_ATTEMPTS; +import static java.lang.String.format; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.CREATED; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; @@ -47,6 +50,7 @@ import it.grid.storm.srm.types.InvalidTRequestTokenAttributesException; import it.grid.storm.srm.types.TRequestToken; import it.grid.storm.tape.recalltable.TapeRecallCatalog; +import it.grid.storm.tape.recalltable.model.TapeRecallStatus; public class TaskResourceTest { @@ -387,16 +391,44 @@ public void testGETTasksInProgressEmpty() TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), getTapeRecallCatalogInProgressNotEmpty()); - Response res = recallEndpoint.getTasks(10); + Response res = recallEndpoint.getTasksInProgress(10); assertThat(res.getStatus(), equalTo(OK.getStatusCode())); } - private TapeRecallCatalog getTapeRecallCatalogGroupTaskId(UUID groupTaskId, - boolean existsResponse) throws DataAccessException { + private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdNotExists(UUID groupTaskId) + throws DataAccessException { TapeRecallCatalog catalog = Mockito.mock(TapeRecallCatalog.class); - Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(existsResponse); + Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(false); + Mockito + .when(catalog.changeGroupTaskStatus(Mockito.eq(groupTaskId), + Mockito.any(TapeRecallStatus.class), Mockito.any(Date.class))) + .thenThrow(new DataAccessException()); + return catalog; + } + + private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(UUID groupTaskId) + throws DataAccessException { + + TapeRecallCatalog catalog = Mockito.mock(TapeRecallCatalog.class); + Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(true); + Mockito + .when(catalog.changeGroupTaskStatus(Mockito.eq(groupTaskId), + Mockito.any(TapeRecallStatus.class), Mockito.any(Date.class))) + .thenReturn(true); + return catalog; + } + + private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(UUID groupTaskId) + throws DataAccessException { + + TapeRecallCatalog catalog = Mockito.mock(TapeRecallCatalog.class); + Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(true); + Mockito + .when(catalog.changeGroupTaskStatus(Mockito.eq(groupTaskId), + Mockito.any(TapeRecallStatus.class), Mockito.any(Date.class))) + .thenThrow(new DataAccessException()); return catalog; } @@ -408,8 +440,8 @@ public void testPUTTaskStatusToSuccessWorks() InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, true)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(NO_CONTENT.getStatusCode())); } @@ -421,8 +453,8 @@ public void testPUTTaskRetryValueWorks() InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, true)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(NO_CONTENT.getStatusCode())); } @@ -434,10 +466,11 @@ public void testPUTTaskStatusOnTaskIdNotFound() InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, false)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdNotExists(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(NOT_FOUND.getStatusCode())); - assertThat(res.getEntity().toString(), equalTo("No Recall Group Task found with ID = '" + groupTaskId + "'")); + assertThat(res.getEntity().toString(), + equalTo("No Recall Group Task found with ID = '" + groupTaskId + "'")); } @Test @@ -448,10 +481,10 @@ public void testPUTTaskStatusWithWrongKeyInBody() InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, true)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); - assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODY + "]'")); + assertThat(res.getEntity().toString(), equalTo(INVALID_BODY)); } @Test @@ -459,13 +492,14 @@ public void testPUTTaskStatusWithWrongValueInBody() throws NamespaceException, ResourceNotFoundException, DataAccessException { final String BODY = "status=queued"; + final String EXPECTED_BODY = format(NOT_INT_VALUE, "queued"); InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, true)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); - assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODY + "]'")); + assertThat(res.getEntity().toString(), equalTo(EXPECTED_BODY)); } @Test @@ -473,14 +507,28 @@ public void testPUTTaskStatusWithBothStatusAndRetryValuesInBody() throws NamespaceException, ResourceNotFoundException, DataAccessException { final String BODY = "status=0\nretry-value=2"; - final String BODYSTR = "status=0, retry-value=2"; InputStream stubInputStream = IOUtils.toInputStream(BODY); UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskId(groupTaskId, true)); - Response res = recallEndpoint.putNewTaskStatusOrRetryValue(groupTaskId, stubInputStream); + getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(BAD_REQUEST.getStatusCode())); - assertThat(res.getEntity().toString(), equalTo("Invalid body '[" + BODYSTR + "]'")); + assertThat(res.getEntity().toString(), equalTo("Expected one property. Found 2.")); + + } + + @Test + public void testPUTTaskStatusExceptionOnUpdate() + throws NamespaceException, ResourceNotFoundException, DataAccessException { + + final String BODY = "status=0"; + InputStream stubInputStream = IOUtils.toInputStream(BODY); + UUID groupTaskId = UUID.randomUUID(); + TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), + getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(groupTaskId)); + Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); + assertThat(res.getStatus(), equalTo(INTERNAL_SERVER_ERROR.getStatusCode())); + assertThat(res.getEntity().toString(), equalTo("Expected one property. Found 2.")); } } From f839ec7faa38698e6069ad7ac29ebb8ca88bfc48 Mon Sep 17 00:00:00 2001 From: enricovianello Date: Thu, 14 May 2020 14:16:00 +0200 Subject: [PATCH 3/4] Fixed test --- .../recalltable/resources/TaskResourceTest.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java index 4c7a88c2b..44c46c108 100644 --- a/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java +++ b/src/test/java/it/grid/storm/tape/recalltable/resources/TaskResourceTest.java @@ -420,15 +420,15 @@ private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdExistsAndUpdateIsOk(UUI return catalog; } - private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(UUID groupTaskId) - throws DataAccessException { + private TapeRecallCatalog getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(UUID groupTaskId, + String errorMessage) throws DataAccessException { TapeRecallCatalog catalog = Mockito.mock(TapeRecallCatalog.class); Mockito.when(catalog.existsGroupTask(groupTaskId)).thenReturn(true); Mockito .when(catalog.changeGroupTaskStatus(Mockito.eq(groupTaskId), Mockito.any(TapeRecallStatus.class), Mockito.any(Date.class))) - .thenThrow(new DataAccessException()); + .thenThrow(new DataAccessException(errorMessage)); return catalog; } @@ -521,14 +521,19 @@ public void testPUTTaskStatusWithBothStatusAndRetryValuesInBody() public void testPUTTaskStatusExceptionOnUpdate() throws NamespaceException, ResourceNotFoundException, DataAccessException { + UUID groupTaskId = UUID.randomUUID(); + final String BODY = "status=0"; + final String EXPECTED_BODY = format( + "Unable to change the status for group task id %s to status 0 DataAccessException : ErrorMessage", + groupTaskId); + InputStream stubInputStream = IOUtils.toInputStream(BODY); - UUID groupTaskId = UUID.randomUUID(); TaskResource recallEndpoint = getTaskResource(getResourceService(STORI), - getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(groupTaskId)); + getTapeRecallCatalogGroupTaskIdThrowExceptionOnUpdate(groupTaskId, "ErrorMessage")); Response res = recallEndpoint.updateGroupTasks(groupTaskId, stubInputStream); assertThat(res.getStatus(), equalTo(INTERNAL_SERVER_ERROR.getStatusCode())); - assertThat(res.getEntity().toString(), equalTo("Expected one property. Found 2.")); + assertThat(res.getEntity().toString(), equalTo(EXPECTED_BODY)); } } From b7d3d61361694ff6f99d7779b765d04afd7ec02d Mon Sep 17 00:00:00 2001 From: enricovianello Date: Fri, 5 Jun 2020 15:22:49 +0200 Subject: [PATCH 4/4] Cosmetic fixes on Configuration --- .../java/it/grid/storm/config/Configuration.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/it/grid/storm/config/Configuration.java b/src/main/java/it/grid/storm/config/Configuration.java index 7f4208ff9..e2cf8548f 100644 --- a/src/main/java/it/grid/storm/config/Configuration.java +++ b/src/main/java/it/grid/storm/config/Configuration.java @@ -48,10 +48,6 @@ public class Configuration { - public static final String DEFAULT_STORM_CONFIG_FILE = - "/etc/storm/backend-server/storm.properties"; - public static final int DEFAULT_STORM_CONFIG_REFRESH_RATE = 0; - private static final Logger log = LoggerFactory.getLogger(Configuration.class); private final ConfigReader cr; @@ -199,12 +195,15 @@ public class Configuration { private Configuration() throws ConfigurationException { - String filePath = getProperty(CONFIG_FILE_PATH, DEFAULT_STORM_CONFIG_FILE); + final int DEFAULT_REFRESH_RATE = 0; + final String DEFAULT_CONFIG_FILE_PATH = "/etc/storm/backend-server/storm.properties"; + + String filePath = getProperty(CONFIG_FILE_PATH, DEFAULT_CONFIG_FILE_PATH); int refreshRate; try { refreshRate = Integer.valueOf(getProperty(REFRESH_RATE)); } catch (NumberFormatException e) { - refreshRate = DEFAULT_STORM_CONFIG_REFRESH_RATE; + refreshRate = DEFAULT_REFRESH_RATE; } cr = new ConfigReader(filePath, refreshRate); } @@ -214,7 +213,7 @@ private Configuration() throws ConfigurationException { */ public static Configuration getInstance() { - return Configuration.instance; + return instance; } /**