Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/SecurityConfig.java 0 → 100644 +41 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SecurityConfig { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); @Value("${cors.allowed.origin}") private String corsAllowedOrigin; /** * CORS are necessary only for development (API access from npm server). */ @Bean @Profile("dev") public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { LOG.warn("Development profile active: CORS filter enabled"); registry.addMapping("/**") .allowedOrigins(corsAllowedOrigin) .allowedMethods("*") .allowCredentials(true); } }; } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VOSpaceException.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui; public class VOSpaceException extends RuntimeException { public VOSpaceException(String message) { super(message); } public VOSpaceException(String message, Throwable cause) { super(message, cause); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VospaceUiApplication.java→vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VOSpaceUiApplication.java +2 −2 Original line number Diff line number Diff line Loading @@ -4,9 +4,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class VospaceUiApplication { public class VOSpaceUiApplication { public static void main(String[] args) { SpringApplication.run(VospaceUiApplication.class, args); SpringApplication.run(VOSpaceUiApplication.class, args); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java 0 → 100644 +121 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui.client; import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.vospace.ui.VOSpaceException; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.net.ConnectException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.Scanner; import java.util.concurrent.CompletionException; import java.util.function.Function; import net.ivoa.xml.vospace.v2.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class VOSpaceClient { private static final Logger LOG = LoggerFactory.getLogger(VOSpaceClient.class); private static final ObjectMapper MAPPER = new ObjectMapper(); private final HttpClient httpClient; private final String baseUrl; public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) { if (backendUrl.endsWith("/")) { // Remove final slash from configured URL backendUrl = backendUrl.substring(0, backendUrl.length() - 1); } baseUrl = backendUrl; httpClient = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .version(HttpClient.Version.HTTP_1_1) .build(); } public Node getNode(String path) { HttpRequest request = getRequest("/nodes/" + path) .header("Accept", "application/json") .build(); return call(request, BodyHandlers.ofInputStream(), 200, res -> parseJson(res, Node.class)); } private <T, U> U call(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, int expectedStatusCode, Function<T, U> responseHandler) { try { return httpClient.sendAsync(request, responseBodyHandler) .thenApply(response -> { if (response.statusCode() == expectedStatusCode) { return response.body(); } logServerError(request, response); throw new VOSpaceException("Error calling " + request.uri().toString() + ". Server response code is " + response.statusCode()); }) .thenApply(response -> responseHandler.apply(response)) .join(); } catch (CompletionException ex) { if (ex.getCause() != null) { if (ex.getCause() instanceof ConnectException) { throw new VOSpaceException("Cannot connect to " + request.uri().getHost() + " on port " + request.uri().getPort()); } if (ex.getCause() instanceof VOSpaceException) { throw (VOSpaceException) ex.getCause(); } LOG.error("Error calling " + request.uri().toString(), ex.getCause()); throw new VOSpaceException("Error calling " + request.uri().toString(), ex.getCause()); } LOG.error("Error calling " + request.uri().toString(), ex); throw new VOSpaceException("Error calling " + request.uri().toString(), ex); } } private HttpRequest.Builder getRequest(String path) { return HttpRequest.newBuilder(URI.create(baseUrl + path)); } private static <T> T parseJson(InputStream in, Class<T> type) { try { return MAPPER.readValue(in, type); } catch (IOException ex) { LOG.error("Invalid JSON for class {}", type.getCanonicalName()); throw new UncheckedIOException(ex); } } private static <T> void logServerError(HttpRequest request, HttpResponse<T> response) { if (response.body() instanceof String) { logServerErrorString(request, (HttpResponse<String>) response); } else if (response.body() instanceof InputStream) { logServerErrorInputStream(request, (HttpResponse<InputStream>) response); } else { throw new UnsupportedOperationException("Unable to log error for response body type " + response.body().getClass().getCanonicalName()); } } private static void logServerErrorString(HttpRequest request, HttpResponse<String> response) { LOG.error("Error while reading " + request.uri() + "\nServer response status code is " + response.statusCode() + "\nServer response text is " + response.body()); } private static void logServerErrorInputStream(HttpRequest request, HttpResponse<InputStream> response) { Scanner s = new Scanner(response.body()).useDelimiter("\\A"); String responseBody = s.hasNext() ? s.next() : ""; String error = "Error while reading " + request.uri() + "\nServer response status code is " + response.statusCode() + "\nServer response text is " + responseBody; LOG.error(error); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/IndexController.java 0 → 100644 +16 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class IndexController { @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE) public String index(HttpServletRequest request, HttpServletResponse response) { return "index.html"; } } Loading
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/SecurityConfig.java 0 → 100644 +41 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SecurityConfig { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); @Value("${cors.allowed.origin}") private String corsAllowedOrigin; /** * CORS are necessary only for development (API access from npm server). */ @Bean @Profile("dev") public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { LOG.warn("Development profile active: CORS filter enabled"); registry.addMapping("/**") .allowedOrigins(corsAllowedOrigin) .allowedMethods("*") .allowCredentials(true); } }; } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VOSpaceException.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui; public class VOSpaceException extends RuntimeException { public VOSpaceException(String message) { super(message); } public VOSpaceException(String message, Throwable cause) { super(message, cause); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VospaceUiApplication.java→vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/VOSpaceUiApplication.java +2 −2 Original line number Diff line number Diff line Loading @@ -4,9 +4,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class VospaceUiApplication { public class VOSpaceUiApplication { public static void main(String[] args) { SpringApplication.run(VospaceUiApplication.class, args); SpringApplication.run(VOSpaceUiApplication.class, args); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java 0 → 100644 +121 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui.client; import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.vospace.ui.VOSpaceException; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.net.ConnectException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.Scanner; import java.util.concurrent.CompletionException; import java.util.function.Function; import net.ivoa.xml.vospace.v2.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class VOSpaceClient { private static final Logger LOG = LoggerFactory.getLogger(VOSpaceClient.class); private static final ObjectMapper MAPPER = new ObjectMapper(); private final HttpClient httpClient; private final String baseUrl; public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) { if (backendUrl.endsWith("/")) { // Remove final slash from configured URL backendUrl = backendUrl.substring(0, backendUrl.length() - 1); } baseUrl = backendUrl; httpClient = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .version(HttpClient.Version.HTTP_1_1) .build(); } public Node getNode(String path) { HttpRequest request = getRequest("/nodes/" + path) .header("Accept", "application/json") .build(); return call(request, BodyHandlers.ofInputStream(), 200, res -> parseJson(res, Node.class)); } private <T, U> U call(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, int expectedStatusCode, Function<T, U> responseHandler) { try { return httpClient.sendAsync(request, responseBodyHandler) .thenApply(response -> { if (response.statusCode() == expectedStatusCode) { return response.body(); } logServerError(request, response); throw new VOSpaceException("Error calling " + request.uri().toString() + ". Server response code is " + response.statusCode()); }) .thenApply(response -> responseHandler.apply(response)) .join(); } catch (CompletionException ex) { if (ex.getCause() != null) { if (ex.getCause() instanceof ConnectException) { throw new VOSpaceException("Cannot connect to " + request.uri().getHost() + " on port " + request.uri().getPort()); } if (ex.getCause() instanceof VOSpaceException) { throw (VOSpaceException) ex.getCause(); } LOG.error("Error calling " + request.uri().toString(), ex.getCause()); throw new VOSpaceException("Error calling " + request.uri().toString(), ex.getCause()); } LOG.error("Error calling " + request.uri().toString(), ex); throw new VOSpaceException("Error calling " + request.uri().toString(), ex); } } private HttpRequest.Builder getRequest(String path) { return HttpRequest.newBuilder(URI.create(baseUrl + path)); } private static <T> T parseJson(InputStream in, Class<T> type) { try { return MAPPER.readValue(in, type); } catch (IOException ex) { LOG.error("Invalid JSON for class {}", type.getCanonicalName()); throw new UncheckedIOException(ex); } } private static <T> void logServerError(HttpRequest request, HttpResponse<T> response) { if (response.body() instanceof String) { logServerErrorString(request, (HttpResponse<String>) response); } else if (response.body() instanceof InputStream) { logServerErrorInputStream(request, (HttpResponse<InputStream>) response); } else { throw new UnsupportedOperationException("Unable to log error for response body type " + response.body().getClass().getCanonicalName()); } } private static void logServerErrorString(HttpRequest request, HttpResponse<String> response) { LOG.error("Error while reading " + request.uri() + "\nServer response status code is " + response.statusCode() + "\nServer response text is " + response.body()); } private static void logServerErrorInputStream(HttpRequest request, HttpResponse<InputStream> response) { Scanner s = new Scanner(response.body()).useDelimiter("\\A"); String responseBody = s.hasNext() ? s.next() : ""; String error = "Error while reading " + request.uri() + "\nServer response status code is " + response.statusCode() + "\nServer response text is " + responseBody; LOG.error(error); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/IndexController.java 0 → 100644 +16 −0 Original line number Diff line number Diff line package it.inaf.ia2.vospace.ui.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class IndexController { @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE) public String index(HttpServletRequest request, HttpServletResponse response) { return "index.html"; } }