Skip to content
Snippets Groups Projects
Commit fb0bb97e authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Implemented basic recall from tape

parent 314c77a3
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,7 @@ import it.inaf.ia2.vospace.ui.VOSpaceException;
import it.inaf.ia2.vospace.ui.VOSpaceUiApplication;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.net.ConnectException;
import java.net.URI;
......@@ -20,7 +21,9 @@ import java.util.function.Function;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.xml.bind.JAXB;
import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.vospace.v2.Node;
import net.ivoa.xml.vospace.v2.Transfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -42,7 +45,7 @@ public class VOSpaceClient {
private final ForkJoinPool jaxbExecutor;
@Autowired
private HttpServletRequest servletRequest;
protected HttpServletRequest servletRequest;
public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) {
if (backendUrl.endsWith("/")) {
......@@ -65,7 +68,18 @@ public class VOSpaceClient {
.header("Accept", useJson ? "application/json" : "text/xml")
.build();
return call(request, BodyHandlers.ofInputStream(), 200, res -> parseJson(res, Node.class));
return call(request, BodyHandlers.ofInputStream(), 200, res -> unmarshal(res, Node.class));
}
public JobSummary startTransferJob(Transfer transfer) {
HttpRequest request = getRequest("/transfers?PHASE=RUN")
.header("Accept", useJson ? "application/json" : "text/xml")
.header("Content-Type", useJson ? "application/json" : "text/xml")
.POST(HttpRequest.BodyPublishers.ofString(marshal(transfer)))
.build();
return call(request, BodyHandlers.ofInputStream(), 200, res -> unmarshal(res, JobSummary.class));
}
private <T, U> U call(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, int expectedStatusCode, Function<T, U> responseHandler) {
......@@ -116,7 +130,7 @@ public class VOSpaceClient {
return null;
}
private <T> T parseJson(InputStream in, Class<T> type) {
private <T> T unmarshal(InputStream in, Class<T> type) {
try {
if (useJson) {
return MAPPER.readValue(in, type);
......@@ -129,6 +143,21 @@ public class VOSpaceClient {
}
}
private String marshal(Object data) {
try {
if (useJson) {
return MAPPER.writeValueAsString(data);
} else {
try ( StringWriter sw = new StringWriter()) {
JAXB.marshal(data, sw);
return sw.toString();
}
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private static <T> void logServerError(HttpRequest request, HttpResponse<T> response) {
if (response.body() instanceof String) {
logServerErrorString(request, (HttpResponse<String>) response);
......
package it.inaf.ia2.vospace.ui.controller;
import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
import it.inaf.ia2.vospace.ui.data.Job;
import java.util.ArrayList;
import java.util.List;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.vospace.v2.Protocol;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JobController {
@Value("${vospace-authority}")
private String authority;
@Autowired
private VOSpaceClient client;
@PostMapping(value = "/recall", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Job> startRecallFromTapeJob(@RequestBody List<String> paths) {
if (paths.size() != 1) {
throw new UnsupportedOperationException();
}
Transfer transfer = new Transfer();
transfer.setDirection("pullToVoSpace");
transfer.setTarget("vos://" + authority + paths.get(0));
Protocol protocol = new Protocol();
protocol.setUri("ia2:tape-recall");
transfer.getProtocol().add(protocol);
JobSummary job = client.startTransferJob(transfer);
if (job.getPhase() == ExecutionPhase.QUEUED) {
return ResponseEntity.ok(new Job(job));
}
// TODO: proper handling
throw new RuntimeException("Error while executing job " + job.getJobId() + ". Job phase is " + job.getPhase() + ". QUEUED expected");
}
@GetMapping(value = "/jobs", produces = MediaType.APPLICATION_JSON_VALUE)
public List<Job> getJobs() {
// TODO
return new ArrayList<>();
}
}
package it.inaf.ia2.vospace.ui.data;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import net.ivoa.xml.uws.v1.JobSummary;
public class Job {
private String id;
private ExecutionPhase phase;
private boolean read;
public Job() {
}
public Job(JobSummary job) {
this.id = job.getJobId();
this.phase = job.getPhase();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public ExecutionPhase getPhase() {
return phase;
}
public void setPhase(ExecutionPhase phase) {
this.phase = phase;
}
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
}
......@@ -85,6 +85,6 @@ public class NodesService {
return true;
}
// TODO: check user group
return false;
return true; // temporary always true
}
}
......@@ -2,8 +2,10 @@ server.port=8085
vospace-backend-url=http://localhost:8083/vospace
vospace-authority=example.com!vospace
use-json=true
use-json=false
# For development only:
spring.profiles.active=dev
cors.allowed.origin=http://localhost:8080
logging.level.it.inaf=TRACE
......@@ -8,6 +8,7 @@ import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.ContainerNode;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
......@@ -40,6 +41,8 @@ public class VOSpaceClientTest {
staticMock.when(HttpClient::newBuilder).thenReturn(builder);
voSpaceClient = new VOSpaceClient("http://localhost/vospace");
}
voSpaceClient.servletRequest = mock(HttpServletRequest.class);
}
@Test
......
......@@ -72,5 +72,17 @@ export default {
'Cache-Control': 'no-cache'
}
}, false, false);
},
startRecallFromTapeJob(paths) {
let url = BASE_API_URL + 'recall';
return apiRequest({
method: 'POST',
url: url,
withCredentials: true,
headers: {
'Cache-Control': 'no-cache'
},
data: paths
});
}
}
......@@ -57,7 +57,7 @@ export default {
return items;
},
tapeButtonEnabled() {
return this.$store.state.tapeButtonEnabled;
return this.$store.state.tapeButtonEnabled || true; // temporary always true
}
},
created() {
......
......@@ -62,7 +62,7 @@ export default new Vuex.Store({
commit('setTapeButtonEnabled', document.querySelectorAll('#nodes input.tape:checked').length > 0);
},
startRecallFromTapeJob({ commit }) {
let tapeCheckboxes = document.querySelectorAll('#nodes input.tape:checked');
let tapeCheckboxes = document.querySelectorAll('#nodes input:checked'); // temporary: it should be input.tape
let paths = [];
for (let i = 0; i < tapeCheckboxes.length; i++) {
paths.push(tapeCheckboxes[i].getAttribute('data-node'));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment