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, "getSolutionsListFromPeer: 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 encodeSolution(solution, theHttpRequest);
145 response = JsonResponse.<MLPSolution> buildResponse()
146 .withMessage("solution details")
147 .withContent(solution)
149 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
151 catch (Exception x) {
152 response = JsonResponse.<MLPSolution> buildErrorResponse()
155 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
156 log.error(EELFLoggerDelegate.errorLogger, "An error occurred while fetching solution " + theSolutionId, x);
162 * @param theSolutionId
164 * @param theHttpResponse
165 * HttpServletResponse
166 * @return List of Published ML Solutions in JSON format.
169 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
170 @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")
171 @RequestMapping(value = { API.Paths.SOLUTION_REVISIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
173 public JsonResponse<List<MLPSolutionRevision>> getSolutionRevisions(HttpServletResponse theHttpResponse,
174 @PathVariable("solutionId") String theSolutionId) {
175 JsonResponse<List<MLPSolutionRevision>> response = null;
176 List<MLPSolutionRevision> solutionRevisions = null;
177 log.debug(EELFLoggerDelegate.debugLogger, API.Paths.SOLUTION_REVISIONS);
179 solutionRevisions = catalogService.getSolutionRevisions(theSolutionId, new ControllerContext());
180 response = JsonResponse.<List<MLPSolutionRevision>> buildResponse()
181 .withMessage("solution revisions")
182 .withContent(solutionRevisions)
184 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
185 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionsRevisions for solution {} provided {} revisions",
186 theSolutionId, solutionRevisions == null ? 0 : solutionRevisions.size());
188 catch (Exception x) {
189 response = JsonResponse.<List<MLPSolutionRevision>> buildErrorResponse()
192 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
193 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revisions", x);
200 * @param theSolutionId
202 * @param theRevisionId
204 * @param theHttpResponse
205 * HttpServletResponse
206 * @return List of Published ML Solutions in JSON format.
209 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
210 @ApiOperation(value = "Invoked by peer Acumos to get solution revision details from the local Acumos Instance .", response = MLPSolutionRevision.class)
211 @RequestMapping(value = {
212 API.Paths.SOLUTION_REVISION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
214 public JsonResponse<MLPSolutionRevision> getSolutionRevisionDetails(HttpServletResponse theHttpResponse,
215 @PathVariable("solutionId") String theSolutionId, @PathVariable("revisionId") String theRevisionId) {
216 JsonResponse<MLPSolutionRevision> response = null;
217 MLPSolutionRevision solutionRevision = null;
218 log.debug(EELFLoggerDelegate.debugLogger,
219 API.Paths.SOLUTION_REVISION_DETAILS + "(" + theSolutionId + "," + theRevisionId + ")");
221 solutionRevision = catalogService.getSolutionRevision(theSolutionId, theRevisionId,
222 new ControllerContext());
223 response = JsonResponse.<MLPSolutionRevision> buildResponse()
224 .withMessage("solution revision details")
225 .withContent(solutionRevision)
227 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
229 catch (Exception x) {
230 response = JsonResponse.<MLPSolutionRevision> buildErrorResponse()
233 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
234 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revision " + theRevisionId + " details", x);
240 * @param theHttpRequest
242 * @param theHttpResponse
243 * HttpServletResponse
244 * @param theSolutionId
246 * @param theRevisionId
248 * @return List of Published ML Solutions in JSON format.
251 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
252 @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision artifacts from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
253 @RequestMapping(value = {
254 API.Paths.SOLUTION_REVISION_ARTIFACTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
256 public JsonResponse<List<MLPArtifact>> getSolutionRevisionArtifacts(HttpServletRequest theHttpRequest,
257 HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
258 @PathVariable("revisionId") String theRevisionId) {
259 JsonResponse<List<MLPArtifact>> response = null;
260 List<MLPArtifact> solutionRevisionArtifacts = null;
261 ControllerContext context = new ControllerContext();
262 log.debug(EELFLoggerDelegate.debugLogger,
263 API.Paths.SOLUTION_REVISION_ARTIFACTS + "(" + theSolutionId + "," + theRevisionId + ")");
265 solutionRevisionArtifacts = catalogService.getSolutionRevisionArtifacts(theSolutionId, theRevisionId, context);
266 for (MLPArtifact artifact : solutionRevisionArtifacts) {
267 if (!context.getPeer().getPeerInfo().isLocal()) {
268 encodeArtifact(artifact, theHttpRequest);
271 response = JsonResponse.<List<MLPArtifact>> buildResponse()
272 .withMessage("solution revision artifacts")
273 .withContent(solutionRevisionArtifacts)
275 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
276 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts provided {} artifacts",
277 solutionRevisionArtifacts == null ? 0 : solutionRevisionArtifacts.size());
279 catch (Exception x) {
280 response = JsonResponse.<List<MLPArtifact>> buildErrorResponse()
283 theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
284 log.error(EELFLoggerDelegate.errorLogger, "An error occured while fetching solution " + theSolutionId + " revision " + theRevisionId + " artifacts", x);
290 * @param theHttpRequest
292 * @param theHttpResponse
293 * HttpServletResponse
294 * @param theArtifactId
296 * @return Archive file of the Artifact for the Solution.
299 @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
300 @ApiOperation(value = "API to download the Machine Learning Artifact of the Machine Learning Solution", response = InputStreamResource.class, code = 200)
301 @RequestMapping(value = {
302 API.Paths.ARTIFACT_DOWNLOAD }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
304 public Callable<InputStreamResource> downloadSolutionArtifact(HttpServletRequest theHttpRequest,
305 HttpServletResponse theHttpResponse, @PathVariable("artifactId") String theArtifactId) {
307 theHttpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
308 theHttpResponse.setHeader("Pragma", "no-cache");
309 theHttpResponse.setHeader("Expires", "0");
310 theHttpResponse.setStatus(HttpServletResponse.SC_OK);
312 final ControllerContext ctx = new ControllerContext();
313 return new Callable<InputStreamResource>() {
314 public InputStreamResource call() throws Exception {
316 return artifactService.getArtifactContent(
317 catalogService.getSolutionRevisionArtifact(theArtifactId, ctx), ctx);
319 catch (Exception x) {
320 log.error(EELFLoggerDelegate.errorLogger,
321 "An error occurred while retrieving artifact content " + theArtifactId, x);
329 private void encodeSolution(MLPSolution theSolution, HttpServletRequest theRequest) throws URISyntaxException {
330 //encode the 'origin'
331 if (null == theSolution.getOrigin()) {
332 URI requestUri = new URI(theRequest.getRequestURL().toString());
333 URI solutionUri = API.SOLUTION_DETAIL
335 new URI(requestUri.getScheme(), null, requestUri.getHost(),
336 requestUri.getPort(), null, null, null).toString(),
337 theSolution.getSolutionId());
338 theSolution.setOrigin(solutionUri.toString());
343 private void encodeArtifact(MLPArtifact theArtifact, HttpServletRequest theRequest) throws URISyntaxException {
345 String artifactUri = theArtifact.getUri();
349 URI requestUri = new URI(theRequest.getRequestURL().toString());
350 URI redirectUri = API.ARTIFACT_DOWNLOAD
352 new URI(requestUri.getScheme(), null, requestUri.getHost(),
353 requestUri.getPort(), null, null, null).toString(),
354 theArtifact.getArtifactId());
355 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts: redirected artifact uri " + redirectUri);
356 theArtifact.setUri(redirectUri.toString());
359 if (ArtifactType.DockerImage == ArtifactType.forCode(theArtifact.getArtifactTypeCode())) {
360 if (artifactUri != null) {
361 Identifier imageId = Identifier.fromCompoundString(artifactUri);
363 String imageTag = imageId.tag.get(); //there should always be a tag, right??
364 log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts: encoded docker image uri to tag " + imageTag);
365 theArtifact.setName(imageTag);
366 theArtifact.setDescription(imageTag);