2 * ===============LICENSE_START=======================================================
4 * ===================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
6 * ===================================================================================
7 * This Acumos software file is distributed by AT&T and Tech Mahindra
8 * under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * This file is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ===============LICENSE_END=========================================================
21 package org.acumos.federation.gateway.controller;
24 import java.net.URISyntaxException;
26 import java.util.List;
28 import java.util.concurrent.Callable;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
33 import org.acumos.cds.domain.MLPArtifact;
34 import org.acumos.cds.domain.MLPSolution;
35 import org.acumos.cds.domain.MLPSolutionRevision;
36 import org.acumos.federation.gateway.cds.ArtifactType;
37 import org.acumos.federation.gateway.common.API;
38 import org.acumos.federation.gateway.common.JSONTags;
39 import org.acumos.federation.gateway.common.JsonResponse;
40 import org.acumos.federation.gateway.config.EELFLoggerDelegate;
41 import org.acumos.federation.gateway.security.Peer;
42 import org.acumos.federation.gateway.service.CatalogService;
43 import org.acumos.federation.gateway.service.ArtifactService;
44 import org.acumos.federation.gateway.service.ServiceContext;
45 import org.acumos.federation.gateway.util.Utils;
47 import org.springframework.beans.factory.annotation.Autowired;
48 import org.springframework.core.io.InputStreamResource;
49 import org.springframework.http.MediaType;
50 import org.springframework.security.access.prepost.PreAuthorize;
51 import org.springframework.security.core.context.SecurityContextHolder;
52 import org.springframework.stereotype.Controller;
53 import org.springframework.util.Base64Utils;
54 import org.springframework.web.bind.annotation.CrossOrigin;
55 import org.springframework.web.bind.annotation.PathVariable;
56 import org.springframework.web.bind.annotation.RequestMapping;
57 import org.springframework.web.bind.annotation.RequestMethod;
58 import org.springframework.web.bind.annotation.RequestParam;
59 import org.springframework.web.bind.annotation.ResponseBody;
61 import io.swagger.annotations.ApiOperation;
63 import com.github.dockerjava.api.model.Identifier;
70 @RequestMapping(API.Roots.FEDERATION)
71 public class CatalogController extends AbstractController {
73 private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(CatalogController.class.getName());
76 private CatalogService catalogService;
78 private ArtifactService artifactService;
81 * @param theHttpResponse
85 * @return List of Published ML Solutions in JSON format.
88 // @PreAuthorize("hasAuthority('PEER')"
89 @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).CATALOG_ACCESS)")
90 @ApiOperation(value = "Invoked by Peer Acumos to get a list of Published Solutions from the Catalog of the local Acumos Instance .", response = MLPSolution.class, responseContainer = "List")
91 @RequestMapping(value = { API.Paths.SOLUTIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
93 public JsonResponse<List<MLPSolution>> getSolutions(
94 HttpServletRequest theHttpRequest,
95 HttpServletResponse theHttpResponse,
96 @RequestParam(value = API.QueryParameters.SOLUTIONS_SELECTOR, required = false) String theSelector) {
97 JsonResponse<List<MLPSolution>> response = null;
98 List<MLPSolution> solutions = null;
99 log.debug(EELFLoggerDelegate.debugLogger, API.Paths.SOLUTIONS);
101 log.debug(EELFLoggerDelegate.debugLogger, "getSolutions: selector " + theSelector);
102 Map<String, ?> selector = null;
103 if (theSelector != null)
104 selector = Utils.jsonStringToMap(new String(Base64Utils.decodeFromString(theSelector), "UTF-8"));
106 solutions = catalogService.getSolutions(selector, new ControllerContext());
107 if (solutions != null) {
108 for (MLPSolution solution: solutions) {
109 encodeSolution(solution, theHttpRequest);
113 response = JsonResponse.<List<MLPSolution>> buildResponse()
114 .withMessage("available public solution for given filter")
115 .withContent(solutions)
117 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
118 log.debug(EELFLoggerDelegate.debugLogger, "getSolutions: provided {} solutions", solutions == null ? 0 : solutions.size());
120 catch (Exception x) {
121 response = JsonResponse.<List<MLPSolution>> buildErrorResponse()
124 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
125 log.error(EELFLoggerDelegate.errorLogger, "Exception occurred while fetching solutions", x);
131 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
132 @ApiOperation(value = "Invoked by Peer Acumos to get a list detailed solution information from the Catalog of the local Acumos Instance .", response = MLPSolution.class)
133 @RequestMapping(value = { API.Paths.SOLUTION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
135 public JsonResponse<MLPSolution> getSolutionDetails(
136 HttpServletRequest theHttpRequest,
137 HttpServletResponse theHttpResponse,
138 @PathVariable(value = "solutionId") String theSolutionId) {
139 JsonResponse<MLPSolution> response = null;
140 MLPSolution solution = null;
141 log.debug(EELFLoggerDelegate.debugLogger, API.Paths.SOLUTION_DETAILS + ": " + theSolutionId);
143 solution = catalogService.getSolution(theSolutionId, new ControllerContext());
144 if (null == solution) {
145 response = JsonResponse.<MLPSolution> buildResponse()
146 .withMessage("No solution with id " + theSolutionId + " is available.")
148 theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
151 encodeSolution(solution, theHttpRequest);
152 response = JsonResponse.<MLPSolution> buildResponse()
153 .withMessage("solution details")
154 .withContent(solution)
156 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
159 catch (Exception x) {
160 response = JsonResponse.<MLPSolution> buildErrorResponse()
163 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
164 log.error(EELFLoggerDelegate.errorLogger, "An error occurred while fetching solution " + theSolutionId, x);
170 * @param theSolutionId
172 * @param theHttpResponse
173 * HttpServletResponse
174 * @return List of Published ML Solutions in JSON format.
177 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
178 @ApiOperation(value = "Invoked by Peer Acumos to get a list of Solution Revision from the Catalog of the local Acumos Instance .", response = MLPSolutionRevision.class, responseContainer = "List")
179 @RequestMapping(value = { API.Paths.SOLUTION_REVISIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
181 public JsonResponse<List<MLPSolutionRevision>> getSolutionRevisions(HttpServletResponse theHttpResponse,
182 @PathVariable("solutionId") String theSolutionId) {
183 JsonResponse<List<MLPSolutionRevision>> response = null;
184 List<MLPSolutionRevision> solutionRevisions = null;
185 log.debug(EELFLoggerDelegate.debugLogger, API.Paths.SOLUTION_REVISIONS);
187 solutionRevisions = catalogService.getSolutionRevisions(theSolutionId, new ControllerContext());
188 if (null == solutionRevisions) {
189 response = JsonResponse.<List<MLPSolutionRevision>> buildResponse()
190 .withMessage("No solution with id " + theSolutionId + " is available.")
192 theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
195 response = JsonResponse.<List<MLPSolutionRevision>> buildResponse()
196 .withMessage("solution revisions")
197 .withContent(solutionRevisions)
199 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
200 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionsRevisions for solution {} provided {} revisions",
201 theSolutionId, solutionRevisions == null ? 0 : solutionRevisions.size());
204 catch (Exception x) {
205 response = JsonResponse.<List<MLPSolutionRevision>> buildErrorResponse()
208 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
209 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revisions", x);
216 * @param theSolutionId
218 * @param theRevisionId
220 * @param theHttpResponse
221 * HttpServletResponse
222 * @return List of Published ML Solutions in JSON format.
225 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
226 @ApiOperation(value = "Invoked by peer Acumos to get solution revision details from the local Acumos Instance .", response = MLPSolutionRevision.class)
227 @RequestMapping(value = {
228 API.Paths.SOLUTION_REVISION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
230 public JsonResponse<MLPSolutionRevision> getSolutionRevisionDetails(HttpServletResponse theHttpResponse,
231 @PathVariable("solutionId") String theSolutionId, @PathVariable("revisionId") String theRevisionId) {
232 JsonResponse<MLPSolutionRevision> response = null;
233 MLPSolutionRevision solutionRevision = null;
234 log.debug(EELFLoggerDelegate.debugLogger,
235 API.Paths.SOLUTION_REVISION_DETAILS + "(" + theSolutionId + "," + theRevisionId + ")");
237 solutionRevision = catalogService.getSolutionRevision(theSolutionId, theRevisionId,
238 new ControllerContext());
239 if (null == solutionRevision) {
240 response = JsonResponse.<MLPSolutionRevision> buildResponse()
241 .withMessage("No solution revision " + theSolutionId + "/" + theRevisionId + " is available.")
243 theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
246 response = JsonResponse.<MLPSolutionRevision> buildResponse()
247 .withMessage("solution revision details")
248 .withContent(solutionRevision)
250 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
253 catch (Exception x) {
254 response = JsonResponse.<MLPSolutionRevision> buildErrorResponse()
257 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
258 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revision " + theRevisionId + " details", x);
264 * @param theHttpRequest
266 * @param theHttpResponse
267 * HttpServletResponse
268 * @param theSolutionId
270 * @param theRevisionId
272 * @return List of Published ML Solutions in JSON format.
275 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
276 @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision artifacts from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
277 @RequestMapping(value = {
278 API.Paths.SOLUTION_REVISION_ARTIFACTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
280 public JsonResponse<List<MLPArtifact>> getSolutionRevisionArtifacts(HttpServletRequest theHttpRequest,
281 HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
282 @PathVariable("revisionId") String theRevisionId) {
283 JsonResponse<List<MLPArtifact>> response = null;
284 List<MLPArtifact> solutionRevisionArtifacts = null;
285 ControllerContext context = new ControllerContext();
286 log.debug(EELFLoggerDelegate.debugLogger,
287 API.Paths.SOLUTION_REVISION_ARTIFACTS + "(" + theSolutionId + "," + theRevisionId + ")");
289 solutionRevisionArtifacts = catalogService.getSolutionRevisionArtifacts(theSolutionId, theRevisionId, context);
290 if (null == solutionRevisionArtifacts) {
291 response = JsonResponse.<List<MLPArtifact>> buildResponse()
292 .withMessage("No solution revision " + theSolutionId + "/" + theRevisionId + " is available.")
294 theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
297 for (MLPArtifact artifact : solutionRevisionArtifacts) {
298 if (!context.getPeer().getPeerInfo().isLocal()) {
299 encodeArtifact(artifact, theHttpRequest);
302 response = JsonResponse.<List<MLPArtifact>> buildResponse()
303 .withMessage("solution revision artifacts")
304 .withContent(solutionRevisionArtifacts)
306 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
307 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts provided {} artifacts",
308 solutionRevisionArtifacts == null ? 0 : solutionRevisionArtifacts.size());
311 catch (Exception x) {
312 response = JsonResponse.<List<MLPArtifact>> buildErrorResponse()
315 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
316 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revision " + theRevisionId + " artifacts", x);
322 * @param theHttpRequest
324 * @param theHttpResponse
325 * HttpServletResponse
326 * @param theArtifactId
328 * @return Archive file of the Artifact for the Solution.
331 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
332 @ApiOperation(value = "API to download the Machine Learning Artifact of the Machine Learning Solution", response = InputStreamResource.class, code = 200)
333 @RequestMapping(value = {
334 API.Paths.ARTIFACT_DOWNLOAD }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
336 public Callable<InputStreamResource> downloadSolutionArtifact(HttpServletRequest theHttpRequest,
337 HttpServletResponse theHttpResponse, @PathVariable("artifactId") String theArtifactId) {
339 theHttpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
340 theHttpResponse.setHeader("Pragma", "no-cache");
341 theHttpResponse.setHeader("Expires", "0");
342 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
344 final ControllerContext ctx = new ControllerContext();
345 return new Callable<InputStreamResource>() {
346 public InputStreamResource call() throws Exception {
348 return artifactService.getArtifactContent(
349 catalogService.getSolutionRevisionArtifact(theArtifactId, ctx), ctx);
351 catch (Exception x) {
352 log.error(EELFLoggerDelegate.errorLogger,
353 "An error occurred while retrieving artifact content " + theArtifactId, x);
361 private void encodeSolution(MLPSolution theSolution, HttpServletRequest theRequest) throws URISyntaxException {
362 //encode the 'origin'
363 if (null == theSolution.getOrigin()) {
364 URI requestUri = new URI(theRequest.getRequestURL().toString());
365 URI solutionUri = API.SOLUTION_DETAIL
367 new URI(requestUri.getScheme(), null, requestUri.getHost(),
368 requestUri.getPort(), null, null, null).toString(),
369 theSolution.getSolutionId());
370 theSolution.setOrigin(solutionUri.toString());
375 private void encodeArtifact(MLPArtifact theArtifact, HttpServletRequest theRequest) throws URISyntaxException {
377 String artifactUri = theArtifact.getUri();
381 URI requestUri = new URI(theRequest.getRequestURL().toString());
382 URI redirectUri = API.ARTIFACT_DOWNLOAD
384 new URI(requestUri.getScheme(), null, requestUri.getHost(),
385 requestUri.getPort(), null, null, null).toString(),
386 theArtifact.getArtifactId());
387 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts: redirected artifact uri " + redirectUri);
388 theArtifact.setUri(redirectUri.toString());
391 if (ArtifactType.DockerImage == ArtifactType.forCode(theArtifact.getArtifactTypeCode())) {
392 if (artifactUri != null) {
393 Identifier imageId = Identifier.fromCompoundString(artifactUri);
395 String imageTag = imageId.tag.get(); //there should always be a tag, right??
396 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts: encoded docker image uri to tag " + imageTag);
397 theArtifact.setName(imageTag);
398 theArtifact.setDescription(imageTag);