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; ...@@ -6,6 +6,7 @@ import it.inaf.ia2.vospace.ui.VOSpaceException;
import it.inaf.ia2.vospace.ui.VOSpaceUiApplication; import it.inaf.ia2.vospace.ui.VOSpaceUiApplication;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringWriter;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.URI; import java.net.URI;
...@@ -20,7 +21,9 @@ import java.util.function.Function; ...@@ -20,7 +21,9 @@ import java.util.function.Function;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.xml.bind.JAXB; 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.Node;
import net.ivoa.xml.vospace.v2.Transfer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -42,7 +45,7 @@ public class VOSpaceClient { ...@@ -42,7 +45,7 @@ public class VOSpaceClient {
private final ForkJoinPool jaxbExecutor; private final ForkJoinPool jaxbExecutor;
@Autowired @Autowired
private HttpServletRequest servletRequest; protected HttpServletRequest servletRequest;
public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) { public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) {
if (backendUrl.endsWith("/")) { if (backendUrl.endsWith("/")) {
...@@ -65,7 +68,18 @@ public class VOSpaceClient { ...@@ -65,7 +68,18 @@ public class VOSpaceClient {
.header("Accept", useJson ? "application/json" : "text/xml") .header("Accept", useJson ? "application/json" : "text/xml")
.build(); .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) { private <T, U> U call(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, int expectedStatusCode, Function<T, U> responseHandler) {
...@@ -116,7 +130,7 @@ public class VOSpaceClient { ...@@ -116,7 +130,7 @@ public class VOSpaceClient {
return null; return null;
} }
private <T> T parseJson(InputStream in, Class<T> type) { private <T> T unmarshal(InputStream in, Class<T> type) {
try { try {
if (useJson) { if (useJson) {
return MAPPER.readValue(in, type); return MAPPER.readValue(in, type);
...@@ -129,6 +143,21 @@ public class VOSpaceClient { ...@@ -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) { private static <T> void logServerError(HttpRequest request, HttpResponse<T> response) {
if (response.body() instanceof String) { if (response.body() instanceof String) {
logServerErrorString(request, (HttpResponse<String>) response); 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 { ...@@ -85,6 +85,6 @@ public class NodesService {
return true; return true;
} }
// TODO: check user group // TODO: check user group
return false; return true; // temporary always true
} }
} }
...@@ -2,8 +2,10 @@ server.port=8085 ...@@ -2,8 +2,10 @@ server.port=8085
vospace-backend-url=http://localhost:8083/vospace vospace-backend-url=http://localhost:8083/vospace
vospace-authority=example.com!vospace vospace-authority=example.com!vospace
use-json=true use-json=false
# For development only: # For development only:
spring.profiles.active=dev spring.profiles.active=dev
cors.allowed.origin=http://localhost:8080 cors.allowed.origin=http://localhost:8080
logging.level.it.inaf=TRACE
...@@ -8,6 +8,7 @@ import java.net.http.HttpClient; ...@@ -8,6 +8,7 @@ import java.net.http.HttpClient;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.ContainerNode; import net.ivoa.xml.vospace.v2.ContainerNode;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
...@@ -40,6 +41,8 @@ public class VOSpaceClientTest { ...@@ -40,6 +41,8 @@ public class VOSpaceClientTest {
staticMock.when(HttpClient::newBuilder).thenReturn(builder); staticMock.when(HttpClient::newBuilder).thenReturn(builder);
voSpaceClient = new VOSpaceClient("http://localhost/vospace"); voSpaceClient = new VOSpaceClient("http://localhost/vospace");
} }
voSpaceClient.servletRequest = mock(HttpServletRequest.class);
} }
@Test @Test
......
...@@ -72,5 +72,17 @@ export default { ...@@ -72,5 +72,17 @@ export default {
'Cache-Control': 'no-cache' 'Cache-Control': 'no-cache'
} }
}, false, false); }, 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 { ...@@ -57,7 +57,7 @@ export default {
return items; return items;
}, },
tapeButtonEnabled() { tapeButtonEnabled() {
return this.$store.state.tapeButtonEnabled; return this.$store.state.tapeButtonEnabled || true; // temporary always true
} }
}, },
created() { created() {
......
...@@ -62,7 +62,7 @@ export default new Vuex.Store({ ...@@ -62,7 +62,7 @@ export default new Vuex.Store({
commit('setTapeButtonEnabled', document.querySelectorAll('#nodes input.tape:checked').length > 0); commit('setTapeButtonEnabled', document.querySelectorAll('#nodes input.tape:checked').length > 0);
}, },
startRecallFromTapeJob({ commit }) { 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 = []; let paths = [];
for (let i = 0; i < tapeCheckboxes.length; i++) { for (let i = 0; i < tapeCheckboxes.length; i++) {
paths.push(tapeCheckboxes[i].getAttribute('data-node')); 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