diff options
author | 2023-12-18 11:12:30 +0100 | |
---|---|---|
committer | 2023-12-18 11:16:33 +0100 | |
commit | 6d7077ca105bad3593881db079ef2b5eb1aaa6b1 (patch) | |
tree | 520451e439035ce6bfdf4358b9c5e8f35096fa1c /aai-traversal/src/main/java/org/onap/aai | |
parent | 33521592f3a466fd5cb6959340c1ec70e412dfa8 (diff) |
Make the jax-rs resource a spring boot RestController
- replace jax-rs annotations with spring boot equivalents
- replace the jersey exception handler with a ControllerAdvice class
- move AAIErrorResponse definitions from test/ to main/
Issue-ID: AAI-3694
Change-Id: I5a45309727bfd84bb2aee5c20957fd845c484d5e
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
Diffstat (limited to 'aai-traversal/src/main/java/org/onap/aai')
11 files changed, 249 insertions, 240 deletions
diff --git a/aai-traversal/src/main/java/org/onap/aai/entities/AAIErrorResponse.java b/aai-traversal/src/main/java/org/onap/aai/entities/AAIErrorResponse.java index b80b83d..7cb7edf 100644 --- a/aai-traversal/src/main/java/org/onap/aai/entities/AAIErrorResponse.java +++ b/aai-traversal/src/main/java/org/onap/aai/entities/AAIErrorResponse.java @@ -22,9 +22,11 @@ package org.onap.aai.entities; import lombok.Builder; import lombok.Data; +import lombok.extern.jackson.Jacksonized; @Data @Builder +@Jacksonized public class AAIErrorResponse { - private RequestError requestError; + private final RequestError requestError; } diff --git a/aai-traversal/src/main/java/org/onap/aai/entities/RequestError.java b/aai-traversal/src/main/java/org/onap/aai/entities/RequestError.java index 4aaf43c..aca87b4 100644 --- a/aai-traversal/src/main/java/org/onap/aai/entities/RequestError.java +++ b/aai-traversal/src/main/java/org/onap/aai/entities/RequestError.java @@ -22,9 +22,11 @@ package org.onap.aai.entities; import lombok.Builder; import lombok.Data; +import lombok.extern.jackson.Jacksonized; @Data @Builder +@Jacksonized public class RequestError { private ServiceException serviceException; } diff --git a/aai-traversal/src/main/java/org/onap/aai/entities/ServiceException.java b/aai-traversal/src/main/java/org/onap/aai/entities/ServiceException.java index 3eb9a21..39a7490 100644 --- a/aai-traversal/src/main/java/org/onap/aai/entities/ServiceException.java +++ b/aai-traversal/src/main/java/org/onap/aai/entities/ServiceException.java @@ -24,9 +24,11 @@ import java.util.List; import lombok.Builder; import lombok.Data; +import lombok.extern.jackson.Jacksonized; @Data @Builder +@Jacksonized public class ServiceException { private String messageId; private String text; diff --git a/aai-traversal/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java b/aai-traversal/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java index 854eeed..06c10e6 100644 --- a/aai-traversal/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java +++ b/aai-traversal/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.util.*; import javax.annotation.Priority; -import javax.servlet.http.HttpServletResponse; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; @@ -38,8 +37,6 @@ import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.util.AAIConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - @Priority(AAIResponseFilterPriority.RESPONSE_TRANS_LOGGING) public class ResponseTransactionLogging extends AAIContainerFilter implements ContainerResponseFilter { @@ -54,9 +51,6 @@ public class ResponseTransactionLogging extends AAIContainerFilter private final static String RECENTS_API_PATH_SEGMENT = "recents"; private final static Set<String> READ_ONLY_QUERIES = getReadOnlyQueries(); - @Autowired - private HttpServletResponse httpServletResponse; - @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { @@ -80,23 +74,22 @@ public class ResponseTransactionLogging extends AAIContainerFilter } catch (AAIException e) { return; } - - String transId = requestContext.getHeaderString(AAIHeaderProperties.TRANSACTION_ID); - String fromAppId = requestContext.getHeaderString(AAIHeaderProperties.FROM_APP_ID); - String fullUri = requestContext.getUriInfo().getRequestUri().toString(); - String requestTs = (String) requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST_TS); - String httpMethod = requestContext.getMethod(); - String status = Integer.toString(responseContext.getStatus()); - - String request = (String) requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST); - String response = this.getResponseString(responseContext); - if (!Boolean.parseBoolean(logValue)) { } else if (!Boolean.parseBoolean(postValue) && "POST".equals(httpMethod)) { } else { + String transId = requestContext.getHeaderString(AAIHeaderProperties.TRANSACTION_ID); + String fromAppId = requestContext.getHeaderString(AAIHeaderProperties.FROM_APP_ID); + String fullUri = requestContext.getUriInfo().getRequestUri().toString(); + String requestTs = (String) requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST_TS); + + String status = Integer.toString(responseContext.getStatus()); + + String request = (String) requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST); + String response = this.getResponseString(responseContext); + JsonObject logEntry = new JsonObject(); logEntry.addProperty("transactionId", transId); logEntry.addProperty("status", status); @@ -141,7 +134,7 @@ public class ResponseTransactionLogging extends AAIContainerFilter private String getResponseString(ContainerResponseContext responseContext) { JsonObject response = new JsonObject(); response.addProperty("ID", responseContext.getHeaderString(AAIHeaderProperties.AAI_TX_ID)); - response.addProperty("Content-Type", this.httpServletResponse.getContentType()); + response.addProperty("Content-Type", responseContext.getHeaders().getFirst("Content-Type").toString()); response.addProperty("Response-Code", responseContext.getStatus()); response.addProperty("Headers", responseContext.getHeaders().toString()); Optional<Object> entityOptional = Optional.ofNullable(responseContext.getEntity()); diff --git a/aai-traversal/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java b/aai-traversal/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java index b37a804..8c73033 100644 --- a/aai-traversal/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java +++ b/aai-traversal/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java @@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.Random; import java.util.UUID; @@ -38,6 +39,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; import org.glassfish.jersey.message.internal.ReaderWriter; import org.glassfish.jersey.server.ContainerException; import org.onap.aai.exceptions.AAIException; @@ -46,17 +49,14 @@ import org.onap.aai.interceptors.AAIHeaderProperties; import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; import org.onap.aai.util.HbaseSaltPrefixer; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; +// Here @PreMatching @Priority(AAIRequestFilterPriority.REQUEST_TRANS_LOGGING) public class RequestTransactionLogging extends AAIContainerFilter implements ContainerRequestFilter { - @Autowired - private HttpServletRequest httpServletRequest; - private static final String DEFAULT_CONTENT_TYPE = MediaType.APPLICATION_JSON; private static final String DEFAULT_RESPONSE_TYPE = MediaType.APPLICATION_XML; @@ -67,23 +67,15 @@ public class RequestTransactionLogging extends AAIContainerFilter private static final String APPLICATION_JSON = "application/json"; @Override - public void filter(ContainerRequestContext requestContext) throws IOException { - + public void filter(ContainerRequestContext requestContext) throws IOException { String currentTimeStamp = genDate(); String fullId = this.getAAITxIdToHeader(currentTimeStamp); - this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_TX_ID, fullId); - this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_REQUEST, - this.getRequest(requestContext, fullId)); - this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_REQUEST_TS, - currentTimeStamp); + requestContext.setProperty(AAIHeaderProperties.AAI_TX_ID, fullId); + requestContext.setProperty(AAIHeaderProperties.AAI_REQUEST, this.getRequest(requestContext, fullId)); + requestContext.setProperty(AAIHeaderProperties.AAI_REQUEST_TS, currentTimeStamp); this.addDefaultContentType(requestContext); } - private void addToRequestContext(ContainerRequestContext requestContext, String name, - String aaiTxIdToHeader) { - requestContext.setProperty(name, aaiTxIdToHeader); - } - private void addDefaultContentType(ContainerRequestContext requestContext) { String contentType = requestContext.getHeaderString(CONTENT_TYPE); @@ -124,27 +116,16 @@ public class RequestTransactionLogging extends AAIContainerFilter return txId; } - private String getRequest(ContainerRequestContext requestContext, String fullId) { + private String getRequest(ContainerRequestContext requestContext, String fullId) throws IOException { JsonObject request = new JsonObject(); request.addProperty("ID", fullId); request.addProperty("Http-Method", requestContext.getMethod()); - request.addProperty(CONTENT_TYPE, httpServletRequest.getContentType()); request.addProperty("Headers", requestContext.getHeaders().toString()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - InputStream in = requestContext.getEntityStream(); - - try { - if (in.available() > 0) { - ReaderWriter.writeTo(in, out); - byte[] requestEntity = out.toByteArray(); - request.addProperty("Payload", new String(requestEntity, "UTF-8")); - requestContext.setEntityStream(new ByteArrayInputStream(requestEntity)); - } - } catch (IOException ex) { - throw new ContainerException(ex); - } + // String requestEntity = IOUtils.toString(requestContext.getEntityStream(), Charsets.UTF_8); + // InputStream in = IOUtils.toInputStream(requestEntity, Charset.defaultCharset()); + // requestContext.setEntityStream(in); return request.toString(); } diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java b/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java index d814b48..2a02ff5 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java @@ -22,34 +22,24 @@ package org.onap.aai.rest; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.UriInfo; +import org.antlr.v4.runtime.tree.ParseTreeListener; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.janusgraph.core.SchemaViolationException; -import org.onap.aai.concurrent.AaiCallable; +import org.onap.aai.edges.EdgeIngestor; import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.LoaderFactory; import org.onap.aai.introspection.ModelType; import org.onap.aai.rest.db.HttpEntry; import org.onap.aai.rest.dsl.DslQueryProcessor; @@ -57,7 +47,6 @@ import org.onap.aai.rest.enums.QueryVersion; import org.onap.aai.rest.search.GenericQueryProcessor; import org.onap.aai.rest.search.GremlinServerSingleton; import org.onap.aai.rest.search.QueryProcessorType; -import org.onap.aai.restcore.HttpMethod; import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.engines.TransactionalGraphEngine; import org.onap.aai.serialization.queryformats.Format; @@ -73,6 +62,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -81,7 +80,8 @@ import com.google.gson.JsonParser; import io.micrometer.core.annotation.Timed; @Timed -@Path("{version: v[1-9][0-9]*|latest}/dsl") +@RestController +@RequestMapping("/{version:v[1-9][0-9]*|latest}/dsl") public class DslConsumer extends TraversalConsumer { private static final Logger LOGGER = LoggerFactory.getLogger(DslConsumer.class); @@ -89,55 +89,58 @@ public class DslConsumer extends TraversalConsumer { private static final QueryVersion DEFAULT_VERSION = QueryVersion.V1; private final HttpEntry traversalUriHttpEntry; - private final DslQueryProcessor dslQueryProcessor; private final SchemaVersions schemaVersions; private final String basePath; private final GremlinServerSingleton gremlinServerSingleton; private final XmlFormatTransformer xmlFormatTransformer; + // private final Map<QueryVersion, ParseTreeListener> dslListeners; + private final EdgeIngestor edgeIngestor; + private final LoaderFactory loaderFactory; private QueryVersion dslApiVersion = DEFAULT_VERSION; @Autowired - public DslConsumer(HttpEntry traversalUriHttpEntry, DslQueryProcessor dslQueryProcessor, + public DslConsumer(HttpEntry traversalUriHttpEntry, SchemaVersions schemaVersions, GremlinServerSingleton gremlinServerSingleton, XmlFormatTransformer xmlFormatTransformer, + EdgeIngestor edgeIngestor, LoaderFactory loaderFactory, @Value("${schema.uri.base.path}") String basePath) { this.traversalUriHttpEntry = traversalUriHttpEntry; - this.dslQueryProcessor = dslQueryProcessor; this.schemaVersions = schemaVersions; this.gremlinServerSingleton = gremlinServerSingleton; this.xmlFormatTransformer = xmlFormatTransformer; this.basePath = basePath; + this.edgeIngestor = edgeIngestor; + this.loaderFactory = loaderFactory; } - @PUT - @Consumes({MediaType.APPLICATION_JSON}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response executeQuery(String dslQuery, @PathParam("version") String versionParam, - @DefaultValue("graphson") @QueryParam("format") String queryFormat, - @DefaultValue("no_op") @QueryParam("subgraph") String subgraph, - @DefaultValue("all") @QueryParam("validate") String validate, - @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex, - @DefaultValue("-1") @QueryParam("resultSize") String resultSize, - @Context HttpHeaders headers, - @Context HttpServletRequest req, - @Context UriInfo info) throws FileNotFoundException, AAIException { - Set<String> roles = this.getRoles(req.getUserPrincipal()); - - return processExecuteQuery(dslQuery, req, versionParam, queryFormat, subgraph, - validate, headers, info, resultIndex, resultSize, roles); + @PutMapping(produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) + public ResponseEntity<String> executeQuery(@RequestBody String dslQuery, + @PathVariable("version") String versionParam, + @RequestParam(defaultValue = "graphson") String format, + @RequestParam(defaultValue = "no_op") String subgraph, + @RequestParam(defaultValue = "all") String validate, + @RequestParam(defaultValue = "-1") String resultIndex, + @RequestParam(defaultValue = "-1") String resultSize, + @RequestHeader HttpHeaders headers, + HttpServletRequest request) throws FileNotFoundException, AAIException { + Set<String> roles = this.getRoles(request.getUserPrincipal()); + + return processExecuteQuery(dslQuery, request, versionParam, format, subgraph, + validate, headers, resultIndex, resultSize, roles); } - public Response processExecuteQuery(String dslQuery, HttpServletRequest request, String versionParam, - String queryFormat, String subgraph, String validate, HttpHeaders headers, UriInfo info, + public ResponseEntity<String> processExecuteQuery(String dslQuery, HttpServletRequest request, String versionParam, + String queryFormat, String subgraph, String validate, HttpHeaders headers, String resultIndex, String resultSize, Set<String> roles) throws FileNotFoundException, AAIException { final SchemaVersion version = new SchemaVersion(versionParam); - final String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); - final String dslOverride = headers.getRequestHeaders().getFirst("X-DslOverride"); + final String sourceOfTruth = headers.getFirst("X-FromAppId"); + final String dslOverride = headers.getFirst("X-DslOverride"); + final MultivaluedMap<String,String> queryParams = toMultivaluedMap(request.getParameterMap()); Optional<String> dslApiVersionHeader = - Optional.ofNullable(headers.getRequestHeaders().getFirst("X-DslApiVersion")); + Optional.ofNullable(headers.getFirst("X-DslApiVersion")); if (dslApiVersionHeader.isPresent()) { try { dslApiVersion = QueryVersion.valueOf(dslApiVersionHeader.get()); @@ -146,25 +149,25 @@ public class DslConsumer extends TraversalConsumer { } } - String result = executeQuery(dslQuery, request, queryFormat, subgraph, validate, info.getQueryParameters(), resultIndex, resultSize, + String result = executeQuery(dslQuery, request, queryFormat, subgraph, validate, queryParams, resultIndex, resultSize, roles, version, sourceOfTruth, dslOverride); + MediaType acceptType = headers.getAccept().stream() + .filter(Objects::nonNull) + .filter(header -> !header.equals(MediaType.ALL)) + .findAny() + .orElse(MediaType.APPLICATION_JSON); - String acceptType = headers.getHeaderString("Accept"); - if (acceptType == null) { - acceptType = MediaType.APPLICATION_JSON; - } - - if (MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(acceptType))) { + if (MediaType.APPLICATION_XML.isCompatibleWith(acceptType)) { result = xmlFormatTransformer.transform(result); } if (traversalUriHttpEntry.isPaginated()) { - return Response.status(Status.OK).type(acceptType) - .header("total-results", traversalUriHttpEntry.getTotalVertices()) - .header("total-pages", traversalUriHttpEntry.getTotalPaginationBuckets()) - .entity(result).build(); + return ResponseEntity.ok() + .header("total-results", String.valueOf(traversalUriHttpEntry.getTotalVertices())) + .header("total-pages", String.valueOf(traversalUriHttpEntry.getTotalPaginationBuckets())) + .body(result); } else { - return Response.status(Status.OK).type(acceptType).entity(result).build(); + return ResponseEntity.ok(result); } } @@ -176,7 +179,6 @@ public class DslConsumer extends TraversalConsumer { req.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/"); traversalUriHttpEntry.setHttpEntryProperties(version, serverBase); traversalUriHttpEntry.setPaginationParameters(resultIndex, resultSize); - final TransactionalGraphEngine dbEngine = traversalUriHttpEntry.getDbEngine(); JsonObject input = JsonParser.parseString(content).getAsJsonObject(); JsonElement dslElement = input.get("dsl"); @@ -189,6 +191,12 @@ public class DslConsumer extends TraversalConsumer { && !AAIConfig.get(TraversalConstants.DSL_OVERRIDE).equals("false") && dslOverride.equals(AAIConfig.get(TraversalConstants.DSL_OVERRIDE)); + Map<QueryVersion, ParseTreeListener> dslListeners = new HashMap<>(); + dslListeners.put(QueryVersion.V1, + new org.onap.aai.rest.dsl.v1.DslListener(edgeIngestor, schemaVersions, loaderFactory)); + dslListeners.put(QueryVersion.V2, + new org.onap.aai.rest.dsl.v2.DslListener(edgeIngestor, schemaVersions, loaderFactory)); + DslQueryProcessor dslQueryProcessor = new DslQueryProcessor(dslListeners); if (isDslOverride) { dslQueryProcessor.setStartNodeValidationFlag(false); } @@ -205,9 +213,10 @@ public class DslConsumer extends TraversalConsumer { validateHistoryParams(format, queryParameters); } + final TransactionalGraphEngine dbEngine = traversalUriHttpEntry.getDbEngine(); GraphTraversalSource traversalSource = getTraversalSource(dbEngine, format, queryParameters, roles); - + GenericQueryProcessor processor = new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton) .queryFrom(dsl, "dsl").queryProcessor(dslQueryProcessor).version(dslApiVersion) @@ -217,12 +226,11 @@ public class DslConsumer extends TraversalConsumer { SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph); List<Object> vertTemp = processor.execute(subGraphStyle); - // Dedup if duplicate objects are returned in each array in the aggregate format - // scenario. - List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResult(vertTemp); - List<Object> vertices; if (isAggregate(format)) { + // Dedup if duplicate objects are returned in each array in the aggregate format + // scenario. + List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResult(vertTemp); vertices = traversalUriHttpEntry .getPaginatedVertexListForAggregateFormat(vertTempDedupedObjectList); } else { @@ -252,6 +260,12 @@ public class DslConsumer extends TraversalConsumer { return result; } + private List<Object> dedupObjectInAggregateFormatResultStreams(List<Object> vertTemp) { + return vertTemp.stream() + .filter(o -> o instanceof ArrayList) + .map(o -> ((ArrayList<?>) o).stream().distinct().collect(Collectors.toList())) + .collect(Collectors.toList()); + } private List<Object> dedupObjectInAggregateFormatResult(List<Object> vertTemp) { List<Object> vertTempDedupedObjectList = new ArrayList<Object>(); Iterator<Object> itr = vertTemp.listIterator(); @@ -264,4 +278,15 @@ public class DslConsumer extends TraversalConsumer { } return vertTempDedupedObjectList; } + + private MultivaluedMap<String, String> toMultivaluedMap(Map<String, String[]> map) { + MultivaluedMap<String, String> multivaluedMap = new MultivaluedHashMap<>(); + + for (Map.Entry<String, String[]> entry : map.entrySet()) { + for (String val : entry.getValue()) + multivaluedMap.add(entry.getKey(), val); + } + + return multivaluedMap; + } } diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/ExceptionHandler.java b/aai-traversal/src/main/java/org/onap/aai/rest/ExceptionHandler.java deleted file mode 100644 index 447ecdd..0000000 --- a/aai-traversal/src/main/java/org/onap/aai/rest/ExceptionHandler.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2023 Deutsche Telekom SA. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.rest; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.sun.istack.SAXParseException2; - -import java.util.ArrayList; - -import javax.annotation.Priority; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -import org.janusgraph.core.SchemaViolationException; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.ErrorLogHelper; - -/** - * The Class ExceptionHandler. - */ -@Provider -@Priority(10000) -public class ExceptionHandler implements ExceptionMapper<Exception> { - - private static final String AAI_4007 = "AAI_4007"; - - @Context - private HttpServletRequest request; - - @Context - private HttpHeaders headers; - - @Override - public Response toResponse(Exception exception) { - // the general case is that cxf will give us a WebApplicationException - // with a linked exception - if (exception instanceof WebApplicationException) { - if (exception.getCause() instanceof SAXParseException2) { - return buildAAIExceptionResponse(new AAIException(AAI_4007, exception)); - } else { - return ((WebApplicationException) exception).getResponse(); - } - } else if (exception instanceof JsonParseException) { - // jackson does it differently so we get the direct JsonParseException - return buildAAIExceptionResponse(new AAIException(AAI_4007, exception)); - } else if (exception instanceof JsonMappingException) { - // jackson does it differently so we get the direct JsonParseException - return buildAAIExceptionResponse(new AAIException(AAI_4007, exception)); - } else if (exception instanceof SchemaViolationException) { - return buildAAIExceptionResponse(new AAIException("AAI_4020", exception)); - // it didn't get set above, we wrap a general fault here - } else if (exception instanceof AAIException) { - return buildAAIExceptionResponse((AAIException) exception); - } else { - return defaultException(exception); - } - } - - private Response buildAAIExceptionResponse(AAIException exception) { - ArrayList<String> templateVars = new ArrayList<>(); - templateVars.add(request.getMethod()); - templateVars.add(request.getRequestURI()); - - // prefer xml, use json otherwise - return headers.getAcceptableMediaTypes().stream() - .filter(MediaType.APPLICATION_ATOM_XML_TYPE::isCompatible) - .findAny() - .map(xmlType -> - Response.status(400).type(MediaType.APPLICATION_XML_TYPE) - .entity(ErrorLogHelper.getRESTAPIErrorResponse( - headers.getAcceptableMediaTypes(), exception, templateVars)) - .build()) - .orElseGet(() -> - Response.status(400).type(MediaType.APPLICATION_JSON_TYPE) - .entity(ErrorLogHelper.getRESTAPIErrorResponse( - headers.getAcceptableMediaTypes(), exception, templateVars)) - .build()); - } - - private Response defaultException(Exception exception) { - return buildAAIExceptionResponse(new AAIException("AAI_4000", exception)); - } -}
\ No newline at end of file diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/GlobalExceptionHandler.java b/aai-traversal/src/main/java/org/onap/aai/rest/GlobalExceptionHandler.java new file mode 100644 index 0000000..c934e8c --- /dev/null +++ b/aai-traversal/src/main/java/org/onap/aai/rest/GlobalExceptionHandler.java @@ -0,0 +1,109 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2023 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.rest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.MediaType; + +import org.janusgraph.core.SchemaViolationException; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +@ControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + private static final String AAI_4007 = "AAI_4007"; + + @ExceptionHandler({JsonParseException.class, JsonMappingException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity<String> handleJsonException( + JsonParseException exception, + WebRequest request + ){ + return buildAAIExceptionResponse(new AAIException(AAI_4007, exception)); + } + + @ExceptionHandler({SchemaViolationException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity<String> handleSchemaViolationException( + SchemaViolationException exception, + WebRequest request + ){ + return buildAAIExceptionResponse(new AAIException("AAI_4020", exception)); + } + + @ExceptionHandler({AAIException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity<String> handleAAIException( + AAIException exception, + WebRequest request + ){ + return buildAAIExceptionResponse(exception); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity<String> handleUnknownException( + Exception exception, + WebRequest request) { + return defaultException(exception); + } + + private ResponseEntity<String> buildAAIExceptionResponse(AAIException exception) { + + String body = getResponseBody(exception); + return new ResponseEntity<>(body, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + private String getResponseBody(AAIException exception) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + ArrayList<String> templateVars = new ArrayList<>(); + templateVars.add(request.getMethod()); + templateVars.add(request.getRequestURI()); + + List<MediaType> mediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON_TYPE); + String body = ErrorLogHelper.getRESTAPIErrorResponse( + mediaTypes, exception, templateVars); + return body; + } + + private ResponseEntity<String> defaultException(Exception exception) { + return buildAAIExceptionResponse(new AAIException("AAI_4000", exception)); + } +} diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java b/aai-traversal/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java index 695e125..59d4df1 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java @@ -50,7 +50,7 @@ public class DslQueryProcessor { private Map<QueryVersion, ParseTreeListener> dslListeners; private boolean startNodeValidationFlag = true; private String validationRules = ""; - private String packageName = "org.onap.aai.dsl."; + private static final String DSL_BASE_PACKAGE = "org.onap.aai.dsl."; private static final String LEXER = "AAIDslLexer"; private static final String PARSER = "AAIDslParser"; private static final String EOF_TOKEN = "<EOF>"; @@ -69,8 +69,7 @@ public class DslQueryProcessor { InputStream stream = new ByteArrayInputStream(aaiQuery.getBytes(StandardCharsets.UTF_8)); - packageName = packageName + version.toString().toLowerCase() + "."; - + String packageName = DSL_BASE_PACKAGE + version.toString().toLowerCase() + "."; Class<?> lexerClass = Class.forName(packageName + LEXER); Class<?> parserClass = Class.forName(packageName + PARSER); diff --git a/aai-traversal/src/main/java/org/onap/aai/web/JerseyConfiguration.java b/aai-traversal/src/main/java/org/onap/aai/web/JerseyConfiguration.java index f4f3949..0bfbd9d 100644 --- a/aai-traversal/src/main/java/org/onap/aai/web/JerseyConfiguration.java +++ b/aai-traversal/src/main/java/org/onap/aai/web/JerseyConfiguration.java @@ -33,6 +33,7 @@ import java.util.logging.Logger; import javax.annotation.Priority; import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletProperties; import org.onap.aai.rest.*; import org.onap.aai.rest.search.ModelAndNamedQueryRestProvider; import org.onap.aai.rest.search.SearchProvider; @@ -63,11 +64,12 @@ public class JerseyConfiguration { public ResourceConfig resourceConfig() { ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.property(ServletProperties.FILTER_FORWARD_ON_404, true); Set<Class<?>> classes = Sets.newHashSet(SearchProvider.class, - ModelAndNamedQueryRestProvider.class, QueryConsumer.class, RecentAPIConsumer.class, - DslConsumer.class, EchoResponse.class, CQ2Gremlin.class, CQ2GremlinTest.class); + ModelAndNamedQueryRestProvider.class, QueryConsumer.class, RecentAPIConsumer.class, EchoResponse.class, CQ2Gremlin.class, CQ2GremlinTest.class); Set<Class<?>> filterClasses = - Sets.newHashSet(org.onap.aai.interceptors.pre.RequestTransactionLogging.class, + Sets.newHashSet( + org.onap.aai.interceptors.pre.RequestTransactionLogging.class, org.onap.aai.interceptors.pre.HeaderValidation.class, org.onap.aai.interceptors.pre.HttpHeaderInterceptor.class, org.onap.aai.interceptors.pre.OneWaySslAuthorization.class, @@ -77,9 +79,10 @@ public class JerseyConfiguration { org.onap.aai.interceptors.pre.RequestHeaderManipulation.class, org.onap.aai.interceptors.pre.RequestModification.class, org.onap.aai.interceptors.post.InvalidResponseStatus.class, + org.onap.aai.interceptors.post.ResponseTransactionLogging.class, - org.onap.aai.rest.ExceptionHandler.class, - org.onap.aai.interceptors.post.ResponseHeaderManipulation.class); + org.onap.aai.interceptors.post.ResponseHeaderManipulation.class + ); resourceConfig.registerClasses(classes); logger.debug("REGISTERED CLASSES " + classes.toString()); @@ -93,7 +96,7 @@ public class JerseyConfiguration { } private <T> void throwIfPriorityAnnotationAbsent(Collection<Class<? extends T>> classes) { - for (Class clazz : classes) { + for (Class<? extends T> clazz : classes) { if (!clazz.isAnnotationPresent(Priority.class)) { logger.debug("throwIfPriorityAnnotationAbsent: missing filter priority for : " + clazz.getName()); diff --git a/aai-traversal/src/main/java/org/onap/aai/web/WebConfiguration.java b/aai-traversal/src/main/java/org/onap/aai/web/WebConfiguration.java index a2a4271..2dee807 100644 --- a/aai-traversal/src/main/java/org/onap/aai/web/WebConfiguration.java +++ b/aai-traversal/src/main/java/org/onap/aai/web/WebConfiguration.java @@ -19,24 +19,25 @@ */ package org.onap.aai.web; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -public class WebConfiguration { +public class WebConfiguration implements WebMvcConfigurer { - @Bean - public WebMvcConfigurerAdapter forwardToIndex() { - return new WebMvcConfigurerAdapter() { - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/swagger").setViewName("redirect:/swagger/index.html"); - registry.addViewController("/swagger/").setViewName("redirect:/swagger/index.html"); - registry.addViewController("/docs").setViewName("redirect:/docs/html/index.html"); - registry.addViewController("/docs/").setViewName("redirect:/docs/html/index.html"); - } - }; + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/swagger").setViewName("redirect:/swagger/index.html"); + registry.addViewController("/swagger/").setViewName("redirect:/swagger/index.html"); + registry.addViewController("/docs").setViewName("redirect:/docs/html/index.html"); + registry.addViewController("/docs/").setViewName("redirect:/docs/html/index.html"); + } + + @Override + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + configurer.defaultContentType(MediaType.APPLICATION_JSON); } } |