diff options
Diffstat (limited to 'aai-core/src/main')
150 files changed, 6549 insertions, 7680 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/audit/ListEndpoints.java b/aai-core/src/main/java/org/onap/aai/audit/ListEndpoints.java index dcfd58a5..fe2e1ff7 100644 --- a/aai-core/src/main/java/org/onap/aai/audit/ListEndpoints.java +++ b/aai-core/src/main/java/org/onap/aai/audit/ListEndpoints.java @@ -22,8 +22,8 @@ package org.onap.aai.audit; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; import java.util.ArrayList; @@ -53,7 +53,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext */ public class ListEndpoints { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ListEndpoints.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ListEndpoints.class); private static final String START = "inventory"; private static final String[] blacklist = {"search", "aai-internal"}; diff --git a/aai-core/src/main/java/org/onap/aai/auth/AAIAuthCore.java b/aai-core/src/main/java/org/onap/aai/auth/AAIAuthCore.java deleted file mode 100644 index cde2faa3..00000000 --- a/aai-core/src/main/java/org/onap/aai/auth/AAIAuthCore.java +++ /dev/null @@ -1,372 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.auth; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.UnsupportedEncodingException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.eclipse.jetty.util.security.Password; -import org.eclipse.persistence.internal.oxm.conversion.Base64; -import org.onap.aai.auth.exceptions.AAIUnrecognizedFunctionException; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.LoggingContext; -import org.onap.aai.logging.LoggingContext.StatusCode; -import org.onap.aai.util.AAIConfig; -import org.onap.aai.util.AAIConstants; -import org.onap.aai.util.FileWatcher; - -/** - * The Class AAIAuthCore. - */ -public final class AAIAuthCore { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIAuthCore.class); - - private static final String ERROR_CODE_AAI_4001 = "AAI_4001"; - - private String globalAuthFileName = AAIConstants.AAI_AUTH_CONFIG_FILENAME; - - private final Pattern AUTH_POLICY_PATTERN; - private final Set<String> validFunctions = new HashSet<>(); - private Map<String, AAIUser> users; - private boolean timerSet = false; - private Timer timer = null; - - private String basePath; - - /** - * Instantiates a new AAI auth core. - */ - public AAIAuthCore(String basePath) { - this.basePath = basePath; - AUTH_POLICY_PATTERN = Pattern.compile("^" + this.basePath + "/v\\d+/([\\w\\-]*)"); - init(); - } - - /** - * Inits the. - */ - private synchronized void init() { - - LOGGER.debug("Initializing Auth Policy Config"); - - reloadUsers(); - - /* - * this timer code is setting up a recurring task that checks if the - * auth config file has been updated and reloads the users if so to get - * the most up to date info (that update check logic is within - * FileWatcher) - * - * the timing this method uses is coarser than the frequency of requests - * AI&I gets so we're looking at better ways of doing this (TODO) - */ - TimerTask task = new FileWatcher(new File(globalAuthFileName)) { - @Override - protected void onChange(File file) { - reloadUsers(); - } - }; - - if (!timerSet) { - timerSet = true; - timer = new Timer(); - - // repeat the check every second - timer.schedule(task, new Date(), 10000); - } - LOGGER.debug("Static Initializiation complete"); - } - - /** - * Cleanup. - */ - // just ends the auth config file update checking timer - public void cleanup() { - timer.cancel(); - } - - /** - * Reload users. - */ - /* - * this essentially takes the data file, which is organized role-first with - * users under each role and converts it to data organized user-first with - * each user containing their role with its associated allowed functions - * this data stored in the class field users - */ - private synchronized void reloadUsers() { - - Map<String, AAIUser> tempUsers = new HashMap<>(); - - try { - LOGGER.debug("Reading from " + globalAuthFileName); - String authFile = new String(Files.readAllBytes(Paths.get(globalAuthFileName))); - - JsonParser parser = new JsonParser(); - JsonObject authObject = parser.parse(authFile).getAsJsonObject(); - if (authObject.has("roles")) { - JsonArray roles = authObject.getAsJsonArray("roles"); - for (JsonElement role : roles) { - if (role.isJsonObject()) { - JsonObject roleObject = role.getAsJsonObject(); - String roleName = roleObject.get("name").getAsString(); - Map<String, Boolean> usrs = this.getUsernamesFromRole(roleObject); - List<String> aaiFunctions = this.getAAIFunctions(roleObject); - - usrs.forEach((key, value) -> { - final AAIUser au = tempUsers.getOrDefault(key, new AAIUser(key, value)); - au.addRole(roleName); - aaiFunctions.forEach(f -> { - List<String> httpMethods = this.getRoleHttpMethods(f, roleObject); - httpMethods.forEach(hm -> au.setUserAccess(f, hm)); - this.validFunctions.add(f); - }); - - tempUsers.put(key, au); - - }); - } - } - if (!tempUsers.isEmpty()) { - users = tempUsers; - } - } - } catch (FileNotFoundException e) { - ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Exception: " + e); - } catch (JsonProcessingException e) { - ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Not valid JSON: " + e); - } catch (Exception e) { - ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Exception caught: " + e); - } - } - - private List<String> getRoleHttpMethods(String aaiFunctionName, JsonObject roleObject) { - List<String> httpMethods = new ArrayList<>(); - - JsonArray ja = roleObject.getAsJsonArray("functions"); - for (JsonElement je : ja) { - if (je.isJsonObject() && je.getAsJsonObject().has("name") - && je.getAsJsonObject().get("name").getAsString().equals(aaiFunctionName)) { - JsonArray jaMeth = je.getAsJsonObject().getAsJsonArray("methods"); - for (JsonElement jeMeth : jaMeth) { - if (jeMeth.isJsonObject() && jeMeth.getAsJsonObject().has("name")) { - httpMethods.add(jeMeth.getAsJsonObject().get("name").getAsString()); - } - } - } - } - - return httpMethods; - } - - private List<String> getAAIFunctions(JsonObject roleObject) { - List<String> aaiFunctions = new ArrayList<>(); - - JsonArray ja = roleObject.getAsJsonArray("functions"); - for (JsonElement je : ja) { - if (je.isJsonObject() && je.getAsJsonObject().has("name")) { - aaiFunctions.add(je.getAsJsonObject().get("name").getAsString()); - } - } - - return aaiFunctions; - } - - private Map<String, Boolean> getUsernamesFromRole(JsonObject roleObject) throws UnsupportedEncodingException { - Map<String, Boolean> usernames = new HashMap<>(); - - JsonArray uja = roleObject.getAsJsonArray("users"); - for (JsonElement je : uja) { - if (je.isJsonObject()) { - if (je.getAsJsonObject().has("username")) { - if (je.getAsJsonObject().has("is-wildcard-id")) { - usernames.put(je.getAsJsonObject().get("username").getAsString().toLowerCase(), - je.getAsJsonObject().get("is-wildcard-id").getAsBoolean()); - } else { - usernames.put(je.getAsJsonObject().get("username").getAsString().toLowerCase(), false); - } - } else if (je.getAsJsonObject().has("user")) { - String auth = je.getAsJsonObject().get("user").getAsString() + ":" - + Password.deobfuscate(je.getAsJsonObject().get("pass").getAsString()); - String authorizationCode = new String(Base64.base64Encode(auth.getBytes("utf-8"))); - usernames.put(authorizationCode, false); - } - } - } - - return usernames; - } - - public String getAuthPolicyFunctName(String uri) { - String authPolicyFunctionName = ""; - if (uri.startsWith(basePath + "/search")) { - authPolicyFunctionName = "search"; - } else if (uri.startsWith(basePath + "/recents")) { - authPolicyFunctionName = "recents"; - } else if (uri.startsWith(basePath + "/cq2gremlin")) { - authPolicyFunctionName = "cq2gremlin"; - } else if (uri.startsWith(basePath + "/cq2gremlintest")) { - authPolicyFunctionName = "cq2gremlintest"; - } else if (uri.startsWith(basePath + "/util/echo")) { - authPolicyFunctionName = "util"; - } else if (uri.startsWith(basePath + "/tools")) { - authPolicyFunctionName = "tools"; - } else { - Matcher match = AUTH_POLICY_PATTERN.matcher(uri); - if (match.find()) { - authPolicyFunctionName = match.group(1); - } - } - return authPolicyFunctionName; - } - - /** - * for backwards compatibility - * - * @param username - * @param uri - * @param httpMethod - * @param haProxyUser - * @return - * @throws AAIUnrecognizedFunctionException - */ - public boolean authorize(String username, String uri, String httpMethod, String haProxyUser) - throws AAIUnrecognizedFunctionException { - return authorize(username, uri, httpMethod, haProxyUser, null); - } - - /** - * - * @param username - * @param uri - * @param httpMethod - * @param haProxyUser - * @param issuer issuer of the cert - * @return - * @throws AAIUnrecognizedFunctionException - */ - public boolean authorize(String username, String uri, String httpMethod, String haProxyUser, String issuer) - throws AAIUnrecognizedFunctionException { - String aaiMethod = this.getAuthPolicyFunctName(uri); - if (!this.validFunctions.contains(aaiMethod)) { - throw new AAIUnrecognizedFunctionException(aaiMethod); - } - boolean wildcardCheck = isWildcardIssuer(issuer); - boolean authorized; - LOGGER.debug( - "Authorizing the user for the request cert {}, haproxy header {}, aai method {}, httpMethod {}, cert issuer {}", - username, haProxyUser, aaiMethod, httpMethod, issuer); - Optional<AAIUser> oau = this.getUser(username, wildcardCheck); - if (oau.isPresent()) { - AAIUser au = oau.get(); - if (au.hasRole("HAProxy")) { - LOGGER.debug("User has HAProxy role"); - if ("GET".equalsIgnoreCase(httpMethod) && "util".equalsIgnoreCase(aaiMethod) && haProxyUser.isEmpty()) { - LOGGER.debug("Authorized user has HAProxy role with echo request"); - authorized = this.authorize(au, aaiMethod, httpMethod); - } else { - authorized = this.authorize(haProxyUser, uri, httpMethod, "", issuer); - } - } else { - LOGGER.debug("User doesn't have HAProxy role so assuming its a regular client"); - authorized = this.authorize(au, aaiMethod, httpMethod); - } - } else { - LOGGER.debug("User not found: " + username + " on function " + aaiMethod + " request type " + httpMethod); - authorized = false; - } - - return authorized; - } - - private boolean isWildcardIssuer(String issuer) { - if (issuer != null && !issuer.isEmpty()) { - List<String> validIssuers = Arrays - .asList(AAIConfig.get("aaf.valid.issuer.wildcard", UUID.randomUUID().toString()).split("\\|")); - for (String validIssuer : validIssuers) { - if (issuer.contains(validIssuer)) { - return true; - } - } - } - return false; - } - - /** - * returns aai user either matching the username or containing the wildcard. - * - * @param username - * @return - */ - public Optional<AAIUser> getUser(String username, boolean wildcardCheck) { - if (users.containsKey(username)) { - return Optional.of(users.get(username)); - } else if (wildcardCheck) { - List<AAIUser> laus = - users.entrySet().stream().filter(e -> e.getValue().isWildcard() && username.contains(e.getKey())) - .map(Map.Entry::getValue).collect(Collectors.toList()); - if (!laus.isEmpty()) { - return Optional.of(laus.get(0)); - } - } - return Optional.empty(); - } - - /** - * - * @param aaiUser - * aai user with the username - * @param aaiMethod - * aai function the authorization is required on - * @param httpMethod - * http action user is attempting - * @return true, if successful - */ - private boolean authorize(AAIUser aaiUser, String aaiMethod, String httpMethod) { - if (aaiUser.hasAccess(aaiMethod, httpMethod)) { - LoggingContext.statusCode(StatusCode.COMPLETE); - LOGGER.debug("AUTH ACCEPTED: " + aaiUser.getUsername() + " on function " + aaiMethod + " request type " - + httpMethod); - return true; - } else { - LoggingContext.statusCode(StatusCode.ERROR); - LOGGER.debug("AUTH FAILED: " + aaiUser.getUsername() + " on function " + aaiMethod + " request type " - + httpMethod); - return false; - } - } -} diff --git a/aai-core/src/main/java/org/onap/aai/auth/AAIUser.java b/aai-core/src/main/java/org/onap/aai/auth/AAIUser.java deleted file mode 100644 index f1e1b084..00000000 --- a/aai-core/src/main/java/org/onap/aai/auth/AAIUser.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Modifications Copyright © 2018 IBM. - * ================================================================================ - * 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.auth; - -import java.util.*; - -public class AAIUser { - - private String username; - - private boolean isWildcard = false; - private Set<String> roles; - private Map<String, Set<String>> aaiFunctionToHttpMethod; - - public AAIUser(String username) { - this(username, false); - } - - public AAIUser(String username, boolean isWildcard) { - this.username = username; - this.roles = new HashSet<>(); - this.aaiFunctionToHttpMethod = new HashMap<>(); - this.isWildcard = isWildcard; - } - - public boolean isWildcard() { - return isWildcard; - } - - public String getUsername() { - return username; - } - - public void addRole(String role) { - this.roles.add(role); - } - - public boolean hasRole(String role) { - return this.roles.contains(role); - } - - public void setUserAccess(String aaiMethod, String... httpMethods) { - for (String httpMethod : httpMethods) { - this.addUserAccess(aaiMethod, httpMethod); - } - } - - private void addUserAccess(String aaiMethod, String httpMethod) { - Set<String> httpMethods = new HashSet<>(); - if (this.aaiFunctionToHttpMethod.containsKey(aaiMethod)) { - httpMethods = this.aaiFunctionToHttpMethod.get(aaiMethod); - } - httpMethods.add(httpMethod); - this.aaiFunctionToHttpMethod.put(aaiMethod, httpMethods); - } - - public boolean hasAccess(String aaiMethod, String httpMethod) { - return this.aaiFunctionToHttpMethod.getOrDefault(aaiMethod, Collections.emptySet()).contains(httpMethod); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/auth/exceptions/AAIUnrecognizedFunctionException.java b/aai-core/src/main/java/org/onap/aai/auth/exceptions/AAIUnrecognizedFunctionException.java deleted file mode 100644 index 1410b445..00000000 --- a/aai-core/src/main/java/org/onap/aai/auth/exceptions/AAIUnrecognizedFunctionException.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Modifications Copyright © 2018 IBM. - * ================================================================================ - * 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.auth.exceptions; - -import org.onap.aai.exceptions.AAIException; - -public class AAIUnrecognizedFunctionException extends AAIException { - - private static final String AAI_3012 = "AAI_3012"; - private static final long serialVersionUID = 3297064867724071290L; - - public AAIUnrecognizedFunctionException() { - } - - public AAIUnrecognizedFunctionException(String message) { - super(AAI_3012, message); - } - - public AAIUnrecognizedFunctionException(Throwable cause) { - super(AAI_3012, cause); - } - - public AAIUnrecognizedFunctionException(String message, Throwable cause) { - super(AAI_3012, cause, message); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/concurrent/AaiCallable.java b/aai-core/src/main/java/org/onap/aai/concurrent/AaiCallable.java index 6ea67b90..69560ec1 100644 --- a/aai-core/src/main/java/org/onap/aai/concurrent/AaiCallable.java +++ b/aai-core/src/main/java/org/onap/aai/concurrent/AaiCallable.java @@ -44,7 +44,9 @@ public abstract class AaiCallable<T> implements Callable<T> { * The call method */ public T call() throws Exception { - MDC.setContextMap(mdcCopy); + if ( mdcCopy != null ) { + MDC.setContextMap(mdcCopy); + } return process(); } diff --git a/aai-core/src/main/java/org/onap/aai/config/AuthorizationConfiguration.java b/aai-core/src/main/java/org/onap/aai/config/AuthorizationConfiguration.java index 9f8bbc45..24e7ec5a 100644 --- a/aai-core/src/main/java/org/onap/aai/config/AuthorizationConfiguration.java +++ b/aai-core/src/main/java/org/onap/aai/config/AuthorizationConfiguration.java @@ -20,7 +20,7 @@ package org.onap.aai.config; -import org.onap.aai.auth.AAIAuthCore; +import org.onap.aai.aaf.auth.AAIAuthCore; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/aai-core/src/main/java/org/onap/aai/logging/CustomLogPatternLayoutEncoder.java b/aai-core/src/main/java/org/onap/aai/config/XmlFormatTransformerConfiguration.java index 010d828b..0c83c2b3 100644 --- a/aai-core/src/main/java/org/onap/aai/logging/CustomLogPatternLayoutEncoder.java +++ b/aai-core/src/main/java/org/onap/aai/config/XmlFormatTransformerConfiguration.java @@ -17,24 +17,17 @@ * limitations under the License. * ============LICENSE_END========================================================= */ +package org.onap.aai.config; -package org.onap.aai.logging; +import org.onap.aai.transforms.XmlFormatTransformer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; -import ch.qos.logback.access.PatternLayout; +@Configuration +public class XmlFormatTransformerConfiguration { -public class CustomLogPatternLayoutEncoder extends ch.qos.logback.access.PatternLayoutEncoder { - - /** - * @{inheritDoc} - */ - @Override - public void start() { - PatternLayout patternLayout = new CustomLogPatternLayout(); - patternLayout.setContext(context); - patternLayout.setPattern(getPattern()); - patternLayout.start(); - this.layout = patternLayout; - super.start(); + @Bean + public XmlFormatTransformer xmlFormatTransformer(){ + return new XmlFormatTransformer(); } - } diff --git a/aai-core/src/main/java/org/onap/aai/db/props/AAIProperties.java b/aai-core/src/main/java/org/onap/aai/db/props/AAIProperties.java index a0f6d77e..7dbc3a1d 100644 --- a/aai-core/src/main/java/org/onap/aai/db/props/AAIProperties.java +++ b/aai-core/src/main/java/org/onap/aai/db/props/AAIProperties.java @@ -32,9 +32,15 @@ public class AAIProperties { public static final String RESOURCE_VERSION = "resource-version"; public static final String AAI_URI = "aai-uri"; public static final Integer MAXIMUM_DEPTH = 10000; + public static final Integer MINIMUM_DEPTH = 0; public static final String LINKED = "linked"; public static final String DB_ALIAS_SUFFIX = "-local"; public static final String AAI_UUID = "aai-uuid"; + public static final String START_TS = "start-ts"; + public static final String END_TS = "end-ts"; + public static final String END_SOT = "end-source-of-truth"; + public static final String START_TX_ID = "start-tx-id"; + public static final String END_TX_ID = "end-tx-id"; private AAIProperties() { diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java index fdc8e814..d3dfd150 100644 --- a/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java +++ b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java @@ -20,8 +20,8 @@ package org.onap.aai.dbgen; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -77,7 +77,7 @@ public final class GraphSONPartialReader implements GraphReader { private boolean unwrapAdjacencyList = false; private final GraphSONReader reader; - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(InMemoryGraph.class); + private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryGraph.class); final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() {}; diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java index bfd1f55b..d253f876 100644 --- a/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java +++ b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java @@ -20,8 +20,8 @@ package org.onap.aai.dbgen; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.collect.Multimap; import java.util.HashMap; @@ -49,7 +49,7 @@ import org.onap.aai.util.AAIConfig; public class SchemaGenerator { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SchemaGenerator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SchemaGenerator.class); /** * Load schema into JanusGraph. diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator4Hist.java b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator4Hist.java new file mode 100644 index 00000000..0f88de24 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator4Hist.java @@ -0,0 +1,238 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.dbgen; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.common.collect.Multimap; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.Cardinality; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.Multiplicity; +import org.janusgraph.core.PropertyKey; +import org.janusgraph.core.schema.JanusGraphManagement; +import org.onap.aai.config.SpringContextAware; +import org.onap.aai.edges.EdgeIngestor; +import org.onap.aai.edges.EdgeRule; +import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderUtil; +import org.onap.aai.logging.LogFormatTools; +import org.onap.aai.schema.enums.PropertyMetadata; +import org.onap.aai.util.AAIConfig; + +import java.util.*; + +import static org.onap.aai.db.props.AAIProperties.*; + +public class SchemaGenerator4Hist { + + private static final Logger LOGGER = LoggerFactory.getLogger(SchemaGenerator4Hist.class); + + /** + * Load schema into JanusGraph. + * + * @param graph + * the graph + * @param graphMgmt + * the graph mgmt + */ + public static void loadSchemaIntoJanusGraph(final JanusGraph graph, final JanusGraphManagement graphMgmt, + String backend) { + + try { + AAIConfig.init(); + } catch (Exception ex) { + LOGGER.error(" ERROR - Could not run AAIConfig.init(). " + LogFormatTools.getStackTop(ex)); + // System.out.println(" ERROR - Could not run AAIConfig.init(). "); + System.exit(1); + } + + // NOTE - JanusGraph 0.5.3 doesn't keep a list of legal node Labels. + // They are only used when a vertex is actually being created. + // JanusGraph 1.1 will keep track (we think). + + // Use EdgeRules to make sure edgeLabels are defined in the db. NOTE: + // the multiplicty used here is + // always "MULTI". This is not the same as our internal "Many2Many", + // "One2One", "One2Many" or "Many2One" + // We use the same edge-label for edges between many different types of + // nodes and our internal + // multiplicty definitions depends on which two types of nodes are being + // connected. + + Multimap<String, EdgeRule> edges = null; + Set<String> labels = new HashSet<>(); + + EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class); + + try { + edges = edgeIngestor.getAllCurrentRules(); + } catch (EdgeRuleNotFoundException e) { + LOGGER.error("Unable to find all rules {}", LogFormatTools.getStackTop(e)); + } + + for (EdgeRule rule : edges.values()) { + labels.add(rule.getLabel()); + } + + for (String label : labels) { + if (graphMgmt.containsRelationType(label)) { + String dmsg = " EdgeLabel [" + label + "] already existed. "; + LOGGER.debug(dmsg); + } else { + String dmsg = "Making EdgeLabel: [" + label + "]"; + LOGGER.debug(dmsg); + graphMgmt.makeEdgeLabel(label).multiplicity(Multiplicity.valueOf("MULTI")).make(); + } + } + + Loader loader = LoaderUtil.getLatestVersion(); + + Map<String, Introspector> objs = loader.getAllObjects(); + Map<String, PropertyKey> seenProps = new HashMap<>(); + + for (Introspector obj : objs.values()) { + for (String propName : obj.getProperties()) { + String dbPropName = propName; + Optional<String> alias = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS); + if (alias.isPresent()) { + dbPropName = alias.get(); + } + if (graphMgmt.containsRelationType(dbPropName)) { + String dmsg = " PropertyKey [" + dbPropName + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else { + Class<?> type = obj.getClass(propName); + Cardinality cardinality = Cardinality.LIST; + boolean process = false; + if (obj.isListType(propName) && obj.isSimpleGenericType(propName)) { + // NOTE - For history - All properties have cardinality = LIST + // It is assumed that there is special processing in the Resources MS + // for History to turn what used to be SET (referred to as isListType + // above) will be stored in our db as a single String. And that + // single string will have Cardinality = LIST so we can track its + // history. + //cardinality = Cardinality.SET; + type = obj.getGenericTypeClass(propName); + process = true; + } else if (obj.isSimpleType(propName)) { + process = true; + } + + if (process) { + + String imsg = " Creating PropertyKey: [" + dbPropName + "], [" + type.getSimpleName() + "], [" + + cardinality + "]"; + LOGGER.info(imsg); + PropertyKey propK; + if (!seenProps.containsKey(dbPropName)) { + propK = graphMgmt.makePropertyKey(dbPropName).dataType(type).cardinality(cardinality) + .make(); + seenProps.put(dbPropName, propK); + } else { + propK = seenProps.get(dbPropName); + } + if (graphMgmt.containsGraphIndex(dbPropName)) { + String dmsg = " Index [" + dbPropName + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else { + if (obj.getIndexedProperties().contains(propName)) { + // NOTE - for History we never add a unique index - just a regular index + imsg = "Add index for PropertyKey: [" + dbPropName + "]"; + LOGGER.info(imsg); + graphMgmt.buildIndex(dbPropName, Vertex.class).addKey(propK).buildCompositeIndex(); + } else { + imsg = "No index needed/added for PropertyKey: [" + dbPropName + "]"; + LOGGER.info(imsg); + } + } + } + } + } + }// Done processing all properties defined in the OXM + + // Add the 3 new History properties are in the DB + // They are all Cardinality=Single since instance of a Node, Edge or Property can + // only have one of them. That is, a Property can show up many times in a + // node, but each instance of that property will only have a single start-ts, + // end-ts, end-source-of-truth. Same goes for a node or edge itself. + if (graphMgmt.containsRelationType(END_SOT)) { + String dmsg = "PropertyKey [" + END_SOT + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else if (!seenProps.containsKey(END_SOT) ) { + String imsg = " Creating PropertyKey: [" + END_SOT + "], [String], [SINGLE]"; + LOGGER.info(imsg); + graphMgmt.makePropertyKey(END_SOT).dataType(String.class) + .cardinality(Cardinality.SINGLE).make(); + } + + if (graphMgmt.containsRelationType(START_TS)) { + String dmsg = " PropertyKey [" + START_TS + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else if (!seenProps.containsKey(START_TS) ) { + String imsg = " Creating PropertyKey: [" + START_TS + "], [Long], [SINGLE]"; + LOGGER.info(imsg); + graphMgmt.makePropertyKey(START_TS).dataType(Long.class) + .cardinality(Cardinality.SINGLE).make(); + } + + if (graphMgmt.containsRelationType(END_TS)) { + String dmsg = "PropertyKey [" + END_TS + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else if (!seenProps.containsKey(END_TS) ) { + String imsg = " Creating PropertyKey: [" + END_TS + "], [Long], [SINGLE]"; + LOGGER.info(imsg); + graphMgmt.makePropertyKey(END_TS).dataType(Long.class) + .cardinality(Cardinality.SINGLE).make(); + } + + if (graphMgmt.containsRelationType(START_TX_ID)) { + String dmsg = "PropertyKey [" + START_TX_ID + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else if (!seenProps.containsKey(START_TX_ID) ) { + String imsg = " Creating PropertyKey: [" + START_TX_ID + "], [String], [SINGLE]"; + LOGGER.info(imsg); + graphMgmt.makePropertyKey(START_TX_ID).dataType(String.class) + .cardinality(Cardinality.SINGLE).make(); + } + + if (graphMgmt.containsRelationType(END_TX_ID)) { + String dmsg = "PropertyKey [" + END_TX_ID + "] already existed in the DB. "; + LOGGER.debug(dmsg); + } else if (!seenProps.containsKey(END_TX_ID) ) { + String imsg = " Creating PropertyKey: [" + END_TX_ID + "], [String], [SINGLE]"; + LOGGER.info(imsg); + graphMgmt.makePropertyKey(END_TX_ID).dataType(String.class) + .cardinality(Cardinality.SINGLE).make(); + } + + String imsg = "-- About to call graphMgmt commit"; + LOGGER.info(imsg); + graphMgmt.commit(); + if (backend != null) { + LOGGER.info("Successfully loaded the schema to " + backend); + } + + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraph.java b/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraph.java index ca4e02f0..5bb4b656 100644 --- a/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraph.java +++ b/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraph.java @@ -22,13 +22,8 @@ package org.onap.aai.dbmap; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import org.apache.commons.configuration.ConfigurationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -36,10 +31,15 @@ import org.apache.tinkerpop.gremlin.structure.io.IoCore; import org.janusgraph.core.JanusGraph; import org.janusgraph.core.JanusGraphFactory; import org.janusgraph.core.schema.JanusGraphManagement; +import org.onap.aai.config.SpringContextAware; import org.onap.aai.dbgen.SchemaGenerator; +import org.onap.aai.dbgen.SchemaGenerator4Hist; import org.onap.aai.exceptions.AAIException; import org.onap.aai.util.AAIConstants; +import java.io.FileNotFoundException; +import java.util.Properties; + /** * Database Mapping class which acts as the middle man between the REST * interface objects and JanusGraph DB objects. This class provides methods to commit @@ -48,16 +48,13 @@ import org.onap.aai.util.AAIConstants; * object to load, commit/rollback and shutdown for each request. The data model * rules such as keys/required properties are handled by calling DBMeth methods * which are driven by a specification file in json. - * - * + * + * */ public class AAIGraph { - private static final EELFLogger logger = EELFManager.getInstance().getLogger(AAIGraph.class); - protected static final String COMPONENT = "aaidbmap"; - protected Map<String, JanusGraph> graphs = new HashMap<>(); - private static final String REALTIME_DB = "realtime"; - private static final String CACHED_DB = "cached"; + private static final Logger logger = LoggerFactory.getLogger(AAIGraph.class); + protected JanusGraph graph; private static boolean isInit = false; /** @@ -67,15 +64,10 @@ public class AAIGraph { try { String serviceName = System.getProperty("aai.service.name", "NA"); String rtConfig = System.getProperty("realtime.db.config"); - String cachedConfig = System.getProperty("cached.db.config"); if (rtConfig == null) { rtConfig = AAIConstants.REALTIME_DB_CONFIG; } - if (cachedConfig == null) { - cachedConfig = AAIConstants.CACHED_DB_CONFIG; - } - this.loadGraph(REALTIME_DB, rtConfig, serviceName); - this.loadGraph(CACHED_DB, cachedConfig, serviceName); + this.loadGraph(rtConfig, serviceName); } catch (Exception e) { throw new RuntimeException("Failed to instantiate graphs", e); } @@ -103,18 +95,17 @@ public class AAIGraph { return isInit; } - private void loadGraph(final String name, final String configPath, final String serviceName) - throws AAIException, ConfigurationException { + private void loadGraph(String configPath, String serviceName) throws Exception { // Graph being opened by JanusGraphFactory is being placed in hashmap to be used later // These graphs shouldn't be closed until the application shutdown try { - final PropertiesConfiguration propertiesConfiguration = - new AAIGraphConfig.Builder(configPath).forService(serviceName).withGraphType(name).buildConfiguration(); - final JanusGraph graph = JanusGraphFactory.open(propertiesConfiguration); + PropertiesConfiguration propertiesConfiguration = new AAIGraphConfig.Builder(configPath) + .forService(serviceName).withGraphType("realtime").buildConfiguration(); + graph = JanusGraphFactory.open(propertiesConfiguration); - final Properties graphProps = new Properties(); + Properties graphProps = new Properties(); propertiesConfiguration.getKeys() - .forEachRemaining(k -> graphProps.setProperty(k, propertiesConfiguration.getString(k))); + .forEachRemaining(k -> graphProps.setProperty(k, propertiesConfiguration.getString(k))); if ("inmemory".equals(graphProps.get("storage.backend"))) { // Load the propertyKeys, indexes and edge-Labels into the DB @@ -126,13 +117,12 @@ public class AAIGraph { throw new AAIException("AAI_5102"); } - graphs.put(name, graph); - } catch (final FileNotFoundException fnfe) { - throw new AAIException("AAI_4001"); + } catch (FileNotFoundException e) { + throw new AAIException("AAI_4001", e); } } - private void loadSnapShotToInMemoryGraph(final JanusGraph graph, final Properties graphProps) { + private void loadSnapShotToInMemoryGraph(JanusGraph graph, Properties graphProps) { if (logger.isDebugEnabled()) { logger.debug("Load Snapshot to InMemory Graph"); } @@ -141,12 +131,12 @@ public class AAIGraph { if ("true".equals(value)) { try (Graph transaction = graph.newTransaction()) { String location = System.getProperty("snapshot.location"); - logAndPrint(logger, "Loading snapshot to inmemory graph."); + logAndPrint("Loading snapshot to inmemory graph."); transaction.io(IoCore.graphson()).readGraph(location); transaction.tx().commit(); - logAndPrint(logger, "Snapshot loaded to inmemory graph."); + logAndPrint("Snapshot loaded to inmemory graph."); } catch (Exception e) { - logAndPrint(logger, "ERROR: Could not load datasnapshot to in memory graph. \n" + logAndPrint("ERROR: Could not load datasnapshot to in memory graph. \n" + ExceptionUtils.getFullStackTrace(e)); throw new RuntimeException(e); } @@ -154,19 +144,25 @@ public class AAIGraph { } } - private void loadSchema(final JanusGraph graph) { + private void loadSchema(JanusGraph graph) { // Load the propertyKeys, indexes and edge-Labels into the DB JanusGraphManagement graphMgt = graph.openManagement(); System.out.println("-- loading schema into JanusGraph"); - SchemaGenerator.loadSchemaIntoJanusGraph(graph, graphMgt, "inmemory"); + if ("true".equals(SpringContextAware.getApplicationContext().getEnvironment().getProperty("history.enabled", "false"))) { + SchemaGenerator4Hist.loadSchemaIntoJanusGraph(graph, graphMgt, "inmemory"); + } else { + SchemaGenerator.loadSchemaIntoJanusGraph(graph, graphMgt, "inmemory"); + } } /** * Close all of the graph connections made in the instance. */ public void graphShutdown() { - graphs.values().stream().filter(JanusGraph::isOpen).forEach(JanusGraph::close); + if (graph != null && graph.isOpen()) { + graph.close(); + } } /** @@ -175,30 +171,10 @@ public class AAIGraph { * @return the graph */ public JanusGraph getGraph() { - return graphs.get(REALTIME_DB); - } - - public void graphShutdown(final DBConnectionType connectionType) { - - graphs.get(this.getGraphName(connectionType)).close(); - } - - public JanusGraph getGraph(final DBConnectionType connectionType) { - return graphs.get(this.getGraphName(connectionType)); - } - - private String getGraphName(final DBConnectionType connectionType) { - String graphName = ""; - if (DBConnectionType.CACHED.equals(connectionType)) { - graphName = this.CACHED_DB; - } else if (DBConnectionType.REALTIME.equals(connectionType)) { - graphName = this.REALTIME_DB; - } - - return graphName; + return graph; } - private void logAndPrint(final EELFLogger logger, final String msg) { + private void logAndPrint(String msg) { System.out.println(msg); logger.info(msg); } diff --git a/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraphConfig.java b/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraphConfig.java index d2f81610..ea939b32 100644 --- a/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraphConfig.java +++ b/aai-core/src/main/java/org/onap/aai/dbmap/AAIGraphConfig.java @@ -22,8 +22,8 @@ package org.onap.aai.dbmap; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.*; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterators; @@ -46,7 +46,7 @@ import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration; */ public class AAIGraphConfig { - private static final EELFLogger logger = EELFManager.getInstance().getLogger(AAIGraphConfig.class); + private static final Logger logger = LoggerFactory.getLogger(AAIGraphConfig.class); private AAIGraphConfig() { }; diff --git a/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java b/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java index 54986f37..d017b7e9 100644 --- a/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java +++ b/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java @@ -20,12 +20,15 @@ package org.onap.aai.dbmap; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; + +import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.tinkerpop.gremlin.structure.io.IoCore; import org.janusgraph.core.JanusGraph; import org.janusgraph.core.JanusGraphFactory; @@ -33,52 +36,56 @@ import org.janusgraph.core.JanusGraphTransaction; import org.janusgraph.core.schema.JanusGraphManagement; import org.onap.aai.dbgen.GraphSONPartialIO; import org.onap.aai.dbgen.SchemaGenerator; +import org.onap.aai.logging.LogFormatTools; public class InMemoryGraph { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(InMemoryGraph.class); + private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryGraph.class); private JanusGraph graph = null; - public InMemoryGraph(final Builder builder) throws IOException { + public InMemoryGraph(Builder builder) throws IOException { /* * Create a In-memory graph */ - try (final InputStream is = new FileInputStream(builder.propertyFile);) { + InputStream is = new FileInputStream(builder.propertyFile); + try { graph = JanusGraphFactory.open(builder.propertyFile); - final Properties graphProps = new Properties(); + Properties graphProps = new Properties(); graphProps.load(is); - final JanusGraphManagement graphMgt = graph.openManagement(); + JanusGraphManagement graphMgt = graph.openManagement(); if (builder.isSchemaEnabled) { LOGGER.info("Schema Enabled"); SchemaGenerator.loadSchemaIntoJanusGraph(graph, graphMgt, graphProps.getProperty("storage.backend")); } - try (final JanusGraphTransaction transaction = graph.newTransaction();) { - LOGGER.info("Loading snapshot"); - if (builder.isPartialGraph) { - if ((builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0)) { - transaction.io(GraphSONPartialIO.build()).readGraph(builder.graphsonLocation); - } else { - transaction.io(GraphSONPartialIO.build()).reader().create().readGraph(builder.seqInputStream, + JanusGraphTransaction transaction = graph.newTransaction(); + LOGGER.info("Loading snapshot"); + if (builder.isPartialGraph) { + if ((builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0)) { + transaction.io(GraphSONPartialIO.build()).readGraph(builder.graphsonLocation); + } else { + transaction.io(GraphSONPartialIO.build()).reader().create().readGraph(builder.seqInputStream, graph); - } + } + } else { + if ((builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0)) { + transaction.io(IoCore.graphson()).readGraph(builder.graphsonLocation); } else { - if ((builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0)) { - transaction.io(IoCore.graphson()).readGraph(builder.graphsonLocation); - } else { - transaction.io(IoCore.graphson()).reader().create().readGraph(builder.seqInputStream, graph); - } + transaction.io(IoCore.graphson()).reader().create().readGraph(builder.seqInputStream, graph); } - transaction.commit(); - } catch (final IOException e) { - LOGGER.error("ERROR: Could not load datasnapshot to in memory graph. \n", e); - throw new IllegalStateException("Could not load datasnapshot to in memory graph"); } + transaction.commit(); - } catch (final IOException e) { - LOGGER.error("ERROR: Could not load datasnapshot to in memory graph. \n", e); + } catch (Exception e) { + // TODO : Changesysout to logger + e.printStackTrace(); + LOGGER.error("ERROR: Could not load datasnapshot to in memory graph. \n" + LogFormatTools.getStackTop(e)); throw new IllegalStateException("Could not load datasnapshot to in memory graph"); + + } finally { + is.close(); } + } public static class Builder { diff --git a/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSConsumer.java b/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSConsumer.java index 44ff599f..b3a8773e 100644 --- a/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSConsumer.java +++ b/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSConsumer.java @@ -22,43 +22,45 @@ package org.onap.aai.dmaap; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.util.Objects; -import java.util.UUID; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; - -import org.apache.log4j.MDC; import org.json.JSONException; import org.json.JSONObject; -import org.onap.aai.logging.LogFormatTools; -import org.onap.aai.logging.LoggingContext; -import org.onap.aai.logging.LoggingContext.LoggingField; -import org.onap.aai.logging.LoggingContext.StatusCode; +import org.onap.aai.aailog.logs.AaiDmaapMetricLog; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.AaiElsErrorCode; +import org.onap.aai.logging.ErrorLogHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.client.RestTemplate; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; +import java.util.Map; +import java.util.Objects; + public class AAIDmaapEventJMSConsumer implements MessageListener { private static final String EVENT_TOPIC = "event-topic"; - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIDmaapEventJMSConsumer.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AAIDmaapEventJMSConsumer.class); private RestTemplate restTemplate; private HttpHeaders httpHeaders; private Environment environment; + private Map<String, String> mdcCopy; + public AAIDmaapEventJMSConsumer(Environment environment, RestTemplate restTemplate, HttpHeaders httpHeaders) { + super(); + mdcCopy = MDC.getCopyOfContextMap(); Objects.nonNull(environment); Objects.nonNull(restTemplate); Objects.nonNull(httpHeaders); @@ -76,41 +78,47 @@ public class AAIDmaapEventJMSConsumer implements MessageListener { String jsmMessageTxt = ""; String aaiEvent = ""; + JSONObject aaiEventHeader; + JSONObject joPayload; + String transactionId = ""; + String serviceName = ""; String eventName = ""; - LoggingContext.save(); - LoggingContext.init(); + String aaiElsErrorCode = AaiElsErrorCode.SUCCESS; + String errorDescription = ""; + + if ( mdcCopy != null ) { + MDC.setContextMap(mdcCopy); + } + if (message instanceof TextMessage) { + AaiDmaapMetricLog metricLog = new AaiDmaapMetricLog(); try { jsmMessageTxt = ((TextMessage) message).getText(); JSONObject jo = new JSONObject(jsmMessageTxt); - if (jo.has("aaiEventPayload")) { - aaiEvent = jo.getJSONObject("aaiEventPayload").toString(); + joPayload = jo.getJSONObject("aaiEventPayload"); + aaiEvent = joPayload.toString(); } else { return; } - if (jo.getString("transId") != null) { - LoggingContext.requestId(jo.getString("transId")); - } else { - final UUID generatedRequestUuid = UUID.randomUUID(); - LoggingContext.requestId(generatedRequestUuid.toString()); - } - if (jo.getString("fromAppId") != null) { - LoggingContext.partnerName(jo.getString("fromAppId")); - } - if (jo.getString(EVENT_TOPIC) != null) { - eventName = jo.getString(EVENT_TOPIC); + if (jo.getString("event-topic") != null) { + eventName = jo.getString("event-topic"); } - - LoggingContext.targetEntity("DMAAP"); - if (jo.getString(EVENT_TOPIC) != null) { - eventName = jo.getString(EVENT_TOPIC); - LoggingContext.targetServiceName(eventName); + if (joPayload.has("event-header")) { + try { + aaiEventHeader = joPayload.getJSONObject("event-header"); + if (aaiEventHeader.has("id")) { + transactionId = aaiEventHeader.get("id").toString(); + } + if (aaiEventHeader.has("entity-link")) { + serviceName = aaiEventHeader.get("entity-link").toString(); + } + } + catch (JSONException jexc) { + // ignore, this is just used for logging + } } - LoggingContext.serviceName("AAI"); - LoggingContext.statusCode(StatusCode.COMPLETE); - LoggingContext.responseCode(LoggingContext.SUCCESS); - LOGGER.info(eventName + "|" + aaiEvent); + metricLog.pre(eventName, aaiEvent, transactionId, serviceName); HttpEntity httpEntity = new HttpEntity(aaiEvent, httpHeaders); @@ -121,21 +129,20 @@ public class AAIDmaapEventJMSConsumer implements MessageListener { if ("AAI-EVENT".equals(eventName)) { restTemplate.exchange(baseUrl + endpoint, HttpMethod.POST, httpEntity, String.class); } else { - LoggingContext.statusCode(StatusCode.ERROR); LOGGER.error(eventName + "|Event Topic invalid."); } } catch (JMSException | JSONException e) { - LoggingContext.statusCode(StatusCode.ERROR); - LoggingContext.responseCode(LoggingContext.DATA_ERROR); - LOGGER.error("AAI_7350 Error parsing aaievent jsm message for sending to dmaap. {} {}", jsmMessageTxt, - LogFormatTools.getStackTop(e)); + aaiElsErrorCode = AaiElsErrorCode.DATA_ERROR; + errorDescription = e.getMessage(); + ErrorLogHelper.logException(new AAIException("AAI_7350")); } catch (Exception e) { - LoggingContext.statusCode(StatusCode.ERROR); - LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR); - LOGGER.error("AAI_7350 Error sending message to dmaap. {} {}", jsmMessageTxt, - LogFormatTools.getStackTop(e)); + aaiElsErrorCode = AaiElsErrorCode.AVAILABILITY_TIMEOUT_ERROR; + errorDescription = e.getMessage(); + ErrorLogHelper.logException(new AAIException("AAI_7304", jsmMessageTxt)); + } + finally { + metricLog.post(aaiElsErrorCode, errorDescription); } } - } } diff --git a/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSProducer.java b/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSProducer.java index 4036b907..eb0d1658 100644 --- a/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSProducer.java +++ b/aai-core/src/main/java/org/onap/aai/dmaap/AAIDmaapEventJMSProducer.java @@ -50,4 +50,12 @@ public class AAIDmaapEventJMSProducer implements MessageProducer { ccf.destroy(); } } + + public void sendMessageToDefaultDestination(String msg) { + if (jmsTemplate != null) { + jmsTemplate.convertAndSend(msg); + CachingConnectionFactory ccf = (CachingConnectionFactory) this.jmsTemplate.getConnectionFactory(); + ccf.destroy(); + } + } } diff --git a/aai-core/src/main/java/org/onap/aai/dmaap/MessageProducer.java b/aai-core/src/main/java/org/onap/aai/dmaap/MessageProducer.java index 9b17d881..1df64b69 100644 --- a/aai-core/src/main/java/org/onap/aai/dmaap/MessageProducer.java +++ b/aai-core/src/main/java/org/onap/aai/dmaap/MessageProducer.java @@ -25,4 +25,5 @@ import org.json.JSONObject; public interface MessageProducer { void sendMessageToDefaultDestination(JSONObject finalJson); + void sendMessageToDefaultDestination(String msg); } diff --git a/aai-core/src/main/java/org/onap/aai/domain/model/AAIResources.java b/aai-core/src/main/java/org/onap/aai/domain/model/AAIResources.java index 4e5f0392..413faf62 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/model/AAIResources.java +++ b/aai-core/src/main/java/org/onap/aai/domain/model/AAIResources.java @@ -20,10 +20,10 @@ package org.onap.aai.domain.model; -import java.util.HashMap; - import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import java.util.HashMap; + public class AAIResources { private DynamicJAXBContext jaxbContext; diff --git a/aai-core/src/main/java/org/onap/aai/domain/notificationEvent/NotificationEvent.java b/aai-core/src/main/java/org/onap/aai/domain/notificationEvent/NotificationEvent.java index d8b9b997..1e6307cc 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/notificationEvent/NotificationEvent.java +++ b/aai-core/src/main/java/org/onap/aai/domain/notificationEvent/NotificationEvent.java @@ -18,30 +18,25 @@ * ============LICENSE_END========================================================= */ // -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2016.01.06 at 05:38:00 PM EST +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2016.01.06 at 05:38:00 PM EST // package org.onap.aai.domain.notificationEvent; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - import org.w3c.dom.Element; +import javax.xml.bind.annotation.*; + /** * <p> * Java class for anonymous complex type. - * + * * <p> * The following schema fragment specifies the expected content contained within this class. - * + * * <pre> * <complexType> * <complexContent> @@ -77,8 +72,8 @@ import org.w3c.dom.Element; * </complexContent> * </complexType> * </pre> - * - * + * + * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"cambriaPartition", "eventHeader", "entity"}) @@ -94,11 +89,11 @@ public class NotificationEvent { /** * Gets the value of the eventHeader property. - * + * * @return * possible object is * {@link EventHeader } - * + * */ public EventHeader getEventHeader() { return eventHeader; @@ -106,11 +101,11 @@ public class NotificationEvent { /** * Sets the value of the eventHeader property. - * + * * @param value * allowed object is * {@link EventHeader } - * + * */ public void setEventHeader(EventHeader value) { this.eventHeader = value; @@ -118,12 +113,12 @@ public class NotificationEvent { /** * Gets the value of the any property. - * + * * @return * possible object is * {@link Object } * {@link Element } - * + * */ public Object getEntity() { return entity; @@ -131,12 +126,12 @@ public class NotificationEvent { /** * Sets the value of the any property. - * + * * @param value * allowed object is * {@link Object } * {@link Element } - * + * */ public void setEntity(Object value) { this.entity = value; @@ -144,11 +139,11 @@ public class NotificationEvent { /** * Gets the value of the cambriaPartition property. - * + * * @return * possible object is * {@link String } - * + * */ public String getCambriaPartition() { return cambriaPartition; @@ -156,11 +151,11 @@ public class NotificationEvent { /** * Sets the value of the cambriaPartition property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setCambriaPartition(String value) { this.cambriaPartition = value; @@ -169,10 +164,10 @@ public class NotificationEvent { /** * <p> * Java class for anonymous complex type. - * + * * <p> * The following schema fragment specifies the expected content contained within this class. - * + * * <pre> * <complexType> * <complexContent> @@ -196,8 +191,8 @@ public class NotificationEvent { * </complexContent> * </complexType> * </pre> - * - * + * + * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType( @@ -235,11 +230,11 @@ public class NotificationEvent { /** * Gets the value of the id property. - * + * * @return * possible object is * {@link String } - * + * */ public String getId() { return id; @@ -247,11 +242,11 @@ public class NotificationEvent { /** * Sets the value of the id property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setId(String value) { this.id = value; @@ -259,11 +254,11 @@ public class NotificationEvent { /** * Gets the value of the timestamp property. - * + * * @return * possible object is * {@link String } - * + * */ public String getTimestamp() { return timestamp; @@ -271,11 +266,11 @@ public class NotificationEvent { /** * Sets the value of the timestamp property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setTimestamp(String value) { this.timestamp = value; @@ -283,11 +278,11 @@ public class NotificationEvent { /** * Gets the value of the sourceName property. - * + * * @return * possible object is * {@link String } - * + * */ public String getSourceName() { return sourceName; @@ -295,11 +290,11 @@ public class NotificationEvent { /** * Sets the value of the sourceName property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setSourceName(String value) { this.sourceName = value; @@ -307,11 +302,11 @@ public class NotificationEvent { /** * Gets the value of the domain property. - * + * * @return * possible object is * {@link String } - * + * */ public String getDomain() { return domain; @@ -319,11 +314,11 @@ public class NotificationEvent { /** * Sets the value of the domain property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setDomain(String value) { this.domain = value; @@ -331,11 +326,11 @@ public class NotificationEvent { /** * Gets the value of the sequenceNumber property. - * + * * @return * possible object is * {@link String } - * + * */ public String getSequenceNumber() { return sequenceNumber; @@ -343,11 +338,11 @@ public class NotificationEvent { /** * Sets the value of the sequenceNumber property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setSequenceNumber(String value) { this.sequenceNumber = value; @@ -355,11 +350,11 @@ public class NotificationEvent { /** * Gets the value of the severity property. - * + * * @return * possible object is * {@link String } - * + * */ public String getSeverity() { return severity; @@ -367,11 +362,11 @@ public class NotificationEvent { /** * Sets the value of the severity property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setSeverity(String value) { this.severity = value; @@ -379,11 +374,11 @@ public class NotificationEvent { /** * Gets the value of the eventType property. - * + * * @return * possible object is * {@link String } - * + * */ public String getEventType() { return eventType; @@ -391,11 +386,11 @@ public class NotificationEvent { /** * Sets the value of the eventType property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setEventType(String value) { this.eventType = value; @@ -403,11 +398,11 @@ public class NotificationEvent { /** * Gets the value of the version property. - * + * * @return * possible object is * {@link String } - * + * */ public String getVersion() { return version; @@ -415,11 +410,11 @@ public class NotificationEvent { /** * Sets the value of the version property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setVersion(String value) { this.version = value; @@ -427,11 +422,11 @@ public class NotificationEvent { /** * Gets the value of the action property. - * + * * @return * possible object is * {@link String } - * + * */ public String getAction() { return action; @@ -439,11 +434,11 @@ public class NotificationEvent { /** * Sets the value of the action property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setAction(String value) { this.action = value; @@ -451,11 +446,11 @@ public class NotificationEvent { /** * Gets the value of the entityType property. - * + * * @return * possible object is * {@link String } - * + * */ public String getEntityType() { return entityType; @@ -463,11 +458,11 @@ public class NotificationEvent { /** * Sets the value of the entityType property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setEntityType(String value) { this.entityType = value; @@ -475,11 +470,11 @@ public class NotificationEvent { /** * Gets the value of the topEntityType property. - * + * * @return * possible object is * {@link String } - * + * */ public String getTopEntityType() { return topEntityType; @@ -487,11 +482,11 @@ public class NotificationEvent { /** * Sets the value of the topEntityType property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setTopEntityType(String value) { this.topEntityType = value; @@ -499,11 +494,11 @@ public class NotificationEvent { /** * Gets the value of the entityLink property. - * + * * @return * possible object is * {@link String } - * + * */ public String getEntityLink() { return entityLink; @@ -511,11 +506,11 @@ public class NotificationEvent { /** * Sets the value of the entityLink property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setEntityLink(String value) { this.entityLink = value; @@ -523,11 +518,11 @@ public class NotificationEvent { /** * Gets the value of the status property. - * + * * @return * possible object is * {@link String } - * + * */ public String getStatus() { return status; @@ -535,11 +530,11 @@ public class NotificationEvent { /** * Sets the value of the status property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setStatus(String value) { this.status = value; diff --git a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessage.java b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessage.java index 6cd82eef..53311990 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessage.java +++ b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessage.java @@ -20,11 +20,7 @@ package org.onap.aai.domain.responseMessage; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) @XmlType( diff --git a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageData.java b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageData.java index 76b73642..c48f1ed8 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageData.java +++ b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageData.java @@ -23,22 +23,16 @@ package org.onap.aai.domain.responseMessage; // -//This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -//See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -//Any modifications to this file will be lost upon recompilation of the source schema. -//Generated on: 2015.09.11 at 11:53:27 AM EDT +//This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +//See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> +//Any modifications to this file will be lost upon recompilation of the source schema. +//Generated on: 2015.09.11 at 11:53:27 AM EDT // +import javax.xml.bind.annotation.*; import java.util.ArrayList; import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"aaiResponseMessageDatum", "any"}) @XmlRootElement(name = "aai-response-message-data", namespace = "http://org.onap.aai.inventory") diff --git a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageDatum.java b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageDatum.java index 90c6e280..a8adffde 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageDatum.java +++ b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessageDatum.java @@ -20,11 +20,7 @@ package org.onap.aai.domain.responseMessage; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"aaiResponseMessageDatumKey", "aaiResponseMessageDatumValue", diff --git a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessages.java b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessages.java index 09bf30e3..8263e58d 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessages.java +++ b/aai-core/src/main/java/org/onap/aai/domain/responseMessage/AAIResponseMessages.java @@ -21,29 +21,23 @@ package org.onap.aai.domain.responseMessage; // -//This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -//See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -//Any modifications to this file will be lost upon recompilation of the source schema. -//Generated on: 2015.09.11 at 11:53:27 AM EDT +//This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +//See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> +//Any modifications to this file will be lost upon recompilation of the source schema. +//Generated on: 2015.09.11 at 11:53:27 AM EDT // +import javax.xml.bind.annotation.*; import java.util.ArrayList; import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - /** * <p> * Java class for anonymous complex type. - * + * * <p> * The following schema fragment specifies the expected content contained within this class. - * + * * <pre> * <complexType> * <complexContent> @@ -77,8 +71,8 @@ import javax.xml.bind.annotation.XmlType; * </complexContent> * </complexType> * </pre> - * - * + * + * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"aaiResponseMessage", "any"}) diff --git a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/Fault.java b/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/Fault.java deleted file mode 100644 index 55e45b64..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/Fault.java +++ /dev/null @@ -1,376 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.02.11 at 04:54:39 PM EST -// - -package org.onap.aai.domain.restPolicyException; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -/** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="requestError"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="policyException"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = {"requestError"}) -@XmlRootElement(name = "Fault") -public class Fault { - - @XmlElement(required = true) - protected RequestError requestError; - - /** - * Gets the value of the requestError property. - * - * @return - * possible object is - * {@link RequestError } - * - */ - public RequestError getRequestError() { - return requestError; - } - - /** - * Sets the value of the requestError property. - * - * @param value - * allowed object is - * {@link RequestError } - * - */ - public void setRequestError(RequestError value) { - this.requestError = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="policyException"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"policyException"}) - public static class RequestError { - - @XmlElement(required = true) - protected PolicyException policyException; - - /** - * Gets the value of the policyException property. - * - * @return - * possible object is - * {@link PolicyException } - * - */ - public PolicyException getPolicyException() { - return policyException; - } - - /** - * Sets the value of the policyException property. - * - * @param value - * allowed object is - * {@link PolicyException } - * - */ - public void setPolicyException(PolicyException value) { - this.policyException = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"messageId", "text", "variables"}) - public static class PolicyException { - - @XmlElement(required = true) - protected String messageId; - @XmlElement(required = true) - protected String text; - @XmlElement(required = true) - protected Variables variables; - - /** - * Gets the value of the messageId property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getMessageId() { - return messageId; - } - - /** - * Sets the value of the messageId property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setMessageId(String value) { - this.messageId = value; - } - - /** - * Gets the value of the text property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getText() { - return text; - } - - /** - * Sets the value of the text property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setText(String value) { - this.text = value; - } - - /** - * Gets the value of the variables property. - * - * @return - * possible object is - * {@link Variables } - * - */ - public Variables getVariables() { - return variables; - } - - /** - * Sets the value of the variables property. - * - * @param value - * allowed object is - * {@link Variables } - * - */ - public void setVariables(Variables value) { - this.variables = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"variable"}) - public static class Variables { - - protected List<String> variable; - - /** - * Gets the value of the variable property. - * - * <p> - * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a <CODE>set</CODE> method for the variable property. - * - * <p> - * For example, to add a new item, do as follows: - * - * <pre> - * getVariable().add(newItem); - * </pre> - * - * - * <p> - * Objects of the following type(s) are allowed in the list - * {@link String } - * - * @return the variable - */ - public List<String> getVariable() { - if (variable == null) { - variable = new ArrayList<String>(); - } - return this.variable; - } - - } - - } - - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/ObjectFactory.java b/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/ObjectFactory.java deleted file mode 100644 index f85fe748..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/ObjectFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.02.11 at 04:54:39 PM EST -// - -package org.onap.aai.domain.restPolicyException; - -import javax.xml.bind.annotation.XmlRegistry; - -/** - * This object contains factory methods for each - * Java content interface and Java element interface - * generated in the org.onap.aai.domain.restPolicyException package. - * <p> - * An ObjectFactory allows you to programatically - * construct new instances of the Java representation - * for XML content. The Java representation of XML - * content can consist of schema derived interfaces - * and classes representing the binding of schema - * type definitions, element declarations and model - * groups. Factory methods for each of these are - * provided in this class. - * - */ -@XmlRegistry -public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.onap.aai.domain.restPolicyException - * - */ - public ObjectFactory() { - } - - /** - * Create an instance of {@link Fault }. - * - * @return the fault - */ - public Fault createFault() { - return new Fault(); - } - - /** - * Create an instance of {@link Fault.RequestError } - * - * @return the request error - */ - public Fault.RequestError createFaultRequestError() { - return new Fault.RequestError(); - } - - /** - * Create an instance of {@link Fault.RequestError.PolicyException } - * - * @return the policy exception - */ - public Fault.RequestError.PolicyException createFaultRequestErrorPolicyException() { - return new Fault.RequestError.PolicyException(); - } - - /** - * Create an instance of {@link Fault.RequestError.PolicyException.Variables } - * - * @return the variables - */ - public Fault.RequestError.PolicyException.Variables createFaultRequestErrorPolicyExceptionVariables() { - return new Fault.RequestError.PolicyException.Variables(); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/PolicyException.java b/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/PolicyException.java deleted file mode 100644 index 1aa2673a..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/PolicyException.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restPolicyException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"messageId", "text", "variables"}) -public class PolicyException { - - @JsonProperty("messageId") - private String messageId; - @JsonProperty("text") - private String text; - @JsonProperty("variables") - private List<String> variables = new ArrayList<String>(); - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the message id. - * - * @return The messageId - */ - @JsonProperty("messageId") - public String getMessageId() { - return messageId; - } - - /** - * Sets the message id. - * - * @param messageId The messageId - */ - @JsonProperty("messageId") - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - /** - * Gets the text. - * - * @return The text - */ - @JsonProperty("text") - public String getText() { - return text; - } - - /** - * Sets the text. - * - * @param text The text - */ - @JsonProperty("text") - public void setText(String text) { - this.text = text; - } - - /** - * Gets the variables. - * - * @return The variables - */ - @JsonProperty("variables") - public List<String> getVariables() { - return variables; - } - - /** - * Sets the variables. - * - * @param variables The variables - */ - @JsonProperty("variables") - public void setVariables(List<String> variables) { - this.variables = variables; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RESTResponse.java b/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RESTResponse.java deleted file mode 100644 index 262ca855..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RESTResponse.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restPolicyException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"requestError"}) -public class RESTResponse { - - @JsonProperty("requestError") - private RequestError requestError; - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the request error. - * - * @return The requestError - */ - @JsonProperty("requestError") - public RequestError getRequestError() { - return requestError; - } - - /** - * Sets the request error. - * - * @param requestError The requestError - */ - @JsonProperty("requestError") - public void setRequestError(RequestError requestError) { - this.requestError = requestError; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RequestError.java b/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RequestError.java deleted file mode 100644 index 54580de7..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restPolicyException/RequestError.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restPolicyException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"policyException"}) -public class RequestError { - - @JsonProperty("policyException") - private PolicyException policyException; - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the policy exception. - * - * @return The policyException - */ - @JsonProperty("policyException") - public PolicyException getPolicyException() { - return policyException; - } - - /** - * Sets the policy exception. - * - * @param policyException The policyException - */ - @JsonProperty("policyException") - public void setPolicyException(PolicyException policyException) { - this.policyException = policyException; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/Info.java b/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/Info.java deleted file mode 100644 index 789fe926..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/Info.java +++ /dev/null @@ -1,380 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.10.28 at 05:53:17 PM EDT -// - -package org.onap.aai.domain.restResponseInfo; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -/** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="responseMessages" minOccurs="0"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="responseMessage" maxOccurs="unbounded" minOccurs="0"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = {"responseMessages"}) -@XmlRootElement(name = "Info") -public class Info { - - protected ResponseMessages responseMessages; - - /** - * Gets the value of the responseMessages property. - * - * @return - * possible object is - * {@link ResponseMessages } - * - */ - public ResponseMessages getResponseMessages() { - return responseMessages; - } - - /** - * Sets the value of the responseMessages property. - * - * @param value - * allowed object is - * {@link ResponseMessages } - * - */ - public void setResponseMessages(ResponseMessages value) { - this.responseMessages = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="responseMessage" maxOccurs="unbounded" minOccurs="0"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"responseMessage"}) - public static class ResponseMessages { - - protected List<ResponseMessage> responseMessage; - - /** - * Gets the value of the responseMessage property. - * - * <p> - * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a <CODE>set</CODE> method for the responseMessage property. - * - * <p> - * For example, to add a new item, do as follows: - * - * <pre> - * getResponseMessage().add(newItem); - * </pre> - * - * - * <p> - * Objects of the following type(s) are allowed in the list - * {@link ResponseMessage } - * - * @return the response message - */ - public List<ResponseMessage> getResponseMessage() { - if (responseMessage == null) { - responseMessage = new ArrayList<ResponseMessage>(); - } - return this.responseMessage; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"messageId", "text", "variables"}) - public static class ResponseMessage { - - @XmlElement(required = true) - protected String messageId; - @XmlElement(required = true) - protected String text; - @XmlElement(required = true) - protected Variables variables; - - /** - * Gets the value of the messageId property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getMessageId() { - return messageId; - } - - /** - * Sets the value of the messageId property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setMessageId(String value) { - this.messageId = value; - } - - /** - * Gets the value of the text property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getText() { - return text; - } - - /** - * Sets the value of the text property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setText(String value) { - this.text = value; - } - - /** - * Gets the value of the variables property. - * - * @return - * possible object is - * {@link Variables } - * - */ - public Variables getVariables() { - return variables; - } - - /** - * Sets the value of the variables property. - * - * @param value - * allowed object is - * {@link Variables } - * - */ - public void setVariables(Variables value) { - this.variables = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"variable"}) - public static class Variables { - - protected List<String> variable; - - /** - * Gets the value of the variable property. - * - * <p> - * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a <CODE>set</CODE> method for the variable property. - * - * <p> - * For example, to add a new item, do as follows: - * - * <pre> - * getVariable().add(newItem); - * </pre> - * - * - * <p> - * Objects of the following type(s) are allowed in the list - * {@link String } - * - * @return the variable - */ - public List<String> getVariable() { - if (variable == null) { - variable = new ArrayList<String>(); - } - return this.variable; - } - - } - - } - - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/ObjectFactory.java b/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/ObjectFactory.java deleted file mode 100644 index 4efafb23..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restResponseInfo/ObjectFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.10.28 at 05:53:17 PM EDT -// - -package org.onap.aai.domain.restResponseInfo; - -import javax.xml.bind.annotation.XmlRegistry; - -/** - * This object contains factory methods for each - * Java content interface and Java element interface - * generated in the org.onap.aai.domain.restResponseInfo package. - * <p> - * An ObjectFactory allows you to programatically - * construct new instances of the Java representation - * for XML content. The Java representation of XML - * content can consist of schema derived interfaces - * and classes representing the binding of schema - * type definitions, element declarations and model - * groups. Factory methods for each of these are - * provided in this class. - * - */ -@XmlRegistry -public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.onap.aai.domain.restResponseInfo - * - */ - public ObjectFactory() { - } - - /** - * Create an instance of {@link Info }. - * - * @return the info - */ - public Info createInfo() { - return new Info(); - } - - /** - * Create an instance of {@link Info.ResponseMessages } - * - * @return the response messages - */ - public Info.ResponseMessages createInfoResponseMessages() { - return new Info.ResponseMessages(); - } - - /** - * Create an instance of {@link Info.ResponseMessages.ResponseMessage } - * - * @return the response message - */ - public Info.ResponseMessages.ResponseMessage createInfoResponseMessagesResponseMessage() { - return new Info.ResponseMessages.ResponseMessage(); - } - - /** - * Create an instance of {@link Info.ResponseMessages.ResponseMessage.Variables } - * - * @return the variables - */ - public Info.ResponseMessages.ResponseMessage.Variables createInfoResponseMessagesResponseMessageVariables() { - return new Info.ResponseMessages.ResponseMessage.Variables(); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/Fault.java b/aai-core/src/main/java/org/onap/aai/domain/restServiceException/Fault.java deleted file mode 100644 index 3efa13c3..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/Fault.java +++ /dev/null @@ -1,376 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.02.11 at 04:54:29 PM EST -// - -package org.onap.aai.domain.restServiceException; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -/** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="requestError"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="serviceException"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = {"requestError"}) -@XmlRootElement(name = "Fault") -public class Fault { - - @XmlElement(required = true) - protected RequestError requestError; - - /** - * Gets the value of the requestError property. - * - * @return - * possible object is - * {@link RequestError } - * - */ - public RequestError getRequestError() { - return requestError; - } - - /** - * Sets the value of the requestError property. - * - * @param value - * allowed object is - * {@link RequestError } - * - */ - public void setRequestError(RequestError value) { - this.requestError = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="serviceException"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"serviceException"}) - public static class RequestError { - - @XmlElement(required = true) - protected ServiceException serviceException; - - /** - * Gets the value of the serviceException property. - * - * @return - * possible object is - * {@link ServiceException } - * - */ - public ServiceException getServiceException() { - return serviceException; - } - - /** - * Sets the value of the serviceException property. - * - * @param value - * allowed object is - * {@link ServiceException } - * - */ - public void setServiceException(ServiceException value) { - this.serviceException = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="messageId" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/> - * <element name="variables"> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </element> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"messageId", "text", "variables"}) - public static class ServiceException { - - @XmlElement(required = true) - protected String messageId; - @XmlElement(required = true) - protected String text; - @XmlElement(required = true) - protected Variables variables; - - /** - * Gets the value of the messageId property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getMessageId() { - return messageId; - } - - /** - * Sets the value of the messageId property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setMessageId(String value) { - this.messageId = value; - } - - /** - * Gets the value of the text property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getText() { - return text; - } - - /** - * Sets the value of the text property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setText(String value) { - this.text = value; - } - - /** - * Gets the value of the variables property. - * - * @return - * possible object is - * {@link Variables } - * - */ - public Variables getVariables() { - return variables; - } - - /** - * Sets the value of the variables property. - * - * @param value - * allowed object is - * {@link Variables } - * - */ - public void setVariables(Variables value) { - this.variables = value; - } - - /** - * <p> - * Java class for anonymous complex type. - * - * <p> - * The following schema fragment specifies the expected content contained within this class. - * - * <pre> - * <complexType> - * <complexContent> - * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <sequence> - * <element name="variable" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/> - * </sequence> - * </restriction> - * </complexContent> - * </complexType> - * </pre> - * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = {"variable"}) - public static class Variables { - - protected List<String> variable; - - /** - * Gets the value of the variable property. - * - * <p> - * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a <CODE>set</CODE> method for the variable property. - * - * <p> - * For example, to add a new item, do as follows: - * - * <pre> - * getVariable().add(newItem); - * </pre> - * - * - * <p> - * Objects of the following type(s) are allowed in the list - * {@link String } - * - * @return the variable - */ - public List<String> getVariable() { - if (variable == null) { - variable = new ArrayList<String>(); - } - return this.variable; - } - - } - - } - - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ObjectFactory.java b/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ObjectFactory.java deleted file mode 100644 index 95eb2cba..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ObjectFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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========================================================= - */ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.02.11 at 04:54:29 PM EST -// - -package org.onap.aai.domain.restServiceException; - -import javax.xml.bind.annotation.XmlRegistry; - -/** - * This object contains factory methods for each - * Java content interface and Java element interface - * generated in the org.onap.aai.domain.restServiceException package. - * <p> - * An ObjectFactory allows you to programatically - * construct new instances of the Java representation - * for XML content. The Java representation of XML - * content can consist of schema derived interfaces - * and classes representing the binding of schema - * type definitions, element declarations and model - * groups. Factory methods for each of these are - * provided in this class. - * - */ -@XmlRegistry -public class ObjectFactory { - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: - * org.onap.aai.domain.restServiceException - * - */ - public ObjectFactory() { - } - - /** - * Create an instance of {@link Fault }. - * - * @return the fault - */ - public Fault createFault() { - return new Fault(); - } - - /** - * Create an instance of {@link Fault.RequestError } - * - * @return the request error - */ - public Fault.RequestError createFaultRequestError() { - return new Fault.RequestError(); - } - - /** - * Create an instance of {@link Fault.RequestError.ServiceException } - * - * @return the service exception - */ - public Fault.RequestError.ServiceException createFaultRequestErrorServiceException() { - return new Fault.RequestError.ServiceException(); - } - - /** - * Create an instance of {@link Fault.RequestError.ServiceException.Variables } - * - * @return the variables - */ - public Fault.RequestError.ServiceException.Variables createFaultRequestErrorServiceExceptionVariables() { - return new Fault.RequestError.ServiceException.Variables(); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RESTResponse.java b/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RESTResponse.java deleted file mode 100644 index 09178937..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RESTResponse.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restServiceException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"requestError"}) -public class RESTResponse { - - @JsonProperty("requestError") - private RequestError requestError; - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the request error. - * - * @return The requestError - */ - @JsonProperty("requestError") - public RequestError getRequestError() { - return requestError; - } - - /** - * Sets the request error. - * - * @param requestError The requestError - */ - @JsonProperty("requestError") - public void setRequestError(RequestError requestError) { - this.requestError = requestError; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RequestError.java b/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RequestError.java deleted file mode 100644 index 38320218..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/RequestError.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restServiceException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"serviceException"}) -public class RequestError { - - @JsonProperty("serviceException") - private ServiceException serviceException; - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the service exception. - * - * @return The serviceException - */ - @JsonProperty("serviceException") - public ServiceException getServiceException() { - return serviceException; - } - - /** - * Sets the service exception. - * - * @param serviceException The serviceException - */ - @JsonProperty("serviceException") - public void setServiceException(ServiceException serviceException) { - this.serviceException = serviceException; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ServiceException.java b/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ServiceException.java deleted file mode 100644 index ec658ce3..00000000 --- a/aai-core/src/main/java/org/onap/aai/domain/restServiceException/ServiceException.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.domain.restServiceException; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Generated; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("org.jsonschema2pojo") -@JsonPropertyOrder({"messageId", "text", "variables"}) -public class ServiceException { - - @JsonProperty("messageId") - private String messageId; - @JsonProperty("text") - private String text; - @JsonProperty("variables") - private List<String> variables = new ArrayList<String>(); - @JsonIgnore - private Map<String, Object> additionalProperties = new HashMap<String, Object>(); - - /** - * Gets the message id. - * - * @return The messageId - */ - @JsonProperty("messageId") - public String getMessageId() { - return messageId; - } - - /** - * Sets the message id. - * - * @param messageId The messageId - */ - @JsonProperty("messageId") - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - /** - * Gets the text. - * - * @return The text - */ - @JsonProperty("text") - public String getText() { - return text; - } - - /** - * Sets the text. - * - * @param text The text - */ - @JsonProperty("text") - public void setText(String text) { - this.text = text; - } - - /** - * Gets the variables. - * - * @return The variables - */ - @JsonProperty("variables") - public List<String> getVariables() { - return variables; - } - - /** - * Sets the variables. - * - * @param variables The variables - */ - @JsonProperty("variables") - public void setVariables(List<String> variables) { - this.variables = variables; - } - - /** - * Gets the additional properties. - * - * @return the additional properties - */ - @JsonAnyGetter - public Map<String, Object> getAdditionalProperties() { - return this.additionalProperties; - } - - /** - * Sets the additional property. - * - * @param name the name - * @param value the value - */ - @JsonAnySetter - public void setAdditionalProperty(String name, Object value) { - this.additionalProperties.put(name, value); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntries.java b/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntries.java index 7f02cb53..e81b3e3c 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntries.java +++ b/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntries.java @@ -18,29 +18,28 @@ * ============LICENSE_END========================================================= */ // -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 -// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2015.03.20 at 09:46:47 AM CDT +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2015.03.20 at 09:46:47 AM CDT // package org.onap.aai.domain.translog; -import java.util.ArrayList; -import java.util.List; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; /** * <p> * Java class for anonymous complex type. - * + * * <p> * The following schema fragment specifies the expected content contained within this class. - * + * * <pre> * <complexType> * <complexContent> @@ -102,8 +101,8 @@ import javax.xml.bind.annotation.XmlType; * </complexContent> * </complexType> * </pre> - * - * + * + * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"transactionLogEntries"}) diff --git a/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntry.java b/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntry.java index 2ce485be..16c773ae 100644 --- a/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntry.java +++ b/aai-core/src/main/java/org/onap/aai/domain/translog/TransactionLogEntry.java @@ -20,14 +20,10 @@ package org.onap.aai.domain.translog; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - import org.eclipse.persistence.oxm.annotations.XmlCDATA; +import javax.xml.bind.annotation.*; + @XmlAccessorType(XmlAccessType.FIELD) @XmlType( name = "", @@ -70,11 +66,11 @@ public class TransactionLogEntry { /** * Gets the value of the transcationLogEntryId property. - * + * * @return * possible object is * {@link String } - * + * */ public String getTransactionLogEntryId() { return transactionLogEntryId; @@ -82,11 +78,11 @@ public class TransactionLogEntry { /** * Sets the value of the transactionLogEntryId property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setTransactionLogEntryId(String value) { this.transactionLogEntryId = value; @@ -94,11 +90,11 @@ public class TransactionLogEntry { /** * Gets the value of the status property. - * + * * @return * possible object is * {@link String } - * + * */ public String getStatus() { return status; @@ -106,11 +102,11 @@ public class TransactionLogEntry { /** * Sets the value of the status property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setStatus(String value) { this.status = value; @@ -118,11 +114,11 @@ public class TransactionLogEntry { /** * Gets the value of the rqstDate property. - * + * * @return * possible object is * {@link String } - * + * */ public String getRqstDate() { @@ -131,11 +127,11 @@ public class TransactionLogEntry { /** * Sets the value of the rqstDate property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setRqstDate(String value) { this.rqstDate = value; @@ -143,11 +139,11 @@ public class TransactionLogEntry { /** * Gets the value of the respDate property. - * + * * @return * possible object is * {@link String } - * + * */ public String getRespDate() { @@ -156,11 +152,11 @@ public class TransactionLogEntry { /** * Sets the value of the respDate property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setRespDate(String value) { this.respDate = value; @@ -168,11 +164,11 @@ public class TransactionLogEntry { /** * Gets the value of the sourceId property. - * + * * @return * possible object is * {@link String } - * + * */ public String getSourceId() { return sourceId; @@ -180,11 +176,11 @@ public class TransactionLogEntry { /** * Sets the value of the sourceId property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setSourceId(String value) { this.sourceId = value; @@ -192,11 +188,11 @@ public class TransactionLogEntry { /** * Gets the value of the resourceId property. - * + * * @return * possible object is * {@link String } - * + * */ public String getResourceId() { return resourceId; @@ -204,11 +200,11 @@ public class TransactionLogEntry { /** * Sets the value of the resourceId property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setResourceId(String value) { this.resourceId = value; @@ -216,11 +212,11 @@ public class TransactionLogEntry { /** * Gets the value of the resourceType property. - * + * * @return * possible object is * {@link String } - * + * */ public String getResourceType() { return resourceType; @@ -228,11 +224,11 @@ public class TransactionLogEntry { /** * Sets the value of the resourceType property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setResourceType(String value) { this.resourceType = value; @@ -240,11 +236,11 @@ public class TransactionLogEntry { /** * Gets the value of the rqstBuf property. - * + * * @return * possible object is * {@link String } - * + * */ public String getRqstBuf() { return rqstBuf; @@ -252,11 +248,11 @@ public class TransactionLogEntry { /** * Sets the value of the rqstBuf property. - * + * * @param value * allowed object is * {@link String } - * + * */ @XmlCDATA public void setRqstBuf(String value) { @@ -265,11 +261,11 @@ public class TransactionLogEntry { /** * Gets the value of the respBuf property. - * + * * @return * possible object is * {@link String } - * + * */ public String getrespBuf() { return respBuf; @@ -277,11 +273,11 @@ public class TransactionLogEntry { /** * Sets the value of the respBuf property. - * + * * @param value * allowed object is * {@link String } - * + * */ @XmlCDATA public void setrespBuf(String value) { @@ -290,11 +286,11 @@ public class TransactionLogEntry { /** * Gets the value of the notificationPayload property. - * + * * @return * possible object is * {@link String } - * + * */ public String getNotificationPayload() { return notificationPayload; @@ -302,11 +298,11 @@ public class TransactionLogEntry { /** * Sets the value of the notificationPayload property. - * + * * @param value * allowed object is * {@link String } - * + * */ @XmlCDATA public void setNotificationPayload(String value) { @@ -315,11 +311,11 @@ public class TransactionLogEntry { /** * Gets the value of the notificationId property. - * + * * @return * possible object is * {@link String } - * + * */ public String getNotificationId() { return notificationId; @@ -327,11 +323,11 @@ public class TransactionLogEntry { /** * Sets the value of the notificationId property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setNotificationId(String value) { this.notificationId = value; @@ -339,11 +335,11 @@ public class TransactionLogEntry { /** * Gets the value of the notificationId property. - * + * * @return * possible object is * {@link String } - * + * */ public String getNotificationStatus() { return notificationStatus; @@ -351,11 +347,11 @@ public class TransactionLogEntry { /** * Sets the value of the notificationId property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setNotificationStatus(String value) { this.notificationStatus = value; @@ -363,11 +359,11 @@ public class TransactionLogEntry { /** * Gets the value of the notificationTopic property. - * + * * @return * possible object is * {@link String } - * + * */ public String getNotificationTopic() { return notificationTopic; @@ -384,11 +380,11 @@ public class TransactionLogEntry { /** * Gets the value of the notificationEntityLink property. - * + * * @return * possible object is * {@link String } - * + * */ public String getNotificationEntityLink() { return notificationEntityLink; diff --git a/aai-core/src/main/java/org/onap/aai/exceptions/AAIException.java b/aai-core/src/main/java/org/onap/aai/exceptions/AAIException.java deleted file mode 100644 index 320d5630..00000000 --- a/aai-core/src/main/java/org/onap/aai/exceptions/AAIException.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Modifications Copyright © 2018 IBM. - * ================================================================================ - * 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.exceptions; - -import java.util.Collection; -import java.util.LinkedList; - -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.ErrorObject; -import org.onap.aai.logging.ErrorObjectNotFoundException; - -public class AAIException extends Exception { - - private static final String UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE = - " - update error.properties before using this exception code"; - private static final String FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE = - "Failed to instantiate AAIException with code="; - public static final String DEFAULT_EXCEPTION_CODE = "AAI_4000"; - private static final long serialVersionUID = 1L; - - private final String code; - private final ErrorObject errorObject; - private final Collection<String> templateVars; - - /** - * Instantiates a new AAI exception. - */ - public AAIException() { - super(); - this.code = DEFAULT_EXCEPTION_CODE; - this.templateVars = new LinkedList<>(); - - try { - this.errorObject = ErrorLogHelper.getErrorObject(getCode()); - } catch (ErrorObjectNotFoundException e) { - throw new RuntimeException(FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE + getCode() - + UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE); - } - } - - /** - * Instantiates a new AAI exception. - * - * @param code the code - */ - public AAIException(String code) { - super(); - - this.code = code; - this.templateVars = new LinkedList<>(); - - try { - this.errorObject = ErrorLogHelper.getErrorObject(getCode()); - } catch (ErrorObjectNotFoundException e) { - throw new RuntimeException(FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE + getCode() - + UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE); - } - } - - /** - * Instantiates a new AAI exception. - * - * @param code the code - * @param details the details - */ - public AAIException(String code, String details) { - super(details); - - this.code = code; - this.templateVars = new LinkedList<>(); - - try { - this.errorObject = ErrorLogHelper.getErrorObject(getCode()); - errorObject.setDetails(details); - } catch (ErrorObjectNotFoundException e) { - throw new RuntimeException(FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE + getCode() - + UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE); - } - } - - /** - * Instantiates a new AAI exception. - * - * @param code the code - * @param cause the cause - */ - public AAIException(String code, Throwable cause) { - super(cause); - - this.code = code; - this.templateVars = new LinkedList<>(); - - try { - this.errorObject = ErrorLogHelper.getErrorObject(getCode()); - } catch (ErrorObjectNotFoundException e) { - throw new RuntimeException(FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE + getCode() - + UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE); - } - } - - /** - * Instantiates a new AAI exception. - * - * @param code the code - * @param cause the cause - * @param details the details - */ - public AAIException(String code, Throwable cause, String details) { - super(details, cause); - - this.code = code; - this.templateVars = new LinkedList<>(); - - try { - this.errorObject = ErrorLogHelper.getErrorObject(getCode()); - } catch (ErrorObjectNotFoundException e) { - throw new RuntimeException(FAILED_TO_INSTANTIATE_AAI_EXCEPTION_WITH_CODE + getCode() - + UPDATE_ERROR_PROPERTIES_BEFORE_USING_THIS_EXCEPTION_CODE); - } - } - - public String getCode() { - return code; - } - - public ErrorObject getErrorObject() { - return errorObject; - } - - public Collection<String> getTemplateVars() { - return templateVars; - } -} diff --git a/aai-core/src/main/java/org/onap/aai/exceptions/AAIExceptionWithInfo.java b/aai-core/src/main/java/org/onap/aai/exceptions/AAIExceptionWithInfo.java deleted file mode 100644 index e7f2901e..00000000 --- a/aai-core/src/main/java/org/onap/aai/exceptions/AAIExceptionWithInfo.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.exceptions; - -import java.util.HashMap; - -public class AAIExceptionWithInfo extends AAIException { - - HashMap<String, Object> infoHash; - String info; - - /** - * Instantiates a new AAI exception with info. - * - * @param infoHash the info hash - * @param info the info - */ - public AAIExceptionWithInfo(HashMap<String, Object> infoHash, String info) { - super(); - setInfoHash(infoHash); - setInfo(info); - } - - /** - * Instantiates a new AAI exception with info. - * - * @param code the code - * @param infoHash the info hash - * @param info the info - */ - public AAIExceptionWithInfo(String code, HashMap<String, Object> infoHash, String info) { - super(code); - setInfoHash(infoHash); - setInfo(info); - } - - /** - * Instantiates a new AAI exception with info. - * - * @param code the code - * @param details the details - * @param infoHash the info hash - * @param info the info - */ - public AAIExceptionWithInfo(String code, String details, HashMap<String, Object> infoHash, String info) { - super(code, details); - setInfoHash(infoHash); - setInfo(info); - } - - /** - * Instantiates a new AAI exception with info. - * - * @param code the code - * @param cause the cause - * @param infoHash the info hash - * @param info the info - */ - public AAIExceptionWithInfo(String code, Throwable cause, HashMap<String, Object> infoHash, String info) { - super(code, cause); - setInfoHash(infoHash); - setInfo(info); - } - - /** - * Instantiates a new AAI exception with info. - * - * @param code the code - * @param cause the cause - * @param details the details - * @param infoHash the info hash - * @param info the info - */ - public AAIExceptionWithInfo(String code, Throwable cause, String details, HashMap<String, Object> infoHash, - String info) { - super(code, cause, details); - setInfoHash(infoHash); - setInfo(info); - } - - /** - * Gets the info hash. - * - * @return the info hash - */ - public HashMap<String, Object> getInfoHash() { - return infoHash; - } - - /** - * Sets the info hash. - * - * @param infoHash the info hash - */ - public void setInfoHash(HashMap<String, Object> infoHash) { - this.infoHash = infoHash; - } - - /** - * Gets the info. - * - * @return the info - */ - public String getInfo() { - return info; - } - - /** - * Sets the info. - * - * @param info the new info - */ - public void setInfo(String info) { - this.info = info; - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/extensions/ExtensionController.java b/aai-core/src/main/java/org/onap/aai/extensions/ExtensionController.java index 13c2de94..ec28858e 100644 --- a/aai-core/src/main/java/org/onap/aai/extensions/ExtensionController.java +++ b/aai-core/src/main/java/org/onap/aai/extensions/ExtensionController.java @@ -20,8 +20,8 @@ package org.onap.aai.extensions; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Method; @@ -30,7 +30,7 @@ import org.onap.aai.util.AAIConfig; public class ExtensionController { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ExtensionController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionController.class); /** * Run extension. diff --git a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java index 11e6233f..53f2a2cd 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java @@ -20,15 +20,9 @@ package org.onap.aai.introspection; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; - -import java.io.UnsupportedEncodingException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.stream.Collectors; - import org.apache.commons.lang.ClassUtils; import org.eclipse.persistence.exceptions.DynamicException; import org.onap.aai.config.SpringContextAware; @@ -43,9 +37,14 @@ import org.onap.aai.schema.enums.PropertyMetadata; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.workarounds.NamingExceptions; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.stream.Collectors; + public abstract class Introspector implements Cloneable { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Introspector.class); protected String className; protected String uriChain = ""; @@ -54,6 +53,7 @@ public abstract class Introspector implements Cloneable { private Set<String> uniqueProperties = null; private Set<String> indexedProperties = null; private Set<String> allKeys = null; + private Set<String> dslStartNodeProperties = null; protected CaseFormatStore caseFormatStore = null; protected NodeIngestor nodeIngestor; @@ -177,7 +177,7 @@ public abstract class Introspector implements Cloneable { if (obj != null) { try { - if (!nameClass.isAssignableFrom(obj.getClass())) { + if (!obj.getClass().getName().equals(nameClass.getName())) { if (nameClass.isPrimitive()) { nameClass = ClassUtils.primitiveToWrapper(nameClass); result = nameClass.getConstructor(String.class).newInstance(obj.toString()); @@ -229,15 +229,8 @@ public abstract class Introspector implements Cloneable { public Set<String> getProperties(PropertyPredicate<Introspector, String> p) { final Set<String> temp = new LinkedHashSet<>(); - this.getProperties().stream().filter(item -> { - return p.test(this, item); - }).forEach(item -> { - temp.add(item); - }); - final Set<String> result = Collections.unmodifiableSet(temp); - - return result; - + this.getProperties().stream().filter(item -> p.test(this, item)).forEach(temp::add); + return Collections.unmodifiableSet(temp); } public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p) { @@ -301,6 +294,31 @@ public abstract class Introspector implements Cloneable { return result; } + public Set<String> getDslStartNodeProperties() { + Set<String> result = null; + + if (this.dslStartNodeProperties == null) { + /* + * The dslStartNodeProperties will have keys by default + * If dslStartNodeProps exist in the oxm use it + * if not use the indexedProps + */ + result = new LinkedHashSet<>(this.getKeys()); + + String dslKeys = this.getMetadata(ObjectMetadata.DSL_START_NODE_PROPS); + String indexedKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS); + if (dslKeys != null) { + Arrays.stream(dslKeys.split(",")).forEach(result::add); + } + else if(indexedKeys != null){ + Arrays.stream(indexedKeys.split(",")).forEach(result::add); + } + this.dslStartNodeProperties = Collections.unmodifiableSet(result); + } + result = this.dslStartNodeProperties; + return result; + } + public Set<String> getUniqueProperties() { Set<String> result = null; if (this.uniqueProperties == null) { diff --git a/aai-core/src/main/java/org/onap/aai/introspection/IntrospectorWalker.java b/aai-core/src/main/java/org/onap/aai/introspection/IntrospectorWalker.java index aa5ae156..93a48290 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/IntrospectorWalker.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/IntrospectorWalker.java @@ -20,8 +20,8 @@ package org.onap.aai.introspection; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.LinkedHashSet; @@ -34,7 +34,7 @@ import org.onap.aai.logging.LogFormatTools; public class IntrospectorWalker { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(IntrospectorWalker.class); + private static final Logger LOGGER = LoggerFactory.getLogger(IntrospectorWalker.class); private Wanderer w = null; private Set<String> blacklist = null; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java index e628d5a4..55580dc3 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java @@ -26,7 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.schema.enums.PropertyMetadata; @@ -150,7 +149,7 @@ public class JSONStrategy extends Introspector { Object resultObject = null; Class<?> resultClass = null; resultObject = this.getValue(name); - if (resultObject instanceof JSONArray) { + if (resultObject.getClass().getName().equals("org.json.simple.JSONArray")) { resultClass = ((List) resultObject).get(0).getClass(); } @@ -274,10 +273,10 @@ public class JSONStrategy extends Introspector { /* * @Override * public String findEdgeName(String parent, String child) { - * + * * // Always has for now * return "has"; - * + * * } */ diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java index 02254a8b..7471c745 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java @@ -20,8 +20,8 @@ package org.onap.aai.introspection; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableMap; @@ -52,7 +52,7 @@ import org.onap.aai.workarounds.NamingExceptions; public class MoxyLoader extends Loader { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyLoader.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MoxyLoader.class); private DynamicJAXBContext jaxbContext = null; private Map<String, Introspector> allObjs = null; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java index 15311638..57e41081 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java @@ -20,8 +20,8 @@ package org.onap.aai.introspection; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; import com.google.common.base.Joiner; @@ -54,7 +54,7 @@ import org.springframework.web.util.UriUtils; public class MoxyStrategy extends Introspector { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyStrategy.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MoxyStrategy.class); private DynamicEntity internalObject = null; private DynamicType internalType = null; private DynamicJAXBContext jaxbContext = null; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/generator/CreateExample.java b/aai-core/src/main/java/org/onap/aai/introspection/generator/CreateExample.java index 73f0a346..8288fd68 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/generator/CreateExample.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/generator/CreateExample.java @@ -20,17 +20,17 @@ package org.onap.aai.introspection.generator; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.*; + import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.introspection.*; - public class CreateExample implements Wanderer { private SecureRandom rand = new SecureRandom(); - private final long range = 100000000L; + private static final long range = 100000000L; private Loader loader = null; private Introspector result = null; private String objectName = null; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/DataCopy.java b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/DataCopy.java index 6a6ee4c1..0994f023 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/DataCopy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/DataCopy.java @@ -20,17 +20,6 @@ package org.onap.aai.introspection.sideeffect; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.List; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; - -import javax.ws.rs.core.MultivaluedMap; - import org.apache.tinkerpop.gremlin.structure.Vertex; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; @@ -42,6 +31,16 @@ import org.onap.aai.schema.enums.PropertyMetadata; import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import javax.ws.rs.core.MultivaluedMap; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; + public class DataCopy extends SideEffect { public DataCopy(Introspector obj, Vertex self, TransactionalGraphEngine dbEngine, DBSerializer serializer) { @@ -69,14 +68,12 @@ public class DataCopy extends SideEffect { } else { if (results.isEmpty()) { throw new AAIException("AAI_6114", "object located at " + uri + " not found"); - } else if (results.size() > 1) { + } else { throw new AAIMultiplePropertiesException( "multiple values of " + entry.getKey() + " found when searching " + uri); } } - } else { - // skip processing because no required properties were specified - } + } //else skip processing because no required properties were specified } @Override diff --git a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/PrivateEdge.java b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/PrivateEdge.java index f4f0bfac..06586317 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/PrivateEdge.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/PrivateEdge.java @@ -21,15 +21,6 @@ package org.onap.aai.introspection.sideeffect; import com.google.common.collect.Multimap; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; -import java.util.Map.Entry; - -import javax.ws.rs.core.MultivaluedMap; - import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; @@ -42,7 +33,10 @@ import org.onap.aai.edges.enums.EdgeType; import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException; import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException; import org.onap.aai.exceptions.AAIException; -import org.onap.aai.introspection.*; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.ModelType; import org.onap.aai.introspection.sideeffect.exceptions.AAIMultiplePropertiesException; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.restcore.util.URITools; @@ -52,6 +46,13 @@ import org.onap.aai.serialization.db.EdgeSerializer; import org.onap.aai.serialization.db.exceptions.EdgeMultiplicityException; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import javax.ws.rs.core.MultivaluedMap; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.Map.Entry; + public class PrivateEdge extends SideEffect { public PrivateEdge(Introspector obj, Vertex self, TransactionalGraphEngine dbEngine, DBSerializer serializer) { @@ -111,40 +112,38 @@ public class PrivateEdge extends SideEffect { throw new EdgeMultiplicityException(message); } - for (Entry<String, EdgeRule> edgeEntry : edgeRulesMap.entries()) { - EdgeRule edgeRule = edgeIngestor.getRule(edgeQuery); - Iterator<Edge> edges = self.edges(edgeRule.getDirection(), edgeRule.getLabel().toString()); - if (edges.hasNext()) { - Edge edge = edges.next(); - EdgeStatus status = checkStatus(obj, self); - switch (status) { - case CREATED: - edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, - otherVertex, edgeRule.getLabel()); - break; - case MODIFIED: - edge.remove(); - edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, - otherVertex, edgeRule.getLabel()); - break; - case REMOVED: - edge.remove(); - break; - case UNCHANGED: - break; - } - } else { - edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, - otherVertex, edgeRule.getLabel()); + EdgeRule edgeRule = edgeIngestor.getRule(edgeQuery); + Iterator<Edge> edges = self.edges(edgeRule.getDirection(), edgeRule.getLabel()); + if (edges.hasNext()) { + Edge edge = edges.next(); + EdgeStatus status = checkStatus(obj, self); + switch (status) { + case CREATED: + edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, + otherVertex, edgeRule.getLabel()); + break; + case MODIFIED: + edge.remove(); + edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, + otherVertex, edgeRule.getLabel()); + break; + case REMOVED: + edge.remove(); + break; + case UNCHANGED: + break; } + } else { + edgeSerializer.addPrivateEdge(this.dbEngine.asAdmin().getTraversalSource(), self, + otherVertex, edgeRule.getLabel()); } } } else { if (results.isEmpty()) { throw new AAIException("AAI_6114", "object located at " + uri + " not found"); - } else if (results.size() > 1) { + } else { throw new AAIMultiplePropertiesException( - "multiple values of " + entry.getKey() + " found when searching " + uri); + "multiple values of " + entry.getKey() + " found when searching " + uri); } } } @@ -159,7 +158,7 @@ public class PrivateEdge extends SideEffect { for (String key : templateKeys) { String currentObjValue = obj.getValue(key); Map<PropertyMetadata, String> map = obj.getPropertyMetadata(key); - String oldVertexValue = null; + String oldVertexValue; if (map.containsKey(PropertyMetadata.DB_ALIAS)) { oldVertexValue = self.<String>property(key + AAIProperties.DB_ALIAS_SUFFIX).orElse(null); diff --git a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffect.java b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffect.java index d86c18a0..a71ffa4e 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffect.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffect.java @@ -20,8 +20,8 @@ package org.onap.aai.introspection.sideeffect; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; @@ -46,7 +46,7 @@ import org.onap.aai.setup.SchemaVersions; public abstract class SideEffect { protected static final Pattern template = Pattern.compile("\\{(.*?)\\}"); - private static final EELFLogger logger = EELFManager.getInstance().getLogger(SideEffect.class); + private static final Logger logger = LoggerFactory.getLogger(SideEffect.class); protected final Introspector obj; protected final TransactionalGraphEngine dbEngine; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffectRunner.java b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffectRunner.java index ffd9a8c8..8bc83156 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffectRunner.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/sideeffect/SideEffectRunner.java @@ -20,8 +20,8 @@ package org.onap.aai.introspection.sideeffect; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; diff --git a/aai-core/src/main/java/org/onap/aai/logging/CNName.java b/aai-core/src/main/java/org/onap/aai/logging/CNName.java deleted file mode 100644 index 5337afe3..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/CNName.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import static java.util.Base64.getDecoder; - -import ch.qos.logback.access.pattern.AccessConverter; -import ch.qos.logback.access.spi.IAccessEvent; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.security.cert.X509Certificate; - -import javax.security.auth.x500.X500Principal; -import javax.servlet.http.HttpServletRequest; - -public class CNName extends AccessConverter { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(CNName.class); - - /** - * Converts access events to String response codes - * - * @param accessEvent the IAccessEvent - */ - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_HEADER_CONV"; - } - - String cipherSuite = (String) accessEvent.getRequest().getAttribute("javax.servlet.request.cipher_suite"); - String authUser = null; - if (cipherSuite != null) { - try { - X509Certificate certChain[] = (X509Certificate[]) accessEvent.getRequest() - .getAttribute("javax.servlet.request.X509Certificate"); - if (certChain == null || certChain.length == 0) { - - HttpServletRequest request = accessEvent.getRequest(); - - String authorization = request.getHeader("Authorization"); - - // Set the auth user to "-" so if the authorization header is not found - // Or if the decoded basic auth credentials are not found in the format required - // it should return "-" - // If the decoded string is in the right format, find the index of ":" - // Then get the substring of the starting point to the colon not including the colon - - authUser = "-"; - - if (authorization != null && authorization.startsWith("Basic ")) { - String credentials = authorization.replace("Basic ", ""); - byte[] userCredentials = getDecoder().decode(credentials.getBytes("utf-8")); - credentials = new String(userCredentials); - - int codePoint = credentials.indexOf(':'); - - if (codePoint != -1) { - authUser = credentials.substring(0, codePoint); - } - - } - - return authUser; - - } else { - X509Certificate clientCert = certChain[0]; - X500Principal subjectDN = clientCert.getSubjectX500Principal(); - authUser = subjectDN.toString(); - return authUser; - } - } catch (Exception e) { - LOGGER.error(e.getMessage(), e); - return "-"; - } - } else { - return "-"; - } - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/DME2RestFlag.java b/aai-core/src/main/java/org/onap/aai/logging/DME2RestFlag.java deleted file mode 100644 index 768c095b..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/DME2RestFlag.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.access.pattern.AccessConverter; -import ch.qos.logback.access.spi.IAccessEvent; - -public class DME2RestFlag extends AccessConverter { - - /** - * @{inheritDoc} - */ - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_HEADER_CONV"; - } - - String flag = "-"; - - if (accessEvent.getRequestParameter("envContext").length > 0 - && !accessEvent.getRequestParameter("envContext")[0].isEmpty() - && !accessEvent.getRequestParameter("envContext")[0].equals("-") - && accessEvent.getRequestParameter("routeOffer").length > 0 - && !accessEvent.getRequestParameter("routeOffer")[0].isEmpty() - && !accessEvent.getRequestParameter("routeOffer")[0].equals("-") - && accessEvent.getRequestParameter("version").length > 0 - && !accessEvent.getRequestParameter("version")[0].isEmpty() - && !accessEvent.getRequestParameter("version")[0].equals("-")) { - flag = "DME2"; - } else { - flag = "REST"; - } - - return flag; - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompElapsedTime.java b/aai-core/src/main/java/org/onap/aai/logging/EcompElapsedTime.java deleted file mode 100644 index 66d27868..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompElapsedTime.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompElapsedTime extends ClassicConverter { - - private static final String DEFAULT_ELAPSED_TIME_FORMAT = "%d"; - - private String ELAPSED_TIME_FORMAT; - - @Override - public void start() { - ELAPSED_TIME_FORMAT = getFirstOption(); - } - - @Override - public String convert(ILoggingEvent event) { - final long end = event.getTimeStamp(); - - if (!event.getMDCPropertyMap().containsKey(LoggingField.START_TIME.toString())) { - return format(0); - } else if (event.getMDCPropertyMap().containsKey(LoggingField.ELAPSED_TIME.toString())) { - return format(Integer.parseInt(event.getMDCPropertyMap().get(LoggingField.ELAPSED_TIME.toString()))); - } - - final long start = - LogFormatTools.toTimestamp(event.getMDCPropertyMap().get(LoggingField.START_TIME.toString())); - - return format(end - start); - } - - private String format(long elapsedTime) { - if (ELAPSED_TIME_FORMAT == null) { - return format(DEFAULT_ELAPSED_TIME_FORMAT, elapsedTime); - } - - return format(ELAPSED_TIME_FORMAT, elapsedTime); - } - - private String format(String format, long elapsedTime) { - return String.format(format, elapsedTime); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompEncoder.java b/aai-core/src/main/java/org/onap/aai/logging/EcompEncoder.java deleted file mode 100644 index 88587297..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompEncoder.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.PatternLayout; -import ch.qos.logback.classic.encoder.PatternLayoutEncoder; - -public class EcompEncoder extends PatternLayoutEncoder { - - @Override - public void start() { - PatternLayout patternLayout = new EcompPatternLayout(); - patternLayout.setContext(context); - patternLayout.setPattern(getPattern()); - patternLayout.setOutputPatternAsHeader(outputPatternAsHeader); - patternLayout.start(); - this.layout = patternLayout; - super.start(); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompErrorCategory.java b/aai-core/src/main/java/org/onap/aai/logging/EcompErrorCategory.java deleted file mode 100644 index 452fcd08..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompErrorCategory.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -public class EcompErrorCategory extends ClassicConverter { - - @Override - public String convert(ILoggingEvent event) { - - final Level lev = event.getLevel(); - final String defaultCategory = "WARN"; - - if ((Level.WARN).equals(lev)) { - return (defaultCategory); - } else if ((Level.ERROR).equals(lev)) { - return ("ERROR"); - } - return (defaultCategory); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompPatternLayout.java b/aai-core/src/main/java/org/onap/aai/logging/EcompPatternLayout.java deleted file mode 100644 index 43c147a0..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompPatternLayout.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.PatternLayout; - -public class EcompPatternLayout extends PatternLayout { - static { - PatternLayout.defaultConverterMap.put("ecompStartTime", EcompStartTime.class.getName()); - PatternLayout.defaultConverterMap.put("ecompElapsedTime", EcompElapsedTime.class.getName()); - PatternLayout.defaultConverterMap.put("eelfClassOfCaller", EelfClassOfCaller.class.getName()); - PatternLayout.defaultConverterMap.put("ecompErrorCategory", EcompErrorCategory.class.getName()); - PatternLayout.defaultConverterMap.put("ecompResponseCode", EcompResponseCode.class.getName()); - PatternLayout.defaultConverterMap.put("ecompResponseDescription", EcompResponseDescription.class.getName()); - PatternLayout.defaultConverterMap.put("ecompStatusCode", EcompStatusCode.class.getName()); - PatternLayout.defaultConverterMap.put("ecompServiceName", EcompServiceName.class.getName()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompResponseCode.java b/aai-core/src/main/java/org/onap/aai/logging/EcompResponseCode.java deleted file mode 100644 index 1dc59b4e..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompResponseCode.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompResponseCode extends ClassicConverter { - - @Override - public String convert(ILoggingEvent event) { - - if (!event.getMDCPropertyMap().containsKey(LoggingField.RESPONSE_CODE.toString())) { - // if response code is not set, return "unknown" (900) - return LoggingContext.UNKNOWN_ERROR; - } - return event.getMDCPropertyMap().get(LoggingField.RESPONSE_CODE.toString()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompResponseDescription.java b/aai-core/src/main/java/org/onap/aai/logging/EcompResponseDescription.java deleted file mode 100644 index b72cc100..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompResponseDescription.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompResponseDescription extends ClassicConverter { - public static final String DEFAULT_DESCRIPTION = "Unknown response/error description"; - - @Override - public String convert(ILoggingEvent event) { - - if (!event.getMDCPropertyMap().containsKey(LoggingField.RESPONSE_DESCRIPTION.toString())) { - return (DEFAULT_DESCRIPTION); - } - // Replace pipes and new lines - String currentDesc = event.getMDCPropertyMap().get(LoggingField.RESPONSE_DESCRIPTION.toString()); - if ((currentDesc == null) || (currentDesc.length() == 0)) { - return (DEFAULT_DESCRIPTION); - } - currentDesc = currentDesc.replaceAll("\\|", "!"); - currentDesc = currentDesc.replaceAll("[\\r\\n]+", "^"); - return event.getMDCPropertyMap().get(currentDesc); - } -} - - diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompServiceName.java b/aai-core/src/main/java/org/onap/aai/logging/EcompServiceName.java deleted file mode 100644 index 10d7a211..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompServiceName.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompServiceName extends ClassicConverter { - @Override - public String convert(ILoggingEvent event) { - if (!event.getMDCPropertyMap().containsKey(LoggingField.SERVICE_NAME.toString())) { - return "AAI"; - } - return event.getMDCPropertyMap().get(LoggingField.SERVICE_NAME.toString()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompStartTime.java b/aai-core/src/main/java/org/onap/aai/logging/EcompStartTime.java deleted file mode 100644 index 8f015414..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompStartTime.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompStartTime extends ClassicConverter { - - @Override - public String convert(ILoggingEvent event) { - - if (!event.getMDCPropertyMap().containsKey(LoggingField.START_TIME.toString())) { - return LogFormatTools.toDate(event.getTimeStamp()); - } - - return event.getMDCPropertyMap().get(LoggingField.START_TIME.toString()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EcompStatusCode.java b/aai-core/src/main/java/org/onap/aai/logging/EcompStatusCode.java deleted file mode 100644 index 4319bbbd..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EcompStatusCode.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.ClassicConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public class EcompStatusCode extends ClassicConverter { - @Override - public String convert(ILoggingEvent event) { - if (!event.getMDCPropertyMap().containsKey(LoggingField.STATUS_CODE.toString())) { - return LoggingContext.StatusCode.COMPLETE.toString(); - } - return event.getMDCPropertyMap().get(LoggingField.STATUS_CODE.toString()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/EelfClassOfCaller.java b/aai-core/src/main/java/org/onap/aai/logging/EelfClassOfCaller.java deleted file mode 100644 index dc9bc2c2..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/EelfClassOfCaller.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import ch.qos.logback.classic.pattern.NamedConverter; -import ch.qos.logback.classic.spi.CallerData; -import ch.qos.logback.classic.spi.ILoggingEvent; - -public class EelfClassOfCaller extends NamedConverter { - protected String getFullyQualifiedName(ILoggingEvent event) { - - StackTraceElement[] cda = event.getCallerData(); - - // If using the EELFLogger, it "hides" the calling class because it wraps the logging calls - // Without this, you'd only ever see "EELF SLF4jWrapper" when using the - // %C pattern converter - if (cda != null && cda.length > 2) { - return cda[2].getClassName(); - } else if (cda != null && cda.length > 0) { - return cda[0].getClassName(); - } else { - return CallerData.NA; - } - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/ErrorLogHelper.java b/aai-core/src/main/java/org/onap/aai/logging/ErrorLogHelper.java deleted file mode 100644 index 01327606..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/ErrorLogHelper.java +++ /dev/null @@ -1,633 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; - -import javax.ws.rs.core.MediaType; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; - -import org.apache.commons.lang.StringUtils; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.LoggingContext.StatusCode; -import org.onap.aai.util.AAIConfig; -import org.onap.aai.util.AAIConstants; -import org.onap.aai.util.MapperUtil; -import org.slf4j.MDC; - -/** - * - * This classes loads the application error properties file - * and provides a method that returns an ErrorObject - * - */ - -public class ErrorLogHelper { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ErrorLogHelper.class); - private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject>(); - - static { - try { - loadProperties(); - } catch (IOException e) { - throw new RuntimeException("Failed to load error.properties file", e); - } catch (ErrorObjectFormatException e) { - throw new RuntimeException("Failed to parse error.properties file", e); - } - } - - /** - * Load properties. - * - * @throws ErrorObjectFormatException - * @throws Exception the exception - */ - public static void loadProperties() throws IOException, ErrorObjectFormatException { - final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties"; - final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); - final Properties properties = new Properties(); - - if (is != null) { - properties.load(is); - } else { - try (final FileInputStream fis = new FileInputStream(filePath)) { - properties.load(fis); - } - } - - for (Entry<Object, Object> entry : properties.entrySet()) { - final String key = (String) entry.getKey(); - final String value = (String) entry.getValue(); - final String[] errorProperties = value.split(":"); - - if (errorProperties.length != 7) - throw new ErrorObjectFormatException(); - - final ErrorObject errorObject = new ErrorObject(); - - errorObject.setDisposition(errorProperties[0].trim()); - errorObject.setCategory(errorProperties[1].trim()); - errorObject.setSeverity(errorProperties[2].trim()); - errorObject.setErrorCode(errorProperties[3].trim()); - errorObject.setHTTPResponseCode(errorProperties[4].trim()); - errorObject.setRESTErrorCode(errorProperties[5].trim()); - errorObject.setErrorText(errorProperties[6].trim()); - - ERROR_OBJECTS.put(key, errorObject); - } - } - - /** - * Logs a known A&AI exception (i.e. one that can be found in error.properties) - * - * @param key The key for the error in the error.properties file - * @throws IOException - * @throws ErrorObjectNotFoundException - * @throws ErrorObjectFormatException - */ - public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException { - - if (code == null) - throw new IllegalArgumentException("Key cannot be null"); - - final ErrorObject errorObject = ERROR_OBJECTS.get(code); - - if (errorObject == null) { - LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException"); - return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE); - } - - return errorObject; - } - - /** - * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error. - * The AAIRESTException may contain a different ErrorObject than that created with the REST error key. - * This allows lower level exception detail to be returned to the client to help troubleshoot the problem. - * If no error object is embedded in the AAIException, one will be created using the error object from the - * AAIException. - * - * @param are must have a restError value whose numeric value must match what should be returned in the REST API - * @param variables optional list of variables to flesh out text in error string - * @return appropriately formatted JSON response per the REST API spec. - * @throws ErrorObjectFormatException - * @throws ErrorObjectNotFoundException - * @throws IOException - * @deprecated - */ - public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) { - List<MediaType> acceptHeaders = new ArrayList<MediaType>(); - acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE); - - return getRESTAPIErrorResponse(acceptHeaders, are, variables); - } - - /** - * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error. - * The AAIRESTException may contain a different ErrorObject than that created with the REST error key. - * This allows lower level exception detail to be returned to the client to help troubleshoot the problem. - * If no error object is embedded in the AAIException, one will be created using the error object from the - * AAIException. - * - * @param acceptHeadersOrig the accept headers orig - * @param are must have a restError value whose numeric value must match what should be returned in the REST API - * @param variables optional list of variables to flesh out text in error string - * @return appropriately formatted JSON response per the REST API spec. - * @throws ErrorObjectFormatException - * @throws ErrorObjectNotFoundException - * @throws IOException - */ - public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are, - ArrayList<String> variables) { - - StringBuilder text = new StringBuilder(); - String response = null; - - List<MediaType> acceptHeaders = new ArrayList<MediaType>(); - // we might have an exception but no accept header, so we'll set default to JSON - boolean foundValidAcceptHeader = false; - for (MediaType mt : acceptHeadersOrig) { - if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) || MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) { - acceptHeaders.add(mt); - foundValidAcceptHeader = true; - } - } - if (foundValidAcceptHeader == false) { - // override the exception, client needs to set an appropriate Accept header - are = new AAIException("AAI_4014"); - acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE); - } - - final ErrorObject eo = are.getErrorObject(); - - int restErrorCode = Integer.parseInt(eo.getRESTErrorCode()); - - ErrorObject restErrorObject; - - try { - restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode); - } catch (ErrorObjectNotFoundException e) { - LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object " - + eo.getErrorCode() + "; using AAI_" + restErrorCode); - restErrorObject = eo; - } - - text.append(restErrorObject.getErrorText()); - - // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n - // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the - // error, are ordered based on the error string. - int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%"); - text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")"); - - if (variables == null) { - variables = new ArrayList<String>(); - } - - if (variables.size() < localDataIndex) { - ErrorLogHelper.logError("AAI_4011", "data missing for rest error"); - while (variables.size() < localDataIndex) { - variables.add("null"); - } - } - - // This will put the error code and error text into the right positions - if (are.getMessage() == null || are.getMessage().length() == 0) { - variables.add(localDataIndex++, eo.getErrorText()); - } else { - variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage()); - } - variables.add(localDataIndex, eo.getErrorCodeString()); - - for (MediaType mediaType : acceptHeaders) { - if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) { - JAXBContext context = null; - try { - if (eo.getCategory().equals("1")) { - - context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class); - Marshaller m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - - org.onap.aai.domain.restPolicyException.ObjectFactory factory = - new org.onap.aai.domain.restPolicyException.ObjectFactory(); - org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault(); - org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = - factory.createFaultRequestError(); - org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = - factory.createFaultRequestErrorPolicyException(); - org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = - factory.createFaultRequestErrorPolicyExceptionVariables(); - - policyException.setMessageId("POL" + eo.getRESTErrorCode()); - policyException.setText(text.toString()); - for (int i = 0; i < variables.size(); i++) { - polvariables.getVariable().add(variables.get(i)); - } - policyException.setVariables(polvariables); - requestError.setPolicyException(policyException); - fault.setRequestError(requestError); - - StringWriter sw = new StringWriter(); - m.marshal(fault, sw); - - response = sw.toString(); - - } else { - - context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class); - Marshaller m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - - org.onap.aai.domain.restServiceException.ObjectFactory factory = - new org.onap.aai.domain.restServiceException.ObjectFactory(); - org.onap.aai.domain.restServiceException.Fault fault = factory.createFault(); - org.onap.aai.domain.restServiceException.Fault.RequestError requestError = - factory.createFaultRequestError(); - org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = - factory.createFaultRequestErrorServiceException(); - org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = - factory.createFaultRequestErrorServiceExceptionVariables(); - serviceException.setMessageId("SVC" + eo.getRESTErrorCode()); - serviceException.setText(text.toString()); - for (int i = 0; i < variables.size(); i++) { - svcvariables.getVariable().add(variables.get(i)); - } - serviceException.setVariables(svcvariables); - requestError.setServiceException(serviceException); - fault.setRequestError(requestError); - - StringWriter sw = new StringWriter(); - m.marshal(fault, sw); - - response = sw.toString(); - - } - } catch (Exception ex) { - LOGGER.error( - "We were unable to create a rest exception to return on an API because of a parsing error " - + ex.getMessage()); - } - } else { - try { - if (eo.getCategory().equals("1")) { - org.onap.aai.domain.restPolicyException.RESTResponse restresp = - new org.onap.aai.domain.restPolicyException.RESTResponse(); - org.onap.aai.domain.restPolicyException.RequestError reqerr = - new org.onap.aai.domain.restPolicyException.RequestError(); - org.onap.aai.domain.restPolicyException.PolicyException polexc = - new org.onap.aai.domain.restPolicyException.PolicyException(); - polexc.setMessageId("POL" + eo.getRESTErrorCode()); - polexc.setText(text.toString()); - polexc.setVariables(variables); - reqerr.setPolicyException(polexc); - restresp.setRequestError(reqerr); - response = (MapperUtil.writeAsJSONString((Object) restresp)); - - } else { - org.onap.aai.domain.restServiceException.RESTResponse restresp = - new org.onap.aai.domain.restServiceException.RESTResponse(); - org.onap.aai.domain.restServiceException.RequestError reqerr = - new org.onap.aai.domain.restServiceException.RequestError(); - org.onap.aai.domain.restServiceException.ServiceException svcexc = - new org.onap.aai.domain.restServiceException.ServiceException(); - svcexc.setMessageId("SVC" + eo.getRESTErrorCode()); - svcexc.setText(text.toString()); - svcexc.setVariables(variables); - reqerr.setServiceException(svcexc); - restresp.setRequestError(reqerr); - response = (MapperUtil.writeAsJSONString((Object) restresp)); - } - } catch (AAIException ex) { - LOGGER.error( - "We were unable to create a rest exception to return on an API because of a parsing error " - + ex.getMessage()); - } - } - } - - return response; - } - - /** - * Gets the RESTAPI error response with logging. - * - * @param acceptHeadersOrig the accept headers orig - * @param are the are - * @param variables the variables - * @param logline the logline - * @return the RESTAPI error response with logging - * @throws ErrorObjectFormatException - * @throws ErrorObjectNotFoundException - * @throws IOException - */ - public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are, - ArrayList<String> variables) { - String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables); - - LOGGER.error(are.getMessage() + " " + LogFormatTools.getStackTop(are)); - - return response; - } - - /** - * Gets the RESTAPI info response. - * - * @param acceptHeaders the accept headers - * @param areList the are list - * @return the RESTAPI info response - * @throws ErrorObjectFormatException - * @throws ErrorObjectNotFoundException - * @throws IOException - */ - public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders, - HashMap<AAIException, ArrayList<String>> areList) { - - Object respObj = null; - - org.onap.aai.domain.restResponseInfo.ObjectFactory factory = - new org.onap.aai.domain.restResponseInfo.ObjectFactory(); - org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo(); - org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages = - factory.createInfoResponseMessages(); - Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator(); - - while (it.hasNext()) { - Entry<AAIException, ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>) it.next(); - AAIException are = pair.getKey(); - ArrayList<String> variables = pair.getValue(); - - StringBuilder text = new StringBuilder(); - - ErrorObject eo = are.getErrorObject(); - - int restErrorCode = Integer.parseInt(eo.getRESTErrorCode()); - ErrorObject restErrorObject; - try { - restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + String.format("%04d", restErrorCode)); - } catch (ErrorObjectNotFoundException e) { - restErrorObject = eo; - } - text.append(restErrorObject.getErrorText()); - - // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n - // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the - // error, are ordered based on the error string. - int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%"); - text.append(" (msg=%").append(localDataIndex + 1).append(") (rc=%").append(localDataIndex + 2).append(")"); - - if (variables == null) { - variables = new ArrayList<String>(); - } - - if (variables.size() < localDataIndex) { - ErrorLogHelper.logError("AAI_4011", "data missing for rest error"); - while (variables.size() < localDataIndex) { - variables.add("null"); - } - } - - // This will put the error code and error text into the right positions - if (are.getMessage() == null) { - variables.add(localDataIndex++, eo.getErrorText()); - } else { - variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage()); - } - variables.add(localDataIndex, eo.getErrorCodeString()); - - try { - org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage = - factory.createInfoResponseMessagesResponseMessage(); - org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables = - factory.createInfoResponseMessagesResponseMessageVariables(); - - responseMessage.setMessageId("INF" + eo.getRESTErrorCode()); - responseMessage.setText(text.toString()); - for (int i = 0; i < variables.size(); i++) { - infovariables.getVariable().add(variables.get(i)); - } - - responseMessage.setVariables(infovariables); - responseMessages.getResponseMessage().add(responseMessage); - - } catch (Exception ex) { - LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " - + ex.getMessage()); - } - } - - info.setResponseMessages(responseMessages); - respObj = (Object) info; - - return respObj; - } - - /** - * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error. - * The AAIRESTException may contain a different ErrorObject than that created with the REST error key. - * This allows lower level exception detail to be returned to the client to help troubleshoot the problem. - * If no error object is embedded in the AAIException, one will be created using the error object from the - * AAIException. - * - * @param are must have a restError value whose numeric value must match what should be returned in the REST API - * @param variables optional list of variables to flesh out text in error string - * @return appropriately formatted JSON response per the REST API spec. - * @throws ErrorObjectFormatException - * @throws ErrorObjectNotFoundException - * @throws IOException - */ - public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) { - - StringBuilder text = new StringBuilder(); - String response = null; - JAXBContext context = null; - - ErrorObject eo = are.getErrorObject(); - - int restErrorCode = Integer.parseInt(eo.getRESTErrorCode()); - ErrorObject restErrorObject; - try { - restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode); - } catch (ErrorObjectNotFoundException e) { - restErrorObject = eo; - } - - text.append(restErrorObject.getErrorText()); - - // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n - // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the - // error, are ordered based on the error string. - int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%"); - text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")"); - - if (variables == null) { - variables = new ArrayList<String>(); - } - - if (variables.size() < localDataIndex) { - ErrorLogHelper.logError("AAI_4011", "data missing for rest error"); - while (variables.size() < localDataIndex) { - variables.add("null"); - } - } - - // This will put the error code and error text into the right positions - if (are.getMessage() == null) { - variables.add(localDataIndex++, eo.getErrorText()); - } else { - variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage()); - } - variables.add(localDataIndex, eo.getErrorCodeString()); - - try { - if (eo.getCategory().equals("1")) { - - context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class); - Marshaller m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - - org.onap.aai.domain.restPolicyException.ObjectFactory factory = - new org.onap.aai.domain.restPolicyException.ObjectFactory(); - org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault(); - org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = - factory.createFaultRequestError(); - org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = - factory.createFaultRequestErrorPolicyException(); - org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = - factory.createFaultRequestErrorPolicyExceptionVariables(); - - policyException.setMessageId("POL" + eo.getRESTErrorCode()); - policyException.setText(text.toString()); - for (int i = 0; i < variables.size(); i++) { - polvariables.getVariable().add(variables.get(i)); - } - policyException.setVariables(polvariables); - requestError.setPolicyException(policyException); - fault.setRequestError(requestError); - - StringWriter sw = new StringWriter(); - m.marshal(fault, sw); - - response = sw.toString(); - - } else { - - context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class); - Marshaller m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - - org.onap.aai.domain.restServiceException.ObjectFactory factory = - new org.onap.aai.domain.restServiceException.ObjectFactory(); - org.onap.aai.domain.restServiceException.Fault fault = factory.createFault(); - org.onap.aai.domain.restServiceException.Fault.RequestError requestError = - factory.createFaultRequestError(); - org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = - factory.createFaultRequestErrorServiceException(); - org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = - factory.createFaultRequestErrorServiceExceptionVariables(); - serviceException.setMessageId("POL" + eo.getRESTErrorCode()); - serviceException.setText(text.toString()); - for (int i = 0; i < variables.size(); i++) { - svcvariables.getVariable().add(variables.get(i)); - } - serviceException.setVariables(svcvariables); - requestError.setServiceException(serviceException); - fault.setRequestError(requestError); - - StringWriter sw = new StringWriter(); - m.marshal(fault, sw); - - response = sw.toString(); - - } - } catch (Exception ex) { - LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " - + ex.getMessage()); - } - return response; - } - - public static void logException(AAIException e) { - final ErrorObject errorObject = e.getErrorObject(); - - // MDC.put("severity", errorObject.getSeverity()); //TODO Use LoggingContext.severity(int severity) - String severityCode = errorObject.getSeverityCode(errorObject.getSeverity()); - - if (!AAIConfig.isEmpty(severityCode)) { - int sevCode = Integer.parseInt(severityCode); - if (sevCode > 0 && sevCode <= 3) { - LoggingContext.severity(sevCode); - } - } - String stackTrace = ""; - try { - stackTrace = LogFormatTools.getStackTop(e); - } catch (Exception a) { - // ignore - } - final String errorMessage = new StringBuilder().append(errorObject.getErrorText()).append(":") - .append(errorObject.getRESTErrorCode()).append(":").append(errorObject.getHTTPResponseCode()) - .append(":").append(e.getMessage()).toString().replaceAll("\\n", "^"); - - LoggingContext.responseCode(Integer.toString(errorObject.getHTTPResponseCode().getStatusCode())); - LoggingContext.responseDescription(errorMessage); - LoggingContext.statusCode(StatusCode.ERROR); - - final String details = - new StringBuilder().append(errorObject.getErrorCodeString()).append(" ").append(stackTrace).toString(); - - if (errorObject.getSeverity().equalsIgnoreCase("WARN")) - LOGGER.warn(details); - else if (errorObject.getSeverity().equalsIgnoreCase("ERROR")) - LOGGER.error(details); - else if (errorObject.getSeverity().equalsIgnoreCase("FATAL")) - LOGGER.error(details); - else if (errorObject.getSeverity().equals("INFO")) - LOGGER.info(details); - } - - public static void logError(String code) { - logError(code, ""); - } - - public static void logError(String code, String message) { - logException(new AAIException(code, message)); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/ErrorObject.java b/aai-core/src/main/java/org/onap/aai/logging/ErrorObject.java deleted file mode 100644 index 6048c18b..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/ErrorObject.java +++ /dev/null @@ -1,359 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import javax.ws.rs.core.Response.Status; - -/** - * - * Contains the definition of all error message fields to be mapped from the Error - * properties file - * - */ -public class ErrorObject { - - private String disposition; - private String category; - private String severity; - private Status httpResponseCode = Status.INTERNAL_SERVER_ERROR; // default - private String restErrorCode = "3002"; - private String errorCode; - private String errorText; - private String details; - - /** - * Instantiates a new error object. - */ - public ErrorObject() { - super(); - } - - /** - * Creates an error object with the default HTTP Error Code (Status.INTERNAL_SERVER_ERROR) - * - * @param disposition the disposition - * @param category the category - * @param severity the severity - * @param httpResponseCode the http response code - * @param restErrorCode the rest error code - * @param errorCode the error code - * @param errorText the error text - */ - public ErrorObject(String disposition, String category, String severity, Integer httpResponseCode, - String restErrorCode, String errorCode, String errorText) { - super(); - this.setDisposition(disposition); - this.setCategory(category); - this.severity = severity; - this.setHTTPResponseCode(httpResponseCode); - this.setRESTErrorCode(restErrorCode); - this.setErrorCode(errorCode); - this.setErrorText(errorText); - } - - // OLD STARTS HERE - - /** - * Instantiates a new error object. - * - * @param severity the severity - * @param errorCode the error code - * @param errorText the error text - * @param disposition the disposition - * @param category the category - */ - public ErrorObject(String severity, String errorCode, String errorText, String disposition, String category) { - this(severity, Status.INTERNAL_SERVER_ERROR, errorCode, errorText, disposition, category); - } - - /** - * Instantiates a new error object. - * - * @param severity the severity - * @param httpResponseCode the http response code - * @param errorCode the error code - * @param errorText the error text - * @param disposition the disposition - * @param category the category - */ - public ErrorObject(String severity, Integer httpResponseCode, String errorCode, String errorText, - String disposition, String category) { - super(); - this.severity = severity; - this.setHTTPResponseCode(httpResponseCode); - this.setErrorCode(errorCode); - this.setErrorText(errorText); - this.setDisposition(disposition); - this.setCategory(category); - } - - /** - * Instantiates a new error object. - * - * @param severity the severity - * @param httpResponseCode the http response code - * @param errorCode the error code - * @param errorText the error text - * @param disposition the disposition - * @param category the category - */ - public ErrorObject(String severity, Status httpResponseCode, String errorCode, String errorText, String disposition, - String category) { - super(); - this.severity = severity; - this.setHTTPResponseCode(httpResponseCode); - this.setErrorCode(errorCode); - this.setErrorText(errorText); - this.setDisposition(disposition); - this.setCategory(category); - } - - /** - * Gets the disposition. - * - * @return the disposition - */ - public String getDisposition() { - return disposition; - } - - /** - * Sets the disposition. - * - * @param disposition the new disposition - */ - public void setDisposition(String disposition) { - this.disposition = disposition; - } - - /** - * Gets the category. - * - * @return the category - */ - public String getCategory() { - return category; - } - - /** - * Sets the category. - * - * @param category the new category - */ - public void setCategory(String category) { - this.category = category; - } - - /** - * Gets the severity. - * - * @return the severity - */ - public String getSeverity() { - return severity; - } - - /** - * Sets the severity. - * - * @param severity the new severity - */ - public void setSeverity(String severity) { - this.severity = severity; - } - - /** - * Gets the error code. - * - * @return the error code - */ - public String getErrorCode() { - return errorCode; - } - - /** - * Sets the error code. - * - * @param errorCode the new error code - */ - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - /** - * Gets the HTTP response code. - * - * @return the HTTP response code - */ - public Status getHTTPResponseCode() { - return httpResponseCode; - } - - /** - * Sets the HTTP response code. - * - * @param httpResponseCode the new HTTP response code - */ - public void setHTTPResponseCode(Integer httpResponseCode) { - this.httpResponseCode = Status.fromStatusCode(httpResponseCode); - if (this.httpResponseCode == null) { - throw new IllegalArgumentException( - "setHTTPResponseCode was passed an invalid Integer value, fix error.properties or your code " - + httpResponseCode); - } - } - - /** - * Sets the HTTP response code. - * - * @param httpResponseCode the new HTTP response code - */ - public void setHTTPResponseCode(String httpResponseCode) { - this.httpResponseCode = Status.fromStatusCode(Integer.valueOf(httpResponseCode)); - if (this.httpResponseCode == null) { - throw new IllegalArgumentException( - "setHTTPResponseCode was passed an invalid String value, fix error.properties or your code " - + httpResponseCode); - } - } - - /** - * Sets the REST error code. - * - * @param restErrorCode the new REST error code - */ - public void setRESTErrorCode(String restErrorCode) { - this.restErrorCode = restErrorCode; - } - - /** - * Gets the REST error code. - * - * @return the REST error code - */ - public String getRESTErrorCode() { - return this.restErrorCode; - } - - /** - * Sets the HTTP response code. - * - * @param httpResponseCode the new HTTP response code - */ - public void setHTTPResponseCode(Status httpResponseCode) { - this.httpResponseCode = httpResponseCode; - if (this.httpResponseCode == null) { - throw new IllegalArgumentException( - "setHTTPResponseCode was passed an invalid String value, fix error.properties or your code " - + httpResponseCode); - } - } - - /** - * Gets the error text. - * - * @return the error text - */ - public String getErrorText() { - return errorText; - } - - /** - * Sets the error text. - * - * @param errorText the new error text - */ - public void setErrorText(String errorText) { - this.errorText = errorText; - } - - /** - * Gets the details. - * - * @return the details - */ - public String getDetails() { - return details; - } - - /** - * Sets the details. - * - * @param details the new details - */ - public void setDetails(String details) { - this.details = details == null ? "" : details; - } - - /** - * Gets the error code string. This is also the string - * configured in Nagios to alert on - * - * @return the error code string - */ - // Get the X.Y.Z representation of the error code - public String getErrorCodeString() { - String prefix = null; - switch (disposition) { - default: - prefix = ""; - break; - case "5": - prefix = "ERR."; - break; - } - return prefix + disposition + "." + category + "." + errorCode; - } - - /** - * Gets the severity Code. This is also the string - * configured in Nagios to alert on - * - * @return the severity - */ - // Get the numerical value of severity - public String getSeverityCode(String severity) { - String severityCode = ""; - switch (severity) { - case "WARN": - severityCode = "1"; - break; - case "ERROR": - severityCode = "2"; - break; - case "FATAL": - severityCode = "3"; - break; - } - return severityCode; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "ErrorObject [errorCode=" + errorCode + ", errorText=" + errorText + ", restErrorCode=" + restErrorCode - + ", httpResponseCode=" + httpResponseCode + ", severity=" + severity + ", disposition=" + disposition - + ", category=" + category + "]"; - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/ErrorObjectNotFoundException.java b/aai-core/src/main/java/org/onap/aai/logging/ErrorObjectNotFoundException.java deleted file mode 100644 index 3daf7137..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/ErrorObjectNotFoundException.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -public class ErrorObjectNotFoundException extends Exception { - - private static final long serialVersionUID = 4115316781400786740L; - - public ErrorObjectNotFoundException() { - // TODO Auto-generated constructor stub - } - - public ErrorObjectNotFoundException(String message) { - super(message); - // TODO Auto-generated constructor stub - } - - public ErrorObjectNotFoundException(Throwable cause) { - super(cause); - // TODO Auto-generated constructor stub - } - - public ErrorObjectNotFoundException(String message, Throwable cause) { - super(message, cause); - // TODO Auto-generated constructor stub - } - - public ErrorObjectNotFoundException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - // TODO Auto-generated constructor stub - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/LogFormatTools.java b/aai-core/src/main/java/org/onap/aai/logging/LogFormatTools.java deleted file mode 100644 index 804801f1..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/LogFormatTools.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import java.time.Instant; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; - -import org.apache.commons.lang.exception.ExceptionUtils; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.util.AAIConfig; -import org.onap.aai.util.AAIConstants; - -public class LogFormatTools { - - private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern(DATE_FORMAT).withZone(ZoneOffset.UTC); - - public static String getCurrentDateTime() { - return DTF.format(ZonedDateTime.now()); - } - - public static String toDate(long timestamp) { - return DTF.format(Instant.ofEpochMilli(timestamp)); - } - - public static long toTimestamp(String date) { - return ZonedDateTime.parse(date, DTF).toInstant().toEpochMilli(); - } - - /** - * Gets the stack top. - * - * @param e the e - * @return the stack top - * @throws NumberFormatException the number format exception - * @throws AAIException the AAI exception - */ - public static String getStackTop(Throwable e) { - // StringBuilder is more efficient than StringBuffer and should only - // StringBuffer is only supposed to be used if multiple threads are modifying - // the same object and since this object is created locally not necessary - StringBuilder stackMessage = new StringBuilder(); - int maxStackTraceEntries = 10; - try { - maxStackTraceEntries = Integer.valueOf(AAIConfig.get(AAIConstants.LOGGING_MAX_STACK_TRACE_ENTRIES)); - } catch (AAIException a) { - // ignore, use default - } catch (NumberFormatException n) { - // ignore, use default - } - if (e != null) { - Throwable rootCause = ExceptionUtils.getRootCause(e); - if (rootCause != null) { - stackMessage.append("root cause=" + ExceptionUtils.getRootCause(e)); - StackTraceElement[] elements = rootCause.getStackTrace(); - int i = 0; - for (StackTraceElement element : elements) { - if (i < maxStackTraceEntries) { - stackMessage.append(" ClassName- "); - stackMessage.append(element.getClassName()); - stackMessage.append(" :LineNumber- "); - stackMessage.append(element.getLineNumber()); - stackMessage.append(" :MethodName- "); - stackMessage.append(element.getMethodName()); - } - i++; - } - } else if (e.getCause() != null) { - stackMessage.append("cause=" + e.getCause()); - StackTraceElement[] elements = e.getCause().getStackTrace(); - int i = 0; - for (StackTraceElement element : elements) { - if (i < maxStackTraceEntries) { - stackMessage.append(" ClassName- "); - stackMessage.append(element.getClassName()); - stackMessage.append(" :LineNumber- "); - stackMessage.append(element.getLineNumber()); - stackMessage.append(" :MethodName- "); - stackMessage.append(element.getMethodName()); - } - i++; - } - } else if (e.getStackTrace() != null) { - stackMessage.append("ex=" + e.toString()); - StackTraceElement[] elements = e.getStackTrace(); - int i = 0; - for (StackTraceElement element : elements) { - if (i < maxStackTraceEntries) { - stackMessage.append(" ClassName- "); - stackMessage.append(element.getClassName()); - stackMessage.append(" :LineNumber- "); - stackMessage.append(element.getLineNumber()); - stackMessage.append(" :MethodName- "); - stackMessage.append(element.getMethodName()); - } - i++; - } - } - } - return stackMessage.toString(); - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/LoggingContext.java b/aai-core/src/main/java/org/onap/aai/logging/LoggingContext.java deleted file mode 100644 index 5630fd10..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/LoggingContext.java +++ /dev/null @@ -1,417 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.MDC; - -public class LoggingContext { - - public enum StatusCode { - COMPLETE, ERROR - } - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(LoggingContext.class); - - private static final String PREVIOUS_CONTEXTS_KEY = "_PREVIOUS_CONTEXTS"; - - // Response codes from Logging Guidelines - public static final String SUCCESS = "0"; - public static final String PERMISSION_ERROR = "100"; - public static final String AVAILABILITY_TIMEOUT_ERROR = "200"; - public static final String DATA_ERROR = "300"; - public static final String SCHEMA_ERROR = "400"; - public static final String BUSINESS_PROCESS_ERROR = "500"; - public static final String UNKNOWN_ERROR = "900"; - - protected static final Map<String, String> responseMap = new HashMap(); - - static { - responseMap.put(SUCCESS, "Success"); - responseMap.put(UNKNOWN_ERROR, "Unknown error"); - } - - // Specific Log Event Fields - public enum LoggingField { - START_TIME("startTime"), REQUEST_ID("requestId"), SERVICE_INSTANCE_ID("serviceInstanceId"), SERVER_NAME( - "serverName"), SERVICE_NAME("serviceName"), PARTNER_NAME("partnerName"), STATUS_CODE( - "statusCode"), RESPONSE_CODE("responseCode"), RESPONSE_DESCRIPTION( - "responseDescription"), INSTANCE_UUID("instanceUUID"), SEVERITY( - "severity"), SERVER_IP_ADDRESS( - "serverIpAddress"), ELAPSED_TIME("elapsedTime"), SERVER( - "server"), CLIENT_IP_ADDRESS("clientIpAddress"), UNUSED( - "unused"), PROCESS_KEY("processKey"), CUSTOM_FIELD_1( - "customField1"), CUSTOM_FIELD_2( - "customField2"), CUSTOM_FIELD_3( - "customField3"), CUSTOM_FIELD_4( - "customField4"), - - // Specific Metric Log Event Fields - TARGET_ENTITY("targetEntity"), TARGET_SERVICE_NAME("targetServiceName"), - // A&AI Specific Log Event Fields - COMPONENT("component"), STOP_WATCH_START("stopWatchStart"); - - private final String text; - - private LoggingField(final String text) { - this.text = text; - } - - @Override - public String toString() { - return text; - } - } - - public static void init() { - LoggingContext.clear(); - LoggingContext.startTime(); - LoggingContext.server(); - LoggingContext.serverIpAddress(); - } - - public static void startTime() { - MDC.put(LoggingField.START_TIME.toString(), LogFormatTools.getCurrentDateTime()); - } - - public static UUID requestId() { - final String sUuid = MDC.get(LoggingField.REQUEST_ID.toString()); - - if (sUuid == null) - return null; - - return UUID.fromString(sUuid); - } - - public static void requestId(UUID requestId) { - MDC.put(LoggingField.REQUEST_ID.toString(), requestId.toString()); - } - - public static void requestId(String requestId) { - try { - if (requestId.contains(":")) { - String[] uuidParts = requestId.split(":"); - requestId = uuidParts[0]; - } - MDC.put(LoggingField.REQUEST_ID.toString(), UUID.fromString(requestId).toString()); - } catch (IllegalArgumentException e) { - final UUID generatedRequestUuid = UUID.randomUUID(); - MDC.put(LoggingField.REQUEST_ID.toString(), generatedRequestUuid.toString()); - LoggingContext.save(); - // set response code to 0 since we don't know what the outcome of this request is yet - String responseCode = LoggingContext.DATA_ERROR; - LoggingContext.responseCode(responseCode); - LoggingContext.responseDescription("Unable to use UUID " + requestId + " (Not formatted properly) "); - LoggingContext.statusCode(StatusCode.ERROR); - - LOGGER.warn("Using generated UUID=" + generatedRequestUuid); - LoggingContext.restore(); - - } - } - - public static void serviceInstanceId(String serviceInstanceId) { - MDC.put(LoggingField.SERVICE_INSTANCE_ID.toString(), serviceInstanceId); - } - - public static void serverName(String serverName) { - MDC.put(LoggingField.SERVER_NAME.toString(), serverName); - } - - public static void serviceName(String serviceName) { - MDC.put(LoggingField.SERVICE_NAME.toString(), serviceName); - } - - public static void partnerName(String partnerName) { - MDC.put(LoggingField.PARTNER_NAME.toString(), partnerName); - } - - public static void statusCode(StatusCode statusCode) { - MDC.put(LoggingField.STATUS_CODE.toString(), statusCode.toString()); - } - - public static String responseCode() { - return MDC.get(LoggingField.RESPONSE_CODE.toString()); - } - - public static void responseCode(String responseCode) { - MDC.put(LoggingField.RESPONSE_CODE.toString(), responseCode); - } - - public static void responseDescription(String responseDescription) { - MDC.put(LoggingField.RESPONSE_DESCRIPTION.toString(), responseDescription); - } - - public static Object instanceUuid() { - return UUID.fromString(MDC.get(LoggingField.INSTANCE_UUID.toString())); - } - - public static void instanceUuid(UUID instanceUuid) { - MDC.put(LoggingField.INSTANCE_UUID.toString(), instanceUuid.toString()); - } - - public static void severity(int severity) { - MDC.put(LoggingField.SEVERITY.toString(), String.valueOf(severity)); - } - - public static void successStatusFields() { - responseCode(SUCCESS); - statusCode(StatusCode.COMPLETE); - responseDescription("Success"); - } - - private static void serverIpAddress() { - try { - MDC.put(LoggingField.SERVER_IP_ADDRESS.toString(), InetAddress.getLocalHost().getHostAddress()); - } catch (UnknownHostException e) { - LOGGER.warn("Unable to resolve server IP address - will not be displayed in logged events"); - } - } - - public static void elapsedTime(long elapsedTime, TimeUnit timeUnit) { - MDC.put(LoggingField.ELAPSED_TIME.toString(), - String.valueOf(TimeUnit.MILLISECONDS.convert(elapsedTime, timeUnit))); - } - - private static void server() { - try { - MDC.put(LoggingField.SERVER.toString(), InetAddress.getLocalHost().getCanonicalHostName()); - } catch (UnknownHostException e) { - LOGGER.warn("Unable to resolve server IP address - hostname will not be displayed in logged events"); - } - } - - public static void clientIpAddress(InetAddress clientIpAddress) { - MDC.put(LoggingField.CLIENT_IP_ADDRESS.toString(), clientIpAddress.getHostAddress()); - } - - public static void clientIpAddress(String clientIpAddress) { - try { - MDC.put(LoggingField.CLIENT_IP_ADDRESS.toString(), InetAddress.getByName(clientIpAddress).getHostAddress()); - } catch (UnknownHostException e) { - // Ignore, will not be thrown since InetAddress.getByName(String) only - // checks the validity of the passed in string - } - } - - public static void unused(String unused) { - LOGGER.warn("Using field '" + LoggingField.UNUSED + "' (seems like this should go unused...)"); - MDC.put(LoggingField.UNUSED.toString(), unused); - } - - public static void processKey(String processKey) { - MDC.put(LoggingField.PROCESS_KEY.toString(), processKey); - } - - public static String customField1() { - return MDC.get(LoggingField.CUSTOM_FIELD_1.toString()); - } - - public static void customField1(String customField1) { - MDC.put(LoggingField.CUSTOM_FIELD_1.toString(), customField1); - } - - public static void customField2(String customField2) { - MDC.put(LoggingField.CUSTOM_FIELD_2.toString(), customField2); - } - - public static void customField3(String customField3) { - MDC.put(LoggingField.CUSTOM_FIELD_3.toString(), customField3); - } - - public static void customField4(String customField4) { - MDC.put(LoggingField.CUSTOM_FIELD_4.toString(), customField4); - } - - public static void component(String component) { - MDC.put(LoggingField.COMPONENT.toString(), component); - } - - public static void targetEntity(String targetEntity) { - MDC.put(LoggingField.TARGET_ENTITY.toString(), targetEntity); - } - - public static void targetServiceName(String targetServiceName) { - MDC.put(LoggingField.TARGET_SERVICE_NAME.toString(), targetServiceName); - } - - public static boolean isStopWatchStarted() { - final String rawStopWatchStart = MDC.get(LoggingField.STOP_WATCH_START.toString()); - return rawStopWatchStart != null; - } - - public static void stopWatchStart() { - MDC.put(LoggingField.STOP_WATCH_START.toString(), String.valueOf(System.nanoTime())); - } - - public static double stopWatchStop() { - final long stopWatchEnd = System.nanoTime(); - final String rawStopWatchStart = MDC.get(LoggingField.STOP_WATCH_START.toString()); - - if (rawStopWatchStart == null) - throw new StopWatchNotStartedException(); - - final Long stopWatchStart = Long.valueOf(rawStopWatchStart); - - MDC.remove(LoggingField.STOP_WATCH_START.toString()); - - final double elapsedTimeMillis = (stopWatchEnd - stopWatchStart) / 1000.0 / 1000.0; - - LoggingContext.elapsedTime((long) elapsedTimeMillis, TimeUnit.MILLISECONDS); - - return elapsedTimeMillis; - } - - public static void put(String key, String value) { - MDC.put(key, value); - } - - public static void clear() { - MDC.clear(); - } - - public static void remove(String key) { - MDC.remove(key); - } - - public static void save() { - final JSONObject context = new JSONObject(); - - for (LoggingField field : LoggingField.values()) { - if (field == LoggingField.ELAPSED_TIME) - continue; - - try { - context.put(field.toString(), MDC.get(field.toString())); - } catch (JSONException e) { - // Ignore - only occurs when the key is null (which can't happen) - // or the value is invalid (everything is converted to a string - // before it get put() to the MDC) - } - } - - final String rawJsonArray = MDC.get(PREVIOUS_CONTEXTS_KEY); - - if (rawJsonArray == null) { - final JSONArray stack = new JSONArray().put(context); - - MDC.put(PREVIOUS_CONTEXTS_KEY, stack.toString()); - } else { - try { - final JSONArray stack = new JSONArray(rawJsonArray).put(context); - - MDC.put(PREVIOUS_CONTEXTS_KEY, stack.toString()); - } catch (JSONException e) { - // Ignore - } - } - } - - public static void restore() { - - final String rawPreviousContexts = MDC.get(PREVIOUS_CONTEXTS_KEY); - - if (rawPreviousContexts == null) { - throw new LoggingContextNotExistsException(); - } - - try { - final JSONArray previousContexts = new JSONArray(rawPreviousContexts); - final JSONObject previousContext = previousContexts.getJSONObject(previousContexts.length() - 1); - - @SuppressWarnings("unchecked") - final Iterator<String> keys = previousContext.keys(); - boolean foundElapsedTime = false; - while (keys.hasNext()) { - final String key = keys.next(); - if (LoggingField.ELAPSED_TIME.toString().equals(key)) { - foundElapsedTime = true; - } - try { - MDC.put(key, previousContext.getString(key)); - } catch (JSONException e) { - // Ignore, only occurs when the key is null (cannot happen) - // or the value is invalid (they are all strings) - } - } - if (!foundElapsedTime) { - MDC.remove(LoggingField.ELAPSED_TIME.toString()); - } - MDC.put(PREVIOUS_CONTEXTS_KEY, removeLast(previousContexts).toString()); - } catch (JSONException e) { - // Ignore, the previousContext is serialized from a JSONObject - } - } - - public static void restoreIfPossible() { - try { - restore(); - } catch (LoggingContextNotExistsException e) { - // Ignore - } - } - - /** - * AJSC declares an ancient version of org.json:json in one of the parent POMs of this project. - * I tried to update our version of that library in our POM, but it's ignored because of the way - * AJSC has organized their <dependencies>. Had they put it into the <dependencyManagement> section, - * this method would not be necessary. - */ - private static JSONArray removeLast(JSONArray previousContexts) { - final JSONArray result = new JSONArray(); - - for (int i = 0; i < previousContexts.length() - 1; i++) { - try { - result.put(previousContexts.getJSONObject(i)); - } catch (JSONException e) { - // Ignore - not possible - } - } - - return result; - } - - public static Map<String, String> getCopy() { - final Map<String, String> copy = new HashMap<>(); - - for (LoggingField field : LoggingField.values()) { - final String value = MDC.get(field.toString()); - - if (value != null) - copy.put(field.toString(), value); - } - - return copy; - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/StopWatch.java b/aai-core/src/main/java/org/onap/aai/logging/StopWatch.java deleted file mode 100644 index 0d93827f..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/StopWatch.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -import org.onap.aai.logging.LoggingContext.LoggingField; - -public final class StopWatch { - - private StopWatch() { - } - - public static void start() { - LoggingContext.stopWatchStart(); - } - - public static double stop() { - return LoggingContext.stopWatchStop(); - } - - public static void conditionalStart() { - if (LoggingContext.isStopWatchStarted()) { - return; - } - start(); - } - - public static double stopIfStarted() { - if (LoggingContext.isStopWatchStarted()) { - return (stop()); - } - return (0); - } - - public static void clear() { - LoggingContext.remove(LoggingField.STOP_WATCH_START.toString()); - LoggingContext.remove(LoggingField.ELAPSED_TIME.toString()); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/logging/StopWatchNotStartedException.java b/aai-core/src/main/java/org/onap/aai/logging/StopWatchNotStartedException.java deleted file mode 100644 index e4819c5c..00000000 --- a/aai-core/src/main/java/org/onap/aai/logging/StopWatchNotStartedException.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.logging; - -public class StopWatchNotStartedException extends RuntimeException { - - private static final long serialVersionUID = -4540164295822859408L; - - public StopWatchNotStartedException() { - super(); - } - - public StopWatchNotStartedException(String message) { - super(message); - } - - public StopWatchNotStartedException(Throwable cause) { - super(cause); - } - - public StopWatchNotStartedException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/parsers/query/LegacyQueryParser.java b/aai-core/src/main/java/org/onap/aai/parsers/query/LegacyQueryParser.java index c9412733..ca735357 100644 --- a/aai-core/src/main/java/org/onap/aai/parsers/query/LegacyQueryParser.java +++ b/aai-core/src/main/java/org/onap/aai/parsers/query/LegacyQueryParser.java @@ -20,8 +20,8 @@ package org.onap.aai.parsers.query; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -49,7 +49,7 @@ import org.onap.aai.schema.enums.PropertyMetadata; */ public class LegacyQueryParser extends QueryParser implements Parsable { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(LegacyQueryParser.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LegacyQueryParser.class); private Introspector previous = null; diff --git a/aai-core/src/main/java/org/onap/aai/parsers/query/RelationshipQueryParser.java b/aai-core/src/main/java/org/onap/aai/parsers/query/RelationshipQueryParser.java index 0958a81e..e1319802 100644 --- a/aai-core/src/main/java/org/onap/aai/parsers/query/RelationshipQueryParser.java +++ b/aai-core/src/main/java/org/onap/aai/parsers/query/RelationshipQueryParser.java @@ -20,8 +20,8 @@ package org.onap.aai.parsers.query; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; @@ -41,7 +41,7 @@ import org.springframework.context.ApplicationContext; */ public class RelationshipQueryParser extends LegacyQueryParser { - private static final EELFLogger logger = EELFManager.getInstance().getLogger(RelationshipQueryParser.class); + private static final Logger logger = LoggerFactory.getLogger(RelationshipQueryParser.class); private Introspector relationship = null; diff --git a/aai-core/src/main/java/org/onap/aai/parsers/relationship/RelationshipToURI.java b/aai-core/src/main/java/org/onap/aai/parsers/relationship/RelationshipToURI.java index 0b072b88..0ee22f50 100644 --- a/aai-core/src/main/java/org/onap/aai/parsers/relationship/RelationshipToURI.java +++ b/aai-core/src/main/java/org/onap/aai/parsers/relationship/RelationshipToURI.java @@ -20,8 +20,8 @@ package org.onap.aai.parsers.relationship; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -57,7 +57,7 @@ import org.springframework.context.ApplicationContext; */ public class RelationshipToURI { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(RelationshipToURI.class); + private static final Logger LOGGER = LoggerFactory.getLogger(RelationshipToURI.class); private Introspector relationship = null; diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/Validation.java b/aai-core/src/main/java/org/onap/aai/prevalidation/Validation.java new file mode 100644 index 00000000..d420c558 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/Validation.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.prevalidation; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; +import java.util.Objects; + +public class Validation { + + @SerializedName("validationId") + private String validationId; + + @SerializedName("action") + private String action; + + @SerializedName("violations") + private List<Violation> violations; + + public String getValidationId() { + return validationId; + } + + public void setValidationId(String validationId) { + this.validationId = validationId; + } + + public List<Violation> getViolations() { + return violations; + } + + public void setViolations(List<Violation> violations) { + this.violations = violations; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Validation that = (Validation) o; + return Objects.equals(validationId, that.validationId) && + Objects.equals(action, that.action) && + Objects.equals(violations, that.violations); + } + + @Override + public int hashCode() { + return Objects.hash(validationId, action, violations); + } + + @Override + public String toString() { + return "Validation{" + + "validationId='" + validationId + '\'' + + ", action='" + action + '\'' + + ", violations=" + violations + + '}'; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationConfiguration.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationConfiguration.java new file mode 100644 index 00000000..36569dd0 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationConfiguration.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.prevalidation; + +import org.onap.aai.restclient.RestClient; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Profile("pre-validation") +@Configuration +public class ValidationConfiguration { + + @Bean(name = "validationRestClient") + @ConditionalOnProperty(name = "validation.service.client", havingValue = "two-way-ssl", matchIfMissing = true) + public RestClient validationRestClientTwoWaySSL() { + return new ValidationServiceRestClient(); + } + + @Bean(name = "validationRestClient") + @ConditionalOnProperty(name = "validation.service.client", havingValue = "no-auth") + public RestClient validationRestClientNoAuth() { + return new ValidationServiceNoAuthClient(); + } + + @Bean(name = "validationRestClient") + @ConditionalOnProperty(name = "validation.service.client", havingValue = "one-way-ssl") + public RestClient validationRestClientOneWaySSL() { + return new ValidationServiceOneWayClient(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java new file mode 100644 index 00000000..78083ecb --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java @@ -0,0 +1,299 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.prevalidation; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import org.apache.http.conn.ConnectTimeoutException; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.rest.ueb.NotificationEvent; +import org.onap.aai.restclient.RestClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * <b>ValidationService</b> routes all the writes to the database + * excluding deletes for now to the validation service to verify + * that the request is an valid one before committing to the database + */ +@Service +@Profile("pre-validation") +public class ValidationService { + + /** + * Error indicating that the service trying to connect is down + */ + static final String CONNECTION_REFUSED_STRING = + "Connection refused to the validation microservice due to service unreachable"; + + /** + * Error indicating that the server is unable to reach the port + * Could be server related connectivity issue + */ + static final String CONNECTION_TIMEOUT_STRING = + "Connection timeout to the validation microservice as this could " + + "indicate the server is unable to reach port, " + + "please check on server by running: nc -w10 -z -v ${VALIDATION_HOST} ${VALIDATION_PORT}"; + + /** + * Error indicating that the request exceeded the allowed time + * + * Note: This means that the service could be active its + * just taking some time to process our request + */ + static final String REQUEST_TIMEOUT_STRING = + "Request to validation service took longer than the currently set timeout"; + + static final String VALIDATION_ENDPOINT = "/v1/app/validate"; + static final String VALIDATION_HEALTH_ENDPOINT = "/v1/core/core-service/info"; + + private static final String ENTITY_TYPE = "entity-type"; + private static final String ACTION = "action"; + private static final String SOURCE_NAME = "source-name"; + + private static final String DELETE = "DELETE"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ValidationService.class); + + private final RestClient validationRestClient; + + private final String appName; + + private final Set<String> validationNodeTypes; + + private List<Pattern> exclusionList; + + private final Gson gson; + + @Autowired + public ValidationService( + @Qualifier("validationRestClient") RestClient validationRestClient, + @Value("${spring.application.name}") String appName, + @Value("${validation.service.node-types}") String validationNodes, + @Value("${validation.service.exclusion-regexes}") String exclusionRegexes + ){ + this.validationRestClient = validationRestClient; + this.appName = appName; + + this.validationNodeTypes = Arrays + .stream(validationNodes.split(",")) + .collect(Collectors.toSet()); + + if(exclusionRegexes == null || exclusionRegexes.isEmpty()){ + this.exclusionList = new ArrayList<>(); + } else { + this.exclusionList = Arrays + .stream(exclusionRegexes.split(",")) + .map(Pattern::compile) + .collect(Collectors.toList()); + } + this.gson = new Gson(); + LOGGER.info("Successfully initialized the pre validation service"); + } + + @PostConstruct + public void initialize() throws AAIException { + + Map<String, String> httpHeaders = new HashMap<>(); + + httpHeaders.put("X-FromAppId", appName); + httpHeaders.put("X-TransactionID", UUID.randomUUID().toString()); + httpHeaders.put("Content-Type", "application/json"); + + ResponseEntity<String> healthCheckResponse = null; + + try { + + healthCheckResponse = validationRestClient.execute( + VALIDATION_HEALTH_ENDPOINT, + HttpMethod.GET, + httpHeaders, + null + ); + + } catch(Exception ex){ + AAIException validationException = new AAIException("AAI_4021", ex); + throw validationException; + } + + if(!isSuccess(healthCheckResponse)){ + throw new AAIException("AAI_4021"); + } + + LOGGER.info("Successfully connected to the validation service endpoint"); + } + + public boolean shouldValidate(String nodeType){ + return this.validationNodeTypes.contains(nodeType); + } + + public void validate(List<NotificationEvent> notificationEvents) throws AAIException { + + if(notificationEvents == null || notificationEvents.isEmpty()){ + return; + } + + { + // Get the first notification and if the source of that notification + // is in one of the regexes then we skip sending it to validation + NotificationEvent notification = notificationEvents.get(0); + Introspector eventHeader = notification.getEventHeader(); + if(eventHeader != null){ + String source = eventHeader.getValue(SOURCE_NAME); + for(Pattern pattern: exclusionList){ + if(pattern.matcher(source).matches()){ + return; + } + } + } + + } + + for (NotificationEvent event : notificationEvents) { + + Introspector eventHeader = event.getEventHeader(); + + if(eventHeader == null){ + // Should I skip processing the request and let it continue + // or fail the request and cause client impact + continue; + } + + String entityType = eventHeader.getValue(ENTITY_TYPE); + String action = eventHeader.getValue(ACTION); + + /** + * Skipping the delete events for now + * Note: Might revisit this later when validation supports DELETE events + */ + if(DELETE.equalsIgnoreCase(action)){ + continue; + } + + if (this.shouldValidate(entityType)) { + List<String> violations = this.preValidate(event.getNotificationEvent()); + if(!violations.isEmpty()){ + AAIException aaiException = new AAIException("AAI_4019"); + aaiException.getTemplateVars().addAll(violations); + throw aaiException; + } + } + } + } + + List<String> preValidate(String body) throws AAIException { + + Map<String, String> httpHeaders = new HashMap<>(); + + httpHeaders.put("X-FromAppId", appName); + httpHeaders.put("X-TransactionID", UUID.randomUUID().toString()); + httpHeaders.put("Content-Type", "application/json"); + + List<String> violations = new ArrayList<>(); + ResponseEntity responseEntity; + try { + + responseEntity = validationRestClient.execute( + VALIDATION_ENDPOINT, + HttpMethod.POST, + httpHeaders, + body + ); + + if(isSuccess(responseEntity)){ + LOGGER.debug("Validation Service returned following response status code {} and body {}", responseEntity.getStatusCodeValue(), responseEntity.getBody()); + } else { + Validation validation = null; + try { + validation = gson.fromJson(responseEntity.getBody().toString(), Validation.class); + } catch(JsonSyntaxException jsonException){ + LOGGER.warn("Unable to convert the response body {}", jsonException.getMessage()); + } + + if(validation == null){ + LOGGER.debug( + "Validation Service following status code {} with body {}", + responseEntity.getStatusCodeValue(), + responseEntity.getBody() + ); + } else { + violations.addAll(extractViolations(validation)); + } + } + } catch(Exception e){ + // If the exception cause is client side timeout + // then proceed as if it passed validation + // resources microservice shouldn't be blocked because of validation service + // is taking too long or if the validation service is down + // Any other exception it should block the request from passing? + if(e.getCause() instanceof SocketTimeoutException){ + LOGGER.error(REQUEST_TIMEOUT_STRING, e.getCause()); + } else if(e.getCause() instanceof ConnectException){ + LOGGER.error(CONNECTION_REFUSED_STRING, e.getCause()); + } else if(e.getCause() instanceof ConnectTimeoutException){ + LOGGER.error(CONNECTION_TIMEOUT_STRING, e.getCause()); + } else { + LOGGER.error("Unknown exception thrown please investigate", e.getCause()); + } + } + return violations; + } + + boolean isSuccess(ResponseEntity responseEntity){ + return responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful(); + } + + List<String> extractViolations(Validation validation) { + + List<String> errorMessages = new ArrayList<>(); + + if(validation == null){ + return errorMessages; + } + + List<Violation> violations = validation.getViolations(); + + if (violations != null && !violations.isEmpty()) { + for (Violation violation : validation.getViolations()) { + LOGGER.info(violation.getErrorMessage()); + errorMessages.add(violation.getErrorMessage()); + } + } + + return errorMessages; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceNoAuthClient.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceNoAuthClient.java new file mode 100644 index 00000000..d373ae78 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceNoAuthClient.java @@ -0,0 +1,79 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2018-19 AT&T Intellectual Property. 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.prevalidation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.onap.aai.restclient.NoAuthRestClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.util.MultiValueMap; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +public class ValidationServiceNoAuthClient extends NoAuthRestClient { + + private static Logger logger = LoggerFactory.getLogger(ValidationServiceNoAuthClient.class); + + @Value("${validation.service.base.url}") + private String baseUrl; + + @Value("${validation.service.timeout-in-milliseconds}") + private Integer timeout; + + @Override + protected HttpComponentsClientHttpRequestFactory getHttpRequestFactory() throws Exception { + HttpComponentsClientHttpRequestFactory requestFactory = super.getHttpRequestFactory(); + requestFactory.setConnectionRequestTimeout(timeout); + requestFactory.setReadTimeout(timeout); + requestFactory.setConnectTimeout(timeout); + return requestFactory; + } + + @Override + public String getBaseUrl() { + return baseUrl; + } + + @Override + public MultiValueMap<String, String> getHeaders(Map<String, String> headers) { + HttpHeaders httpHeaders = new HttpHeaders(); + + String defaultAccept = headers.getOrDefault(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString()); + String defaultContentType = + headers.getOrDefault(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString()); + + if (headers.isEmpty()) { + httpHeaders.setAccept(Collections.singletonList(MediaType.parseMediaType(defaultAccept))); + httpHeaders.setContentType(MediaType.parseMediaType(defaultContentType)); + } + + httpHeaders.add("X-FromAppId", appName); + httpHeaders.add("X-TransactionId", UUID.randomUUID().toString()); + headers.forEach(httpHeaders::add); + return httpHeaders; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceOneWayClient.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceOneWayClient.java new file mode 100644 index 00000000..40efd469 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceOneWayClient.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2018-19 AT&T Intellectual Property. 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.prevalidation; + +import org.onap.aai.restclient.OneWaySSLRestClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.util.MultiValueMap; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +public class ValidationServiceOneWayClient extends OneWaySSLRestClient { + + @Value("${validation.service.base.url}") + private String baseUrl; + + @Value("${validation.service.ssl.trust-store}") + private String truststorePath; + + @Value("${validation.service.ssl.trust-store-password}") + private String truststorePassword; + + @Value("${validation.service.timeout-in-milliseconds}") + private Integer timeout; + + @Override + public String getBaseUrl() { + return baseUrl; + } + + @Override + protected String getTruststorePath() { + return truststorePath; + } + + @Override + protected char[] getTruststorePassword() { + return truststorePassword.toCharArray(); + } + + @Override + protected HttpComponentsClientHttpRequestFactory getHttpRequestFactory() throws Exception { + HttpComponentsClientHttpRequestFactory requestFactory = super.getHttpRequestFactory(); + requestFactory.setConnectionRequestTimeout(timeout); + requestFactory.setReadTimeout(timeout); + requestFactory.setConnectTimeout(timeout); + return requestFactory; + } + + @Override + public MultiValueMap<String, String> getHeaders(Map<String, String> headers) { + HttpHeaders httpHeaders = new HttpHeaders(); + + String defaultAccept = headers.getOrDefault(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString()); + String defaultContentType = + headers.getOrDefault(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString()); + + if (headers.isEmpty()) { + httpHeaders.setAccept(Collections.singletonList(MediaType.parseMediaType(defaultAccept))); + httpHeaders.setContentType(MediaType.parseMediaType(defaultContentType)); + } + + httpHeaders.add("X-FromAppId", appName); + httpHeaders.add("X-TransactionId", UUID.randomUUID().toString()); + httpHeaders.add("X-TransactionId", appName); + headers.forEach(httpHeaders::add); + return httpHeaders; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceRestClient.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceRestClient.java new file mode 100644 index 00000000..8674272e --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationServiceRestClient.java @@ -0,0 +1,108 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.prevalidation; + +import org.onap.aai.restclient.TwoWaySSLRestClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.util.MultiValueMap; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +public class ValidationServiceRestClient extends TwoWaySSLRestClient { + + @Value("${validation.service.base.url}") + private String baseUrl; + + @Value("${validation.service.ssl.key-store}") + private String keystorePath; + + @Value("${validation.service.ssl.trust-store}") + private String truststorePath; + + @Value("${validation.service.ssl.key-store-password}") + private String keystorePassword; + + @Value("${validation.service.ssl.trust-store-password}") + private String truststorePassword; + + @Value("${validation.service.timeout-in-milliseconds}") + private Integer timeout; + + @Override + public String getBaseUrl() { + return baseUrl; + } + + @Override + protected String getKeystorePath() { + return keystorePath; + } + + @Override + protected String getTruststorePath() { + return truststorePath; + } + + @Override + protected char[] getKeystorePassword() { + return keystorePassword.toCharArray(); + } + + @Override + protected char[] getTruststorePassword() { + return truststorePassword.toCharArray(); + } + + protected HttpComponentsClientHttpRequestFactory getHttpRequestFactory() throws Exception { + HttpComponentsClientHttpRequestFactory requestFactory = super.getHttpRequestFactory(); + requestFactory.setConnectionRequestTimeout(timeout); + requestFactory.setReadTimeout(timeout); + requestFactory.setConnectTimeout(timeout); + return requestFactory; + } + + @Override + public MultiValueMap<String, String> getHeaders(Map<String, String> headers) { + HttpHeaders httpHeaders = new HttpHeaders(); + + String defaultAccept = headers.getOrDefault(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString()); + String defaultContentType = + headers.getOrDefault(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString()); + + if (headers.isEmpty()) { + httpHeaders.setAccept(Collections.singletonList(MediaType.parseMediaType(defaultAccept))); + httpHeaders.setContentType(MediaType.parseMediaType(defaultContentType)); + } + + httpHeaders.add("X-FromAppId", appName); + httpHeaders.add("X-TransactionId", UUID.randomUUID().toString()); + headers.forEach(httpHeaders::add); + return httpHeaders; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/Violation.java b/aai-core/src/main/java/org/onap/aai/prevalidation/Violation.java new file mode 100644 index 00000000..e5472a7c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/prevalidation/Violation.java @@ -0,0 +1,128 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2018-19 AT&T Intellectual Property. 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.prevalidation; + +import com.google.gson.annotations.SerializedName; + +import java.util.Objects; + +public class Violation { + + @SerializedName("violationId") + private String violationId; + + @SerializedName("modelName") + private String modelName; + + @SerializedName("category") + private String category; + + @SerializedName("severity") + private String severity; + + @SerializedName("violationType") + private String violationType; + + @SerializedName("errorMessage") + private String errorMessage; + + public String getViolationId() { + return violationId; + } + + public void setViolationId(String violationId) { + this.violationId = violationId; + } + + public String getModelName() { + return modelName; + } + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getSeverity() { + return severity; + } + + public void setSeverity(String severity) { + this.severity = severity; + } + + public String getViolationType() { + return violationType; + } + + public void setViolationType(String violationType) { + this.violationType = violationType; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public String toString() { + return "Violation{" + + "violationId='" + violationId + '\'' + + ", modelName='" + modelName + '\'' + + ", category='" + category + '\'' + + ", severity='" + severity + '\'' + + ", violationType='" + violationType + '\'' + + ", errorMessage='" + errorMessage + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Violation violation = (Violation) o; + return Objects.equals(violationId, violation.violationId) && + Objects.equals(modelName, violation.modelName) && + Objects.equals(category, violation.category) && + Objects.equals(severity, violation.severity) && + Objects.equals(violationType, violation.violationType) && + Objects.equals(errorMessage, violation.errorMessage); + } + + @Override + public int hashCode() { + return Objects.hash(violationId, modelName, category, severity, violationType, errorMessage); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java index 272a2c21..1dc3f1d6 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java @@ -64,7 +64,6 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) { super(loader, source); - traversal = (GraphTraversal<Vertex, E>) __.<E>start(); } @@ -73,7 +72,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { * Instantiates a new graph traversal builder. * * @param loader the loader - * @param start the start + * @param start the start */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) { super(loader, source, start); @@ -89,12 +88,33 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) { // correct value call because the index is registered as an Integer - traversal.has(key, this.correctObjectType(value)); - + this.vertexHas(key, this.correctObjectType(value)); stepIndex++; return (QueryBuilder<Vertex>) this; } + @Override + protected void vertexHas(String key, Object value) { + traversal.has(key, value); + } + + @Override + protected void vertexHasNot(String key) { + traversal.hasNot(key); + } + + @Override + protected void vertexHas(String key) { + traversal.has(key); + } + + //TODO: Remove this once we test this - at this point i dont thib this is required + //because predicare is an object + /*@Override + protected void vertexHas(final String key, final P<?> predicate) { + traversal.has(key, predicate); + }*/ + /** * @{inheritDoc} */ @@ -107,7 +127,21 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { correctedValues.add(this.correctObjectType(item)); } - traversal.has(key, P.within(correctedValues)); + this.vertexHas(key, P.within(correctedValues)); + stepIndex++; + return (QueryBuilder<Vertex>) this; + } + + /** + * @{inheritDoc} + */ + public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) { + ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(","))); + int size = values.size(); + for (int i = 0; i < size; i++) { + values.set(i, values.get(i).trim()); + } + this.vertexHas(key, P.within(values)); stepIndex++; return (QueryBuilder<Vertex>) this; @@ -120,7 +154,8 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) { // correct value call because the index is registered as an Integer - traversal.has(key, org.janusgraph.core.attribute.Text.textPrefix(value)); + //TODO Check if this needs to be in QB and add these as internal + this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value)); stepIndex++; return (QueryBuilder<Vertex>) this; @@ -131,8 +166,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesByProperty(String key) { - - traversal.has(key); + this.vertexHas(key); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -142,8 +176,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) { - - traversal.hasNot(key); + this.vertexHasNot(key); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -155,8 +188,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) { // correct value call because the index is registered as an Integer - traversal.has(key, P.neq(this.correctObjectType(value))); - + this.vertexHas(key, P.neq(this.correctObjectType(value))); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -173,16 +205,14 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { correctedValues.add(this.correctObjectType(item)); } - traversal.has(key, P.without(correctedValues)); - + this.vertexHas(key, P.without(correctedValues)); stepIndex++; return (QueryBuilder<Vertex>) this; } @Override public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) { - - traversal.has(key, P.gte(this.correctObjectType(value))); + this.vertexHas(key, P.gte(this.correctObjectType(value))); stepIndex++; return (QueryBuilder<Vertex>) this; @@ -190,8 +220,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { @Override public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) { - - traversal.has(key, P.lte(this.correctObjectType(value))); + this.vertexHas(key, P.lte(this.correctObjectType(value))); stepIndex++; return (QueryBuilder<Vertex>) this; @@ -214,7 +243,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) { for (Map.Entry<String, String> es : map.entrySet()) { - traversal.has(es.getKey(), es.getValue()); + this.vertexHas(es.getKey(), es.getValue()); stepIndex++; } traversal.has(AAIProperties.NODE_TYPE, type); @@ -234,7 +263,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { bValue = (Boolean) value; } - traversal.has(key, bValue); + this.vertexHas(key, bValue); stepIndex++; } return (QueryBuilder<Vertex>) this; @@ -257,9 +286,9 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { if (val != null) { // this is because the index is registered as an Integer if (val.getClass().equals(Long.class)) { - traversal.has(key, new Integer(val.toString())); + this.vertexHas(key, new Integer(val.toString())); } else { - traversal.has(key, val); + this.vertexHas(key, val); } stepIndex++; } @@ -290,9 +319,9 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } // this is because the index is registered as an Integer if (val.getClass().equals(Long.class)) { - traversal.has(prop, new Integer(val.toString())); + this.vertexHas(prop, new Integer(val.toString())); } else { - traversal.has(prop, val); + this.vertexHas(prop, val); } stepIndex++; } @@ -326,7 +355,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) - throws AAIException { + throws AAIException { createTraversal(type, parent, child, false); return (QueryBuilder<Vertex>) this; @@ -334,13 +363,13 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { @Override public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child) - throws AAIException { + throws AAIException { this.createTraversal(type, parent, child, true); return (QueryBuilder<Vertex>) this; } private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge) - throws AAIException { + throws AAIException { String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT); if ("true".equals(isAbstractType)) { markParentBoundary(); @@ -352,18 +381,17 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } /** - * * @{inheritDoc} */ @Override public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, - List<String> labels) throws AAIException { + List<String> labels) throws AAIException { this.edgeQueryToVertex(type, out, in, labels); return (QueryBuilder<Vertex>) this; } private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, - Introspector child, boolean isPrivateEdge) throws AAIException { + Introspector child, boolean isPrivateEdge) throws AAIException { String childName = child.getDbName(); String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS); String[] inheritors = inheritorMetadata.split(","); @@ -399,7 +427,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { innerTraversal.in(inLabels.toArray(new String[inLabels.size()])); } else { innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), - __.in(inLabels.toArray(new String[inLabels.size()]))); + __.in(inLabels.toArray(new String[inLabels.size()]))); } innerTraversal.has(AAIProperties.NODE_TYPE, childName); @@ -411,7 +439,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, - List<String> labels) throws AAIException { + List<String> labels) throws AAIException { Introspector outObj = loader.introspectorFromName(outNodeType); Introspector inObj = loader.introspectorFromName(inNodeType); this.edgeQuery(type, outObj, inObj, labels); @@ -556,6 +584,23 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { return this; } + @Override + public QueryBuilder<E> valueMap() { + this.traversal.valueMap(); + stepIndex++; + + return this; + } + + @Override + public QueryBuilder<E> valueMap(String... names) { + this.traversal.valueMap(names); + stepIndex++; + + return this; + } + + /** * {@inheritDoc} */ @@ -649,12 +694,12 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { * Edge query. * * @param outObj the out type - * @param inObj the in type + * @param inObj the in type * @throws NoEdgeRuleFoundException * @throws AAIException */ private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) - throws AAIException { + throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); @@ -686,7 +731,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } if (rules.isEmpty()) { throw new NoEdgeRuleFoundException( - "No edge rules found for " + outType + " and " + inType + " of type " + type.toString()); + "No edge rules found for " + outType + " and " + inType + " of type " + type.toString()); } } @@ -711,7 +756,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { traversal.in(inLabels.toArray(new String[inLabels.size()])); } else { traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), - __.in(inLabels.toArray(new String[inLabels.size()]))); + __.in(inLabels.toArray(new String[inLabels.size()]))); } stepIndex++; @@ -724,12 +769,12 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { * Edge query. * * @param outObj the out type - * @param inObj the in type + * @param inObj the in type * @throws NoEdgeRuleFoundException * @throws AAIException */ private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) - throws AAIException { + throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); @@ -777,7 +822,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { traversal.inE(inLabels.toArray(new String[inLabels.size()])); } else { traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), - __.inE(inLabels.toArray(new String[inLabels.size()]))); + __.inE(inLabels.toArray(new String[inLabels.size()]))); } } @@ -902,4 +947,5 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { return (QueryBuilder<Edge>) this; } + } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java index 98da0766..fcfeb268 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java @@ -22,6 +22,8 @@ package org.onap.aai.query.builder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -54,10 +56,14 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { private static final String ARGUMENT2 = "#!#argument#!#"; private static final String HAS = ".has('"; + private static final String SINGLE_QUOTE = "'"; + private static final String ESCAPE_SINGLE_QUOTE = "\\'"; private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell(); private GraphTraversal<?, ?> completeTraversal = null; protected List<String> list = null; + private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class); + /** * Instantiates a new gremlin query builder. * @@ -79,6 +85,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { list = new ArrayList<>(); } + @Override public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) { // TODO not implemented because this is implementation is no longer used @@ -87,6 +94,22 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return (QueryBuilder<Vertex>) this; } + @Override + protected void vertexHas(String key, Object value) { + list.add(HAS + key + "', " + value + ")"); + } + + @Override + protected void vertexHasNot(String key) { + list.add(".hasNot('" + key + "')"); + + } + + @Override + protected void vertexHas(String key) { + list.add(HAS + key + "')"); + } + /** * @{inheritDoc} */ @@ -95,11 +118,16 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { String term = ""; if (value != null && !(value instanceof String)) { + String valueString = value.toString(); + if (valueString.indexOf('\'') != -1) { + value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE); + } + LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value); term = value.toString(); } else { term = "'" + value + "'"; } - list.add(HAS + key + "', " + term + ")"); + this.vertexHas(key, term); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -109,7 +137,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) { - list.add(HAS + key + "', " + value + ")"); + this.vertexHas(key, value); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -125,7 +153,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { bValue = (Boolean) value; } - list.add(HAS + key + "', " + bValue + ")"); + this.vertexHas(key, bValue); stepIndex++; } return (QueryBuilder<Vertex>) this; @@ -148,7 +176,31 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } String argument = Joiner.on(",").join(arguments); predicate = predicate.replace(ARGUMENT2, argument); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); + stepIndex++; + return (QueryBuilder<Vertex>) this; + } + + /** + * @{inheritDoc} + */ + @Override + public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) { + ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(","))); + //add the single quotes + for (int i = 0; i < arguments.size(); i++) { + if(arguments.get(i) != null && !arguments.get(i).startsWith("'") + && !arguments.get(i).endsWith("'")) { + arguments.set(i,"'" + arguments.get(i).trim() + "'"); + } + else { + arguments.set(i, arguments.get(i).trim()); + } + } + String predicate = "P.within(#!#argument#!#)"; + String argument = Joiner.on(",").join(arguments); + predicate = predicate.replace(ARGUMENT2, argument); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -158,8 +210,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesByProperty(String key) { - - list.add(HAS + key + "')"); + this.vertexHas(key); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -169,8 +220,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) { - - list.add(".hasNot('" + key + "')"); + this.vertexHasNot(key); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -180,7 +230,6 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) { - String term = ""; String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)"; if (value != null && !(value instanceof String)) { @@ -189,7 +238,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { term = "'" + value + "'"; } predicate = predicate.replace(ARGUMENT2, term); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -208,7 +257,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { term = "'" + value + "'"; } predicate = predicate.replace(ARGUMENT2, term); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -230,7 +279,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } String argument = Joiner.on(",").join(arguments); predicate = predicate.replace(ARGUMENT2, argument); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -245,7 +294,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { term = "'" + value + "'"; } predicate = predicate.replace("#!#argument1#!#", term); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -260,7 +309,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { term = "'" + value + "'"; } predicate = predicate.replace("#!#argument1#!#", term); - list.add(HAS + key + "', " + predicate + ")"); + this.vertexHas(key, predicate); stepIndex++; return (QueryBuilder<Vertex>) this; } @@ -281,6 +330,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) { for (Map.Entry<String, String> es : map.entrySet()) { + //TODO what is this and where is it used - need to check list.add(HAS + es.getKey() + "', '" + es.getValue() + "')"); stepIndex++; } @@ -594,6 +644,20 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } @Override + public QueryBuilder<E> fold() { + this.list.add(".fold()"); + stepIndex++; + return this; + } + + @Override + public QueryBuilder<E> id() { + this.list.add(".id()"); + stepIndex++; + return this; + } + + @Override public QueryBuilder<E> dedup() { this.list.add(".dedup()"); stepIndex++; @@ -656,6 +720,31 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return this; } + + @Override + public QueryBuilder<E> valueMap() { + this.list.add(".valueMap()"); + stepIndex++; + + return this; + } + + @Override + public QueryBuilder<E> valueMap(String... names) { + String stepString = ".valueMap('"; + for (int i = 0; i < names.length; i++) { + stepString = stepString + names[i] + "'"; + if (i != (names.length - 1)) { + stepString = stepString + ",'"; + } + } + stepString = stepString + ")"; + this.list.add(stepString); + stepIndex++; + + return this; + } + /** * {@inheritDoc} @@ -853,4 +942,9 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return (QueryBuilder<Edge>) this; } + /* + * This is required for the subgraphstrategies to work + */ + + } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java index 710db480..9cd35ab9 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java @@ -61,6 +61,7 @@ public class GremlinTraversal<E> extends GremlinQueryBuilder<E> { this.factory = new TraversalStrategy(this.loader, this); } + protected GremlinTraversal(List<String> traversal, Loader loader, GraphTraversalSource source, GremlinQueryBuilder<E> gtb) { super(loader, source); diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/HistoryGremlinTraversal.java b/aai-core/src/main/java/org/onap/aai/query/builder/HistoryGremlinTraversal.java new file mode 100644 index 00000000..5b14995c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/query/builder/HistoryGremlinTraversal.java @@ -0,0 +1,170 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.query.builder; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.parsers.query.TraversalStrategy; + +import javax.ws.rs.core.MultivaluedMap; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * The Class GremlinTraversal. + */ +public class HistoryGremlinTraversal<E> extends GremlinTraversal<E> { + + /** + * Instantiates a new gremlin traversal. + * + * @param loader the loader + */ + public HistoryGremlinTraversal(Loader loader, GraphTraversalSource source) { + super(loader, source); + } + + /** + * Instantiates a new gremlin traversal. + * + * @param loader the loader + * @param start the start + */ + public HistoryGremlinTraversal(Loader loader, GraphTraversalSource source, Vertex start) { + super(loader, source, start); + } + + + protected HistoryGremlinTraversal(List<String> traversal, Loader loader, GraphTraversalSource source, + GremlinQueryBuilder<E> gtb) { + super(traversal, loader, source, gtb); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryParser createQueryFromURI(URI uri) throws UnsupportedEncodingException, AAIException { + return factory.buildURIParser(uri); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryParser createQueryFromRelationship(Introspector relationship) + throws UnsupportedEncodingException, AAIException { + return factory.buildRelationshipParser(relationship); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryParser createQueryFromURI(URI uri, MultivaluedMap<String, String> queryParams) + throws UnsupportedEncodingException, AAIException { + return factory.buildURIParser(uri, queryParams); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryParser createQueryFromObjectName(String objName) { + return factory.buildObjectNameParser(objName); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryBuilder<E> newInstance(Vertex start) { + return new HistoryGremlinTraversal<>(loader, source, start); + } + + /** + * @{inheritDoc} + */ + @Override + public QueryBuilder<E> newInstance() { + return new HistoryGremlinTraversal<>(loader, source); + } + + @Override + protected QueryBuilder<E> cloneQueryAtStep(int index) { + + int idx = index; + + if (idx == 0) { + idx = stepIndex; + } + + List<String> newList = new ArrayList<>(); + for (int i = 0; i < idx; i++) { + newList.add(this.list.get(i)); + } + + return new HistoryGremlinTraversal<>(newList, loader, source, this); + } + + @Override + protected void vertexHas(String key, Object value) { + super.vertexHas(key, value); + touchHistoryProperties(key, value); + } + + @Override + protected void vertexHasNot(String key) { + super.vertexHasNot(key); + touchHistoryProperties(key); + + } + + @Override + protected void vertexHas(String key) { + super.vertexHas(key); + touchHistoryProperties(key); + } + + /* + * This is required for the subgraphstrategies to work + */ + private void touchHistoryProperties(String key){ + if(key != null && !key.isEmpty() && !key.equals(AAIProperties.NODE_TYPE)) { + list.add(".where(__.properties('" + key + "'))"); + } + + } + + private void touchHistoryProperties(String key, Object value) { + if(key != null && !key.isEmpty() && !key.equals(AAIProperties.NODE_TYPE)) { + list.add(".where(__.properties('" + key + "').hasValue(" + value + "))"); + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/HistoryTraversalURIOptimizedQuery.java b/aai-core/src/main/java/org/onap/aai/query/builder/HistoryTraversalURIOptimizedQuery.java new file mode 100644 index 00000000..aeb5cd19 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/query/builder/HistoryTraversalURIOptimizedQuery.java @@ -0,0 +1,93 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.query.builder; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.schema.enums.ObjectMetadata; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class HistoryTraversalURIOptimizedQuery<E> extends TraversalURIOptimizedQuery { + + protected Map<Integer, String> stepToAaiUri = new HashMap<>(); + + public HistoryTraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source) { + super(loader, source); + } + + public HistoryTraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source, Vertex start) { + super(loader, source, start); + } + + protected HistoryTraversalURIOptimizedQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source, + GraphTraversalBuilder gtb) { + super(traversal, loader, source, gtb); + } + + protected HistoryTraversalURIOptimizedQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source, + GraphTraversalBuilder gtb, Map<Integer, String> stepToAaiUri) { + super(traversal, loader, source, gtb, stepToAaiUri); + } + + @Override + protected void vertexHas(String key, Object value) { + super.vertexHas(key, value); + touchHistoryProperties(key, value); + } + + @Override + protected void vertexHasNot(String key) { + super.vertexHasNot(key); + touchHistoryProperties(key); + } + + @Override + protected void vertexHas(String key) { + super.vertexHas(key); + touchHistoryProperties(key); + } + + private void touchHistoryProperties(String key){ + if(key != null && !key.isEmpty() && !key.equals(AAIProperties.NODE_TYPE)) { + traversal.where(__.properties(key)); + } + } + + private void touchHistoryProperties(String key, Object value){ + if(key != null && !key.isEmpty() && !key.equals(AAIProperties.NODE_TYPE)) { + traversal.where(__.properties(key).hasValue(value)); + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java index a214811c..007f9798 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java @@ -20,14 +20,6 @@ package org.onap.aai.query.builder; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.MultivaluedMap; - import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; @@ -42,11 +34,17 @@ import org.onap.aai.edges.enums.EdgeType; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; -import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.query.QueryParserStrategy; import org.springframework.context.ApplicationContext; +import javax.ws.rs.core.MultivaluedMap; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + /** * The Class QueryBuilder. */ @@ -113,9 +111,21 @@ public abstract class QueryBuilder<E> implements Iterator<E> { */ public abstract QueryBuilder<Vertex> getVerticesByProperty(String key, Object value); + + /** + * Gets the edges by property. + * + * @param key the key + * @param value the value + * @return the vertices by property + */ + public QueryBuilder<Edge> getEdgesByProperty(String key, Object value) { + return this.has(key, value.toString()); + } + /** * filters by all the values for this property - * + * * @param key * @param values * @return vertices that match these values @@ -126,7 +136,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * filters by all the values for this property - * + * * @param key * @param values * @return vertices that match these values @@ -134,10 +144,18 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public abstract QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values); /** + * filters by all the values for this property + * + * @param key + * @param value in comma delimited string format + * @return vertices that match these values + */ + public abstract QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value); + + /** * Gets the vertices that have this property key. * * @param key the key - * @param value the value * @return the vertices by property */ public abstract QueryBuilder<Vertex> getVerticesByProperty(String key); @@ -146,14 +164,13 @@ public abstract class QueryBuilder<E> implements Iterator<E> { * Gets the vertices that do not have this property key. * * @param key the key - * @param value the value * @return the vertices by property */ public abstract QueryBuilder<Vertex> getVerticesExcludeByProperty(String key); /** * filters by elements that start with the value for this property - * + * * @param key * @param value * @return vertices that match these values @@ -171,7 +188,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * filters by all the values for this property and excludes the vertices - * + * * @param key * @param values * @return vertices that match these values @@ -182,7 +199,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * filters by all the values for this property and excludes the vertices - * + * * @param key * @param values * @return vertices that match these values @@ -191,18 +208,18 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * filters by all the values greater than for this property - * + * * @param key - * @param values + * @param value * @return vertices that match these values */ public abstract QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value); /** * filters by all the values less than for this property - * + * * @param key - * @param values + * @param value * @return vertices that match these values */ @@ -334,7 +351,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * - * @param MissingOptionalParameter + * @param edgeType * @param outNodeType * @param inNodeType * @return @@ -348,6 +365,22 @@ public abstract class QueryBuilder<E> implements Iterator<E> { return this.createEdgeTraversal(outNodeType, inNodeType); } + /** + * + * @param edgeType + * @param outNodeType + * @param inNodeType + * @return + * @throws AAIException + */ + public QueryBuilder<Vertex> createEdgeTraversalWithLabels(MissingOptionalParameter edgeType, String outNodeType, + String inNodeType, List<String> labels) throws AAIException { + /* + * When no optional parameter edgetype is sent get all edges between the 2 nodetypes + */ + return this.createEdgeTraversalWithLabels(outNodeType, inNodeType, labels); + } + public QueryBuilder<Vertex> createEdgeTraversal(String outNodeType, String inNodeType) throws AAIException { Introspector out = loader.introspectorFromName(outNodeType); @@ -380,6 +413,38 @@ public abstract class QueryBuilder<E> implements Iterator<E> { return queryBuilder; } + public QueryBuilder<Vertex> createEdgeTraversalWithLabels(String outNodeType, String inNodeType, List<String> labels) throws AAIException { + + Introspector out = loader.introspectorFromName(outNodeType); + Introspector in = loader.introspectorFromName(inNodeType); + + QueryBuilder<Vertex> cousinBuilder = null; + QueryBuilder<Vertex> treeBuilder = null; + QueryBuilder<Vertex> queryBuilder = null; + + try { + cousinBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.COUSIN, out, in, labels); + } catch (AAIException e) { + } + + if (cousinBuilder != null) { + try { + treeBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.TREE, out, in, labels); + } catch (AAIException e) { + } + if (treeBuilder != null) { + queryBuilder = this.union(new QueryBuilder[] {cousinBuilder, treeBuilder}); + } else { + queryBuilder = this.union(new QueryBuilder[] {cousinBuilder}); + } + } else { + treeBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.TREE, out, in, labels); + queryBuilder = this.union(new QueryBuilder[] {treeBuilder}); + } + + return queryBuilder; + } + public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, String outNodeType, String inNodeType) throws AAIException { Introspector out = loader.introspectorFromName(outNodeType); @@ -415,6 +480,43 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public abstract QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException; + + /** + * This method and it's overloaded counterpart are conditional statements. + * This method creates an edge traversal step if an optional property is present + * This is necessary in cases such as "if the Optional Property 1 is sent, + * find all Nodes of type A with edges to Nodes of type B with property 1, + * otherwise, simply find all nodes of type A". + * @param type + * @param outNodeType + * @param inNodeType + * @param value + * @return + * @throws AAIException + */ + public QueryBuilder<Vertex> createEdgeTraversalIfParameterIsPresent(EdgeType type, String outNodeType, String inNodeType, + Object value) throws AAIException { + return this.createEdgeTraversal(type, outNodeType, inNodeType); + } + + /** + * This method and it's overloaded counterpart are conditional statements. + * This method skips an edge traversal step if there is an optional property missing. + * This is necessary in cases such as "if the Optional Property 1 is sent, + * find all Nodes of type A with edges to Nodes of type B with property 1, + * otherwise, simply find all nodes of type A". + * @param type + * @param outNodeType + * @param inNodeType + * @param value + * @return + * @throws AAIException + */ + public QueryBuilder<Vertex> createEdgeTraversalIfParameterIsPresent(EdgeType type, String outNodeType, String inNodeType, + MissingOptionalParameter value) throws AAIException { + return (QueryBuilder<Vertex>) this; + } + /** * * @param type @@ -467,7 +569,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * Creates a queryparser from a given object name. - * + * * @param objName - name of the object type as it appears in the database * @return */ @@ -538,7 +640,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * uses all fields in the introspector to create a query - * + * * @param obj * @return */ @@ -547,7 +649,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * lets you join any number of QueryBuilders * <b>be careful about starting with a union it will not use indexes</b> - * + * * @param builder * @return */ @@ -563,6 +665,10 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public abstract QueryBuilder<E> unfold(); + public abstract QueryBuilder<E> fold(); + + public abstract QueryBuilder<E> id(); + public abstract QueryBuilder<E> dedup(); public abstract QueryBuilder<E> emit(); @@ -591,6 +697,10 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public abstract QueryBuilder<E> by(String name); + public abstract QueryBuilder<E> valueMap(); + + public abstract QueryBuilder<E> valueMap(String... names); + public abstract QueryBuilder<E> both(); public abstract QueryBuilder<Tree> tree(); @@ -598,7 +708,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * Used to prevent the traversal from repeating its path through the graph. * See http://tinkerpop.apache.org/docs/3.0.1-incubating/#simplepath-step for more info. - * + * * @return a QueryBuilder with the simplePath step appended to its traversal */ public abstract QueryBuilder<E> simplePath(); @@ -617,7 +727,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * Used to skip step if there is an optional property missing. - * + * * @param key * @param value * @return @@ -629,7 +739,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * TODO the edge direction is hardcoded here, make it more generic * Returns the parent edge of the vertex - * + * * @return */ public QueryBuilder<Edge> getParentEdge() { @@ -640,7 +750,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { /** * TODO the edge direction is hardcoded here, make it more generic * Returns the parent vertex of the vertex - * + * * @return */ public QueryBuilder<Vertex> getParentVertex() { @@ -672,4 +782,13 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, MissingOptionalParameter value) { return getVerticesByProperty(key, value); } + + protected abstract void vertexHas(String key, Object value) ; + + protected abstract void vertexHasNot(String key); + + protected abstract void vertexHas(String key); + + //TODO: This probably is not required but need to test + // protected abstract void vertexHas(final String key, final P<?> predicate); } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java index cf99fd10..579c3571 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java @@ -30,7 +30,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.onap.aai.edges.enums.EdgeType; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; @@ -47,6 +46,7 @@ public class TraversalQuery<E> extends GraphTraversalBuilder<E> { * * @param loader the loader */ + public TraversalQuery(Loader loader, GraphTraversalSource source) { super(loader, source); this.factory = new TraversalStrategy(this.loader, this); @@ -125,6 +125,16 @@ public class TraversalQuery<E> extends GraphTraversalBuilder<E> { } @Override + public QueryBuilder<E> fold() { + return this; + } + + @Override + public QueryBuilder<E> id() { + return this; + } + + @Override protected QueryBuilder<E> cloneQueryAtStep(int index) { GraphTraversal.Admin<Vertex, E> cloneAdmin = getCloneAdmin(index); return new TraversalQuery<>(cloneAdmin, loader, source, this); diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalURIOptimizedQuery.java b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalURIOptimizedQuery.java index 91d2d084..0e2a9cad 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalURIOptimizedQuery.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalURIOptimizedQuery.java @@ -145,7 +145,6 @@ public class TraversalURIOptimizedQuery<E> extends TraversalQuery { } return shouldOptimize; - } private Optional<String> getStepUriFromIntrospector(Introspector obj) { @@ -166,7 +165,7 @@ public class TraversalURIOptimizedQuery<E> extends TraversalQuery { return Optional.of(uri); } - private int getLastURIStepKey() { + protected int getLastURIStepKey() { return stepToAaiUri.keySet().stream().mapToInt(Integer::intValue).max().getAsInt(); } diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/DBRequest.java b/aai-core/src/main/java/org/onap/aai/rest/db/DBRequest.java index 1f94fbe2..e25a9062 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/DBRequest.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/DBRequest.java @@ -26,6 +26,7 @@ import java.util.Optional; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; +import org.onap.aai.aailog.logs.DBRequestWrapper; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.MarshallerProperties; import org.onap.aai.parsers.query.QueryParser; @@ -34,7 +35,7 @@ import org.onap.aai.restcore.HttpMethod; /** * The Class DBRequest. */ -public class DBRequest { +public class DBRequest implements DBRequestWrapper { private final QueryParser parser; diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java index 61fc33a4..86d7e1b7 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java @@ -20,57 +20,24 @@ package org.onap.aai.rest.db; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.fge.jsonpatch.JsonPatchException; import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.UriBuilder; - -import org.apache.commons.lang.StringUtils; -import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.JanusGraphException; import org.javatuples.Pair; -import org.json.JSONException; -import org.json.JSONObject; +import org.onap.aai.aailog.logs.AaiDBMetricLog; import org.onap.aai.db.props.AAIProperties; -import org.onap.aai.dbmap.DBConnectionType; -import org.onap.aai.domain.responseMessage.AAIResponseMessage; -import org.onap.aai.domain.responseMessage.AAIResponseMessageDatum; import org.onap.aai.exceptions.AAIException; -import org.onap.aai.extensions.AAIExtensionMap; -import org.onap.aai.extensions.ExtensionController; import org.onap.aai.introspection.*; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.LoggingContext; import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.parsers.query.QueryParser; -import org.onap.aai.parsers.uri.URIToExtensionInformation; -import org.onap.aai.parsers.uri.URIToObject; +import org.onap.aai.prevalidation.ValidationService; import org.onap.aai.rest.ueb.UEBNotification; import org.onap.aai.restcore.HttpMethod; import org.onap.aai.schema.enums.ObjectMetadata; @@ -84,17 +51,31 @@ import org.onap.aai.serialization.queryformats.FormatFactory; import org.onap.aai.serialization.queryformats.Formatter; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; +import org.onap.aai.transforms.XmlFormatTransformer; import org.onap.aai.util.AAIConfig; +import org.onap.aai.util.AAIConstants; +import org.onap.aai.util.delta.DeltaEvents; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; + +import javax.ws.rs.core.*; +import javax.ws.rs.core.Response.Status; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.stream.Collectors; /** * The Class HttpEntry. */ public class HttpEntry { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(HttpEntry.class); - private static final String TARGET_ENTITY = "DB"; + private static final Logger LOGGER = LoggerFactory.getLogger(HttpEntry.class); private ModelType introspectorFactoryType; @@ -125,8 +106,24 @@ public class HttpEntry { @Value("${schema.uri.base.path}") private String basePath; + @Value("${delta.events.enabled:false}") + private boolean isDeltaEventsEnabled; + + @Autowired + private XmlFormatTransformer xmlFormatTransformer; + + /** + * Inject the validation service if the profile pre-valiation is enabled, + * Otherwise this variable will be set to null and thats why required=false + * so that it can continue even if pre validation isn't enabled + */ + @Autowired(required = false) + private ValidationService validationService; + private UEBNotification notification; + private int notificationDepth; + /** * Instantiates a new http entry. * @@ -138,23 +135,45 @@ public class HttpEntry { this.queryStyle = queryStyle; } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType) { + public HttpEntry setHttpEntryProperties(SchemaVersion version) { this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); - this.dbEngine = new JanusGraphDBEngine(queryStyle, connectionType, loader); + this.dbEngine = new JanusGraphDBEngine(queryStyle, loader); getDbEngine().startTransaction(); this.notification = new UEBNotification(loader, loaderFactory, schemaVersions); + if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){ + this.notificationDepth = AAIProperties.MAXIMUM_DEPTH; + } else { + this.notificationDepth = AAIProperties.MINIMUM_DEPTH; + } return this; } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, - UEBNotification notification) { + public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification) { this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); - this.dbEngine = new JanusGraphDBEngine(queryStyle, connectionType, loader); + this.dbEngine = new JanusGraphDBEngine(queryStyle, loader); this.notification = notification; + + if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){ + this.notificationDepth = AAIProperties.MAXIMUM_DEPTH; + } else { + this.notificationDepth = AAIProperties.MINIMUM_DEPTH; + } + // start transaction on creation + getDbEngine().startTransaction(); + return this; + } + + public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification, int notificationDepth) { + this.version = version; + this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); + this.dbEngine = new JanusGraphDBEngine(queryStyle, loader); + + this.notification = notification; + this.notificationDepth = notificationDepth; // start transaction on creation getDbEngine().startTransaction(); return this; @@ -222,7 +241,7 @@ public class HttpEntry { /** * Returns the pagination size - * + * * @return integer of the size of results to be returned when paginated */ public int getPaginationBucket() { @@ -231,7 +250,7 @@ public class HttpEntry { /** * Setter for the pagination bucket variable which stores in this object the size of results to return - * + * * @param pb */ public void setPaginationBucket(int pb) { @@ -240,7 +259,7 @@ public class HttpEntry { /** * Getter to return the pagination index requested by the user when requesting paginated results - * + * * @return */ public int getPaginationIndex() { @@ -250,7 +269,7 @@ public class HttpEntry { /** * Sets the pagination index that was passed in by the user, to determine which index or results to retrieve when * paginated - * + * * @param pi */ public void setPaginationIndex(int pi) { @@ -262,7 +281,7 @@ public class HttpEntry { /** * Sets the total vertices variables and calculates the amount of pages based on size and total vertices - * + * * @param totalVertices * @param paginationBucketSize */ @@ -293,7 +312,7 @@ public class HttpEntry { /** * Process. - * + * * @param requests the requests * @param sourceOfTruth the source of truth * @@ -303,41 +322,44 @@ public class HttpEntry { public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth, boolean enableResourceVersion) throws AAIException { - DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); - String methodName = "process"; + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, notificationDepth); Response response; - Introspector obj = null; - QueryParser query = null; - URI uri = null; + Introspector obj; + QueryParser query; + URI uri; String transactionId = null; - int depth = AAIProperties.MAXIMUM_DEPTH; + int depth; Format format = null; List<Pair<URI, Response>> responses = new ArrayList<>(); - MultivaluedMap<String, String> params = null; - HttpMethod method = null; - String uriTemp = ""; - Boolean success = true; + MultivaluedMap<String, String> params; + HttpMethod method; + String uriTemp; + boolean success = true; QueryEngine queryEngine = dbEngine.getQueryEngine(); - int maxRetries = 10; - int retry = 0; + Set<Vertex> mainVertexesToNotifyOn = new LinkedHashSet<>(); + + AaiDBMetricLog metricLog = new AaiDBMetricLog(AAIConstants.AAI_RESOURCES_MS); + + String outputMediaType = null; + + if(requests != null && !requests.isEmpty()){ + HttpHeaders headers = requests.get(0).getHeaders(); + outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + } - LoggingContext.save(); for (DBRequest request : requests) { response = null; Status status = Status.NOT_FOUND; method = request.getMethod(); + metricLog.pre(request); try { try { - LoggingContext.targetEntity(TARGET_ENTITY); - LoggingContext.targetServiceName(methodName + " " + method); - obj = request.getIntrospector(); query = request.getParser(); transactionId = request.getTransactionId(); uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", ""); uri = UriBuilder.fromPath(uriTemp).build(); - LoggingContext.startTime(); List<Vertex> vertTemp; List<Vertex> vertices; if (this.isPaginated()) { @@ -348,8 +370,9 @@ public class HttpEntry { } else { vertices = query.getQueryBuilder().toList(); } - boolean isNewVertex = false; - String outputMediaType = getMediaType(request.getHeaders().getAcceptableMediaTypes()); + boolean isNewVertex; + HttpHeaders headers = request.getHeaders(); + outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); String result = null; params = request.getInfo().getQueryParameters(false); depth = setDepth(obj, params.getFirst("depth")); @@ -369,15 +392,14 @@ public class HttpEntry { if (vertices.size() > 1 && processSingle && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) { if (method.equals(HttpMethod.DELETE)) { - LoggingContext.restoreIfPossible(); + throw new AAIException("AAI_6138"); } else { - LoggingContext.restoreIfPossible(); throw new AAIException("AAI_6137"); } } if (method.equals(HttpMethod.PUT)) { - String resourceVersion = (String) obj.getValue("resource-version"); + String resourceVersion = obj.getValue(AAIProperties.RESOURCE_VERSION); if (vertices.isEmpty()) { if (enableResourceVersion) { serializer.verifyResourceVersion("create", query.getResultType(), "", @@ -387,7 +409,7 @@ public class HttpEntry { } else { if (enableResourceVersion) { serializer.verifyResourceVersion("update", query.getResultType(), - vertices.get(0).<String>property("resource-version").orElse(null), + vertices.get(0).<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, obj.getURI()); } isNewVertex = false; @@ -404,6 +426,22 @@ public class HttpEntry { if (!isNewVertex) { v = vertices.get(0); } + + /* + * This skip-related-to query parameter is used to determine if the relationships object will omit the related-to-property + * If a GET is sent to resources without a format, if format=resource, or if format=resource_and_url with this param set to false + * then behavior will be keep the related-to properties. By default, set to true. + * Otherwise, for any other case, when the skip-related-to parameter exists, has value=true, or some unfamiliar input (e.g. skip-related-to=bogusvalue), the value is true. + */ + boolean isSkipRelatedTo = true; + if (params.containsKey("skip-related-to")) { + String skipRelatedTo = params.getFirst("skip-related-to"); + isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false")); + } else { + // if skip-related-to param is missing, then default it to false; + isSkipRelatedTo = false; + } + HashMap<String, Introspector> relatedObjects = new HashMap<>(); String nodeOnly = params.getFirst("nodes-only"); boolean isNodeOnly = nodeOnly != null; @@ -412,12 +450,7 @@ public class HttpEntry { if (format == null) { obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(), - depth, isNodeOnly, cleanUp); - - LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), - TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); + depth, isNodeOnly, cleanUp, isSkipRelatedTo); if (obj != null) { status = Status.OK; @@ -436,6 +469,14 @@ public class HttpEntry { Formatter formatter = ff.get(format, params); result = formatter.output(vertices.stream().map(vertex -> (Object) vertex) .collect(Collectors.toList())).toString(); + + if(outputMediaType == null){ + outputMediaType = MediaType.APPLICATION_JSON; + } + + if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){ + result = xmlFormatTransformer.transform(result); + } status = Status.OK; } @@ -443,12 +484,7 @@ public class HttpEntry { case GET_RELATIONSHIP: if (format == null) { obj = this.getRelationshipObjectFromDb(vertices, serializer, query, - request.getInfo().getRequestUri()); - - LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), - TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); + request.getInfo().getRequestUri(), isSkipRelatedTo); if (obj != null) { status = Status.OK; @@ -471,49 +507,50 @@ public class HttpEntry { Formatter formatter = ff.get(format, params); result = formatter.output(vertices.stream().map(vertex -> (Object) vertex) .collect(Collectors.toList())).toString(); + + if(outputMediaType == null){ + outputMediaType = MediaType.APPLICATION_JSON; + } + + if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){ + result = xmlFormatTransformer.transform(result); + } status = Status.OK; } break; case PUT: - response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, - sourceOfTruth, version, loader, obj, uri, true); if (isNewVertex) { v = serializer.createNewVertex(obj); } serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext); - this.invokeExtension(dbEngine, this.dbEngine.tx(), HttpMethod.PUT, request, - sourceOfTruth, version, loader, obj, uri, false); status = Status.OK; if (isNewVertex) { status = Status.CREATED; } - obj = serializer.getLatestVersionView(v); - if (query.isDependent()) { - relatedObjects = - this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); + + mainVertexesToNotifyOn.add(v); + if (notificationDepth == AAIProperties.MINIMUM_DEPTH) { + Map<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> allImpliedDeleteObjs = serializer.getImpliedDeleteUriObjectPair(); + + for(Map.Entry<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> entry: allImpliedDeleteObjs.entrySet()){ + // The format is purposefully %s/%s%s due to the fact + // that every aai-uri will have a slash at the beginning + // If that assumption isn't true, then its best to change this code + String curUri = String.format("%s/%s%s", basePath , version , entry.getKey()); + Introspector curObj = entry.getValue().getValue0(); + HashMap<String, Introspector> curObjRelated = entry.getValue().getValue1(); + notification.createNotificationEvent(transactionId, sourceOfTruth, Status.NO_CONTENT, URI.create(curUri), curObj, curObjRelated, basePath); + } } - LoggingContext.elapsedTime( - (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(), - TimeUnit.MILLISECONDS); - LOGGER.info("Completed "); - LoggingContext.restoreIfPossible(); - notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, - relatedObjects, basePath); break; case PUT_EDGE: serializer.touchStandardVertexProperties(v, false); - this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, - version, loader, obj, uri, true); - serializer.createEdge(obj, v); - - LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); + Vertex relatedVertex = serializer.createEdge(obj, v); status = Status.OK; - notification.createNotificationEvent(transactionId, sourceOfTruth, status, - new URI(uri.toString().replace("/relationship-list/relationship", "")), - serializer.getLatestVersionView(v), relatedObjects, basePath); + + mainVertexesToNotifyOn.add(v); + serializer.addVertexToEdgeVertexes(relatedVertex); break; case MERGE_PATCH: Introspector existingObj = loader.introspectorFromName(obj.getDbName()); @@ -536,37 +573,23 @@ public class HttpEntry { JsonNode completed = patch.apply(existingNode); String patched = mapper.writeValueAsString(completed); Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched); - if (relationshipList == null) { + if (relationshipList == null && patchedObj.hasProperty("relationship-list")) { // if the caller didn't touch the relationship-list, we shouldn't either patchedObj.setValue("relationship-list", null); } serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext); status = Status.OK; - patchedObj = serializer.getLatestVersionView(v); - if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, - this.loader); - } - LoggingContext.elapsedTime( - (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(), - TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); - notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, - patchedObj, relatedObjects, basePath); + mainVertexesToNotifyOn.add(v); } catch (IOException | JsonPatchException e) { - - LOGGER.info("Caught exception: " + e.getMessage()); - LoggingContext.restoreIfPossible(); throw new AAIException("AAI_3000", "could not perform patch operation"); } break; case DELETE: - String resourceVersion = params.getFirst("resource-version"); - obj = serializer.getLatestVersionView(v); + String resourceVersion = params.getFirst(AAIProperties.RESOURCE_VERSION); + obj = serializer.getLatestVersionView(v, notificationDepth); if (query.isDependent()) { relatedObjects = - this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); + serializer.getRelatedObjects(queryEngine, v, obj, this.loader); } /* * Find all Delete-other-vertex vertices and create structure for notify @@ -576,7 +599,7 @@ public class HttpEntry { */ List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v); - Long vId = (Long) v.id(); + Object vId = v.id(); /* * I am assuming vertexId cant be null @@ -595,17 +618,7 @@ public class HttpEntry { this.buildRelatedObjects(serializer, queryEngine, deleteObjects); } - this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, - version, loader, obj, uri, true); serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion); - this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, - version, loader, obj, uri, false); - - LoggingContext.elapsedTime( - (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(), - TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath); @@ -618,19 +631,16 @@ public class HttpEntry { this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects, uriMap, deleteRelatedObjects, basePath); } - break; case DELETE_EDGE: serializer.touchStandardVertexProperties(v, false); - serializer.deleteEdge(obj, v); + Optional<Vertex> otherV = serializer.deleteEdge(obj, v); - LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info("Completed"); - LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; - notification.createNotificationEvent(transactionId, sourceOfTruth, Status.OK, - new URI(uri.toString().replace("/relationship-list/relationship", "")), - serializer.getLatestVersionView(v), relatedObjects, basePath); + if (otherV.isPresent()) { + mainVertexesToNotifyOn.add(v); + serializer.addVertexToEdgeVertexes(otherV.get()); + } break; default: break; @@ -659,21 +669,14 @@ public class HttpEntry { } } else if (response == null) { response = Response.status(status).type(outputMediaType).build(); - } else { - // response already set to something - } + } // else, response already set to something + Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); } catch (JanusGraphException e) { this.dbEngine.rollback(); - - LOGGER.info("Caught exception: " + e.getMessage()); - LoggingContext.restoreIfPossible(); throw new AAIException("AAI_6134", e); } - if (retry == maxRetries) { - throw new AAIException("AAI_6134"); - } } catch (AAIException e) { success = false; ArrayList<String> templateVars = new ArrayList<>(); @@ -683,31 +686,123 @@ public class HttpEntry { ErrorLogHelper.logException(e); response = Response.status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars)) + .type(outputMediaType) .build(); Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); - continue; } catch (Exception e) { success = false; - e.printStackTrace(); AAIException ex = new AAIException("AAI_4000", e); - ArrayList<String> templateVars = new ArrayList<String>(); + ArrayList<String> templateVars = new ArrayList<>(); templateVars.add(request.getMethod().toString()); // GET, PUT, etc - templateVars.add(request.getUri().getPath().toString()); + templateVars.add(request.getUri().getPath()); ErrorLogHelper.logException(ex); response = Response.status(ex.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars)) + .type(outputMediaType) .build(); Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); - continue; + } + finally { + if (response != null) { + metricLog.post(request, response); + } } } - notification.triggerEvents(); + + if (success) { + generateEvents(sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn); + } else { + notification.clearEvents(); + } + return Pair.with(success, responses); } /** + * Generate notification events for the resulting db requests. + */ + private void generateEvents(String sourceOfTruth, DBSerializer serializer, String transactionId, QueryEngine queryEngine, Set<Vertex> mainVertexesToNotifyOn) throws AAIException { + if (notificationDepth == AAIProperties.MINIMUM_DEPTH) { + serializer.getUpdatedVertexes().entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).forEach(mainVertexesToNotifyOn::add); + } + Set<Vertex> edgeVertexes = serializer.touchStandardVertexPropertiesForEdges().stream() + .filter(v -> !mainVertexesToNotifyOn.contains(v)).collect(Collectors.toSet()); + try { + createNotificationEvents(mainVertexesToNotifyOn, sourceOfTruth, serializer, transactionId, queryEngine, notificationDepth); + if("true".equals(AAIConfig.get("aai.notification.both.sides.enabled", "true"))){ + createNotificationEvents(edgeVertexes, sourceOfTruth, serializer, transactionId, queryEngine, AAIProperties.MINIMUM_DEPTH); + } + } catch (UnsupportedEncodingException e) { + LOGGER.warn("Encountered exception generating events", e); + } + + // Since @Autowired required is set to false, we need to do a null check + // for the existence of the validationService since its only enabled if profile is enabled + if(validationService != null){ + validationService.validate(notification.getEvents()); + } + notification.triggerEvents(); + if (isDeltaEventsEnabled) { + try { + DeltaEvents deltaEvents = new DeltaEvents(transactionId, sourceOfTruth, version.toString(), serializer.getObjectDeltas()); + deltaEvents.triggerEvents(); + } catch (Exception e) { + LOGGER.error("Error sending Delta Events", e); + } + } + } + + /** + * Generate notification events for provided set of vertexes at the specified depth + */ + private void createNotificationEvents(Set<Vertex> vertexesToNotifyOn, String sourceOfTruth, DBSerializer serializer, + String transactionId, QueryEngine queryEngine, int eventDepth) throws AAIException, UnsupportedEncodingException { + for(Vertex vertex : vertexesToNotifyOn){ + if (canGenerateEvent(vertex)) { + boolean isCurVertexNew = vertex.value(AAIProperties.CREATED_TS).equals(vertex.value(AAIProperties.LAST_MOD_TS)); + Status curObjStatus = (isCurVertexNew) ? Status.CREATED : Status.OK; + + Introspector curObj = serializer.getLatestVersionView(vertex, eventDepth); + String aaiUri = vertex.<String>property(AAIProperties.AAI_URI).value(); + String uri = String.format("%s/%s%s", basePath, version, aaiUri); + HashMap<String, Introspector> curRelatedObjs = new HashMap<>(); + if (!curObj.isTopLevel()) { + curRelatedObjs = serializer.getRelatedObjects(queryEngine, vertex, curObj, this.loader); + } + notification.createNotificationEvent(transactionId, sourceOfTruth, curObjStatus, URI.create(uri), curObj, curRelatedObjs, basePath); + } + } + } + + /** + * Verifies that vertex has needed properties to generate on + * @param vertex Vertex to be verified + * @return <code>true</code> if vertex has necessary properties and exists + */ + private boolean canGenerateEvent(Vertex vertex) { + boolean canGenerate = true; + try { + if(!vertex.property(AAIProperties.AAI_URI).isPresent()){ + LOGGER.debug("Encountered an vertex {} with missing aai-uri", vertex.id()); + canGenerate = false; + } else if(!vertex.property(AAIProperties.CREATED_TS).isPresent() || !vertex.property(AAIProperties.LAST_MOD_TS).isPresent()){ + LOGGER.debug("Encountered an vertex {} with missing timestamp", vertex.id()); + canGenerate = false; + } + } catch (IllegalStateException e) { + if (e.getMessage().contains(" was removed")) { + LOGGER.warn("Attempted to generate event for non existent vertex", e); + } else { + LOGGER.warn("Encountered exception generating events", e); + } + canGenerate = false; + } + return canGenerate; + } + + /** * Gets the media type. * * @param mediaTypeList the media type list @@ -760,7 +855,6 @@ public class HttpEntry { return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); } - /** * Gets the object from db. * @@ -770,6 +864,7 @@ public class HttpEntry { * @param uri the uri * @param depth the depth * @param cleanUp the clean up + * @param isSkipRelatedTo include related to flag * @return the object from db * @throws AAIException the AAI exception * @throws IllegalAccessException the illegal access exception @@ -783,9 +878,11 @@ public class HttpEntry { * @throws AAIUnknownObjectException * @throws URISyntaxException */ - private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, - URI uri) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException, - AAIUnknownObjectException { + private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, + Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp, boolean isSkipRelatedTo) + throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, + AAIUnknownObjectException, URISyntaxException { // nothing found if (results.isEmpty()) { @@ -793,104 +890,45 @@ public class HttpEntry { throw new AAIException("AAI_6114", msg); } - if (results.size() > 1) { - throw new AAIException("AAI_6148", uri.getPath()); - } + return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp, isSkipRelatedTo); - Vertex v = results.get(0); - return serializer.dbToRelationshipObject(v); } /** - * Invoke extension. + * Gets the object from db. * - * @param dbEngine the db engine - * @param g the g - * @param httpMethod the http method - * @param fromAppId the from app id - * @param apiVersion the api version - * @param loader the loader - * @param obj the obj + * @param serializer the serializer + * @param query the query * @param uri the uri - * @param isPreprocess the is preprocess - * @return the response + * @return the object from db + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - private Response invokeExtension(TransactionalGraphEngine dbEngine, Graph g, HttpMethod httpMethod, - DBRequest request, String fromAppId, SchemaVersion apiVersion, Loader loader, Introspector obj, URI uri, - boolean isPreprocess) throws IllegalArgumentException, UnsupportedEncodingException, AAIException { - AAIExtensionMap aaiExtMap = new AAIExtensionMap(); - // ModelInjestor injestor = ModelInjestor.getInstance(); - Response response = null; - URIToExtensionInformation extensionInformation = new URIToExtensionInformation(loader, uri); - aaiExtMap.setHttpEntry(this); - aaiExtMap.setDbRequest(request); - aaiExtMap.setTransId(request.getTransactionId()); - aaiExtMap.setFromAppId(fromAppId); - aaiExtMap.setGraph(g); - aaiExtMap.setApiVersion(apiVersion.toString()); - aaiExtMap.setObjectFromRequest(obj); - aaiExtMap.setObjectFromRequestType(obj.getJavaClassName()); - aaiExtMap.setObjectFromResponse(obj); - aaiExtMap.setObjectFromResponseType(obj.getJavaClassName()); - aaiExtMap.setJaxbContext(nodeIngestor.getContextForVersion(apiVersion)); - aaiExtMap.setUri(uri.getRawPath()); - aaiExtMap.setTransactionalGraphEngine(dbEngine); - aaiExtMap.setLoader(loader); - aaiExtMap.setNamespace(extensionInformation.getNamespace()); - - ExtensionController ext = new ExtensionController(); - ext.runExtension(aaiExtMap.getApiVersion(), extensionInformation.getNamespace(), - extensionInformation.getTopObject(), extensionInformation.getMethodName(httpMethod, isPreprocess), - aaiExtMap, isPreprocess); - - if (aaiExtMap.getPrecheckAddedList().size() > 0) { - response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders()); - } - - return response; - } - - /** - * Notify on skeleton creation. - * - * @param aaiExtMap the aai ext map - * @param input the input - * @param headers the headers - * @return the response + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException */ - // Legacy support - private Response notifyOnSkeletonCreation(AAIExtensionMap aaiExtMap, Introspector input, HttpHeaders headers) { - Response response = null; - HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>(); - - StringBuilder keyString = new StringBuilder(); + private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, + URI uri, boolean isSkipRelatedTo) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException, + AAIUnknownObjectException { - Set<String> resourceKeys = input.getKeys(); - for (String key : resourceKeys) { - keyString.append(key).append("=").append(input.getValue(key).toString()).append(" "); + // nothing found + if (results.isEmpty()) { + String msg = createNotFoundMessage(query.getResultType(), uri); + throw new AAIException("AAI_6114", msg); } - for (AAIResponseMessage msg : aaiExtMap.getPrecheckResponseMessages().getAAIResponseMessage()) { - ArrayList<String> templateVars = new ArrayList<>(); - - templateVars.add("PUT " + input.getDbName()); - templateVars.add(keyString.toString()); - List<String> keys = new ArrayList<>(); - templateVars.add(msg.getAaiResponseMessageResourceType()); - for (AAIResponseMessageDatum dat : msg.getAaiResponseMessageData().getAAIResponseMessageDatum()) { - keys.add(dat.getAaiResponseMessageDatumKey() + "=" + dat.getAaiResponseMessageDatumValue()); - } - templateVars.add(StringUtils.join(keys, ", ")); - exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()), templateVars); + if (results.size() > 1) { + throw new AAIException("AAI_6148", uri.getPath()); } - response = Response.status(Status.ACCEPTED) - .entity(ErrorLogHelper.getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList)) - .build(); - return response; + Vertex v = results.get(0); + return serializer.dbToRelationshipObject(v, isSkipRelatedTo); } /** @@ -901,10 +939,7 @@ public class HttpEntry { * @return the string */ private String createNotFoundMessage(String resultType, URI uri) { - - String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); - - return msg; + return "No Node of type " + resultType + " found at: " + uri.getPath(); } /** @@ -915,11 +950,8 @@ public class HttpEntry { * @return the string */ private String createRelationshipNotFoundMessage(String resultType, URI uri) { - - String msg = "No relationship found of type " + resultType + " at the given URI: " + uri.getPath() + return "No relationship found of type " + resultType + " at the given URI: " + uri.getPath() + "/relationship-list"; - - return msg; } /** @@ -933,16 +965,13 @@ public class HttpEntry { int depth = AAIProperties.MAXIMUM_DEPTH; String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", ""); - if (depthParam != null && getAllRandomStr != null && !getAllRandomStr.isEmpty() - && getAllRandomStr.equals(depthParam)) { + if (getAllRandomStr != null && !getAllRandomStr.isEmpty() && getAllRandomStr.equals(depthParam)) { return depth; } if (depthParam == null) { if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) { depth = 0; - } else { - depth = AAIProperties.MAXIMUM_DEPTH; } } else { if (!depthParam.isEmpty() && !"all".equals(depthParam)) { @@ -973,184 +1002,14 @@ public class HttpEntry { return depth; } - /** - * Checks if is modification method. - * - * @param method the method - * @return true, if is modification method - */ - private boolean isModificationMethod(HttpMethod method) { - boolean result = false; - - if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) - || method.equals(HttpMethod.MERGE_PATCH)) { - result = true; - } - - return result; - - } - - /** - * Given an uri, introspector object and loader object - * it will check if the obj is top level object if it is, - * it will return immediately returning the uri passed in - * If it isn't, it will go through, get the uriTemplate - * from the introspector object and get the count of "/"s - * and remove that part of the uri using substring - * and keep doing that until the current object is top level - * Also added the max depth just so worst case scenario - * Then keep adding aai-uri to the list include the aai-uri passed in - * Convert that list into an array and return it - * <p> - * - * Example: - * - * <blockquote> - * aai-uri -> - * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 - * - * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id} - * it converts to /vservers/vserver - * - * lastIndexOf /vservers/vserver in - * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 - * ^ - * | - * | - * lastIndexOf - * Use substring to get the string from 0 to that lastIndexOf - * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1 - * - * From this new aai-uri, generate a introspector from the URITOObject class - * and keep doing this until you - * - * </blockquote> - * - * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex - * @param obj - introspector object of the given starting vertex - * @param loader - Type of loader which will always be MoxyLoader to support model driven - * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex - * @throws UnsupportedEncodingException - * @throws AAIException - */ - String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) - throws UnsupportedEncodingException, AAIException { - - List<String> uriList = new ArrayList<>(); - String template = StringUtils.EMPTY; - String truncatedUri = aaiUri; - int depth = AAIProperties.MAXIMUM_DEPTH; - uriList.add(truncatedUri); - - while (depth >= 0 && !obj.isTopLevel()) { - template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); - - if (template == null) { - LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); - return null; - } - - int templateCount = StringUtils.countMatches(template, "/"); - int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); - - if (templateCount > truncatedUriCount) { - LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); - return null; - } - - int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1); - truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex); - uriList.add(truncatedUri); - obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity(); - depth--; - } - - return uriList.toArray(new String[uriList.size()]); - } - - /** - * - * @param serializer - * @param queryEngine - * @param v - * @param obj - * @param loader - * @return - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - * @throws SecurityException - * @throws InstantiationException - * @throws NoSuchMethodException - * @throws UnsupportedEncodingException - * @throws AAIException - * @throws URISyntaxException - */ - private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v, - Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException, - InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, - UnsupportedEncodingException, AAIException, URISyntaxException { - - HashMap<String, Introspector> relatedVertices = new HashMap<>(); - VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); - - if (!aaiUriProperty.isPresent()) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", - v.id().toString()); - } else { - LOGGER.info( - "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log"); - } - return relatedVertices; - } - - String aaiUri = aaiUriProperty.value().toString(); - - if (!obj.isTopLevel()) { - String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader); - List<Vertex> vertexChain = null; - // If the uriList is null then there is something wrong with converting the uri - // into a list of aai-uris so falling back to the old mechanism for finding parents - if (uriList == null) { - LOGGER.info( - "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal"); - vertexChain = queryEngine.findParents(v); - } else { - vertexChain = queryEngine.findParents(uriList); - } - for (Vertex vertex : vertexChain) { - try { - final Introspector vertexObj = serializer.getVertexProperties(vertex); - relatedVertices.put(vertexObj.getObjectId(), vertexObj); - } catch (AAIUnknownObjectException e) { - LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); - } - } - } else { - try { - final Introspector vertexObj = serializer.getVertexProperties(v); - relatedVertices.put(vertexObj.getObjectId(), vertexObj); - } catch (AAIUnknownObjectException e) { - LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); - } - } - - return relatedVertices; - } - private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) { Map<Vertex, Introspector> deleteObjectMap = new HashMap<>(); for (Vertex vertex : vertices) { try { - // deleteObjectMap.computeIfAbsent(vertex, s -> - // serializer.getLatestVersionView(vertex)); - Introspector deleteObj = serializer.getLatestVersionView(vertex); + Introspector deleteObj = serializer.getLatestVersionView(vertex, notificationDepth); deleteObjectMap.put(vertex, deleteObj); } catch (UnsupportedEncodingException | AAIException e) { LOGGER.warn("Unable to get Introspctor Objects, Just continue"); - continue; } } @@ -1170,7 +1029,6 @@ public class HttpEntry { } } catch (UnsupportedEncodingException e) { LOGGER.warn("Unable to get URIs, Just continue"); - continue; } } @@ -1186,15 +1044,13 @@ public class HttpEntry { for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) { try { HashMap<String, Introspector> relatedObjects = - this.getRelatedObjects(serializer, queryEngine, entry.getKey(), entry.getValue(), this.loader); + serializer.getRelatedObjects(queryEngine, entry.getKey(), entry.getValue(), this.loader); if (null != entry.getValue()) { relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects); } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException - | InstantiationException | NoSuchMethodException | UnsupportedEncodingException | AAIException - | URISyntaxException e) { + } catch (IllegalArgumentException | SecurityException + | UnsupportedEncodingException | AAIException e) { LOGGER.warn("Unable to get realted Objects, Just continue"); - continue; } } @@ -1208,10 +1064,8 @@ public class HttpEntry { Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) { for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) { try { - String vertexObjectId = ""; - if (null != entry.getValue()) { - vertexObjectId = entry.getValue().getObjectId(); + String vertexObjectId = entry.getValue().getObjectId(); if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) { notification.createNotificationEvent(transactionId, sourceOfTruth, status, @@ -1227,12 +1081,38 @@ public class HttpEntry { } public void setPaginationParameters(String resultIndex, String resultSize) { - if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") { + if (resultIndex != null && !"-1".equals(resultIndex) && resultSize != null && !"-1".equals(resultSize)) { this.setPaginationIndex(Integer.parseInt(resultIndex)); this.setPaginationBucket(Integer.parseInt(resultSize)); } } + public List<Object> getPaginatedVertexListForAggregateFormat(List<Object> aggregateVertexList) throws AAIException { + List<Object> finalList = new Vector<>(); + if (this.isPaginated()) { + if (aggregateVertexList != null && !aggregateVertexList.isEmpty()) { + int listSize = aggregateVertexList.size(); + if (listSize == 1) { + List<Object> vertexList = (List<Object>) aggregateVertexList.get(0); + this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket()); + int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket(); + int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size()); + if (startIndex > endIndex) { + throw new AAIException("AAI_6150", + " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex); + } + finalList.add(new ArrayList<Object>()); + for (int i = startIndex; i < endIndex; i++) { + ((ArrayList<Object>) finalList.get(0)).add(((ArrayList<Object>) aggregateVertexList.get(0)).get(i)); + } + return finalList; + } + } + } + // If the list size is greater than 1 or if pagination is not needed, return the original list. + return aggregateVertexList; + } + public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException { List<Object> vertices; if (this.isPaginated()) { diff --git a/aai-core/src/main/java/org/onap/aai/rest/ueb/NotificationEvent.java b/aai-core/src/main/java/org/onap/aai/rest/ueb/NotificationEvent.java index 61beb8d0..14e554cd 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/ueb/NotificationEvent.java +++ b/aai-core/src/main/java/org/onap/aai/rest/ueb/NotificationEvent.java @@ -63,10 +63,13 @@ public class NotificationEvent { StoreNotificationEvent sne = new StoreNotificationEvent(transactionId, sourceOfTruth); - sne.storeEvent(loader, eventHeader, obj); + sne.storeEventAndSendToJms(loader, eventHeader, obj); } + public String getNotificationEvent() throws AAIException { + return new StoreNotificationEvent(transactionId, sourceOfTruth).storeEventOnly(loader, eventHeader, obj); + } /** * Gets the notification version. * diff --git a/aai-core/src/main/java/org/onap/aai/rest/ueb/UEBNotification.java b/aai-core/src/main/java/org/onap/aai/rest/ueb/UEBNotification.java index b189c050..28a644a9 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/ueb/UEBNotification.java +++ b/aai-core/src/main/java/org/onap/aai/rest/ueb/UEBNotification.java @@ -20,22 +20,13 @@ package org.onap.aai.rest.ueb; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import javax.ws.rs.core.Response.Status; - -import org.onap.aai.config.SpringContextAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.ModelType; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.introspection.exceptions.AAIUnmarshallingException; import org.onap.aai.logging.LogFormatTools; @@ -43,15 +34,20 @@ import org.onap.aai.parsers.uri.URIToObject; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; +import javax.ws.rs.core.Response.Status; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.*; + /** * The Class UEBNotification. */ public class UEBNotification { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(UEBNotification.class); + private static final Logger LOGGER = LoggerFactory.getLogger(UEBNotification.class); private Loader currentVersionLoader = null; - protected List<NotificationEvent> events = null; + protected Map<String, NotificationEvent> events = null; private SchemaVersion notificationVersion = null; /** @@ -60,13 +56,27 @@ public class UEBNotification { * @param loader the loader */ public UEBNotification(Loader loader, LoaderFactory loaderFactory, SchemaVersions schemaVersions) { - events = new ArrayList<>(); + events = new LinkedHashMap<>(); SchemaVersion defaultVersion = schemaVersions.getDefaultVersion(); currentVersionLoader = loaderFactory.createLoaderForVersion(loader.getModelType(), defaultVersion); notificationVersion = defaultVersion; } /** + * Instantiates a new UEB notification. + * + * @param modelType - Model type + * @param loaderFactory - the loader factory + * @param schemaVersions the schema versions bean + */ + public UEBNotification(ModelType modelType, LoaderFactory loaderFactory, SchemaVersions schemaVersions) { + events = new LinkedHashMap<>(); + SchemaVersion defaultVersion = schemaVersions.getDefaultVersion(); + currentVersionLoader = loaderFactory.createLoaderForVersion(modelType, defaultVersion); + notificationVersion = defaultVersion; + } + + /** * Creates the notification event. * * @param transactionId the X-TransactionId @@ -97,7 +107,6 @@ public class UEBNotification { Introspector eventHeader = currentVersionLoader.introspectorFromName("notification-event-header"); URIToObject parser = new URIToObject(currentVersionLoader, uri, relatedObjects); - String entityLink = ""; if ((basePath != null) && (!basePath.isEmpty())) { if (!(basePath.startsWith("/"))) { basePath = "/" + basePath; @@ -113,10 +122,12 @@ public class UEBNotification { } } - if (uri.toString().startsWith("/")) { - entityLink = basePath + notificationVersion + uri; + String uriStr = getUri(uri.toString(), basePath); + String entityLink; + if (uriStr.startsWith("/")) { + entityLink = basePath + notificationVersion + uriStr; } else { - entityLink = basePath + notificationVersion + "/" + uri; + entityLink = basePath + notificationVersion + "/" + uriStr; } eventHeader.setValue("entity-link", entityLink); @@ -166,7 +177,7 @@ public class UEBNotification { } final NotificationEvent event = new NotificationEvent(currentVersionLoader, eventHeader, eventObject, transactionId, sourceOfTruth); - events.add(event); + events.put(uri.toString(), event); } catch (AAIUnknownObjectException e) { throw new RuntimeException("Fatal error - notification-event-header object not found!"); } catch (AAIUnmarshallingException e) { @@ -181,14 +192,41 @@ public class UEBNotification { * @throws AAIException the AAI exception */ public void triggerEvents() throws AAIException { - for (NotificationEvent event : events) { + for (NotificationEvent event : events.values()) { event.trigger(); } - events.clear(); + clearEvents(); } public List<NotificationEvent> getEvents() { + return new ArrayList<>(this.events.values()); + } + public Map<String, NotificationEvent> getEventsMap() { return this.events; } + private String getUri(String uri, String basePath) { + if (uri == null || uri.isEmpty()) { + return uri; + } else if (uri.charAt(0) != '/') { + uri = '/' + uri; + } + + if ((basePath != null) && (!basePath.isEmpty())) { + if (!(basePath.startsWith("/"))) { + basePath = "/" + basePath; + } + if (!(basePath.endsWith("/"))) { + basePath = basePath + "/"; + } + } + + LOGGER.trace("Notification header uri base path:'{}', uri:'{}'", basePath, uri); + + return uri.replaceAll("^" + basePath + "v\\d+", ""); + } + + public void clearEvents() { + events.clear(); + } } diff --git a/aai-core/src/main/java/org/onap/aai/restcore/RESTAPI.java b/aai-core/src/main/java/org/onap/aai/restcore/RESTAPI.java index ae102a83..2b1256ba 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/RESTAPI.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/RESTAPI.java @@ -20,64 +20,47 @@ package org.onap.aai.restcore; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import com.google.common.base.Joiner; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.onap.aai.db.props.AAIProperties; -import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; -import org.onap.aai.introspection.tools.CreateUUID; -import org.onap.aai.introspection.tools.DefaultFields; -import org.onap.aai.introspection.tools.InjectKeysFromURI; -import org.onap.aai.introspection.tools.IntrospectorValidator; -import org.onap.aai.introspection.tools.Issue; -import org.onap.aai.introspection.tools.RemoveNonVisibleProperty; +import org.onap.aai.introspection.tools.*; import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.LoggingContext; import org.onap.aai.util.AAIConfig; import org.onap.aai.util.FormatDate; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; + /** * Base class for AAI REST API classes. * Provides method to validate header information * TODO should authenticate caller and authorize them for the API they are calling * TODO should store the transaction * - * + * */ public class RESTAPI { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(RESTAPI.class); - - protected final String COMPONENT = "aairest"; + private static final Logger LOGGER = LoggerFactory.getLogger(RESTAPI.class); /** * The Enum Action. */ public enum Action { GET, PUT, POST, DELETE - }; + } /** * Gets the from app id. @@ -101,8 +84,6 @@ public class RESTAPI { throw new AAIException("AAI_4009"); } - LoggingContext.partnerName(fromAppId); - return fromAppId; } @@ -128,8 +109,6 @@ public class RESTAPI { throw new AAIException("AAI_4010"); } - LoggingContext.requestId(transId); - return transId; } @@ -173,7 +152,7 @@ public class RESTAPI { int depth = AAIProperties.MAXIMUM_DEPTH; // default if (depthParam != null && depthParam.length() > 0 && !depthParam.equals("all")) { try { - depth = Integer.valueOf(depthParam); + depth = Integer.parseInt(depthParam); } catch (Exception e) { throw new AAIException("AAI_4016"); } @@ -192,9 +171,9 @@ public class RESTAPI { */ protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction, AAIException e) { - ArrayList<String> templateVars = new ArrayList<String>(); + ArrayList<String> templateVars = new ArrayList<>(); templateVars.add(templateAction.toString()); // GET, PUT, etc - templateVars.add(info.getPath().toString()); + templateVars.add(info.getPath()); templateVars.addAll(e.getTemplateVars()); ErrorLogHelper.logException(e); @@ -236,7 +215,7 @@ public class RESTAPI { messages.add(issue.getDetail()); } } - String errors = Joiner.on(",").join(messages); + String errors = String.join(",", messages); throw new AAIException("AAI_3000", errors); } // check that key in payload and key in request uri are the same @@ -249,22 +228,6 @@ public class RESTAPI { } } - protected DBConnectionType determineConnectionType(String fromAppId, String realTime) throws AAIException { - if (fromAppId == null) { - throw new AAIException("AAI_4009", "X-FromAppId is not set"); - } - - DBConnectionType type = DBConnectionType.REALTIME; - boolean isRealTimeClient = AAIConfig.get("aai.realtime.clients", "").contains(fromAppId); - if (isRealTimeClient || realTime != null) { - type = DBConnectionType.REALTIME; - } else { - type = DBConnectionType.CACHED; - } - - return type; - } - /** * Gets the input media type. * @@ -272,10 +235,7 @@ public class RESTAPI { * @return the input media type */ protected String getInputMediaType(MediaType mediaType) { - String result = mediaType.getType() + "/" + mediaType.getSubtype(); - - return result; - + return mediaType.getType() + "/" + mediaType.getSubtype(); } /** @@ -288,25 +248,23 @@ public class RESTAPI { * @throws AAIException */ - public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) throws AAIException { + public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) { String[] ignoreAppIds = (appTimeouts).split("\\|"); int appLimit = Integer.parseInt(defaultTimeout); - final Map<String, Integer> m = new HashMap<String, Integer>(); - if (ignoreAppIds != null) { - for (int i = 0; i < ignoreAppIds.length; i++) { - String[] vals = ignoreAppIds[i].split(","); - m.put(vals[0], Integer.parseInt(vals[1])); - } - if (m.get(sot) != null) { - appLimit = m.get(sot); - } + final Map<String, Integer> m = new HashMap<>(); + for (int i = 0; i < ignoreAppIds.length; i++) { + String[] vals = ignoreAppIds[i].split(","); + m.put(vals[0], Integer.parseInt(vals[1])); + } + if (m.get(sot) != null) { + appLimit = m.get(sot); } return appLimit; } /** * Returns whether time out is enabled - * + * * @param sot * @param isEnabled * @param appTimeouts @@ -314,9 +272,8 @@ public class RESTAPI { * @return boolean of whether the timeout is enabled * @throws AAIException */ - public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) - throws AAIException { - Boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled); + public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) { + boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled); int ata = -1; if (isTimeoutEnabled) { ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout); @@ -326,7 +283,7 @@ public class RESTAPI { /** * Executes the process thread and watches the future for the timeout - * + * * @param handler * @param sourceOfTruth * @param appTimeoutLimit @@ -358,7 +315,7 @@ public class RESTAPI { /** * runner sets up the timer logic and invokes it - * + * * @param toe * @param tba * @param tdl diff --git a/aai-core/src/main/java/org/onap/aai/restcore/search/AAIAbstractGroovyShell.java b/aai-core/src/main/java/org/onap/aai/restcore/search/AAIAbstractGroovyShell.java index b521a617..13f3f05e 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/search/AAIAbstractGroovyShell.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/search/AAIAbstractGroovyShell.java @@ -28,13 +28,20 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.ImportCustomizer; +import org.onap.aai.config.SpringContextAware; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.serialization.engines.QueryStyle; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.setup.SchemaVersions; public abstract class AAIAbstractGroovyShell { @@ -76,4 +83,20 @@ public abstract class AAIAbstractGroovyShell { * @return result of graph traversal */ public abstract GraphTraversal<?, ?> executeTraversal(String traversal, Map<String, Object> params); + + /** + * + * @param engine + * @param traversal + * @param params + * @return result of graph traversal + */ + public abstract String executeTraversal(TransactionalGraphEngine engine, String traversal, + Map<String, Object> params, QueryStyle style, GraphTraversalSource source); + + protected Loader getLoader(){ + SchemaVersions schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions"); + return SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(ModelType.MOXY, + schemaVersions.getDefaultVersion()); + } } diff --git a/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java b/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java index 2b39af43..85e47223 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java @@ -26,6 +26,8 @@ import groovy.lang.Script; import java.util.Map; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.onap.aai.serialization.engines.QueryStyle; import org.onap.aai.serialization.engines.TransactionalGraphEngine; /** @@ -57,4 +59,9 @@ public class GremlinGroovyShell extends AAIAbstractGroovyShell { public String executeTraversal(TransactionalGraphEngine engine, String traversal, Map<String, Object> params) { throw new UnsupportedOperationException(); } + + @Override + public String executeTraversal(TransactionalGraphEngine engine, String traversal, Map<String, Object> params, QueryStyle style, GraphTraversalSource source) { + throw new UnsupportedOperationException(); + } } diff --git a/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java b/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java index ba6acb66..fbaa7465 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java @@ -26,6 +26,7 @@ import groovy.lang.Script; import java.util.Map; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.onap.aai.config.SpringContextAware; import org.onap.aai.introspection.Loader; @@ -53,11 +54,8 @@ public class GroovyQueryBuilder extends AAIAbstractGroovyShell { @Override public String executeTraversal(TransactionalGraphEngine engine, String traversal, Map<String, Object> params) { QueryBuilder<Vertex> builder = engine.getQueryBuilder(QueryStyle.GREMLIN_TRAVERSAL); - SchemaVersions schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions"); - Loader loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(ModelType.MOXY, - schemaVersions.getDefaultVersion()); - builder.changeLoader(loader); + builder.changeLoader(getLoader()); Binding binding = new Binding(params); binding.setVariable("builder", builder); Script script = shell.parse(traversal); @@ -74,4 +72,17 @@ public class GroovyQueryBuilder extends AAIAbstractGroovyShell { public GraphTraversal<?, ?> executeTraversal(String traversal, Map<String, Object> params) { throw new UnsupportedOperationException(); } + + @Override + public String executeTraversal(TransactionalGraphEngine engine, String traversal, Map<String, Object> params, QueryStyle style, GraphTraversalSource traversalSource) { + QueryBuilder<Vertex> builder = engine.getQueryBuilder(style, traversalSource); + builder.changeLoader(getLoader()); + Binding binding = new Binding(params); + binding.setVariable("builder", builder); + Script script = shell.parse(traversal); + script.setBinding(binding); + script.run(); + + return builder.getQuery(); + } } diff --git a/aai-core/src/main/java/org/onap/aai/restcore/util/URITools.java b/aai-core/src/main/java/org/onap/aai/restcore/util/URITools.java index c3211fbe..a757d3db 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/util/URITools.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/util/URITools.java @@ -47,22 +47,26 @@ public class URITools { MultivaluedMap<String, String> result = new MultivaluedHashMap<>(); String queryParams = uri.getRawQuery(); if (queryParams != null) { - String[] sections = queryParams.split("&"); - String[] query = null; - String key, value = ""; - for (String section : sections) { - query = section.split("="); - key = UriUtils.decode(query[0], "UTF-8"); - if (query[1] != null) { - query[1] = query[1].replaceAll("\\+", "%20"); + try { + String[] sections = queryParams.split("&"); + String[] query = null; + String key, value = ""; + for (String section : sections) { + query = section.split("="); + key = UriUtils.decode(query[0], "UTF-8"); + if (query[1] != null) { + query[1] = query[1].replaceAll("\\+", "%20"); + } + value = UriUtils.decode(query[1], "UTF-8"); + if (result.containsKey(key)) { + result.add(key, value); + } else { + result.putSingle(key, value); + } } - value = UriUtils.decode(query[1], "UTF-8"); - if (result.containsKey(key)) { - result.add(key, value); - } else { - result.putSingle(key, value); - } - } + } catch (UnsupportedEncodingException e) { + + } } return result; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java index 3996c07d..14fb8cb5 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 AT&T Intellectual Property. 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. @@ -17,29 +17,13 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - package org.onap.aai.serialization.db; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; - -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.ws.rs.core.UriBuilder; - -import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.structure.Direction; @@ -47,14 +31,13 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.SchemaViolationException; -import org.javatuples.Triplet; +import org.javatuples.Pair; import org.onap.aai.concurrent.AaiCallable; import org.onap.aai.config.SpringContextAware; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.edges.EdgeIngestor; import org.onap.aai.edges.EdgeRule; import org.onap.aai.edges.EdgeRuleQuery; -import org.onap.aai.edges.TypeAlphabetizer; import org.onap.aai.edges.enums.AAIDirection; import org.onap.aai.edges.enums.EdgeField; import org.onap.aai.edges.enums.EdgeProperty; @@ -67,10 +50,11 @@ import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.introspection.sideeffect.*; import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.logging.LogFormatTools; -import org.onap.aai.logging.LoggingContext; import org.onap.aai.logging.StopWatch; import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.parsers.relationship.RelationshipToURI; import org.onap.aai.parsers.uri.URIParser; +import org.onap.aai.parsers.uri.URIToObject; import org.onap.aai.parsers.uri.URIToRelationshipObject; import org.onap.aai.query.builder.QueryBuilder; import org.onap.aai.schema.enums.ObjectMetadata; @@ -83,16 +67,32 @@ import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; +import org.onap.aai.util.delta.*; import org.onap.aai.workarounds.NamingExceptions; import org.springframework.context.ApplicationContext; -public class DBSerializer { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); +import javax.ws.rs.core.UriBuilder; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; - private static final String IMPLICIT_DELETE = "Implicit DELETE"; +public class DBSerializer { - private static final String MISSING_REQUIRED_NODE_PROPERTY = "Vertex missing required aai-node-type property"; + private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class); + private static final String RELATIONSHIP_LABEL = "relationship-label"; + private static final String RELATIONSHIP = "relationship"; + public static final String FALSE = "false"; + public static final String AAI_6145 = "AAI_6145"; + public static final String AAI_6129 = "AAI_6129"; private final TransactionalGraphEngine engine; private final String sourceOfTruth; @@ -108,6 +108,12 @@ public class DBSerializer { private SchemaVersions schemaVersions; private Set<String> namedPropNodes; + private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>(); + private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>(); + private Set<Vertex> edgeVertexes = new LinkedHashSet<>(); + private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>(); + private int notificationDepth; + private boolean isDeltaEventsEnabled; /** * Instantiates a new DB serializer. @@ -124,15 +130,39 @@ public class DBSerializer { this.sourceOfTruth = sourceOfTruth; this.introspectionType = introspectionType; this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions"); - SchemaVersion LATEST = schemaVersions.getDefaultVersion(); + SchemaVersion latestVersion = schemaVersions.getDefaultVersion(); this.latestLoader = - SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST); + SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion); this.version = version; this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); this.namedPropNodes = this.latestLoader.getNamedPropNodes(); this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); this.currentTimeMillis = System.currentTimeMillis(); + // If creating the DBSerializer the old way then set the notification depth to maximum + this.notificationDepth = AAIProperties.MAXIMUM_DEPTH; + initBeans(); + } + + public DBSerializer(SchemaVersion version, + TransactionalGraphEngine engine, + ModelType introspectionType, + String sourceOfTruth, + int notificationDepth) throws AAIException { + this.engine = engine; + this.sourceOfTruth = sourceOfTruth; + this.introspectionType = introspectionType; + this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions"); + SchemaVersion latestVersion = schemaVersions.getDefaultVersion(); + this.latestLoader = + SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion); + this.version = version; + this.loader = + SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); + this.namedPropNodes = this.latestLoader.getNamedPropNodes(); + this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); + this.currentTimeMillis = System.currentTimeMillis(); + this.notificationDepth = notificationDepth; initBeans(); } @@ -143,10 +173,7 @@ public class DBSerializer { setEdgeIngestor(ei); EdgeSerializer es = ctx.getBean(EdgeSerializer.class); setEdgeSerializer(es); - } - - private void backupESInit() { - setEdgeSerializer(new EdgeSerializer(this.edgeRules)); + isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE)); } public void setEdgeSerializer(EdgeSerializer edgeSer) { @@ -165,31 +192,95 @@ public class DBSerializer { return this.edgeRules; } + public Map<Vertex, Boolean> getUpdatedVertexes() { + return updatedVertexes; + } + + public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair(){ + return impliedDeleteUriObjectPair; + } + /** * Touch standard vertex properties. - * - * @param v the v + * @param v the v * @param isNewVertex the is new vertex */ public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { String timeNowInSec = Long.toString(currentTimeMillis); - if (isNewVertex) { + String uuid = UUID.randomUUID().toString(); v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); - v.property(AAIProperties.CREATED_TS, timeNowInSec); - v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); + v.property(AAIProperties.CREATED_TS, currentTimeMillis); + v.property(AAIProperties.AAI_UUID, uuid); + v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); + v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis); + v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); + } else { + if(isDeltaEventsEnabled) { + standardVertexPropsDeltas(v, timeNowInSec); + } + v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); + v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis); + v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); } - v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); - v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); - v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); + } + private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) { + String uri = v.property(AAIProperties.AAI_URI).value().toString(); + long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value(); + DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE; + if (getObjectDeltas().containsKey(uri)) { + getObjectDeltas().get(uri).setAction(objDeltaAction); + } + + addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()), objDeltaAction); + addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()), objDeltaAction); + addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()), objDeltaAction); + addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()), objDeltaAction); + + if (objDeltaAction.equals(DeltaAction.UPDATE)) { + addPropDelta( + uri, + AAIProperties.RESOURCE_VERSION, + PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()), + objDeltaAction + ); + addPropDelta( + uri, + AAIProperties.LAST_MOD_TS, + PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()), + objDeltaAction + ); + addPropDelta( + uri, + AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, + PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), + objDeltaAction + ); + } else { + addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction); + addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction); + addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction); + } } - private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { + public Map<String, ObjectDelta> getObjectDeltas() {return objectDeltas;} + + private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) { + ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis)); + objectDelta.addPropertyDelta(prop, delta); + objectDeltas.put(uri, objectDelta); + } + private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) { + ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis)); + objectDelta.addRelationshipDelta(delta); + objectDeltas.put(uri, objectDelta); + } + + private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { v.property(AAIProperties.NODE_TYPE, nodeType); touchStandardVertexProperties(v, isNewVertex); - } /** @@ -197,14 +288,12 @@ public class DBSerializer { * * @param wrappedObject the wrapped object * @return the vertex - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception */ public Vertex createNewVertex(Introspector wrappedObject) { Vertex v; try { StopWatch.conditionalStart(); - v = this.engine.tx().addVertex(); + v = this.engine.tx().addVertex(wrappedObject.getDbName()); touchStandardVertexProperties(wrappedObject.getDbName(), v, true); } finally { dbTimeMsecs += StopWatch.stopIfStarted(); @@ -227,7 +316,7 @@ public class DBSerializer { if (className.lastIndexOf('.') == -1) { return className; } - returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); + returnValue = className.substring(className.lastIndexOf('.') + 1); return returnValue; } @@ -240,15 +329,9 @@ public class DBSerializer { * @param uriQuery the uri query * @param identifier the identifier * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws InterruptedException the interrupted exception - * @throws NoSuchMethodException the no such method exception * @throws AAIException the AAI exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException */ public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { @@ -284,8 +367,6 @@ public class DBSerializer { if (isTopLevel) { addUriIfNeeded(v, obj.getURI()); } - - processObject(obj, v, requestContext); if (!isTopLevel) { URI uri = this.getURIForVertex(v); URIParser parser = new URIParser(this.loader, uri); @@ -293,6 +374,7 @@ public class DBSerializer { addUriIfNeeded(v, uri.toString()); } } + processObject(obj, v, requestContext); } catch (SchemaViolationException e) { throw new AAIException("AAI_6117", e); } finally { @@ -302,7 +384,7 @@ public class DBSerializer { private void addUriIfNeeded(Vertex v, String uri) { VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); - if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) { + if (!uriProp.isPresent() || !uriProp.value().equals(uri)) { v.property(AAIProperties.AAI_URI, uri); } } @@ -310,43 +392,42 @@ public class DBSerializer { /** * Process object. * - * @param <T> the generic type * @param obj the obj * @param v the v * @return the list - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws AAIException the AAI exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException */ /* * Helper method for reflectToDb * Handles all the property setting */ - private <T> List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) + private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { Set<String> properties = new LinkedHashSet<>(obj.getProperties()); properties.remove(AAIProperties.RESOURCE_VERSION); List<Vertex> dependentVertexes = new ArrayList<>(); List<Vertex> processedVertexes = new ArrayList<>(); - boolean isComplexType = false; - boolean isListType = false; - if (!obj.isContainer()) { - this.touchStandardVertexProperties(v, false); + + boolean isComplexType ; + boolean isListType; + + // If the notification depth is set to maximum + // this is the behavior of the expected clients + if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) { + if (!obj.isContainer()) { + this.touchStandardVertexProperties(v, false); + } } this.executePreSideEffects(obj, v); for (String property : properties) { - Object value = null; final String propertyType; propertyType = obj.getType(property); isComplexType = obj.isComplexType(property); isListType = obj.isListType(property); - value = obj.getValue(property); + Object value = obj.getValue(property); if (!(isComplexType || isListType)) { boolean canModify = this.canModify(obj, property, requestContext); @@ -362,16 +443,28 @@ public class DBSerializer { // they are populated dynamically on GETs continue; } + Object oldValue = v.property(dbProperty).orElse(null); + String uri = getURIForVertex(v).toString(); if (value != null) { - if (!value.equals(v.property(dbProperty).orElse(null))) { + if (!value.equals(oldValue)) { if (propertyType.toLowerCase().contains(".long")) { v.property(dbProperty, new Integer(((Long) value).toString())); } else { v.property(dbProperty, value); } + if (isDeltaEventsEnabled) { + createDeltaProperty(uri, value, dbProperty, oldValue); + } + this.updatedVertexes.putIfAbsent(v, false); } } else { - v.property(dbProperty).remove(); + if (oldValue != null) { + v.property(dbProperty).remove(); + if (isDeltaEventsEnabled) { + addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE); + } + this.updatedVertexes.putIfAbsent(v, false); + } } } } else if (isListType) { @@ -386,7 +479,21 @@ public class DBSerializer { } } else { // simple list case - engine.setListProperty(v, property, list); + if (isDeltaEventsEnabled) { + String uri = getURIForVertex(v).toString(); + List<Object> oldVal = engine.getListProperty(v, property); + engine.setListProperty(v, property, list); + if (list == null || list.isEmpty()) { // property delete scenario, there is no new value + if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value + addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE); + } + } else { // is either a create or update and is handled by the called method + createDeltaProperty(uri, list, property, oldVal); + } + } else { + engine.setListProperty(v, property, list); + } + this.updatedVertexes.putIfAbsent(v, false); } } else { // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge @@ -427,62 +534,192 @@ public class DBSerializer { } this.writeThroughDefaults(v, obj); /* handle those vertexes not touched */ - for (Vertex toBeRemoved : processedVertexes) { - dependentVertexes.remove(toBeRemoved); + for (Vertex toBeKept : processedVertexes) { + dependentVertexes.remove(toBeKept); } - // If the dependent vertices are not empty, then with - // the current behaviour, it should remove the vertices implicitly - // We are updating the code to properly log which call - // is doing this so the SE can work with the clients making the call to - // tell them not to call this API and can hopefully deprecate this - // functionality in the future releases - if (!dependentVertexes.isEmpty()) { + ImpliedDelete impliedDelete = new ImpliedDelete(engine, this); + List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes); + + if(notificationDepth == AAIProperties.MINIMUM_DEPTH){ + for(Vertex curVertex : impliedDeleteVertices){ + if(!curVertex.property("aai-uri").isPresent()){ + LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id()); + continue; + } + String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value(); + Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth); - LoggingContext.responseDescription(IMPLICIT_DELETE); + LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>(); - // Find all the deletable vertices from the dependent vertices that should be deleted - // So for each of the following dependent vertices, - // we will use the edge properties and do the cascade delete - List<Vertex> impliedDeleteVertices = this.engine.getQueryEngine().findDeletable(dependentVertexes); - int impliedDeleteCount = impliedDeleteVertices.size(); + if(!curObj.isTopLevel()){ + curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader)); + } - LOGGER.warn( - "For the vertex with id {}, doing an implicit delete on update will delete total of {} vertexes", - v.id(), impliedDeleteCount); + if(!impliedDeleteUriObjectPair.containsKey(curAaiUri)){ + impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated)); + } + } + } - String impliedDeleteLogEnabled = AAIConfig.get(AAIConstants.AAI_IMPLIED_DELETE_LOG_ENABLED, "true"); + impliedDelete.delete(impliedDeleteVertices); - int impliedDeleteLogLimit = AAIConfig.getInt(AAIConstants.AAI_IMPLIED_DELETE_LOG_LIMIT, "-1"); + // touch svp using vertex list for what changed + // if the notification depth is zero + if(notificationDepth == AAIProperties.MINIMUM_DEPTH){ + this.updatedVertexes.entrySet().stream() + .filter(e -> !e.getValue()) + .filter(e -> !edgeVertexes.contains(e.getKey())) + .forEach(e -> { + this.touchStandardVertexProperties(e.getKey(), false); + e.setValue(true); + }); + } + this.executePostSideEffects(obj, v); + return processedVertexes; + } - if (impliedDeleteLogLimit == -1) { - impliedDeleteLogLimit = Integer.MAX_VALUE; - } + private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) { + if (oldValue == null) { + addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE); + } else { + addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE); + } + } - // If the logging is enabled for implied delete - // then log the payload in the latest format - if ("true".equals(impliedDeleteLogEnabled) && impliedDeleteCount <= impliedDeleteLogLimit) { - for (Vertex vertex : impliedDeleteVertices) { - Introspector introspector = null; - try { - introspector = getLatestVersionView(vertex); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Implied delete object in json format {}", introspector.marshal(false)); - } - } catch (Exception ex) { - LOGGER.warn( - "Encountered an exception during retrieval of vertex properties with vertex-id {} -> {}", - v.id(), LogFormatTools.getStackTop(ex)); - } + public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v, + Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException { + + HashMap<String, Introspector> relatedVertices = new HashMap<>(); + VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); + + if (!aaiUriProperty.isPresent()) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", + v.id().toString()); + } else { + LOGGER.info( + "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log"); + } + return relatedVertices; + } + + String aaiUri = aaiUriProperty.value().toString(); + + if (!obj.isTopLevel()) { + String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader); + List<Vertex> vertexChain; + // If the uriList is null then there is something wrong with converting the uri + // into a list of aai-uris so falling back to the old mechanism for finding parents + if (uriList == null) { + LOGGER.info( + "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal"); + vertexChain = queryEngine.findParents(v); + } else if (uriList.length == 1) { + // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query + vertexChain = Collections.singletonList(v); + } else { + // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup. + vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length)); + // inject v into start of vertexChain + vertexChain.add(0, v); + } + for (Vertex vertex : vertexChain) { + try { + final Introspector vertexObj = this.getVertexProperties(vertex); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); } } + } else { + try { + final Introspector vertexObj = this.getVertexProperties(v); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); + } + } + + return relatedVertices; + } + + /** + * Given an uri, introspector object and loader object + * it will check if the obj is top level object if it is, + * it will return immediately returning the uri passed in + * If it isn't, it will go through, get the uriTemplate + * from the introspector object and get the count of "/"s + * and remove that part of the uri using substring + * and keep doing that until the current object is top level + * Also added the max depth just so worst case scenario + * Then keep adding aai-uri to the list include the aai-uri passed in + * Convert that list into an array and return it + * <p> + * + * Example: + * + * <blockquote> + * aai-uri -> + * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * + * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id} + * it converts to /vservers/vserver + * + * lastIndexOf /vservers/vserver in + * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * ^ + * | + * | + * lastIndexOf + * Use substring to get the string from 0 to that lastIndexOf + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1 + * + * From this new aai-uri, generate a introspector from the URITOObject class + * and keep doing this until you + * + * </blockquote> + * + * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex + * @param obj - introspector object of the given starting vertex + * @param loader - Type of loader which will always be MoxyLoader to support model driven + * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex + * @throws UnsupportedEncodingException + * @throws AAIException + */ + String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) + throws UnsupportedEncodingException, AAIException { + + List<String> uriList = new ArrayList<>(); + String template; + String truncatedUri = aaiUri; + int depth = AAIProperties.MAXIMUM_DEPTH; + uriList.add(truncatedUri); + + while (depth >= 0 && !obj.isTopLevel()) { + template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); + + if (template == null) { + LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); + return null; + } - // After all the appropriate logging, calling the delete to delete the affected vertices - this.delete(impliedDeleteVertices); + int templateCount = StringUtils.countMatches(template, "/"); + int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); + + if (templateCount > truncatedUriCount) { + LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); + return null; + } + + int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1); + truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex); + uriList.add(truncatedUri); + obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity(); + depth--; } - this.executePostSideEffects(obj, v); - return processedVertexes; + return uriList.toArray(new String[0]); } /** @@ -491,9 +728,7 @@ public class DBSerializer { * @param obj the obj * @param vertex the vertex * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception */ @@ -517,28 +752,141 @@ public class DBSerializer { * @throws AAIException the AAI exception */ private void processRelationshipList(Introspector wrapped, Vertex v) - throws UnsupportedEncodingException, AAIException { + throws UnsupportedEncodingException, AAIException { + + List<Object> relationships = wrapped.getValue("relationship"); + String mainUri = getURIForVertex(v).toString(); + String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString(); + EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType) + .edgeType(EdgeType.COUSIN) + .version(wrapped.getVersion()); + EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType) + .edgeType(EdgeType.TREE) + .version(wrapped.getVersion()); - List<Object> relationships = (List<Object>) wrapped.getValue("relationship"); - - List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>(); - List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); + EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class); + Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>(); for (Object relationship : relationships) { - Edge e = null; - Vertex cousinVertex = null; - String label = null; + + String label; Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString()); + + if (relUri.startsWith("/vnf/")) { + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + final AAIException ex = new AAIException(AAI_6129, + String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri())); + ex.getTemplateVars().add(parser.getResultType()); + ex.getTemplateVars().add(parser.getUri().toString()); + throw ex; + } else { + // still an issue if there's more than one + if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) { + relUri = results.get(0).value(AAIProperties.AAI_URI); + } else { + LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id()); + continue; + } + } + } - if (wrappedRel.hasProperty("relationship-label")) { - label = wrappedRel.getValue("relationship-label"); + if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) { + label = wrappedRel.getValue(RELATIONSHIP_LABEL); + } else { + URIToObject uriToObject = new URIToObject(loader, URI.create(relUri)); + String bNodeType = uriToObject.getEntityName(); + EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build(); + if (!edgeIngestor.hasRule(ruleQuery)) { + EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build(); + if (edgeIngestor.hasRule(treeQuery)) { + throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule + } + throw new AAIException("AAI_6120", String.format( + "No EdgeRule found for passed nodeTypes: %s, %s.", + aNodeType, bNodeType)); + } else { + try { + final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values()); + if (rules.size() == 1) { + label = rules.get(0).getLabel(); + } else { + Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst(); + if (defaultRule.isPresent()) { + label = defaultRule.get().getLabel(); + } else { + throw new AAIException(AAI_6145); + } + } + } catch (EdgeRuleNotFoundException ea) { + throw new AAIException(AAI_6145, ea); + } + } } + cousinUriAndLabels.add(Pair.with(relUri, label)); + } + + List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v); + Set<Path> toRemove = new HashSet<>(); + + + // for each path 3 things can happen: + // 1. The edge rule that created it is not in this version no action is to be taken on that edge + // 2. The edge rule exits in this version it's included in the request the edge is left alone + // 3. The edge rule exits in this version and is not included in the request it is marked for removal + for (Path path : paths) { + if (path.size() < 3) { + continue; + } + + // Path represents + // v ----related-to--> otherV + // In the above case, + // path objects get(0) returns vertex v + // path objects.get(1) returns edge related-to + // path objects.get(2) returns vertex otherV + Vertex otherV = path.get(2); + + String bUri; + if (otherV.property(AAIProperties.AAI_URI).isPresent()) { + bUri = otherV.value(AAIProperties.AAI_URI); + } else { + continue; + } + String edgeLabel = path.<Edge>get(1).label(); + + Pair<String, String> key = Pair.with(bUri, edgeLabel); + if (cousinUriAndLabels.contains(key)) { + cousinUriAndLabels.remove(key); + } else { + String bNodeType; + if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) { + bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString(); + } else { + continue; + } + EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build(); + if (edgeIngestor.hasRule(ruleQuery)) { + toRemove.add(path); + } + } + + } + + Set<Pair<Vertex, String>> toBeCreated = new HashSet<>(); + for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) { + Edge e; + Vertex cousinVertex; + String label = cousinUriAndLabel.getValue1(); + String cousinUri = cousinUriAndLabel.getValue0(); + QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri)); List<Vertex> results = parser.getQueryBuilder().toList(); if (results.isEmpty()) { - final AAIException ex = new AAIException("AAI_6129", - "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + final AAIException ex = new AAIException(AAI_6129, + "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); ex.getTemplateVars().add(parser.getResultType()); ex.getTemplateVars().add(parser.getUri().toString()); throw ex; @@ -553,38 +901,56 @@ public class DBSerializer { EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label); if (!edgeRules.hasRule(baseQ.build())) { - throw new AAIException("AAI_6120", - "No EdgeRule found for passed nodeTypes: " - + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " - + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() - + (label != null ? (" with label " + label) : "") + "."); + throw new AAIException("AAI_6120", String.format( + "No EdgeRule found for passed nodeTypes: %s, %s%s.", + aNodeType, cousinType, label != null ? (" with label " + label) : "")); } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) - && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { - throw new AAIException("AAI_6145"); + && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { + throw new AAIException(AAI_6145); } e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); if (e == null) { - addEdges.add(new Triplet<>(v, cousinVertex, label)); - } else { - existingEdges.remove(e); + toBeCreated.add(Pair.with(cousinVertex, label)); } } } - for (Edge edge : existingEdges) { - edge.remove(); + for (Path path : toRemove) { + if(isDeltaEventsEnabled) { + deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE); + } + this.updatedVertexes.putIfAbsent(v, false); + this.edgeVertexes.add(path.get(2)); + path.<Edge>get(1).remove(); } - for (Triplet<Vertex, Vertex, String> triplet : addEdges) { + + for (Pair<Vertex, String> create : toBeCreated) { try { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), - triplet.getValue2()); - } catch (NoEdgeRuleFoundException e) { - throw new AAIException("AAI_6129", e); + Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1()); + if (isDeltaEventsEnabled) { + deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE); + } + this.updatedVertexes.putIfAbsent(v, false); + this.edgeVertexes.add(create.getValue0()); + } catch (NoEdgeRuleFoundException ex) { + throw new AAIException(AAI_6129, ex); } } + } + + private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) { + RelationshipDelta relationshipDelta = new RelationshipDelta( + edgeAction, + edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(), + edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(), + edge.inVertex().property(AAIProperties.AAI_URI).value().toString(), + edge.outVertex().property(AAIProperties.AAI_URI).value().toString(), + edge.label()); + edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString())); + addRelationshipDelta(mainUri, relationshipDelta, mainAction); } /** @@ -600,11 +966,9 @@ public class DBSerializer { Set<String> required = latest.getRequiredProperties(); for (String field : required) { - String defaultValue = null; - Object vertexProp = null; - defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); + String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); if (defaultValue != null) { - vertexProp = v.<Object>property(field).orElse(null); + Object vertexProp = v.property(field).orElse(null); if (vertexProp == null) { v.property(field, defaultValue); } @@ -620,11 +984,7 @@ public class DBSerializer { * @param v the v * @param dependentObj the dependent obj * @return the vertex - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws AAIException the AAI exception * @throws UnsupportedEncodingException the unsupported encoding exception @@ -633,23 +993,21 @@ public class DBSerializer { private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { - // QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); - // List<Vertex> items = p.getQuery().toList(); QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v); query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); query.createKeyQuery(dependentObj); List<Vertex> items = query.toList(); - Vertex dependentVertex = null; + Vertex dependentVertex; if (items.size() == 1) { dependentVertex = items.get(0); this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), - (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI()); } else { this.verifyResourceVersion("create", dependentObj.getDbName(), "", - (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI()); dependentVertex = createNewVertex(dependentObj); } @@ -664,11 +1022,7 @@ public class DBSerializer { * @param child the child * @param obj the obj * @return the vertex - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws AAIException the AAI exception * @throws UnsupportedEncodingException the unsupported encoding exception @@ -699,7 +1053,10 @@ public class DBSerializer { child.property(AAIProperties.LINKED, true); } } - edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); + e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); + if(isDeltaEventsEnabled) { + deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE); + } } return child; @@ -724,19 +1081,14 @@ public class DBSerializer { * @param cleanUp the clean up * @return the introspector * @throws AAIException the AAI exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception * @throws SecurityException the security exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws MalformedURLException the malformed URL exception * @throws AAIUnknownObjectException * @throws URISyntaxException */ public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, - String cleanUp) throws UnsupportedEncodingException, AAIException { + String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException { final int internalDepth; if (depth == Integer.MAX_VALUE) { internalDepth = depth--; @@ -747,7 +1099,7 @@ public class DBSerializer { if (vertices.size() > 1 && !obj.isContainer()) { dbTimeMsecs += StopWatch.stopIfStarted(); throw new AAIException("AAI_6136", - "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); + "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); } else if (obj.isContainer()) { final List getList; String listProperty = null; @@ -758,7 +1110,7 @@ public class DBSerializer { } } final String propertyName = listProperty; - getList = (List) obj.getValue(listProperty); + getList = obj.getValue(listProperty); /* * This is an experimental multithreading experiment @@ -768,28 +1120,15 @@ public class DBSerializer { List<Future<Object>> futures = new ArrayList<>(); - QueryEngine tgEngine = this.engine.getQueryEngine(); for (Vertex v : vertices) { - AaiCallable<Object> task = new AaiCallable<Object>() { @Override public Object process() throws UnsupportedEncodingException, AAIException { Set<Vertex> seen = new HashSet<>(); Introspector childObject; - try { - childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); - } catch (AAIUnknownObjectException e) { - throw e; - } - try { - dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp); - } catch (UnsupportedEncodingException e) { - throw e; - } catch (AAIException e) { - throw e; - } + childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); + dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo); return childObject.getUnderlyingObject(); - // getList.add(childObject.getUnderlyingObject()); } }; futures.add(pool.submit(task)); @@ -798,17 +1137,14 @@ public class DBSerializer { for (Future<Object> future : futures) { try { getList.add(future.get()); - } catch (ExecutionException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_4000", e); - } catch (InterruptedException e) { + } catch (ExecutionException | InterruptedException e) { dbTimeMsecs += StopWatch.stopIfStarted(); throw new AAIException("AAI_4000", e); } } } else if (vertices.size() == 1) { Set<Vertex> seen = new HashSet<>(); - dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp); + dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo); } else { // obj = null; } @@ -820,26 +1156,62 @@ public class DBSerializer { /** * Db to object. * + * @param vertices the vertices + * @param obj the obj + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws AAIException the AAI exception + * @throws IllegalArgumentException the illegal argument exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, + String cleanUp) throws UnsupportedEncodingException, AAIException { + return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false); + } + + /** + * Db to object. + * * @param obj the obj * @param v the v * @param seen the seen * @param depth the depth * @param cleanUp the clean up * @return the introspector - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception * @throws SecurityException the security exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception * @throws AAIUnknownObjectException * @throws URISyntaxException */ private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { + return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false); + } + + /** + * Db to object. + * + * @param obj the obj + * @param v the v + * @param seen the seen + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws IllegalArgumentException the illegal argument exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, + String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException { if (depth < 0) { return null; @@ -850,7 +1222,6 @@ public class DBSerializer { boolean modified = false; for (String property : obj.getProperties(PropertyPredicates.isVisible())) { List<Object> getList = null; - Vertex[] vertices = null; if (!(obj.isComplexType(property) || obj.isListType(property))) { this.copySimpleProperty(property, obj, v); @@ -869,9 +1240,8 @@ public class DBSerializer { } else if (property.equals("relationship-list") && !nodeOnly) { /* relationships need to be handled correctly */ Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); - relationshipList = createRelationshipList(v, relationshipList, cleanUp); + relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo); if (relationshipList != null) { - modified = true; obj.setValue(property, relationshipList.getUnderlyingObject()); modified = true; } @@ -890,42 +1260,39 @@ public class DBSerializer { try { rule = edgeRules.getRule( - new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); + new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); } catch (EdgeRuleNotFoundException e) { throw new NoEdgeRuleFoundException(e); } catch (AmbiguousRuleChoiceException e) { throw new MultipleEdgeRuleFoundException(e); } if (!rule.getContains().equals(AAIDirection.NONE.toString())) { - // vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), - // childDbName); + Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>) obj.getValue(property); + List<Vertex> verticesList = new ArrayList<>(); + v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> { + if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) { + verticesList.add(vertex); + } + }); + if (!verticesList.isEmpty()) { + getList = obj.getValue(property); } int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); + for (Vertex childVertex : verticesList) { if (!seen.contains(childVertex)) { Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); Object result = - dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); + dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo); if (result != null) { getList.add(argumentObject.getUnderlyingObject()); } processed++; } else { - removed++; LOGGER.warn("Cycle found while serializing vertex id={}", - childVertex.id().toString()); + childVertex.id().toString()); } } if (processed == 0) { @@ -943,11 +1310,8 @@ public class DBSerializer { getList.addAll(temp); modified = true; } - } - } - } } @@ -969,27 +1333,26 @@ public class DBSerializer { Introspector obj = this.latestLoader.introspectorFromName(nodeType); Set<Vertex> seen = new HashSet<>(); int depth = 0; - String cleanUp = "false"; - boolean nodeOnly = true; StopWatch.conditionalStart(); - this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); + this.dbToObject(obj, v, seen, depth, false, FALSE); dbTimeMsecs += StopWatch.stopIfStarted(); return obj; } public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException { + return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH); + } + + public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException { String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); if (nodeType == null) { throw new AAIException("AAI_6143"); } Introspector obj = this.latestLoader.introspectorFromName(nodeType); Set<Vertex> seen = new HashSet<>(); - int depth = AAIProperties.MAXIMUM_DEPTH; - String cleanUp = "false"; - boolean nodeOnly = false; StopWatch.conditionalStart(); - this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); + this.dbToObject(obj, v, seen, depth, false, FALSE); dbTimeMsecs += StopWatch.stopIfStarted(); return obj; } @@ -1000,11 +1363,7 @@ public class DBSerializer { * @param property the property * @param obj the obj * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception */ private void copySimpleProperty(String property, Introspector obj, Vertex v) { @@ -1014,65 +1373,22 @@ public class DBSerializer { } } - /** - * Load the introspector from the hashmap for the given property key - * - * @param property - vertex property - * @param obj - introspector object representing the vertex - * @param hashMap - Containing a list of pre-fetched properties for a given vertex - */ - private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap) { - - final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); - String dbPropertyName = property; - - if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { - dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); - } - - final Object temp = hashMap.getOrDefault(dbPropertyName, null); - - if (temp != null) { - obj.setValue(property, temp); - } - } - - /** - * Simple db to object. - * - * @param obj the obj - * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - */ - private void simpleDbToObject(Introspector obj, Vertex v) { - for (String key : obj.getProperties()) { - this.copySimpleProperty(key, obj, v); - } - } - public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) { - long startTime = System.currentTimeMillis(); - Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible()); String[] simplePropsArray = new String[simpleProperties.size()]; simplePropsArray = simpleProperties.toArray(simplePropsArray); Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2); - v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value())); + v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value())); return simplePropsHashMap; } - public Introspector dbToRelationshipObject(Vertex v) throws UnsupportedEncodingException, AAIException { + public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException { Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list"); - relationshipList = createRelationshipList(v, relationshipList, "false"); + relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo); return relationshipList; } @@ -1083,136 +1399,96 @@ public class DBSerializer { * @param obj the obj * @param cleanUp the clean up * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception * @throws URISyntaxException */ private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { + // default boolean value for isSkipRelatedTo is false + return createRelationshipList(v, obj, cleanUp, false); + } - String[] cousinRules = new String[0]; - - try { - cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName()); - } catch (ExecutionException e) { - LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", - obj.getDbName(), e); - } - - List<Vertex> cousins = null; - if (cousinRules != null && cousinRules.length != 0) { - cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules); - } else { - cousins = this.engine.getQueryEngine().findCousinVertices(v); - } + /** + * Creates the relationship list. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @param isSkipRelatedTo to determine adding related-to-property in response + * @return the object + * @throws IllegalArgumentException the illegal argument exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws URISyntaxException + */ + private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo) + throws UnsupportedEncodingException, AAIException { - List<Object> relationshipObjList = obj.getValue("relationship"); + List<Object> relationshipObjList = obj.getValue(RELATIONSHIP); VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE); if (!nodeTypeProperty.isPresent()) { - LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY); LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id()); - LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString()); return null; } - String aNodeType = nodeTypeProperty.value().toString(); + List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v); - TypeAlphabetizer alphabetizer = new TypeAlphabetizer(); + String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString(); EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class); - Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys(); - - // For the given vertex, find all the cousins - // For each cousin retrieve the node type and then - // check if the version is greater than the edge label version - // meaning is the current version equal to greater than the version - // where we introduced the edge labels into the relationship payload - // If it is, then we check if the edge key there are multiple labels - // If there are multiple labels, then we need to go to the database - // to retrieve the labels between itself and cousin vertex - // If there is only single label between the edge a and b, then - // we can retrieve what that is without going to the database - // from using the edge rules json and get the edge rule out of it - EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType); - for (Vertex cousin : cousins) { - VertexProperty vertexProperty = cousin.property(AAIProperties.NODE_TYPE); - String bNodeType = null; - if (vertexProperty.isPresent()) { - bNodeType = cousin.property(AAIProperties.NODE_TYPE).value().toString(); - } else { - // If the vertex is missing the aai-node-type - // Then its either a bad vertex or its in the process - // of getting deleted so we should ignore these vertexes - LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString()); - } else { - LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log"); - } - LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString()); + + EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType) + .edgeType(EdgeType.COUSIN) + .version(obj.getVersion()); + + for (Path path : paths){ + if(path.size() < 3){ continue; } - if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) { - String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType); - if (keysWithMultipleLabels.contains(edgeKey)) { - List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin); - for (String edgeLabel : edgeLabels) { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel); - if (result != null) { - relationshipObjList.add(result); - } - } - } else { - EdgeRule edgeRule = null; + // Path represents + // v ----related-to--> otherV + // In the above case, + // path objects get(0) returns vertex v + // path objects.get(1) returns edge related-to + // path objects.get(2) returns vertex otherV + Edge edge = path.get(1); + Vertex otherV= path.get(2); + + // TODO: Come back and revisit this code + // Create a query based on the a nodetype and b nodetype + // which is also a cousin edge and ensure the version + // is used properly so for example in order to be backwards + // compatible if we had allowed a edge between a and b + // in a previous release and we decided to remove it from + // the edge rules in the future we can display the edge + // only for the older apis and the new apis if the edge rule + // is removed will not be seen in the newer version of the API - // Create a query based on the a nodetype and b nodetype - // which is also a cousin edge and ensure the version - // is used properly so for example in order to be backwards - // compatible if we had allowed a edge between a and b - // in a previous release and we decided to remove it from - // the edge rules in the future we can display the edge - // only for the older apis and the new apis if the edge rule - // is removed will not be seen in the newer version of the API + String bNodeType = null; + if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) { + bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString(); + } else { + continue; + } - EdgeRuleQuery ruleQuery = - queryBuilder.to(bNodeType).edgeType(EdgeType.COUSIN).version(obj.getVersion()).build(); + String edgeLabel = edge.label(); + EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build(); - try { - edgeRule = edgeIngestor.getRule(ruleQuery); - } catch (EdgeRuleNotFoundException e) { - LOGGER.warn( - "Caught an edge rule not found exception for query {}, {}," - + " it could be the edge rule is no longer valid for the existing edge in db", - ruleQuery, LogFormatTools.getStackTop(e)); - continue; - } catch (AmbiguousRuleChoiceException e) { - LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}", ruleQuery, - LogFormatTools.getStackTop(e)); - continue; - } + if(!edgeIngestor.hasRule(ruleQuery)){ + LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery); + continue; + } - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeRule.getLabel()); - if (result != null) { - relationshipObjList.add(result); - } - } - } else { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); - if (result != null) { - relationshipObjList.add(result); - } + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP); + Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo); + if (result != null) { + relationshipObjList.add(result); } } @@ -1228,23 +1504,17 @@ public class DBSerializer { * Process edge relationship. * * @param relationshipObj the relationship obj - * @param edge the edge + * @param edgeLabel the edge's label * @param cleanUp the clean up * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception * @throws AAIUnknownObjectException * @throws URISyntaxException */ private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, - String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException { + String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException { VertexProperty aaiUriProperty = cousin.property("aai-uri"); @@ -1254,8 +1524,8 @@ public class DBSerializer { URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build(); - URIToRelationshipObject uriParser = null; - Introspector result = null; + URIToRelationshipObject uriParser; + Introspector result; try { uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); result = uriParser.getResult(); @@ -1269,13 +1539,13 @@ public class DBSerializer { if (cousinVertexNodeType.isPresent()) { String cousinType = cousinVertexNodeType.value().toString(); - if (namedPropNodes.contains(cousinType)) { + if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) { this.addRelatedToProperty(result, cousin, cousinType); } } - if (edgeLabel != null && result.hasProperty("relationship-label")) { - result.setValue("relationship-label", edgeLabel); + if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) { + result.setValue(RELATIONSHIP_LABEL, edgeLabel); } return result.getUnderlyingObject(); @@ -1286,11 +1556,7 @@ public class DBSerializer { * * @param v the v * @return the URI for vertex - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIUnknownObjectException @@ -1300,7 +1566,7 @@ public class DBSerializer { return getURIForVertex(v, false); } - public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { + public URI getURIForVertex(Vertex v, boolean overwrite) { URI uri = UriBuilder.fromPath("/unknown-uri").build(); String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); @@ -1312,28 +1578,11 @@ public class DBSerializer { return uri; } - /** - * Gets the URI from list. - * - * @param list the list - * @return the URI from list - * @throws UnsupportedEncodingException the unsupported encoding exception - */ - private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { - String uri = ""; - StringBuilder sb = new StringBuilder(); - for (Introspector i : list) { - sb.insert(0, i.getURI()); - } - - uri = sb.toString(); - return UriBuilder.fromPath(uri).build(); - } public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType) throws AAIUnknownObjectException { - Introspector obj = null; + Introspector obj; try { obj = this.loader.introspectorFromName(cousinType); @@ -1360,7 +1609,7 @@ public class DBSerializer { } if (!relatedToProperties.isEmpty()) { - List relatedToList = (List) relationship.getValue("related-to-property"); + List<Object> relatedToList = relationship.getValue("related-to-property"); for (Introspector introspector : relatedToProperties) { relatedToList.add(introspector.getUnderlyingObject()); } @@ -1389,22 +1638,22 @@ public class DBSerializer { * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception */ - public boolean createEdge(Introspector relationship, Vertex inputVertex) + public Vertex createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - Vertex relatedVertex = null; + Vertex relatedVertex; StopWatch.conditionalStart(); QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); + if (relationship.hasProperty(RELATIONSHIP_LABEL)) { + label = relationship.getValue(RELATIONSHIP_LABEL); } List<Vertex> results = parser.getQueryBuilder().toList(); if (results.isEmpty()) { dbTimeMsecs += StopWatch.stopIfStarted(); - AAIException e = new AAIException("AAI_6129", + AAIException e = new AAIException(AAI_6129, "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); e.getTemplateVars().add(parser.getResultType()); e.getTemplateVars().add(parser.getUri().toString()); @@ -1420,7 +1669,10 @@ public class DBSerializer { try { e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); if (e == null) { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); + e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); + if(isDeltaEventsEnabled) { + deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE); + } } else { // attempted to link two vertexes already linked } @@ -1430,7 +1682,7 @@ public class DBSerializer { } dbTimeMsecs += StopWatch.stopIfStarted(); - return true; + return relatedVertex; } /** @@ -1439,8 +1691,6 @@ public class DBSerializer { * @param aVertex the out vertex * @param bVertex the in vertex * @return the edges between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException */ private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) { @@ -1476,42 +1726,11 @@ public class DBSerializer { } /** - * Gets all the edges between of the type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @return the edges between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { - - List<Edge> result = new ArrayList<>(); - - if (bVertex != null) { - GraphTraversal<Vertex, Edge> findEdgesBetween = null; - findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); - if (EdgeType.TREE.equals(type)) { - findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"), - __.has(EdgeField.PRIVATE.toString(), true))); - } else { - findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE") - .not(__.has(EdgeField.PRIVATE.toString(), true)); - } - findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); - result = findEdgesBetween.toList(); - } - - return result; - } - - /** * Gets all the edges string between of the type. * * @param aVertex the out vertex * @param bVertex the in vertex * @return the edges between - * @throws AAIException the AAI exception * @throws NoEdgeRuleFoundException */ private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { @@ -1535,30 +1754,6 @@ public class DBSerializer { } /** - * Gets all the edges string between of the type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @return the edges between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) { - - Long result = null; - - if (bVertex != null) { - GraphTraversal<Vertex, Edge> findEdgesBetween = null; - findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); - findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE") - .not(__.has(EdgeField.PRIVATE.toString(), true)); - findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); - result = findEdgesBetween.count().next(); - } - return result; - } - - /** * Gets all the edges between the vertexes with the label and type. * * @param aVertex the out vertex @@ -1628,23 +1823,23 @@ public class DBSerializer { * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception */ - public boolean deleteEdge(Introspector relationship, Vertex inputVertex) + public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - Vertex relatedVertex = null; + Vertex relatedVertex; StopWatch.conditionalStart(); QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); List<Vertex> results = parser.getQueryBuilder().toList(); String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); + if (relationship.hasProperty(RELATIONSHIP_LABEL)) { + label = relationship.getValue(RELATIONSHIP_LABEL); } if (results.isEmpty()) { dbTimeMsecs += StopWatch.stopIfStarted(); - return false; + return Optional.empty(); } relatedVertex = results.get(0); @@ -1653,29 +1848,19 @@ public class DBSerializer { edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); } catch (NoEdgeRuleFoundException e) { dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6129", e); + throw new AAIException(AAI_6129, e); } if (edge != null) { + if(isDeltaEventsEnabled) { + String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString(); + deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE); + } edge.remove(); dbTimeMsecs += StopWatch.stopIfStarted(); - return true; + return Optional.of(relatedVertex); } else { dbTimeMsecs += StopWatch.stopIfStarted(); - return false; - } - - } - - /** - * Delete items with traversal. - * - * @param vertexes the vertexes - * @throws IllegalStateException the illegal state exception - */ - public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { - - for (Vertex v : vertexes) { - deleteWithTraversal(v); + return Optional.empty(); } } @@ -1688,12 +1873,7 @@ public class DBSerializer { public void deleteWithTraversal(Vertex startVertex) { StopWatch.conditionalStart(); List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); - - for (Vertex v : results) { - LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label()); - v.remove(); - } - dbTimeMsecs += StopWatch.stopIfStarted(); + this.delete(results); } /** @@ -1720,12 +1900,47 @@ public class DBSerializer { for (Vertex v : vertices) { LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label()); + if(isDeltaEventsEnabled) { + deltaForVertexDelete(v); + } + //add the cousin vertexes of v to have their resource-version updated and notified on. + v.edges(Direction.BOTH) + .forEachRemaining(e -> { + if (e.property(EdgeProperty.CONTAINS.toString()).isPresent() + && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) { + e.bothVertices().forEachRemaining(cousinV -> { + if (!v.equals(cousinV)) { + edgeVertexes.add(cousinV); + } + }); + } + }); + + //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on + // it should be removed from them as v is to be deleted + edgeVertexes.remove(v); + updatedVertexes.remove(v); v.remove(); } dbTimeMsecs += StopWatch.stopIfStarted(); } + private void deltaForVertexDelete(Vertex v) { + String aaiUri = v.property(AAIProperties.AAI_URI).value().toString(); + v.keys().forEach(k -> { + List<Object> list = new ArrayList<>(); + v.properties(k).forEachRemaining(vp -> list.add(vp.value())); + if (list.size() == 1) { + addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)), DeltaAction.DELETE); + } else { + addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE); + } + + }); + v.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE)); + } + /** * Delete. * @@ -1741,7 +1956,7 @@ public class DBSerializer { boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); /* * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain - * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted + * These are far-fewer than seeing a prevent-delete on the vertex to be deleted * So its better to make these in 2 steps */ if (result && !deletableVertices.isEmpty()) { @@ -1756,7 +1971,6 @@ public class DBSerializer { } } - } /** @@ -1782,7 +1996,6 @@ public class DBSerializer { } } - } /** @@ -1795,15 +2008,14 @@ public class DBSerializer { */ private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { - boolean result = true; - String nodeType = ""; - String errorDetail = " unknown delete semantic found"; - String aaiExceptionCode = ""; + boolean result; + String nodeType; nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, - vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { + if (enableResourceVersion) { + this.verifyResourceVersion("delete", nodeType, + vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType); } - List<Vertex> vertices = new ArrayList<Vertex>(); + List<Vertex> vertices = new ArrayList<>(); vertices.add(vertex); result = verifyPreventDeleteSemantics(vertices); @@ -1819,7 +2031,6 @@ public class DBSerializer { */ private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException { boolean result = true; - String nodeType = ""; String errorDetail = " unknown delete semantic found"; String aaiExceptionCode = ""; @@ -1987,6 +2198,35 @@ public class DBSerializer { /** * Db to object With Filters * This is for a one-time run with Tenant Isloation to only filter relationships + * + * @param obj the obj + * @param v the vertex from the graph + * @param depth the depth + * @param nodeOnly specify if to exclude relationships or not + * @param filterCousinNodes + * @return the introspector + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, + List<String> filterCousinNodes, List<String> filterParentNodes) + throws AAIException, UnsupportedEncodingException { + return dbToObjectWithFilters(obj, v, seen, depth, nodeOnly, + filterCousinNodes, filterParentNodes, false); + } + + /** + * Db to object With Filters + * This is for a one-time run with Tenant Isloation to only filter relationships * TODO: Chnage the original dbToObject to take filter parent/cousins * * @param obj the obj @@ -1994,6 +2234,7 @@ public class DBSerializer { * @param depth the depth * @param nodeOnly specify if to exclude relationships or not * @param filterCousinNodes + * @param isSkipRelatedTo determine to incorporated related-to-property data * @return the introspector * @throws AAIException the AAI exception * @throws IllegalAccessException the illegal access exception @@ -2009,9 +2250,9 @@ public class DBSerializer { */ // TODO - See if you can merge the 2 dbToObjectWithFilters public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, - List<String> filterCousinNodes, List<String> filterParentNodes) + List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException { - String cleanUp = "false"; + String cleanUp = FALSE; if (depth < 0) { return null; } @@ -2020,7 +2261,6 @@ public class DBSerializer { boolean modified = false; for (String property : obj.getProperties(PropertyPredicates.isVisible())) { List<Object> getList = null; - Vertex[] vertices = null; if (!(obj.isComplexType(property) || obj.isListType(property))) { this.copySimpleProperty(property, obj, v); @@ -2032,7 +2272,7 @@ public class DBSerializer { if (!property.equals("relationship-list") && depth >= 0) { Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, - filterCousinNodes, filterParentNodes); + filterCousinNodes, filterParentNodes, isSkipRelatedTo); if (result != null) { obj.setValue(property, argumentObject.getUnderlyingObject()); modified = true; @@ -2041,9 +2281,8 @@ public class DBSerializer { /* relationships need to be handled correctly */ Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); relationshipList = - createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes); + createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes, isSkipRelatedTo); if (relationshipList != null) { - modified = true; obj.setValue(property, relationshipList.getUnderlyingObject()); modified = true; } @@ -2060,7 +2299,7 @@ public class DBSerializer { String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); EdgeRule rule; - boolean isthisParentRequired = + boolean isThisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains); EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build(); @@ -2072,34 +2311,30 @@ public class DBSerializer { } catch (AmbiguousRuleChoiceException e) { throw new MultipleEdgeRuleFoundException(e); } - if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) { - // vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), - // childDbName); + if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) { Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>) obj.getValue(property); + List<Vertex> verticesList = new ArrayList<>(); + v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> { + if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) { + verticesList.add(vertex); + } + }); + if (!verticesList.isEmpty()) { + getList = obj.getValue(property); } int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); + for (Vertex childVertex : verticesList) { if (!seen.contains(childVertex)) { Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, - nodeOnly, filterCousinNodes, filterParentNodes); + nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo); if (result != null) { getList.add(argumentObject.getUnderlyingObject()); } processed++; } else { - removed++; LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); } @@ -2115,7 +2350,7 @@ public class DBSerializer { } else if (obj.isSimpleGenericType(property)) { List<Object> temp = this.engine.getListProperty(v, property); if (temp != null) { - getList = (List<Object>) obj.getValue(property); + getList = obj.getValue(property); getList.addAll(temp); modified = true; } @@ -2143,19 +2378,13 @@ public class DBSerializer { * @param obj the obj * @param cleanUp the clean up * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception * @throws SecurityException the security exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws URISyntaxException */ private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, - List<String> filterNodes) throws UnsupportedEncodingException, AAIException { + List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException { List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v); Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> { @@ -2163,14 +2392,14 @@ public class DBSerializer { return filterNodes.parallelStream().anyMatch(node::contains); }).iterator(); - List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices); + List<Object> relationshipObjList = obj.getValue(RELATIONSHIP); - // items.parallelStream().anyMatch(inputStr::contains) - List<Object> relationshipObjList = obj.getValue("relationship"); + List<Vertex> cousins = new ArrayList<>(); + cousinVertices.forEachRemaining(cousins::add); for (Vertex cousin : cousins) { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo); if (result != null) { relationshipObjList.add(result); } @@ -2184,4 +2413,31 @@ public class DBSerializer { } } + public Set<Vertex> touchStandardVertexPropertiesForEdges() { + this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false)); + return this.edgeVertexes; + } + + public void addVertexToEdgeVertexes(Vertex vertex){ + this.edgeVertexes.add(vertex); + } + + private String urlToUri(String url) { + if (url.startsWith("/")) { + url = url.substring(1); + } + + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + + // TODO - Check if this makes to do for model driven for base uri path + url = url.replaceFirst("[a-z][a-z]*/v\\d+/", ""); + if (url.charAt(0) != '/') { + url = '/' + url; + } + + return url; + } + } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java b/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java index cb713d56..3a7c6d2d 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java @@ -20,11 +20,10 @@ package org.onap.aai.serialization.db; -import java.util.concurrent.atomic.AtomicInteger; - import org.janusgraph.core.JanusGraph; import org.onap.aai.dbmap.AAIGraph; -import org.onap.aai.dbmap.DBConnectionType; + +import java.util.concurrent.atomic.AtomicInteger; /* This class simply calls AAIGraph under the covers for now */ public class GraphSingleton { @@ -63,7 +62,4 @@ public class GraphSingleton { return AAIGraph.getInstance().getGraph(); } - public JanusGraph getTxGraph(DBConnectionType connectionType) { - return AAIGraph.getInstance().getGraph(connectionType); - } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/ImpliedDelete.java b/aai-core/src/main/java/org/onap/aai/serialization/db/ImpliedDelete.java new file mode 100644 index 00000000..437f1634 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/ImpliedDelete.java @@ -0,0 +1,205 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.serialization.db; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.commons.lang.StringUtils; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.logging.LogFormatTools; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.engines.query.QueryEngine; +import org.onap.aai.util.AAIConfigProxy; +import org.onap.aai.util.AAIConstants; + +import java.util.List; +import java.util.Objects; + +/** + * <b>ImpliedDelete</b> class is responsible for deleting children + * of a parent vertex if the client explicitly asks to remove them + * It will check if they are allowed and based on that information + * it will decide whether to proceed with the deletion or + * throw an exception back to the requester if they are not allowed + * + * It implements the AAIProxy Interface and any calls to the + * AAIConfig should be using the proxy methods and any new + * methods that needs to be invoked should be added there first + * + * @see org.onap.aai.util.AAIConfigProxy + */ +public class ImpliedDelete implements AAIConfigProxy { + + private static final Logger LOGGER = LoggerFactory.getLogger(ImpliedDelete.class); + + private static final String IMPLICIT_DELETE = "Implicit DELETE"; + private static final String STAR = "*"; + + private TransactionalGraphEngine engine; + private DBSerializer serializer; + + public ImpliedDelete(TransactionalGraphEngine engine, DBSerializer serializer){ + this.engine = engine; + this.serializer = serializer; + } + + /** + * Checks if the user is allowed to execute implied delete + * If they are allowed to do the delete, then for all the dependent vertices + * it will identify all the deletable vertices to them + * and log them based on the following aaiconfig properties: + * + * aai.implied.delete.log.enabled=true + * aai.implied.delete.log.limit=-1 + * + * Above properties are the default assumption of the code if they are not overwritten + * So the code will log for every vertex it is about to delete + * If there are thousands of vertexes that get implicitly deleted, + * its preferable that the operation timeout rather than + * someone accidentally deleting thousands of children + * + * @param id - Identifier of the vertex whose children could be potentially deleted + * @param sot - source of truth of who the requester who is making the request + * @param objectType - type of the parent object whose children are being deleted + * @param dependentVertexes - list of children vertexes + * @throws AAIException if the user is not allowed to implicitly delete children + */ + public List<Vertex> execute(Object id, String sot, String objectType, List<Vertex> dependentVertexes) throws AAIException { + if(dependentVertexes != null && !dependentVertexes.isEmpty()){ + // Find all the deletable vertices from the dependent vertices that should be deleted + // So for each of the following dependent vertices, + // we will use the edge properties and do the cascade delete + QueryEngine queryEngine = this.engine.getQueryEngine(); + List<Vertex> impliedDeleteVertices = queryEngine.findDeletable(dependentVertexes); + if(this.allow(sot, objectType)){ + + int impliedDeleteCount = impliedDeleteVertices.size(); + + LOGGER.warn( + "For the vertex with id {}, doing an implicit delete on update will delete total of {} vertexes", + id, + impliedDeleteCount + ); + + String impliedDeleteLogEnabled = get(AAIConstants.AAI_IMPLIED_DELETE_LOG_ENABLED, "true"); + + int impliedDeleteLogLimit = getInt(AAIConstants.AAI_IMPLIED_DELETE_LOG_LIMIT, "-1"); + + if (impliedDeleteLogLimit == -1) { + impliedDeleteLogLimit = Integer.MAX_VALUE; + } + + // If the logging is enabled for implied delete + // then log the payload in the latest format + if ("true".equals(impliedDeleteLogEnabled) && impliedDeleteCount <= impliedDeleteLogLimit) { + for (Vertex vertex : impliedDeleteVertices) { + Introspector introspector = null; + try { + introspector = serializer.getLatestVersionView(vertex); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Implied delete object in json format {}", introspector.marshal(false)); + } + } catch (Exception ex) { + LOGGER.warn( + "Encountered an exception during retrieval of vertex properties with vertex-id {} -> {}", + id, LogFormatTools.getStackTop(ex)); + } + } + } + } else { + LOGGER.error("User {} is not allowed to implicit delete on parent object {}", sot, objectType); + throw new AAIException("AAI_9109"); + } + return impliedDeleteVertices; + } else { + // Return null or an empty list back to the user based on input + return dependentVertexes; + } + } + + public void delete(List<Vertex> vertices){ + // After all the appropriate logging, calling the serializer delete to delete the affected vertices + if(vertices != null && !vertices.isEmpty()){ + serializer.delete(vertices); + } + } + + /** + * Checks the property in the aaiconfig properties + * to see if the user is allowed to do implicit delete + * + * Expecting the aaiconfig.properties to have following type of properties + * + * <code> + * aai.implied.delete.whitelist.sdnc=* + * aai.implied.delete.whitelist.sdc='pserver','vserver' + * </code> + * + * So in the above code, the expectation is for any of the following user: + * + * <ul> + * <li>SDC</li> + * <li>SDc</li> + * <li>Sdc</li> + * <li>sDc</li> + * <li>SdC</li> + * <li>sdC</li> + * <li>sdc</li> + * </ul> + * + * They are allowed to delete the children of pserver and vserver by implicit delete + * + * Note: The reason the property values are placed inside the single quotes is + * so if there is an object called volume and there is another object called volume-group + * when doing an contains it can falsely allow volume-group children to be implicitly deleted + * and the cost of turning the string into an array and then searching if its inside it + * or loading into an set which is unnecessary and it could potentially be done for every request + * + * @param sourceOfTruth - the original requester that the request is coming from, + * derived from HTTP Header X-FromAppId + * @param parentObjectType - parent object in which they are trying to do the implicit delete against + * + * @return true - if the requester is allowed to implicit delete against the object type + * false - if they are not allowed + */ + private boolean allow(String sourceOfTruth, String parentObjectType){ + Objects.requireNonNull(sourceOfTruth); + Objects.requireNonNull(parentObjectType); + + String propertyName = AAIConstants.AAI_IMPLIED_DELETE_WHITELIST + sourceOfTruth.toLowerCase(); + String whitelist = get(propertyName, StringUtils.EMPTY); + + if(whitelist.isEmpty()){ + return false; + } + + if(STAR.equals(whitelist)){ + return true; + } + + if(whitelist.contains("'" + parentObjectType + "'")){ + return true; + } + + return false; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/InMemoryGraphSingleton.java b/aai-core/src/main/java/org/onap/aai/serialization/db/InMemoryGraphSingleton.java index 90f71db5..2ca96b5b 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/InMemoryGraphSingleton.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/InMemoryGraphSingleton.java @@ -21,7 +21,6 @@ package org.onap.aai.serialization.db; import org.janusgraph.core.JanusGraph; -import org.onap.aai.dbmap.DBConnectionType; public class InMemoryGraphSingleton extends GraphSingleton { @@ -51,8 +50,4 @@ public class InMemoryGraphSingleton extends GraphSingleton { return inMemgraph; } - @Override - public JanusGraph getTxGraph(DBConnectionType connectionType) { - return inMemgraph; - } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/InMemoryDBEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/InMemoryDBEngine.java index 86c32f4a..66509c97 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/InMemoryDBEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/InMemoryDBEngine.java @@ -20,23 +20,22 @@ package org.onap.aai.serialization.engines; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.JanusGraph; -import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.introspection.Loader; import org.onap.aai.query.builder.*; import org.onap.aai.serialization.db.InMemoryGraphSingleton; import org.onap.aai.serialization.engines.query.GraphTraversalQueryEngine; import org.onap.aai.serialization.engines.query.QueryEngine; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + public class InMemoryDBEngine extends TransactionalGraphEngine { /** @@ -49,10 +48,8 @@ public class InMemoryDBEngine extends TransactionalGraphEngine { */ private JanusGraph graph = null; - private static final TransactionalGraphEngine.Admin admin = null; - - public InMemoryDBEngine(QueryStyle style, DBConnectionType connectionType, Loader loader, JanusGraph graph) { - super(style, loader, connectionType, InMemoryGraphSingleton.getInstance(graph)); + public InMemoryDBEngine(QueryStyle style, Loader loader, JanusGraph graph) { + super(style, loader, InMemoryGraphSingleton.getInstance(graph)); this.graph = graph; } @@ -195,8 +192,7 @@ public class InMemoryDBEngine extends TransactionalGraphEngine { if (this.tx() == null) { this.currentTx = graph.newTransaction(); this.currentTraversal = this.tx().traversal(); - this.readOnlyTraversal = - this.tx().traversal(GraphTraversalSource.build().with(ReadOnlyStrategy.instance())); + this.readOnlyTraversal = this.tx().traversal().withStrategies(ReadOnlyStrategy.instance()); } return currentTx; } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/JanusGraphDBEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/JanusGraphDBEngine.java index 346cf026..2a4511d0 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/JanusGraphDBEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/JanusGraphDBEngine.java @@ -20,16 +20,15 @@ package org.onap.aai.serialization.engines; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.introspection.Loader; import org.onap.aai.serialization.db.JanusGraphSingleton; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + public class JanusGraphDBEngine extends TransactionalGraphEngine { /** @@ -38,8 +37,8 @@ public class JanusGraphDBEngine extends TransactionalGraphEngine { * @param style the style * @param loader the loader */ - public JanusGraphDBEngine(QueryStyle style, DBConnectionType connectionType, Loader loader) { - super(style, loader, connectionType, JanusGraphSingleton.getInstance()); + public JanusGraphDBEngine(QueryStyle style, Loader loader) { + super(style, loader, JanusGraphSingleton.getInstance()); } /** @@ -82,7 +81,7 @@ public class JanusGraphDBEngine extends TransactionalGraphEngine { @Override public List<Object> getListProperty(Vertex v, String name) { - List<Object> result = new ArrayList<Object>(); + List<Object> result = new ArrayList<>(); Iterator<VertexProperty<Object>> iterator = v.properties(name); @@ -90,7 +89,7 @@ public class JanusGraphDBEngine extends TransactionalGraphEngine { result.add(iterator.next().value()); } - if (result.size() == 0) { + if (result.isEmpty()) { result = null; } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java index c291fbcb..f69e4d34 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java @@ -21,5 +21,5 @@ package org.onap.aai.serialization.engines; public enum QueryStyle { - GREMLIN_TRAVERSAL, GREMLIN_UNIQUE, GREMLINPIPELINE_TRAVERSAL, TRAVERSAL, TRAVERSAL_URI + GREMLIN_TRAVERSAL, GREMLIN_UNIQUE, GREMLINPIPELINE_TRAVERSAL, TRAVERSAL, TRAVERSAL_URI, HISTORY_TRAVERSAL, HISTORY_GREMLIN_TRAVERSAL } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java index 731d6c23..b945db05 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java @@ -20,29 +20,26 @@ package org.onap.aai.serialization.engines; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.janusgraph.core.JanusGraph; import org.janusgraph.core.schema.JanusGraphManagement; -import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.introspection.Loader; import org.onap.aai.query.builder.*; import org.onap.aai.serialization.db.GraphSingleton; import org.onap.aai.serialization.engines.query.GraphTraversalQueryEngine; import org.onap.aai.serialization.engines.query.QueryEngine; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + public abstract class TransactionalGraphEngine { protected GraphSingleton singleton = null; - protected QueryEngine queryEngine = null; protected QueryBuilder<Vertex> queryBuilder = null; - protected QueryStyle style = null; - protected final DBConnectionType connectionType; + protected QueryStyle style; protected final Loader loader; protected Graph currentTx = null; protected GraphTraversalSource currentTraversal = null; @@ -55,19 +52,16 @@ public abstract class TransactionalGraphEngine { * @param style the style * @param loader the loader */ - public TransactionalGraphEngine(QueryStyle style, Loader loader, DBConnectionType connectionType, - GraphSingleton singleton) { + public TransactionalGraphEngine(QueryStyle style, Loader loader, GraphSingleton singleton) { this.loader = loader; this.style = style; this.singleton = singleton; - this.connectionType = connectionType; admin = new Admin(); } public TransactionalGraphEngine(QueryStyle style, Loader loader) { this.loader = loader; this.style = style; - this.connectionType = DBConnectionType.REALTIME; admin = new Admin(); } @@ -97,7 +91,7 @@ public abstract class TransactionalGraphEngine { * @return the graph */ private JanusGraph getGraph() { - return singleton.getTxGraph(this.connectionType); + return singleton.getTxGraph(); } /** @@ -150,6 +144,10 @@ public abstract class TransactionalGraphEngine { return getQueryBuilder(this.style, loader); } + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style, GraphTraversalSource source) { + return getQueryBuilder(style, this.loader, source); + } + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style, Loader loader) { if (style.equals(QueryStyle.GREMLIN_TRAVERSAL)) { return new GremlinTraversal<>(loader, this.asAdmin().getTraversalSource()); @@ -161,7 +159,32 @@ public abstract class TransactionalGraphEngine { return new TraversalQuery<>(loader, this.asAdmin().getTraversalSource()); } else if (style.equals(QueryStyle.TRAVERSAL_URI)) { return new TraversalURIOptimizedQuery<>(loader, this.asAdmin().getTraversalSource()); - } else { + } else if (style.equals(QueryStyle.HISTORY_TRAVERSAL)) { + throw new IllegalArgumentException("History Traversal needs history traversal source"); + } else if (style.equals(QueryStyle.HISTORY_GREMLIN_TRAVERSAL)) { + throw new IllegalArgumentException("History Gremlin Traversal needs history traversal source"); + }else { + throw new IllegalArgumentException("Query Builder type not recognized"); + } + return queryBuilder; + } + + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style, Loader loader, GraphTraversalSource source) { + if (style.equals(QueryStyle.GREMLIN_TRAVERSAL)) { + return new GremlinTraversal<>(loader, source); + } else if (style.equals(QueryStyle.GREMLIN_UNIQUE)) { + return new GremlinUnique<>(loader, source); + } else if (style.equals(QueryStyle.GREMLINPIPELINE_TRAVERSAL)) { + // return new GremlinPipelineTraversal(loader); + } else if (style.equals(QueryStyle.TRAVERSAL)) { + return new TraversalQuery<>(loader, source); + } else if (style.equals(QueryStyle.TRAVERSAL_URI)) { + return new TraversalURIOptimizedQuery<>(loader, source); + } else if (style.equals(QueryStyle.HISTORY_TRAVERSAL)) { + return new HistoryTraversalURIOptimizedQuery<>(loader, source); + }else if (style.equals(QueryStyle.HISTORY_GREMLIN_TRAVERSAL)) { + return new HistoryGremlinTraversal<>(loader, source); + }else { throw new IllegalArgumentException("Query Builder type not recognized"); } return queryBuilder; @@ -202,8 +225,7 @@ public abstract class TransactionalGraphEngine { if (this.tx() == null) { this.currentTx = this.getGraph().newTransaction(); this.currentTraversal = this.tx().traversal(); - this.readOnlyTraversal = - this.tx().traversal(GraphTraversalSource.build().with(ReadOnlyStrategy.instance())); + this.readOnlyTraversal =this.tx().traversal().withStrategies(ReadOnlyStrategy.instance()); } return currentTx; } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java index e012aef3..376a2798 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java @@ -20,18 +20,9 @@ package org.onap.aai.serialization.engines.query; -import static org.onap.aai.edges.enums.AAIDirection.IN; -import static org.onap.aai.edges.enums.AAIDirection.NONE; -import static org.onap.aai.edges.enums.AAIDirection.OUT; -import static org.onap.aai.edges.enums.EdgeField.PRIVATE; -import static org.onap.aai.edges.enums.EdgeProperty.CONTAINS; -import static org.onap.aai.edges.enums.EdgeProperty.DELETE_OTHER_V; - -import java.util.List; -import java.util.Set; - import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; @@ -41,9 +32,19 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.edges.enums.EdgeField; +import org.onap.aai.edges.enums.EdgeProperty; import org.onap.aai.introspection.Loader; import org.onap.aai.logging.StopWatch; +import java.util.List; +import java.util.Set; + +import static org.onap.aai.edges.enums.AAIDirection.*; +import static org.onap.aai.edges.enums.EdgeField.PRIVATE; +import static org.onap.aai.edges.enums.EdgeProperty.CONTAINS; +import static org.onap.aai.edges.enums.EdgeProperty.DELETE_OTHER_V; + /* * This class needs some big explanation despite its compact size. * This controls all the queries performed by the CRUD API in A&AI. @@ -256,6 +257,15 @@ public class GraphTraversalQueryEngine extends QueryEngine { return pipeline.toList(); } + public List<Path> findCousinsAsPath(Vertex start){ + return this.g.V(start).bothE().where( + __.and( + __.has(EdgeProperty.CONTAINS.toString(), NONE.toString()), + __.not(__.has(EdgeField.PRIVATE.toString(), true)) + ) + ).otherV().path().toList(); + } + public double getDBTimeMsecs() { return (dbTimeMsecs); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java index 4f30b564..b250d8f3 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java @@ -22,6 +22,7 @@ package org.onap.aai.serialization.engines.query; import java.util.List; +import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.Direction; @@ -81,7 +82,7 @@ public abstract class QueryEngine { /** * Finds all immediate children of start (no grandchildren or so forth) of the given type - * + * * @param start - the start vertex * @param type - the desired aai-node-type * @return the list of immediate child vertices of given type @@ -90,7 +91,7 @@ public abstract class QueryEngine { /** * Finds all immediate children of start (no grandchildren or so forth) - * + * * @param start - the start vertex * @return the list of immediate child vertices */ @@ -173,6 +174,8 @@ public abstract class QueryEngine { */ public abstract List<Vertex> findCousinVertices(Vertex start, String... labels); + public abstract List<Path> findCousinsAsPath(Vertex start); + public abstract double getDBTimeMsecs(); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Aggregate.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Aggregate.java new file mode 100644 index 00000000..4c37aaa1 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Aggregate.java @@ -0,0 +1,313 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.serialization.queryformats; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.gson.*; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.introspection.Loader; +import org.onap.aai.logging.LogFormatTools; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.AsTree; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.NodesOnly; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Aggregate extends MultiFormatMapper { + private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class); + protected JsonParser parser = new JsonParser(); + protected final DBSerializer serializer; + protected final Loader loader; + protected final UrlBuilder urlBuilder; + protected final int depth; + protected final boolean nodesOnly; + + protected Aggregate(Builder builder) { + this.urlBuilder = builder.getUrlBuilder(); + this.loader = builder.getLoader(); + this.serializer = builder.getSerializer(); + this.depth = builder.getDepth(); + this.nodesOnly = builder.isNodesOnly(); + this.isTree = builder.isTree(); + } + + @Override + public Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException{ + JsonObject json = new JsonObject(); + JsonObject outer = new JsonObject(); + Optional<JsonObject> properties = this.createSelectedPropertiesObject(v,selectedProps); + if (properties.isPresent()) { + json.add("properties", properties.get()); + outer.add(this.urlBuilder.pathed(v),json.getAsJsonObject()); + } else { + return Optional.empty(); + } + return Optional.of(outer); + } + + @Override + public int parallelThreshold() { + return 100; + } + + public Optional<JsonObject> createPropertiesObject(Vertex v) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + Iterator<VertexProperty<Object>> iter = v.properties(); + + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + if (prop.value() instanceof String) { + json.addProperty(prop.key(), (String) prop.value()); + } else if (prop.value() instanceof Boolean) { + json.addProperty(prop.key(), (Boolean) prop.value()); + } else if (prop.value() instanceof Number) { + json.addProperty(prop.key(), (Number) prop.value()); + } else if (prop.value() instanceof List) { + Gson gson = new Gson(); + String list = gson.toJson(prop.value()); + + json.addProperty(prop.key(), list); + } else { + // throw exception? + return null; + } + } + + return Optional.of(json); + } + + public Optional<JsonObject> createSelectedPropertiesObject(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + Set<String> propList = null; + String nodeType = v.<String>value(AAIProperties.NODE_TYPE); + if (selectedProps != null && !selectedProps.isEmpty() && selectedProps.containsKey(nodeType)) { + propList = removeSingleQuotesForProperties(selectedProps.get(nodeType)); + } + Iterator<VertexProperty<Object>> iter = v.properties(); + + Gson gson = new Gson(); + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + if (propList != null && !propList.isEmpty()) { + if (propList.contains(prop.label())) { + if (prop.value() instanceof String) { + json.addProperty(prop.key(), (String) prop.value()); + } else if (prop.value() instanceof Boolean) { + json.addProperty(prop.key(), (Boolean) prop.value()); + } else if (prop.value() instanceof Number) { + json.addProperty(prop.key(), (Number) prop.value()); + } else if (prop.value() instanceof List) { + json.addProperty(prop.key(), gson.toJson(prop.value())); + } else { + // throw exception? + return null; + } + } + } else { + return this.createPropertiesObject(v); + } + } + + return Optional.of(json); + } + + private Set<String> removeSingleQuotesForProperties(List<String> props){ + if (props != null && !props.isEmpty()) { + return props.stream().map( + e -> e.substring(1, e.length()-1)).collect(Collectors.toSet()); + } else { + return Collections.emptySet(); + } + } + + public JsonArray process(List<Object> queryResults, Map<String, List<String>> properties) { + JsonArray body = new JsonArray(); + Stream<Object> stream; + if (queryResults.size() >= this.parallelThreshold()) { + stream = queryResults.parallelStream(); + } else { + stream = queryResults.stream(); + } + final boolean isParallel = stream.isParallel(); + + stream.map(o -> { + try { + return this.formatObject(o, properties); + } catch (AAIFormatVertexException e) { + LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e)); + } catch (AAIFormatQueryResultFormatNotSupported e) { + LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e)); + } + + return Optional.<JsonObject>empty(); + }).filter(Optional::isPresent) + .map(Optional::get) + .forEach(json -> { + if (isParallel) { + synchronized (body) { + body.add(json); + } + } else { + body.add(json); + } + }); + return body; + } + + @Override + public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties) + throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + JsonObject json = new JsonObject(); + if (input instanceof ArrayList) { + Optional<JsonArray> ja = processInput(input, properties); + json.add("results", ja.get()); + } else { + throw new AAIFormatQueryResultFormatNotSupported(); + } + return Optional.of(json); + } + + private Optional<JsonArray> processInput(Object input, Map properties) throws AAIFormatVertexException { + JsonArray json = new JsonArray(); + for (Object l : (ArrayList) input) { + if (l instanceof ArrayList) { + JsonArray inner = new JsonArray(); + for (Vertex o : (ArrayList<Vertex>) l) { + if (o instanceof Vertex) { + Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o, properties); + if (obj.isPresent()) { + inner.add(obj.get()); + } else { + continue; + } + } + } + json.add(inner); + } else { + Optional<JsonObject> obj = this.getJsonFromVertex((Vertex)l, properties); + json.add(obj.get()); + } + } + return Optional.of(json); + } + + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { + + protected final Loader loader; + protected final DBSerializer serializer; + protected final UrlBuilder urlBuilder; + protected boolean includeUrl = false; + protected boolean nodesOnly = false; + protected int depth = 1; + protected boolean modelDriven = false; + private boolean tree = false; + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + } + + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + + protected Loader getLoader() { + return this.loader; + } + + protected DBSerializer getSerializer() { + return this.serializer; + } + + protected UrlBuilder getUrlBuilder() { + return this.urlBuilder; + } + + public Builder includeUrl() { + this.includeUrl = true; + return this; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + + public boolean isNodesOnly() { + return this.nodesOnly; + } + + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + + public int getDepth() { + return this.depth; + } + + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public Builder modelDriven() { + this.modelDriven = true; + return this; + } + + public boolean getModelDriven() { + return this.modelDriven; + } + + public Aggregate build() { + return new Aggregate(this); + } + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException { + + JsonObject json = new JsonObject(); + json.addProperty("url", this.urlBuilder.pathed(v)); + Optional<JsonObject> properties = this.createPropertiesObject(v); + if (properties.isPresent()) { + json.add("properties", properties.get()); + } else { + return Optional.empty(); + } + return Optional.of(json); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ChangesFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ChangesFormat.java new file mode 100644 index 00000000..bbaed360 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ChangesFormat.java @@ -0,0 +1,95 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.serialization.queryformats; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.config.SpringContextAware; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ChangesFormat extends MultiFormatMapper { + + private Long startTs = 0L; + + public void startTs(String startTime) { + /* + * StartTs = truncate time + */ + if (startTime == null || startTime.isEmpty() || "now".equals(startTime) || "0".equals(startTime) || "-1".equals(startTime)){ + String historyTruncateDays = SpringContextAware.getApplicationContext().getEnvironment().getProperty("history.truncate.window.days", "365"); + this.startTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(Long.parseLong(historyTruncateDays)); + } else { + this.startTs = Long.parseLong(startTime); + } + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex v) { + JsonObject json = new JsonObject(); + if (!v.properties(AAIProperties.RESOURCE_VERSION).hasNext() || + !v.properties(AAIProperties.NODE_TYPE).hasNext() || + !v.properties(AAIProperties.AAI_URI).hasNext()) { + return Optional.empty(); + } + json.addProperty("node-type", v.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("uri", v.<String>value(AAIProperties.AAI_URI)); + + final Set<Long> changes = new HashSet<>(); + v.properties(AAIProperties.RESOURCE_VERSION).forEachRemaining(o-> + o.properties(AAIProperties.START_TS, AAIProperties.END_TS) + .forEachRemaining(p -> { + Long val = (Long) p.value(); + if(val >= startTs) { + changes.add(val); + } + } + )); + v.edges(Direction.BOTH).forEachRemaining(e -> { + if(e.property(AAIProperties.START_TS).isPresent() && (Long)e.property(AAIProperties.START_TS).value() >= startTs) { + changes.add((Long) e.property(AAIProperties.START_TS).value()); + } + if(e.property(AAIProperties.END_TS).isPresent() && (Long)e.property(AAIProperties.END_TS).value() >= startTs) { + changes.add((Long) e.property(AAIProperties.END_TS).value()); + } + }); + + List<Long> sortedList = new ArrayList<>(changes); + sortedList.sort(Comparator.naturalOrder()); + JsonArray jsonArray = new JsonArray(); + sortedList.forEach(jsonArray::add); + + json.add("changes", jsonArray); + + return Optional.of(json); + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java index ba20c652..7e230c3a 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java @@ -22,8 +22,11 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; +import java.util.List; +import java.util.Map; import java.util.Optional; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; public class Console implements FormatMapper { @@ -38,6 +41,13 @@ public class Console implements FormatMapper { } @Override + public Optional<JsonObject> formatObject(Object o, Map<String, List<String>> properties) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + JsonObject json = new JsonObject(); + json.addProperty("result", o.toString()); + return Optional.of(json); + } + + @Override public int parallelThreshold() { return 100; } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Count.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Count.java index 8ee4e3a4..60a10c51 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Count.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Count.java @@ -23,6 +23,7 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -37,8 +38,7 @@ import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexExcepti public class Count implements FormatMapper { @Override - public Optional<JsonObject> formatObject(Object o) - throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + public Optional<JsonObject> formatObject(Object o) { @SuppressWarnings("unchecked") List<Object> list = (List<Object>) o; @@ -46,12 +46,17 @@ public class Count implements FormatMapper { list.stream().map(this::getCount).filter(Optional::isPresent).map(Optional::get) .collect(Collectors.toConcurrentMap(Pair::getValue0, Pair::getValue1, Long::sum)) - .forEach((k, v) -> countResult.addProperty(k, v)); + .forEach(countResult::addProperty); return Optional.of(countResult); } @Override + public Optional<JsonObject> formatObject(Object o, Map<String, List<String>> properties) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + return Optional.empty(); + } + + @Override public int parallelThreshold() { return 20; } @@ -72,10 +77,10 @@ public class Count implements FormatMapper { } if (pair == null) { - return Optional.<Pair<String, Long>>empty(); + return Optional.empty(); } - return Optional.<Pair<String, Long>>of(pair); + return Optional.of(pair); } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java index 8de38117..f471b3c1 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java @@ -23,7 +23,7 @@ package org.onap.aai.serialization.queryformats; import org.onap.aai.exceptions.AAIException; public enum Format { - graphson, pathed, pathed_resourceversion, id, resource, simple, resource_and_url, console, raw, count, resource_with_sot; + graphson, pathed, pathed_resourceversion, id, resource, simple, resource_and_url, console, raw, count, resource_with_sot, state, lifecycle, changes, aggregate; public static Format getFormat(String format) throws AAIException { try { diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java index 854a20f4..0ae0c5fe 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java @@ -20,9 +20,6 @@ package org.onap.aai.serialization.queryformats; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; - import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Loader; import org.onap.aai.serialization.db.DBSerializer; @@ -31,6 +28,9 @@ import org.onap.aai.serialization.queryformats.utils.QueryParamInjector; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; import org.onap.aai.setup.SchemaVersions; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + public class FormatFactory { private final Loader loader; @@ -47,7 +47,7 @@ public class FormatFactory { } public Formatter get(Format format) throws AAIException { - return get(format, new MultivaluedHashMap<String, String>()); + return get(format, new MultivaluedHashMap<>()); } public Formatter get(Format format, MultivaluedMap<String, String> params) throws AAIException { @@ -59,29 +59,34 @@ public class FormatFactory { formatter = new Formatter(inject(new GraphSON(), params)); break; case pathed: - formatter = new Formatter(inject(new PathedURL(loader, urlBuilder), params)); + formatter = new Formatter(inject(new PathedURL.Builder(loader, serializer, urlBuilder), params).build(), params); break; case pathed_resourceversion: - formatter = new Formatter(inject(new PathedURL(loader, urlBuilder).includeUrl(), params)); + formatter = new Formatter(inject(new PathedURL.Builder(loader, serializer, urlBuilder).includeUrl(), params).build(), params); break; case id: - formatter = new Formatter(inject(new IdURL(loader, urlBuilder), params)); + formatter = new Formatter(inject(new IdURL.Builder(loader, serializer, urlBuilder), params).build(), params); break; case resource: - formatter = new Formatter(inject(new Resource.Builder(loader, serializer, urlBuilder), params).build()); + formatter = new Formatter(inject(new Resource.Builder(loader, serializer, urlBuilder, params), params).build(), params); break; case resource_and_url: formatter = new Formatter( - inject(new Resource.Builder(loader, serializer, urlBuilder).includeUrl(), params).build()); + inject(new Resource.Builder(loader, serializer, urlBuilder, params).includeUrl(), params).build(), params); break; case raw: formatter = - new Formatter(inject(new RawFormat.Builder(loader, serializer, urlBuilder), params).build()); + new Formatter(inject(new RawFormat.Builder(loader, serializer, urlBuilder), params).build(), params); break; case simple: formatter = new Formatter( inject(new RawFormat.Builder(loader, serializer, urlBuilder).depth(0).modelDriven(), params) - .build()); + .build(), params); + break; + case aggregate: + formatter = new Formatter( + inject(new Aggregate.Builder(loader, serializer, urlBuilder).depth(0).modelDriven(), params) + .build(), params); break; case console: formatter = new Formatter(inject(new Console(), params)); @@ -91,7 +96,19 @@ public class FormatFactory { break; case resource_with_sot: formatter = new Formatter( - inject(new ResourceWithSoT.Builder(loader, serializer, urlBuilder), params).build()); + inject(new ResourceWithSoT.Builder(loader, serializer, urlBuilder), params).build(), params); + break; + case changes: + formatter = + new Formatter(inject(new ChangesFormat(), params)); + break; + case state: + formatter = + new Formatter(inject(new StateFormat.Builder(loader, serializer, urlBuilder), params).build(format)); + break; + case lifecycle: + formatter = + new Formatter(inject(new LifecycleFormat.Builder(loader, serializer, urlBuilder), params).build(format)); break; default: break; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java index 26af2e31..cf967f0f 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java @@ -22,6 +22,8 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; +import java.util.List; +import java.util.Map; import java.util.Optional; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; @@ -30,6 +32,7 @@ import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexExcepti public interface FormatMapper { Optional<JsonObject> formatObject(Object o) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported; + Optional<JsonObject> formatObject(Object o, Map<String, List<String>> properties) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported; int parallelThreshold(); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java index a19bd3f6..0e15173a 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java @@ -20,46 +20,70 @@ package org.onap.aai.serialization.queryformats; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import org.json.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.onap.aai.logging.LogFormatTools; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import javax.ws.rs.core.MultivaluedMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import org.onap.aai.logging.LogFormatTools; -import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; -import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; - public class Formatter { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Formatter.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Formatter.class); - protected JsonParser parser = new JsonParser(); protected final FormatMapper format; + protected MultivaluedMap<String, String> params; public Formatter(FormatMapper format) { this.format = format; } - public JsonObject output(List<Object> queryResults) { + public Formatter(FormatMapper format, MultivaluedMap<String, String> params) { + this.format = format; + this.params = params; + } - Stream<Object> stream; - JsonObject result = new JsonObject(); - JsonArray body = new JsonArray(); + public JsonObject output(List<Object> queryResults, Map<String, List<String>> properties) { + + final JsonArray body; if (this.format instanceof Count) { JsonObject countResult; + body = new JsonArray(); try { countResult = format.formatObject(queryResults).orElseThrow(() -> new AAIFormatVertexException("")); body.add(countResult); } catch (Exception e) { LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e)); } + } else if (this.format instanceof LifecycleFormat) { + LifecycleFormat lifecycleFormat = (LifecycleFormat) format; + body = lifecycleFormat.process(queryResults); + } else if (this.format instanceof Aggregate) { + Aggregate aggregateFormat = (Aggregate) format; + body = aggregateFormat.process(queryResults, properties); + JsonObject result = new JsonObject(); + if (body != null && body.size() > 0) { + result.add("results", (body.get(0)).getAsJsonObject().get("results")); + } + return result; } else { + + body = new JsonArray(); + Stream<Object> stream; if (queryResults.size() >= format.parallelThreshold()) { stream = queryResults.parallelStream(); } else { @@ -70,7 +94,11 @@ public class Formatter { stream.map(o -> { try { - return format.formatObject(o); + if (properties!= null && !properties.isEmpty()){ + return format.formatObject(o, properties); + } else { + return format.formatObject(o); + } } catch (AAIFormatVertexException e) { LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e)); } catch (AAIFormatQueryResultFormatNotSupported e) { @@ -78,19 +106,38 @@ public class Formatter { } return Optional.<JsonObject>empty(); - }).filter(Optional::isPresent).map(Optional::get).forEach(json -> { - if (isParallel) { - synchronized (body) { + }).filter(Optional::isPresent) + .map(Optional::get) + .forEach(json -> { + if (isParallel) { + synchronized (body) { + body.add(json); + } + } else { body.add(json); } - } else { - body.add(json); - } - }); + }); } + + if (params !=null && params.containsKey("as-tree")) { + String isAsTree = params.get("as-tree").get(0); + if (isAsTree != null && isAsTree.equalsIgnoreCase("true") + && body != null && body.size() != 0) { + JsonObject jsonObjectBody = body.get(0).getAsJsonObject(); + if (jsonObjectBody != null && jsonObjectBody.size() > 0) { + return body.get(0).getAsJsonObject(); + } + } + } + JsonObject result = new JsonObject(); result.add("results", body); return result.getAsJsonObject(); + + } + + public JsonObject output(List<Object> queryResults) { + return output(queryResults, null); } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java index eeac2195..609f6b0f 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java @@ -28,16 +28,15 @@ import com.google.gson.JsonParser; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; +import java.util.*; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; import org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; public class GraphSON implements FormatMapper { @@ -79,6 +78,11 @@ public class GraphSON implements FormatMapper { } + @Override + public Optional<JsonObject> formatObject(Object o, Map<String, List<String>> properties) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + return Optional.empty(); + } + /** * Removes the private edges from the json object * diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/HistoryFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/HistoryFormat.java new file mode 100644 index 00000000..b11f14aa --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/HistoryFormat.java @@ -0,0 +1,246 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.serialization.queryformats; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.tinkerpop.gremlin.structure.*; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.introspection.Loader; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.EndTs; +import org.onap.aai.serialization.queryformats.params.NodesOnly; +import org.onap.aai.serialization.queryformats.params.StartTs; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class HistoryFormat extends MultiFormatMapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(HistoryFormat.class); + + protected static final String KEY = "key"; + protected static final String VALUE = "value"; + protected static final String TIMESTAMP = "timestamp"; + protected static final String NODE_TYPE = "node-type"; + protected static final String END_TIMESTAMP = "end-timestamp"; + protected static final String SOT = "sot"; + protected static final String END_SOT = "end-sot"; + protected static final String TX_ID = "tx-id"; + protected static final String END_TX_ID = "end-tx-id"; + protected static final String PROPERTIES = "properties"; + protected static final String RELATED_TO = "related-to"; + protected static final String NODE_ACTIONS = "node-actions"; + + protected JsonParser parser = new JsonParser(); + protected final DBSerializer serializer; + protected final Loader loader; + protected final UrlBuilder urlBuilder; + protected final int depth; + protected final boolean nodesOnly; + protected long startTs; + protected long endTs; + protected static final Set<String> ignoredKeys = + Stream.of(AAIProperties.LAST_MOD_TS, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, AAIProperties.CREATED_TS) + .collect(Collectors.toSet()); + + protected HistoryFormat(Builder builder) { + this.urlBuilder = builder.getUrlBuilder(); + this.loader = builder.getLoader(); + this.serializer = builder.getSerializer(); + this.depth = builder.getDepth(); + this.nodesOnly = builder.isNodesOnly(); + this.startTs = builder.getStartTs(); + this.endTs = builder.getEndTs(); + } + + @Override + public int parallelThreshold() { + return 100; + } + + protected JsonObject createMetaPropertiesObject(VertexProperty<Object> prop) { + JsonObject json = new JsonObject(); + Iterator iter = prop.properties(); + + while (iter.hasNext()) { + Property<Object> metaProp = (Property) iter.next(); + mapPropertyValues(json, metaProp.key(), metaProp.value()); + } + + return json; + } + + protected JsonObject mapPropertyValues(JsonObject json, String propertyKey, Object propertyValue) { + if (propertyValue instanceof String) { + json.addProperty(propertyKey, (String) propertyValue); + } else if (propertyValue instanceof Boolean) { + json.addProperty(propertyKey, (Boolean) propertyValue); + } else if (propertyValue instanceof Number) { + json.addProperty(propertyKey, (Number) propertyValue); + } else { + if (!(propertyValue instanceof List)) { + return json; + } + + Gson gson = new Gson(); + String list = gson.toJson(propertyValue); + json.addProperty(propertyKey, list); + } + return json; + } + + protected JsonArray createRelationshipObject(Vertex v) throws AAIFormatVertexException { + JsonArray relatedToList = new JsonArray(); + Iterator<Edge> inIter = v.edges(Direction.IN); + Iterator<Edge> outIter = v.edges(Direction.OUT); + + while (inIter.hasNext()) { + Edge e = inIter.next(); + if (isValidEdge(e)) { + relatedToList.add(getRelatedObject(e, e.outVertex())); + } + } + + while (outIter.hasNext()) { + Edge e = outIter.next(); + if (isValidEdge(e)) { + relatedToList.add(getRelatedObject(e, e.inVertex())); + } + } + + return relatedToList; + + } + + protected abstract boolean isValidEdge(Edge e); + + protected abstract JsonObject getRelatedObject(Edge e, Vertex related) throws AAIFormatVertexException; + + + + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, StartTs<Builder>, EndTs<Builder> { + + protected final Loader loader; + protected final DBSerializer serializer; + protected final UrlBuilder urlBuilder; + protected boolean includeUrl = false; + protected boolean nodesOnly = false; + protected int depth = 1; + protected boolean modelDriven = false; + protected long startTs = -1; + protected long endTs = -1; + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + } + + protected Loader getLoader() { + return this.loader; + } + + protected DBSerializer getSerializer() { + return this.serializer; + } + + protected UrlBuilder getUrlBuilder() { + return this.urlBuilder; + } + + public Builder includeUrl() { + this.includeUrl = true; + return this; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + + public Builder startTs(String startTs) { + this.startTs = Long.parseLong(startTs); + return this; + } + + public Builder endTs(String endTs) { + this.endTs = Long.parseLong(endTs); + return this; + } + + + public boolean isNodesOnly() { + return this.nodesOnly; + } + + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + + public int getDepth() { + return this.depth; + } + + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public Builder modelDriven() { + this.modelDriven = true; + return this; + } + + public boolean getModelDriven() { + return this.modelDriven; + } + + public long getStartTs() { + return this.startTs; + } + + public long getEndTs() { + return this.endTs; + } + + public HistoryFormat build(Format format) { + + if(Format.state.equals(format)) { + return new StateFormat(this); + } else { + return new LifecycleFormat(this); + } + + } + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java index c5cb9254..c2c252a2 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java @@ -23,6 +23,8 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.util.List; +import java.util.Map; import java.util.Optional; import org.apache.tinkerpop.gremlin.structure.Vertex; @@ -31,9 +33,15 @@ import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.AsTree; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.NodesOnly; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; +import javax.ws.rs.core.MultivaluedMap; + public class IdURL extends MultiFormatMapper { private final UrlBuilder urlBuilder; @@ -46,6 +54,13 @@ public class IdURL extends MultiFormatMapper { this.loader = loader; } + public IdURL(Builder builder) throws AAIException { + this.urlBuilder = builder.getUrlBuilder(); + this.parser = new JsonParser(); + this.loader = builder.getLoader(); + this.isTree = builder.isTree(); + } + @Override public int parallelThreshold() { return 2500; @@ -69,4 +84,101 @@ public class IdURL extends MultiFormatMapper { } } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + private boolean includeUrl = false; + private boolean nodesOnly = false; + private int depth = 1; + private MultivaluedMap<String, String> params; + private boolean tree = false; + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + } + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder, MultivaluedMap<String, String> params) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + this.params = params; + } + + protected Loader getLoader() { + return this.loader; + } + + protected DBSerializer getSerializer() { + return this.serializer; + } + + protected UrlBuilder getUrlBuilder() { + return this.urlBuilder; + } + + protected MultivaluedMap<String, String> getParams() { return this.params; } + + public boolean isSkipRelatedTo() { + if (params != null) { + boolean isSkipRelatedTo = true; + if (params.containsKey("skip-related-to")) { + String skipRelatedTo = params.getFirst("skip-related-to"); + isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false")); + } else { + // if skip-related-to param is missing, then default it to false; + isSkipRelatedTo = false; + } + return isSkipRelatedTo; + } + return true; + } + + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + + public Builder includeUrl() { + this.includeUrl = true; + return this; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + + public boolean isNodesOnly() { + return this.nodesOnly; + } + + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + + public int getDepth() { + return this.depth; + } + + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public IdURL build() throws AAIException { + return new IdURL(this); + } + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/LifecycleFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/LifecycleFormat.java new file mode 100644 index 00000000..57bb17c2 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/LifecycleFormat.java @@ -0,0 +1,313 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.serialization.queryformats; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.logging.LogFormatTools; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +public class LifecycleFormat extends HistoryFormat { + + private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class); + + protected LifecycleFormat(Builder builder) { + super(builder); + } + + protected JsonArray createPropertiesObject(Vertex v) { + JsonArray jsonArray = new JsonArray(); + Iterator<VertexProperty<Object>> iter = v.properties(); + List<JsonObject> jsonList = new ArrayList<>(); + + Map<String, Set<Long>> propStartTimes = new HashMap<>(); //vertex end + while (iter.hasNext()) { + JsonObject json = new JsonObject(); + VertexProperty<Object> prop = iter.next(); + if(prop.key() != null && ignoredKeys.contains(prop.key())){ + continue; + } + if (!propStartTimes.containsKey(prop.key())) { + propStartTimes.put(prop.key(), new HashSet<>()); + if (v.property(AAIProperties.END_TS).isPresent()) { + propStartTimes.get(prop.key()).add(v.<Long>value(AAIProperties.END_TS)); + } + } + + json.addProperty(KEY, prop.key()); + json = mapPropertyValues(json, VALUE, prop.value()); + JsonObject metaProperties = createMetaPropertiesObject(prop); + if (isTsInRange(metaProperties.get(AAIProperties.START_TS).getAsLong())) { + JsonObject jo = new JsonObject(); + jo.add(KEY, json.get(KEY)); + jo.add(VALUE, json.get(VALUE)); + jo.add(TIMESTAMP, metaProperties.get(AAIProperties.START_TS)); + jo.add(SOT, metaProperties.get(AAIProperties.SOURCE_OF_TRUTH)); + jo.add(TX_ID, metaProperties.get(AAIProperties.START_TX_ID)); + jsonList.add(jo); + propStartTimes.get(prop.key()).add(metaProperties.get(AAIProperties.START_TS).getAsLong()); + } + if (!AAIProperties.RESOURCE_VERSION.equals(prop.key()) + && metaProperties.has(AAIProperties.END_TS) + && isTsInRange(metaProperties.get(AAIProperties.END_TS).getAsLong()) + && !propStartTimes.get(prop.key()).contains(metaProperties.get(AAIProperties.END_TS).getAsLong())) { + JsonObject jo = new JsonObject(); + jo.add(KEY, json.get(KEY)); + jo.add(VALUE, null); + jo.add(TIMESTAMP, metaProperties.get(AAIProperties.END_TS)); + jo.add(SOT, metaProperties.get(AAIProperties.END_SOT)); + jo.add(TX_ID, metaProperties.get(AAIProperties.END_TX_ID)); + jsonList.add(jo); + } + + } + jsonList.stream() + // remove all the null values that is the start time for another value + .filter(jo -> !jo.get(VALUE).isJsonNull() || !propStartTimes.get(jo.get(KEY).getAsString()).contains(jo.get(TIMESTAMP).getAsLong())) + // sort by ts in decreasing order + .sorted((o1, o2) -> { + if (o1.get(TIMESTAMP).getAsLong() == o2.get(TIMESTAMP).getAsLong()) { + return o1.get(KEY).getAsString().compareTo(o2.get(KEY).getAsString()); + } else { + return Long.compare(o2.get(TIMESTAMP).getAsLong(), o1.get(TIMESTAMP).getAsLong()); + } + }).forEach(jsonArray::add); + + return jsonArray; + } + + private boolean isTsInRange(long ts) { + return ts >= startTs && ts <= endTs; + } + + + @Override + protected boolean isValidEdge(Edge e) { + if (e.property(AAIProperties.END_TS).isPresent()) { + long edgeStartTs = e.<Long>value(AAIProperties.START_TS); + long edgeEndTs = e.<Long>value(AAIProperties.END_TS); + return isTsInRange(edgeStartTs) || isTsInRange(edgeEndTs); + } else { + long edgeStartTs = e.<Long>value(AAIProperties.START_TS); + return isTsInRange(edgeStartTs); + } + } + + @Override + protected JsonObject getRelatedObject(Edge e, Vertex related) throws AAIFormatVertexException { + + JsonObject json = new JsonObject(); + json.addProperty("relationship-label", e.label()); + json.addProperty("node-type", related.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(related)); + if (related.property(AAIProperties.AAI_URI).isPresent()) { + json.addProperty("uri", related.<String>value(AAIProperties.AAI_URI)); + } else { + LOGGER.warn("Vertex {} is missing aai-uri", related.id()); + json.addProperty("uri", "NA"); + } + + if(e.property(AAIProperties.START_TS).isPresent()) { + long edgeStartTimestamp = e.<Long>value(AAIProperties.START_TS); + if (isTsInRange(edgeStartTimestamp)) { + json.addProperty(TIMESTAMP, e.property(AAIProperties.START_TS).isPresent()? e.<Long>value(AAIProperties.START_TS) : 0); + json.addProperty(SOT, e.property(AAIProperties.SOURCE_OF_TRUTH).isPresent()? e.value(AAIProperties.SOURCE_OF_TRUTH) : ""); + json.addProperty(TX_ID, e.property(AAIProperties.START_TX_ID).isPresent()? e.value(AAIProperties.START_TX_ID) : "N/A"); + } + } + + if(e.property(AAIProperties.END_TS).isPresent()) { + long edgeEndTimestamp = e.<Long>value(AAIProperties.END_TS); + if (isTsInRange(edgeEndTimestamp)) { + json.addProperty(END_TIMESTAMP, edgeEndTimestamp); + json.addProperty(END_SOT, e.property(AAIProperties.END_SOT).isPresent() ? e.value(AAIProperties.END_SOT) : ""); + json.addProperty(END_TX_ID, e.property(AAIProperties.END_TX_ID).isPresent() ? e.value(AAIProperties.END_TX_ID) : "N/A"); + } + } + + return json; + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + json.addProperty(NODE_TYPE, v.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(v)); + if (v.property(AAIProperties.AAI_URI).isPresent()) { + json.addProperty("uri", v.<String>value(AAIProperties.AAI_URI)); + } else { + LOGGER.warn("Vertex {} is missing aai-uri", v.id()); + json.addProperty("uri", "NA"); + } + json.addProperty(TIMESTAMP, v.<Long>value(AAIProperties.START_TS)); + + json.add(PROPERTIES, this.createPropertiesObject(v)); + + if (!nodesOnly) { + json.add(RELATED_TO, this.createRelationshipObject(v)); + } + + json.add(NODE_ACTIONS, getNodeActions(v, json)); + + if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray().size() == 0 + && json.getAsJsonObject().get(RELATED_TO).getAsJsonArray().size() == 0 + && json.getAsJsonObject().get(NODE_ACTIONS).getAsJsonArray().size() == 0) { + return Optional.empty(); + } else if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray().size() == 1 + && (json.getAsJsonObject().get(RELATED_TO).getAsJsonArray().size() > 0 + || json.getAsJsonObject().get(NODE_ACTIONS).getAsJsonArray().size() > 0)) { + if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray() + .get(0).getAsJsonObject().get("key").getAsString().equals(AAIProperties.END_TS)) { + json.getAsJsonObject().add(PROPERTIES, new JsonArray()); + } + } + + return Optional.of(json); + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + + private JsonArray getNodeActions(Vertex v, JsonObject json) { + JsonArray nodeActions = new JsonArray(); + JsonObject action; + if (v.property(AAIProperties.END_TS).isPresent()) { + long deletedTs = (Long) v.property(AAIProperties.END_TS).value(); + if (isTsInRange(deletedTs)) { + action = new JsonObject(); + action.addProperty("action", "DELETED"); + action.addProperty(TIMESTAMP, deletedTs); + if (v.property(AAIProperties.END_TS).property(AAIProperties.SOURCE_OF_TRUTH).isPresent()) { + action.addProperty(SOT, v.property(AAIProperties.END_TS).<String>value(AAIProperties.SOURCE_OF_TRUTH)); + } + if (v.property(AAIProperties.END_TS).property(AAIProperties.END_TX_ID).isPresent()) { + action.addProperty(TX_ID, v.property(AAIProperties.END_TS).<String>value(AAIProperties.END_TX_ID)); + } else { + action.addProperty(TX_ID, "N/A"); + } + nodeActions.add(action); + } + } + long createdTs = json.get(TIMESTAMP).getAsLong(); + if (isTsInRange(createdTs)) { + action = new JsonObject(); + action.addProperty("action", "CREATED"); + action.addProperty(TIMESTAMP, createdTs); + action.addProperty(SOT, v.<String>value(AAIProperties.SOURCE_OF_TRUTH)); + if (v.property(AAIProperties.SOURCE_OF_TRUTH).property(AAIProperties.START_TX_ID).isPresent()) { + action.addProperty(TX_ID, v.property(AAIProperties.SOURCE_OF_TRUTH).<String>value(AAIProperties.START_TX_ID)); + } else { + action.addProperty(TX_ID, "N/A"); + } + nodeActions.add(action); + } + return nodeActions; + } + + public JsonArray process(List<Object> queryResults) { + JsonArray body = new JsonArray(); + Stream<Object> stream; + if (queryResults.size() >= this.parallelThreshold()) { + stream = queryResults.parallelStream(); + } else { + stream = queryResults.stream(); + } + + final boolean isParallel = stream.isParallel(); + + stream.map(o -> { + try { + return this.formatObject(o); + } catch (AAIFormatVertexException e) { + LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e)); + } catch (AAIFormatQueryResultFormatNotSupported e) { + LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e)); + } + + return Optional.<JsonObject>empty(); + }).filter(Optional::isPresent) + .map(Optional::get) + .forEach(json -> { + if (isParallel) { + synchronized (body) { + body.add(json); + } + } else { + body.add(json); + } + }); + JsonArray result = organizeBody(body); + result.forEach(jsonElement -> jsonElement.getAsJsonObject().remove(TIMESTAMP)); + return result; + } + + private JsonArray organizeBody(JsonArray body) { + + final MultiValueMap<String, Integer> toBeMerged = new LinkedMultiValueMap<>(); + for (int i = 0; i < body.size(); i++) { + toBeMerged.add(body.get(i).getAsJsonObject().get("uri").getAsString(), i); + } + + final List<List<Integer>> dupes = toBeMerged.values().stream().filter(l -> l.size() > 1).collect(Collectors.toList()); + if (dupes.isEmpty()) { + return body; + } else { + Set<Integer> remove = new HashSet<>(); + for (List<Integer> dupe : dupes) { + dupe.sort((a,b) -> Long.compare(body.get(b).getAsJsonObject().get(TIMESTAMP).getAsLong(), body.get(a).getAsJsonObject().get(TIMESTAMP).getAsLong())); + int keep = dupe.remove(0); + for (Integer idx : dupe) { + body.get(keep).getAsJsonObject().getAsJsonArray(NODE_ACTIONS) + .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(NODE_ACTIONS)); + body.get(keep).getAsJsonObject().getAsJsonArray(PROPERTIES) + .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(PROPERTIES)); + body.get(keep).getAsJsonObject().getAsJsonArray(RELATED_TO) + .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(RELATED_TO)); + remove.add(idx); + } + } + final JsonArray newBody = new JsonArray(); + for (int i = 0; i < body.size(); i++) { + if (!remove.contains(i)) { + newBody.add(body.get(i)); + } + } + return newBody; + } + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/MultiFormatMapper.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/MultiFormatMapper.java index 21b666f2..b06ef7c3 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/MultiFormatMapper.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/MultiFormatMapper.java @@ -22,26 +22,49 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonArray; import com.google.gson.JsonObject; - -import java.util.Iterator; -import java.util.List; -import java.util.Optional; - import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + public abstract class MultiFormatMapper implements FormatMapper { + protected boolean isTree = false; + @Override public Optional<JsonObject> formatObject(Object input) throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { if (input instanceof Vertex) { return this.getJsonFromVertex((Vertex) input); } else if (input instanceof Tree) { - return this.getJsonFomTree((Tree<?>) input); + if (isTree) { + return this.getRelatedNodesFromTree((Tree<?>) input); + } else { + return this.getJsonFomTree((Tree<?>) input); + } + } else if (input instanceof Path) { + return this.getJsonFromPath((Path) input); + } else { + throw new AAIFormatQueryResultFormatNotSupported(); + } + } + + @Override + public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties) + throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported { + if (input instanceof Vertex) { + return this.getJsonFromVertex((Vertex) input, properties); + } else if (input instanceof Tree) { + if (isTree) { + return this.getRelatedNodesFromTree((Tree<?>) input); + } else { + return this.getJsonFomTree((Tree<?>) input); + } } else if (input instanceof Path) { return this.getJsonFromPath((Path) input); } else { @@ -50,6 +73,7 @@ public abstract class MultiFormatMapper implements FormatMapper { } protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input) throws AAIFormatVertexException; + protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException; protected Optional<JsonObject> getJsonFromPath(Path input) throws AAIFormatVertexException { List<Object> path = input.objects(); @@ -59,7 +83,8 @@ public abstract class MultiFormatMapper implements FormatMapper { for (Object o : path) { if (o instanceof Vertex) { - ja.add(this.getJsonFromVertex((Vertex) o).get()); + Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o); + obj.ifPresent(ja::add); } } @@ -74,7 +99,7 @@ public abstract class MultiFormatMapper implements FormatMapper { } JsonObject t = new JsonObject(); - JsonArray ja = this.getNodesArray(tree); + JsonArray ja = this.getNodesArray(tree, "nodes"); if (ja.size() > 0) { t.add("nodes", ja); } @@ -82,20 +107,37 @@ public abstract class MultiFormatMapper implements FormatMapper { return Optional.of(t); } - private JsonArray getNodesArray(Tree<?> tree) throws AAIFormatVertexException { + protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree) throws AAIFormatVertexException { + if (tree.isEmpty()) { + return Optional.of(new JsonObject()); + } - JsonArray nodes = new JsonArray(); - Iterator<?> it = tree.keySet().iterator(); + JsonObject t = new JsonObject(); + JsonArray ja = this.getNodesArray(tree, "related-nodes"); + if (ja.size() > 0) { + t.add("results", ja); + return Optional.of(t); + } + + return Optional.empty(); + } + + protected JsonArray getNodesArray(Tree<?> tree, String nodeIdentifier) throws AAIFormatVertexException { - while (it.hasNext()) { - Object o = it.next(); + JsonArray nodes = new JsonArray(); + for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) { JsonObject me = new JsonObject(); - if (o instanceof Vertex) { - me = this.getJsonFromVertex((Vertex) o).get(); + if (entry.getKey() instanceof Vertex) { + Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) entry.getKey()); + if (obj.isPresent()) { + me = obj.get(); + } else { + continue; + } } - JsonArray ja = this.getNodesArray((Tree<?>) tree.get(o)); + JsonArray ja = this.getNodesArray(entry.getValue(), nodeIdentifier); if (ja.size() > 0) { - me.add("nodes", ja); + me.add(nodeIdentifier, ja); } nodes.add(me); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java index a99ba7f4..417f73cd 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java @@ -23,6 +23,8 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.util.List; +import java.util.Map; import java.util.Optional; import org.apache.tinkerpop.gremlin.structure.Vertex; @@ -31,9 +33,15 @@ import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.AsTree; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.NodesOnly; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; +import javax.ws.rs.core.MultivaluedMap; + public final class PathedURL extends MultiFormatMapper { private final UrlBuilder urlBuilder; @@ -47,6 +55,13 @@ public final class PathedURL extends MultiFormatMapper { this.loader = loader; } + public PathedURL(Builder builder) { + this.urlBuilder = builder.getUrlBuilder(); + this.parser = new JsonParser(); + this.loader = builder.getLoader(); + this.isTree = builder.isTree(); + } + @Override public int parallelThreshold() { return 20; @@ -79,4 +94,101 @@ public final class PathedURL extends MultiFormatMapper { } + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + private boolean includeUrl = false; + private boolean nodesOnly = false; + private int depth = 1; + private MultivaluedMap<String, String> params; + private boolean tree = false; + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + } + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder, MultivaluedMap<String, String> params) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + this.params = params; + } + + protected Loader getLoader() { + return this.loader; + } + + protected DBSerializer getSerializer() { + return this.serializer; + } + + protected UrlBuilder getUrlBuilder() { + return this.urlBuilder; + } + + protected MultivaluedMap<String, String> getParams() { return this.params; } + + public boolean isSkipRelatedTo() { + if (params != null) { + boolean isSkipRelatedTo = true; + if (params.containsKey("skip-related-to")) { + String skipRelatedTo = params.getFirst("skip-related-to"); + isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false")); + } else { + // if skip-related-to param is missing, then default it to false; + isSkipRelatedTo = false; + } + return isSkipRelatedTo; + } + return true; + } + + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + + public Builder includeUrl() { + this.includeUrl = true; + return this; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + + public boolean isNodesOnly() { + return this.nodesOnly; + } + + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + + public int getDepth() { + return this.depth; + } + + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public PathedURL build() throws AAIException { + return new PathedURL(this); + } + } + } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java index 8636ebfa..8dcd3a83 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java @@ -25,9 +25,8 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; @@ -38,6 +37,7 @@ import org.onap.aai.introspection.Loader; import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.AsTree; import org.onap.aai.serialization.queryformats.params.NodesOnly; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; @@ -55,6 +55,25 @@ public class RawFormat extends MultiFormatMapper { this.serializer = builder.getSerializer(); this.depth = builder.getDepth(); this.nodesOnly = builder.isNodesOnly(); + this.isTree = builder.isTree(); + } + + @Override + public Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + json.addProperty("id", v.id().toString()); + json.addProperty("node-type", v.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(v)); + Optional<JsonObject> properties = this.createSelectedPropertiesObject(v, selectedProps); + if (properties.isPresent()) { + json.add("properties", properties.get()); + } else { + return Optional.empty(); + } + if (!nodesOnly) { + json.add("related-to", this.createRelationshipObject(v)); + } + return Optional.of(json); } @Override @@ -88,6 +107,48 @@ public class RawFormat extends MultiFormatMapper { return Optional.of(json); } + public Optional<JsonObject> createSelectedPropertiesObject(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + String nodeType = v.<String>value(AAIProperties.NODE_TYPE); + Set<String> propList = removeSingleQuotesForProperties(selectedProps.get(nodeType)); + Iterator<VertexProperty<Object>> iter = v.properties(); + + Gson gson = new Gson(); + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + if (propList != null && !propList.isEmpty()) { + if (propList.contains(prop.label())) { + if (prop.value() instanceof String) { + json.addProperty(prop.key(), (String) prop.value()); + } else if (prop.value() instanceof Boolean) { + json.addProperty(prop.key(), (Boolean) prop.value()); + } else if (prop.value() instanceof Number) { + json.addProperty(prop.key(), (Number) prop.value()); + } else if (prop.value() instanceof List) { + json.addProperty(prop.key(), gson.toJson(prop.value())); + } else { + // throw exception? + return null; + } + } + } else { + return this.createPropertiesObject(v); + } + } + + return Optional.of(json); + } + + private Set<String> removeSingleQuotesForProperties(List<String> props){ + if (props != null && !props.isEmpty()) { + return props.stream().map( + e -> e.substring(1, e.length()-1)).collect(Collectors.toSet()); + } else { + return Collections.emptySet(); + } + + } + protected JsonArray createRelationshipObject(Vertex v) throws AAIFormatVertexException { JsonArray jarray = new JsonArray(); Iterator<Edge> inIter = v.edges(Direction.IN); @@ -122,7 +183,7 @@ public class RawFormat extends MultiFormatMapper { return json; } - public static class Builder implements NodesOnly<Builder>, Depth<Builder> { + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { protected final Loader loader; protected final DBSerializer serializer; @@ -131,6 +192,7 @@ public class RawFormat extends MultiFormatMapper { protected boolean nodesOnly = false; protected int depth = 1; protected boolean modelDriven = false; + protected boolean tree = false; public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { this.loader = loader; @@ -150,6 +212,13 @@ public class RawFormat extends MultiFormatMapper { return this.urlBuilder; } + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + public Builder includeUrl() { this.includeUrl = true; return this; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java index b92f5858..a53be6a3 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java @@ -20,14 +20,17 @@ package org.onap.aai.serialization.queryformats; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.stream.Stream; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.exceptions.AAIException; @@ -37,9 +40,12 @@ import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.AsTree; import org.onap.aai.serialization.queryformats.params.NodesOnly; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; +import javax.ws.rs.core.MultivaluedMap; + public class Resource extends MultiFormatMapper { private final Loader loader; @@ -49,8 +55,9 @@ public class Resource extends MultiFormatMapper { private final boolean includeUrl; private final boolean nodesOnly; private final int depth; + private final boolean isSkipRelatedTo; - private Resource(Builder builder) { + public Resource(Builder builder) { this.parser = new JsonParser(); this.loader = builder.getLoader(); this.serializer = builder.getSerializer(); @@ -58,6 +65,54 @@ public class Resource extends MultiFormatMapper { this.includeUrl = builder.isIncludeUrl(); this.nodesOnly = builder.isNodesOnly(); this.depth = builder.getDepth(); + this.isSkipRelatedTo = builder.isSkipRelatedTo(); + this.isTree = builder.isTree(); + } + + @Override + protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree) throws AAIFormatVertexException { + if (tree.isEmpty()) { + return Optional.of(new JsonObject()); + } + JsonObject t = new JsonObject(); + JsonArray ja = this.getRelatedNodesArray(tree, "related-nodes"); + if (ja.size() > 0) { + t.add("results", ja); + return Optional.of(t); + } + + return Optional.empty(); + } + + protected JsonArray getRelatedNodesArray(Tree<?> tree, String nodeIdentifier) throws AAIFormatVertexException { + JsonArray nodes = new JsonArray(); + if (tree.isEmpty()) { + return nodes; + } + for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) { + JsonObject me = new JsonObject(); + if (entry.getKey() instanceof Vertex) { + Optional<JsonObject> obj = null; + if (entry.getKey() != null) { + obj = this.getJsonFromVertex((Vertex) entry.getKey()); + } + if (obj != null && obj.isPresent()) { + me = obj.get(); + } else { + continue; + } + } + JsonArray ja = this.getRelatedNodesArray(entry.getValue(), nodeIdentifier); + if (ja.size() > 0) { + try { + me.entrySet().stream().findFirst().get().getValue().getAsJsonObject().add(nodeIdentifier, ja); + } catch(Exception e) { + throw new AAIFormatVertexException("Failed to add related-nodes array: " + e.getMessage(), e); + } + } + nodes.add(me); + } + return nodes; } @Override @@ -77,7 +132,15 @@ public class Resource extends MultiFormatMapper { return Optional.of(json); } + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + protected Optional<JsonObject> vertexToJsonObject(Vertex v) throws AAIFormatVertexException { + if (v == null) { + return Optional.empty(); + } try { final Introspector obj = getLoader().introspectorFromName(v.<String>property(AAIProperties.NODE_TYPE).orElse(null)); @@ -87,7 +150,7 @@ public class Resource extends MultiFormatMapper { wrapper.add(v); try { - getSerializer().dbToObject(wrapper, obj, this.depth, this.nodesOnly, "false"); + getSerializer().dbToObject(wrapper, obj, this.depth, this.nodesOnly, "false", isSkipRelatedTo); } catch (AAIException | UnsupportedEncodingException e) { throw new AAIFormatVertexException( "Failed to format vertex - error while serializing: " + e.getMessage(), e); @@ -118,7 +181,7 @@ public class Resource extends MultiFormatMapper { return parser; } - public static class Builder implements NodesOnly<Builder>, Depth<Builder> { + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { private final Loader loader; private final DBSerializer serializer; @@ -126,6 +189,8 @@ public class Resource extends MultiFormatMapper { private boolean includeUrl = false; private boolean nodesOnly = false; private int depth = 1; + private MultivaluedMap<String, String> params; + private boolean tree = false; public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { this.loader = loader; @@ -133,6 +198,13 @@ public class Resource extends MultiFormatMapper { this.urlBuilder = urlBuilder; } + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder, MultivaluedMap<String, String> params) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + this.params = params; + } + protected Loader getLoader() { return this.loader; } @@ -145,6 +217,31 @@ public class Resource extends MultiFormatMapper { return this.urlBuilder; } + protected MultivaluedMap<String, String> getParams() { return this.params; } + + public boolean isSkipRelatedTo() { + if (params != null) { + boolean isSkipRelatedTo = true; + if (params.containsKey("skip-related-to")) { + String skipRelatedTo = params.getFirst("skip-related-to"); + isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false")); + } else { + // if skip-related-to param is missing, then default it to false; + isSkipRelatedTo = false; + } + return isSkipRelatedTo; + } + return true; + } + + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + + public Builder includeUrl() { this.includeUrl = true; return this; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java index e4107aa9..d70f5c8e 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java @@ -23,6 +23,8 @@ package org.onap.aai.serialization.queryformats; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.util.List; +import java.util.Map; import java.util.Optional; import org.apache.tinkerpop.gremlin.structure.Vertex; @@ -30,11 +32,14 @@ import org.onap.aai.db.props.AAIProperties; import org.onap.aai.introspection.Loader; import org.onap.aai.serialization.db.DBSerializer; import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.AsTree; import org.onap.aai.serialization.queryformats.params.Depth; import org.onap.aai.serialization.queryformats.params.NodesOnly; import org.onap.aai.serialization.queryformats.utils.UrlBuilder; import org.onap.aai.util.AAIConfig; +import java.util.Optional; + public class ResourceWithSoT extends MultiFormatMapper { protected JsonParser parser = new JsonParser(); protected final DBSerializer serializer; @@ -49,6 +54,7 @@ public class ResourceWithSoT extends MultiFormatMapper { this.serializer = builder.getSerializer(); this.depth = builder.getDepth(); this.nodesOnly = builder.isNodesOnly(); + this.isTree = builder.isTree(); } @Override @@ -56,7 +62,7 @@ public class ResourceWithSoT extends MultiFormatMapper { return 100; } - public static class Builder implements NodesOnly<Builder>, Depth<Builder> { + public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> { protected final Loader loader; protected final DBSerializer serializer; @@ -65,6 +71,7 @@ public class ResourceWithSoT extends MultiFormatMapper { protected boolean nodesOnly = false; protected int depth = 1; protected boolean modelDriven = false; + protected boolean tree = false; public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { this.loader = loader; @@ -89,6 +96,13 @@ public class ResourceWithSoT extends MultiFormatMapper { return this; } + protected boolean isTree() { return this.tree; } + + public Builder isTree(Boolean tree) { + this.tree = tree; + return this; + } + public Builder nodesOnly(Boolean nodesOnly) { this.nodesOnly = nodesOnly; return this; @@ -139,8 +153,9 @@ public class ResourceWithSoT extends MultiFormatMapper { @Override protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException { // Null check - if (v == null) + if (v == null) { return null; + } JsonObject json = new JsonObject(); @@ -148,9 +163,9 @@ public class ResourceWithSoT extends MultiFormatMapper { Object lastModifiedTimestampObj = v.property(AAIProperties.LAST_MOD_TS).value(); Object sotObj = v.property(AAIProperties.SOURCE_OF_TRUTH).value(); Object lastModSotObj = v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value(); - long createdTimestamp = Long.valueOf(createdTimestampObj.toString()); - long lastModifiedTimestamp = Long.valueOf(lastModifiedTimestampObj.toString()); - long threshold = Long.valueOf(AAIConfig.get("aai.resource.format.threshold", "10")); + long createdTimestamp = Long.parseLong(createdTimestampObj.toString()); + long lastModifiedTimestamp = Long.parseLong(lastModifiedTimestampObj.toString()); + long threshold = Long.parseLong(AAIConfig.get("aai.resource.format.threshold", "10")); // Add to the property field of the JSON payload json.addProperty("aai-created-ts", createdTimestampObj.toString()); @@ -172,4 +187,9 @@ public class ResourceWithSoT extends MultiFormatMapper { return Optional.of(json); } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/StateFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/StateFormat.java new file mode 100644 index 00000000..b2813476 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/StateFormat.java @@ -0,0 +1,144 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.serialization.queryformats; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +import java.util.*; + +public class StateFormat extends HistoryFormat { + + private static final Logger LOGGER = LoggerFactory.getLogger(StateFormat.class); + + protected StateFormat(HistoryFormat.Builder builder) { + super(builder); + } + + protected JsonArray createPropertiesObject(Vertex v) { + Iterator<VertexProperty<Object>> iter = v.properties(); + List<JsonObject> jsonList = new ArrayList<>(); + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + if (prop.key() != null && ignoredKeys.contains(prop.key())) { + continue; + } + + JsonObject metaProperties = createMetaPropertiesObject(prop); + if (isTsInRange(metaProperties)) { + JsonObject json = new JsonObject(); + json.addProperty(KEY, prop.key()); + json = mapPropertyValues(json, VALUE, prop.value()); + addMetaProperties(json, metaProperties); + jsonList.add(json); + } + } + + JsonArray jsonArray = new JsonArray(); + jsonList.stream().sorted(Comparator.comparingLong(o -> o.get(TIMESTAMP).getAsLong())).forEach(jsonArray::add); + return jsonArray; + } + + private boolean isTsInRange(JsonObject metaProperties) { + long sTs = metaProperties.get(AAIProperties.START_TS).getAsLong(); + long eTs = Long.MAX_VALUE; + if (metaProperties.has(AAIProperties.END_TS)) { + eTs = metaProperties.get(AAIProperties.END_TS).getAsLong(); + } + + return startTs >= sTs && eTs > startTs; + } + + @Override + protected boolean isValidEdge(Edge e) { + if (e.property(AAIProperties.END_TS).isPresent()) { + long edgeEndTs = e.value(AAIProperties.END_TS); + if (startTs >= edgeEndTs) { + return false; + } + } + if (e.property(AAIProperties.START_TS).isPresent()) { + long edgeStartTs = e.value(AAIProperties.START_TS); + return startTs >= edgeStartTs; + } + return true; + } + + @Override + protected JsonObject getRelatedObject(Edge e, Vertex related) throws AAIFormatVertexException { + + JsonObject json = new JsonObject(); + json.addProperty("relationship-label", e.label()); + json.addProperty(NODE_TYPE, related.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(related)); + if (related.property(AAIProperties.AAI_URI).isPresent()) { + json.addProperty("uri", related.<String>value(AAIProperties.AAI_URI)); + } else { + LOGGER.warn("Vertex {} is missing aai-uri", related.id()); + json.addProperty("uri", "NA"); + } + json.addProperty(TIMESTAMP, e.property(AAIProperties.START_TS).isPresent()? e.value(AAIProperties.START_TS) : 0); + json.addProperty(SOT, e.property(AAIProperties.SOURCE_OF_TRUTH).isPresent()? e.value(AAIProperties.SOURCE_OF_TRUTH) : ""); + json.addProperty(TX_ID, e.property(AAIProperties.START_TX_ID).isPresent()? e.value(AAIProperties.START_TX_ID) : "N/A"); + + return json; + } + + + protected void addMetaProperties(JsonObject json, JsonObject metaProperties) { + json.addProperty(TIMESTAMP, metaProperties.get(AAIProperties.START_TS) != null ? metaProperties.get(AAIProperties.START_TS).getAsLong() : 0); + json.addProperty(SOT, metaProperties.get(AAIProperties.SOURCE_OF_TRUTH) != null ? metaProperties.get(AAIProperties.SOURCE_OF_TRUTH).getAsString() : ""); + json.addProperty(TX_ID, metaProperties.get(AAIProperties.START_TX_ID) != null ? metaProperties.get(AAIProperties.START_TX_ID).getAsString() : "N/A"); + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException { + + JsonObject json = new JsonObject(); + json.addProperty(NODE_TYPE, v.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(v)); + json.addProperty("uri", v.property(AAIProperties.AAI_URI).value().toString()); + JsonArray properties = this.createPropertiesObject(v); + + if (properties.size() > 0) { + json.add(PROPERTIES, properties); + } else { + return Optional.empty(); + } + if (!nodesOnly) { + json.add(RELATED_TO, this.createRelationshipObject(v)); + } + return Optional.of(json); + } + + @Override + protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException { + return Optional.empty(); + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/logging/LoggingContextNotExistsException.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/AsTree.java index f1d4c59c..c7479147 100644 --- a/aai-core/src/main/java/org/onap/aai/logging/LoggingContextNotExistsException.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/AsTree.java @@ -18,9 +18,11 @@ * ============LICENSE_END========================================================= */ -package org.onap.aai.logging; +package org.onap.aai.serialization.queryformats.params; -public class LoggingContextNotExistsException extends RuntimeException { +@Inject(name = "as-tree") +public interface AsTree<T> { - private static final long serialVersionUID = -4965807709525739623L; + @Setter + public T isTree(Boolean tree); } diff --git a/aai-core/src/main/java/org/onap/aai/logging/ErrorObjectFormatException.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/EndTs.java index 8d53f2e3..937d87f2 100644 --- a/aai-core/src/main/java/org/onap/aai/logging/ErrorObjectFormatException.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/EndTs.java @@ -18,13 +18,11 @@ * ============LICENSE_END========================================================= */ -package org.onap.aai.logging; +package org.onap.aai.serialization.queryformats.params; -public class ErrorObjectFormatException extends Exception { +@Inject(name = "endTs") +public interface EndTs<T> { - private static final long serialVersionUID = 3732705544448553685L; - - public ErrorObjectFormatException() { - super(); - } + @Setter + public T endTs(String endTs); } diff --git a/aai-core/src/main/java/org/onap/aai/logging/CustomLogPatternLayout.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/StartTs.java index 63cc49f7..e94b893d 100644 --- a/aai-core/src/main/java/org/onap/aai/logging/CustomLogPatternLayout.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/StartTs.java @@ -18,11 +18,11 @@ * ============LICENSE_END========================================================= */ -package org.onap.aai.logging; +package org.onap.aai.serialization.queryformats.params; -public class CustomLogPatternLayout extends ch.qos.logback.access.PatternLayout { - static { - defaultConverterMap.put("z", CNName.class.getName()); - defaultConverterMap.put("y", DME2RestFlag.class.getName()); - } +@Inject(name = "startTs") +public interface StartTs<T> { + + @Setter + public T startTs(String startTs); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java index 1b51088e..f90907e2 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java @@ -20,25 +20,23 @@ package org.onap.aai.serialization.queryformats.utils; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Set; +import org.onap.aai.serialization.queryformats.exceptions.QueryParamInjectionException; +import org.onap.aai.serialization.queryformats.params.*; import javax.ws.rs.core.MultivaluedMap; - -import org.onap.aai.serialization.queryformats.exceptions.QueryParamInjectionException; -import org.onap.aai.serialization.queryformats.params.Inject; -import org.onap.aai.serialization.queryformats.params.Setter; -import org.reflections.Reflections; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; public class QueryParamInjector { - private final Set<Class<?>> results; - - private QueryParamInjector() { - Reflections reflections = new Reflections("org.onap.aai.serialization.queryformats.params"); - results = reflections.getTypesAnnotatedWith(Inject.class); - } + //TODO reimplement this using reflections. + private static final Class<?>[] PARAM_CLASSES = new Class[] { + AsTree.class, + Depth.class, + EndTs.class, + NodesOnly.class, + StartTs.class + }; private static class Helper { private static final QueryParamInjector INSTANCE = new QueryParamInjector(); @@ -50,7 +48,7 @@ public class QueryParamInjector { public <T> T injectParams(T obj, MultivaluedMap<String, String> params) throws QueryParamInjectionException { try { - for (Class<?> item : results) { + for (Class<?> item : PARAM_CLASSES) { if (item.isAssignableFrom(obj.getClass())) { String name = item.getAnnotation(Inject.class).name(); diff --git a/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java b/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java index 316e3017..6c57616e 100644 --- a/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java +++ b/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java @@ -20,8 +20,8 @@ package org.onap.aai.service; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; @@ -37,7 +37,7 @@ import org.springframework.stereotype.Service; @PropertySource(value = "file:${schema.ingest.file}", ignoreResourceNotFound = true) public class NodeValidationService { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(NodeValidationService.class); + private static final Logger LOGGER = LoggerFactory.getLogger(NodeValidationService.class); @Autowired(required = false) private NodeValidator nodeValidator; diff --git a/aai-core/src/main/java/org/onap/aai/tasks/ScheduledTasks.java b/aai-core/src/main/java/org/onap/aai/tasks/ScheduledTasks.java index 911603d2..16c41166 100644 --- a/aai-core/src/main/java/org/onap/aai/tasks/ScheduledTasks.java +++ b/aai-core/src/main/java/org/onap/aai/tasks/ScheduledTasks.java @@ -20,32 +20,30 @@ package org.onap.aai.tasks; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.io.File; -import java.util.Arrays; -import java.util.Date; -import java.util.UUID; - import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.comparator.LastModifiedFileComparator; -import org.onap.aai.logging.LoggingContext; -import org.onap.aai.logging.LoggingContext.StatusCode; +import org.onap.aai.aailog.logs.AaiScheduledTaskAuditLog; import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; +import org.onap.logging.filter.base.ONAPComponents; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.io.File; +import java.util.Arrays; +import java.util.Date; + @Component public class ScheduledTasks { - private static EELFLogger LOGGER = EELFManager.getInstance().getLogger(ScheduledTasks.class); + @Autowired + private AaiScheduledTaskAuditLog auditLog; - private static final String COMPONENT = "Scheduler"; - private static final String FROM_APP_ID = "CronApp"; + private static Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class); private static final long PROPERTY_READ_INTERVAL = 60000; // every minute - private String GlobalPropFileName = AAIConstants.AAI_CONFIG_FILENAME; // for read and possibly reloading aaiconfig.properties and other @@ -55,19 +53,7 @@ public class ScheduledTasks { // configuration properties files @Scheduled(fixedRate = PROPERTY_READ_INTERVAL) public void loadAAIProperties() { - final UUID transId = UUID.randomUUID(); - - // LoggingContext.init(); - LoggingContext.save(); - LoggingContext.requestId(transId); - LoggingContext.partnerName(FROM_APP_ID); - LoggingContext.component(COMPONENT); - LoggingContext.targetEntity("AAI"); - LoggingContext.targetServiceName("loadAAIProperties"); - LoggingContext.serviceName("AAI"); - LoggingContext.statusCode(StatusCode.COMPLETE); - LoggingContext.responseCode(LoggingContext.SUCCESS); - + auditLog.logBefore("LoadAaiPropertiesTask", ONAPComponents.AAI.toString() ); String dir = FilenameUtils.getFullPathNoEndSeparator(GlobalPropFileName); if (dir == null || dir.length() < 3) { dir = "/opt/aai/etc"; @@ -94,6 +80,6 @@ public class ScheduledTasks { break; } } - LoggingContext.restoreIfPossible(); + auditLog.logAfter(); } } diff --git a/aai-core/src/main/java/org/onap/aai/transforms/XmlFormatTransformer.java b/aai-core/src/main/java/org/onap/aai/transforms/XmlFormatTransformer.java new file mode 100644 index 00000000..ce7ead4f --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/transforms/XmlFormatTransformer.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.transforms; + +import com.bazaarvoice.jolt.Chainr; +import com.bazaarvoice.jolt.JsonUtils; +import org.json.JSONObject; +import org.json.XML; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class XmlFormatTransformer { + + private static final Logger LOGGER = LoggerFactory.getLogger(XmlFormatTransformer.class); + + private static final String RESULT_WITH_QUOTES = "\"result\""; + private static final String RESULTS_STRING = "results"; + + private Chainr chainr; + + public XmlFormatTransformer() { + List<Object> spec = JsonUtils.classpathToList("/specs/transform-related-to-node.json"); + this.chainr = Chainr.fromSpec(spec); + } + + public String transform(String input) { + + Object transformedOutput; + + if(!input.contains(RESULT_WITH_QUOTES)){ + Object inputMap = JsonUtils.jsonToMap(input); + transformedOutput = chainr.transform(inputMap); + + JSONObject jsonObject; + if(transformedOutput == null){ + LOGGER.debug("For the input {}, unable to transform it so returning null", input); + jsonObject = new JSONObject(); + } else { + jsonObject = new JSONObject(JsonUtils.toJsonString(transformedOutput)); + } + + return XML.toString(jsonObject, RESULTS_STRING); + } else { + // If the json is already conforming to the following format + // {"results":[{"results":"v[2]"}]} + // Then no transformation is required + return XML.toString(new JSONObject(input)); + } + + } +} diff --git a/aai-core/src/main/java/org/onap/aai/util/AAIConfig.java b/aai-core/src/main/java/org/onap/aai/util/AAIConfig.java deleted file mode 100644 index c2103071..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/AAIConfig.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.util; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.util.Properties; -import java.util.UUID; - -import org.eclipse.jetty.util.security.Password; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.LoggingContext; -import org.onap.aai.logging.LoggingContext.StatusCode; - -public class AAIConfig { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIConfig.class); - private static final String GLOBAL_PROP_FILE_NAME = AAIConstants.AAI_CONFIG_FILENAME; - private static Properties serverProps; - private static boolean propsInitialized = false; - - /** - * Instantiates a new AAI config. - */ - // Don't instantiate - private AAIConfig() { - } - - /** - * Inits the. - * - * @throws AAIException the AAI exception - */ - public synchronized static void init() throws AAIException { - - LoggingContext.save(); - LoggingContext.component("config"); - LoggingContext.partnerName("NA"); - LoggingContext.targetEntity("AAI"); - LoggingContext.requestId(UUID.randomUUID().toString()); - LoggingContext.serviceName("AAI"); - LoggingContext.targetServiceName("init"); - LoggingContext.statusCode(StatusCode.COMPLETE); - - LOGGER.info("Initializing AAIConfig"); - - AAIConfig.getConfigFile(); - AAIConfig.reloadConfig(); - - if (AAIConstants.AAI_NODENAME == null || AAIConstants.AAI_NODENAME == "") { - ErrorLogHelper.logError("AAI_4005", " AAI_NODENAME is not defined"); - } else { - LOGGER.info("A&AI Server Node Name = " + AAIConstants.AAI_NODENAME); - } - LoggingContext.restore(); - } - - /** - * Gets the config file. - * - * @return the config file - */ - public static String getConfigFile() { - return GLOBAL_PROP_FILE_NAME; - } - - /** - * Reload config. - */ - public synchronized static void reloadConfig() { - - String propFileName = GLOBAL_PROP_FILE_NAME; - Properties newServerProps = null; - - LOGGER.debug("Reloading config from " + propFileName); - - try (InputStream is = new FileInputStream(propFileName)) { - newServerProps = new Properties(); - newServerProps.load(is); - propsInitialized = true; - serverProps = newServerProps; - } catch (FileNotFoundException fnfe) { - ErrorLogHelper.logError("AAI_4001", " " + propFileName + ". Exception: " + fnfe.getMessage()); - } catch (IOException e) { - ErrorLogHelper.logError("AAI_4002", " " + propFileName + ". IOException: " + e.getMessage()); - } - } - - /** - * Gets the. - * - * @param key the key - * @param defaultValue the default value - * @return the string - */ - public static String get(String key, String defaultValue) { - String result = defaultValue; - try { - result = get(key); - } catch (AAIException a) { - } - if (result == null || result.isEmpty()) { - result = defaultValue; - } - return (result); - } - - /** - * Gets the. - * - * @param key the key - * @return the string - * @throws AAIException the AAI exception - */ - public static String get(String key) throws AAIException { - String response = null; - - if (key.equals(AAIConstants.AAI_NODENAME)) { - // Get this from InetAddress rather than the properties file - String nodeName = getNodeName(); - if (nodeName != null) { - return nodeName; - } - // else get from property file - } - - if (!propsInitialized || (serverProps == null)) { - reloadConfig(); - } - - if ((key.endsWith("password") || key.endsWith("passwd") || key.endsWith("apisecret")) - && serverProps.containsKey(key + ".x")) { - String valx = serverProps.getProperty(key + ".x"); - return Password.deobfuscate(valx); - } - - if (!serverProps.containsKey(key)) { - throw new AAIException("AAI_4005", "Property key " + key + " cannot be found"); - } else { - response = serverProps.getProperty(key); - if (response == null || response.isEmpty()) { - throw new AAIException("AAI_4005", "Property key " + key + " is null or empty"); - } - } - return response; - } - - /** - * Gets the int. - * - * @param key the key - * @return the int - * @throws AAIException the AAI exception - */ - public static int getInt(String key) throws AAIException { - return Integer.parseInt(AAIConfig.get(key)); - } - - /** - * Gets the int. - * - * @param key the key - * @return the int - */ - public static int getInt(String key, String value) { - return Integer.parseInt(AAIConfig.get(key, value)); - } - - /** - * Gets the server props. - * - * @return the server props - */ - public static Properties getServerProps() { - return serverProps; - } - - /** - * Gets the node name. - * - * @return the node name - */ - public static String getNodeName() { - try { - InetAddress ip = InetAddress.getLocalHost(); - if (ip != null) { - String hostname = ip.getHostName(); - if (hostname != null) { - return hostname; - } - } - } catch (Exception e) { - return null; - } - return null; - } - - /** - * Check if a null or an Empty string is passed in. - * - * @param s the s - * @return boolean - */ - public static boolean isEmpty(String s) { - return (s == null || s.length() == 0); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java b/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java deleted file mode 100644 index e906c280..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Modifications Copyright © 2018 IBM. - * ================================================================================ - * 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.util; - -public final class AAIConstants { - private static final String AJSC_HOME = "AJSC_HOME"; - // - // - /** Default to unix file separator if system property file.separator is null */ - public static final String AAI_FILESEP = - (System.getProperty("file.separator") == null) ? "/" : System.getProperty("file.separator"); - // - /** Default to opt aai if system property aai.home is null, using file.separator */ - public static final String AAI_HOME = - (System.getProperty(AJSC_HOME) == null) ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP + "aai" - : System.getProperty(AJSC_HOME); - public static final String AAI_BUNDLECONFIG_NAME = - (System.getProperty("BUNDLECONFIG_DIR") == null) ? "bundleconfig" : System.getProperty("BUNDLECONFIG_DIR"); - public static final String AAI_HOME_BUNDLECONFIG = (System.getProperty(AJSC_HOME) == null) - ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP + "aai" + AAI_FILESEP + AAI_BUNDLECONFIG_NAME - : System.getProperty(AJSC_HOME) + AAI_FILESEP + AAI_BUNDLECONFIG_NAME; - - /** etc directory, relative to AAI_HOME */ - public static final String AAI_HOME_ETC = AAI_HOME_BUNDLECONFIG + AAI_FILESEP + "etc" + AAI_FILESEP; - public static final String AAI_HOME_ETC_APP_PROPERTIES = AAI_HOME_ETC + "appprops" + AAI_FILESEP; - public static final String AAI_HOME_ETC_AUTH = AAI_HOME_ETC + "auth" + AAI_FILESEP; - public static final String AAI_CONFIG_FILENAME = AAI_HOME_ETC_APP_PROPERTIES + "aaiconfig.properties"; - public static final String AAI_AUTH_CONFIG_FILENAME = AAI_HOME_ETC_AUTH + "aai_policy.json"; - public static final String REALTIME_DB_CONFIG = AAI_HOME_ETC_APP_PROPERTIES + "janusgraph-realtime.properties"; - public static final String CACHED_DB_CONFIG = AAI_HOME_ETC_APP_PROPERTIES + "janusgraph-cached.properties"; - public static final String AAI_HOME_ETC_OXM = AAI_HOME_ETC + "oxm" + AAI_FILESEP; - public static final String AAI_EVENT_DMAAP_PROPS = - AAI_HOME_ETC_APP_PROPERTIES + "aaiEventDMaaPPublisher.properties"; - public static final String AAI_HOME_ETC_SCRIPT = AAI_HOME_ETC + AAI_FILESEP + "scriptdata" + AAI_FILESEP; - - public static final String AAI_LOGBACK_PROPS = "logback.xml"; - public static final String AAI_SCHEMA_MOD_LOGBACK_PROPS = "schemaMod-logback.xml"; - public static final String AAI_FORCE_DELETE_LOGBACK_PROPS = "forceDelete-logback.xml"; - - public static final String AAI_TRUSTSTORE_FILENAME = "aai.truststore.filename"; - public static final String AAI_TRUSTSTORE_PASSWD = "aai.truststore.passwd"; - public static final String AAI_KEYSTORE_FILENAME = "aai.keystore.filename"; - public static final String AAI_KEYSTORE_PASSWD = "aai.keystore.passwd"; - - public static final String AAI_SERVER_URL_BASE = "aai.server.url.base"; - public static final String AAI_SERVER_URL = "aai.server.url"; - public static final String AAI_OLDSERVER_URL = "aai.oldserver.url"; - public static final String AAI_LOCAL_REST = "https://localhost:%d/aai/%s/"; - public static final String AAI_LOCAL_OVERRIDE = "aai.server.localhost.override.url"; - public static final String AAI_LOCAL_OVERRIDE_DEFAULT = "NA"; - public static final String AAI_LOCAL_REST_OVERRIDE = "%s/aai/%s/"; - - public static final int AAI_RESOURCES_PORT = 8447; - public static final int AAI_QUERY_PORT = 8446; - public static final int AAI_LEGACY_PORT = 8443; - - public static final String AAI_DEFAULT_API_VERSION = "v10"; - public static final String AAI_DEFAULT_API_VERSION_PROP = "aai.default.api.version"; - public static final String AAI_NOTIFICATION_CURRENT_VERSION = "aai.notification.current.version"; - - public static final String AAI_NODENAME = "aai.config.nodename"; - - /* - * Logs the objects being deleted when an client deletes objects via implied delete during PUT operation - */ - public static final String AAI_IMPLIED_DELETE_LOG_ENABLED = "aai.implied.delete.log.enabled"; - /* - * Specifies how many objects maximum to log - */ - public static final String AAI_IMPLIED_DELETE_LOG_LIMIT = "aai.implied.delete.log.limit"; - - public static final String AAI_BULKCONSUMER_LIMIT = "aai.bulkconsumer.payloadlimit"; - public static final String AAI_BULKCONSUMER_OVERRIDE_LIMIT = "aai.bulkconsumer.payloadoverride"; - - public static final String AAI_TRAVERSAL_TIMEOUT_LIMIT = "aai.traversal.timeoutlimit"; - public static final String AAI_TRAVERSAL_TIMEOUT_ENABLED = "aai.traversal.timeoutenabled"; - public static final String AAI_TRAVERSAL_TIMEOUT_APP = "aai.traversal.timeout.appspecific"; - - public static final String AAI_GRAPHADMIN_TIMEOUT_LIMIT = "aai.graphadmin.timeoutlimit"; - public static final String AAI_GRAPHADMIN_TIMEOUT_ENABLED = "aai.graphadmin.timeoutenabled"; - public static final String AAI_GRAPHADMIN_TIMEOUT_APP = "aai.graphadmin.timeout.appspecific"; - - public static final String AAI_CRUD_TIMEOUT_LIMIT = "aai.crud.timeoutlimit"; - public static final String AAI_CRUD_TIMEOUT_ENABLED = "aai.crud.timeoutenabled"; - public static final String AAI_CRUD_TIMEOUT_APP = "aai.crud.timeout.appspecific"; - - public static final String AAI_RESVERSION_ENABLEFLAG = "aai.resourceversion.enableflag"; - public static final String AAI_RESVERSION_DISABLED_UUID = "aai.resourceversion.disabled.uuid"; - public static final String AAI_RESVERSION_DISABLED_UUID_DEFAULT = "38cf3090-6a0c-4e9d-8142-4332a7352846"; - - public static final long HISTORY_MAX_HOURS = 192; - - public static final String LOGGING_MAX_STACK_TRACE_ENTRIES = "aai.logging.maxStackTraceEntries"; - - /*** UEB ***/ - public static final String UEB_PUB_PARTITION_AAI = "AAI"; - - /** Micro-service Names */ - public static final String AAI_TRAVERSAL_MS = "aai-traversal"; - public static final String AAI_RESOURCES_MS = "aai-resources"; - - /** - * Instantiates a new AAI constants. - */ - private AAIConstants() { - // prevent instantiation - } - -} diff --git a/aai-core/src/main/java/org/onap/aai/util/FileWatcher.java b/aai-core/src/main/java/org/onap/aai/util/FileWatcher.java deleted file mode 100644 index 07ac2642..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/FileWatcher.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.util; - -import java.io.*; -import java.util.*; - -public abstract class FileWatcher extends TimerTask { - private long timeStamp; - private File file; - - /** - * Instantiates a new file watcher. - * - * @param file the file - */ - public FileWatcher(File file) { - this.file = file; - this.timeStamp = file.lastModified(); - } - - /** - * runs a timer task - * - * @see TimerTask.run - */ - public final void run() { - long timeStamp = file.lastModified(); - - if ((timeStamp - this.timeStamp) > 500) { - this.timeStamp = timeStamp; - onChange(file); - } - } - - /** - * On change. - * - * @param file the file - */ - protected abstract void onChange(File file); -} diff --git a/aai-core/src/main/java/org/onap/aai/util/FormatDate.java b/aai-core/src/main/java/org/onap/aai/util/FormatDate.java deleted file mode 100644 index 9ee2b71d..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/FormatDate.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.util; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; - -public class FormatDate { - - private final String timeZone; - private final String pattern; - - public FormatDate(String pattern) { - this.pattern = pattern; - this.timeZone = "GMT"; - } - - public FormatDate(String pattern, String timeZone) { - this.pattern = pattern; - this.timeZone = timeZone; - } - - public String getDateTime() { - - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); - return formatter.format(ZonedDateTime.now(ZoneId.of(timeZone))); - } -} diff --git a/aai-core/src/main/java/org/onap/aai/util/HbaseSaltPrefixer.java b/aai-core/src/main/java/org/onap/aai/util/HbaseSaltPrefixer.java deleted file mode 100644 index a41ad27b..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/HbaseSaltPrefixer.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. 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.util; - -/* - * logging to hbase encountered hotspotting issues, so per - * http://archive.cloudera.com/cdh5/cdh/5/hbase-0.98.6-cdh5.3.8/book/rowkey.design.html - * we decided to salt the rowkeys - * as these keys are generated in a couple places, I made a class to contain that logic - */ -public class HbaseSaltPrefixer { - private int NUM_REGION_BUCKETS = 3; // the number of hbase region servers per cluster - - private static class SingletonHolder { - private static final HbaseSaltPrefixer INSTANCE = new HbaseSaltPrefixer(); - } - - /** - * Instantiates a new hbase salt prefixer. - */ - private HbaseSaltPrefixer() { - } - - /** - * Gets the single instance of HbaseSaltPrefixer. - * - * @return single instance of HbaseSaltPrefixer - */ - public static HbaseSaltPrefixer getInstance() { - return SingletonHolder.INSTANCE; - } - - /** - * Prepend salt. - * - * @param key the key - * @return the string - */ - public String prependSalt(String key) { - int salt = key.hashCode() % NUM_REGION_BUCKETS; - return salt + "-" + key; - } -} diff --git a/aai-core/src/main/java/org/onap/aai/util/HttpsAuthClient.java b/aai-core/src/main/java/org/onap/aai/util/HttpsAuthClient.java index 133c26a7..c2bfabf4 100644 --- a/aai-core/src/main/java/org/onap/aai/util/HttpsAuthClient.java +++ b/aai-core/src/main/java/org/onap/aai/util/HttpsAuthClient.java @@ -27,10 +27,13 @@ import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.api.json.JSONConfiguration; import com.sun.jersey.client.urlconnection.HTTPSProperties; +import org.onap.aai.aailog.filter.RestControllerClientLoggingInterceptor; +import org.onap.aai.exceptions.AAIException; import java.io.FileInputStream; -import java.security.KeyManagementException; -import java.security.KeyStore; +import java.io.IOException; +import java.security.*; +import java.security.cert.CertificateException; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -63,29 +66,25 @@ public class HttpsAuthClient { e.printStackTrace(); } } - /** * Gets the client. * + * @param truststorePath the truststore path + * @param truststorePassword the truststore password + * @param keystorePath the keystore path + * @param keystorePassword the keystore password * @return the client * @throws KeyManagementException the key management exception */ - public static Client getClient() throws KeyManagementException { + public static Client getClient(String truststorePath, String truststorePassword, String keystorePath, String keystorePassword) throws KeyManagementException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { ClientConfig config = new DefaultClientConfig(); config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); config.getClasses().add(org.onap.aai.restcore.CustomJacksonJaxBJsonProvider.class); - SSLContext ctx = null; try { - String truststore_path = - AAIConstants.AAI_HOME_ETC_AUTH + AAIConfig.get(AAIConstants.AAI_TRUSTSTORE_FILENAME); - String truststore_password = AAIConfig.get(AAIConstants.AAI_TRUSTSTORE_PASSWD); - String keystore_path = AAIConstants.AAI_HOME_ETC_AUTH + AAIConfig.get(AAIConstants.AAI_KEYSTORE_FILENAME); - String keystore_password = AAIConfig.get(AAIConstants.AAI_KEYSTORE_PASSWD); - - System.setProperty("javax.net.ssl.trustStore", truststore_path); - System.setProperty("javax.net.ssl.trustStorePassword", truststore_password); + System.setProperty("javax.net.ssl.trustStore", truststorePath); + System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String string, SSLSession ssls) { return true; @@ -96,36 +95,62 @@ public class HttpsAuthClient { KeyManagerFactory kmf = null; try { kmf = KeyManagerFactory.getInstance("SunX509"); - FileInputStream fin = new FileInputStream(keystore_path); + FileInputStream fin = new FileInputStream(keystorePath); KeyStore ks = KeyStore.getInstance("PKCS12"); - char[] pwd = keystore_password.toCharArray(); + char[] pwd = keystorePassword.toCharArray(); ks.load(fin, pwd); kmf.init(ks, pwd); } catch (Exception e) { System.out.println("Error setting up kmf: exiting"); e.printStackTrace(); - System.exit(1); + throw e; + //System.exit(1); } ctx.init(kmf.getKeyManagers(), null, null); config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, - new HTTPSProperties(new HostnameVerifier() { - @Override - public boolean verify(String s, SSLSession sslSession) { - return true; - } - }, ctx)); + new HTTPSProperties(new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }, ctx)); } catch (Exception e) { System.out.println("Error setting up config: exiting"); e.printStackTrace(); - System.exit(1); + throw e; + //System.exit(1); } Client client = Client.create(config); + client.addFilter(new RestControllerClientLoggingInterceptor()); // uncomment this line to get more logging for the request/response // client.addFilter(new LoggingFilter(System.out)); return client; } + /** + * Gets the client. + * + * @return the client + * @throws KeyManagementException the key management exception + */ + public static Client getClient() throws KeyManagementException, AAIException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { + String truststore_path = null; + String truststore_password = null; + String keystore_path = null; + String keystore_password = null; + try { + truststore_path = + AAIConstants.AAI_HOME_ETC_AUTH + AAIConfig.get(AAIConstants.AAI_TRUSTSTORE_FILENAME); + truststore_password = AAIConfig.get(AAIConstants.AAI_TRUSTSTORE_PASSWD); + keystore_path = AAIConstants.AAI_HOME_ETC_AUTH + AAIConfig.get(AAIConstants.AAI_KEYSTORE_FILENAME); + keystore_password = AAIConfig.get(AAIConstants.AAI_KEYSTORE_PASSWD); + } + catch (AAIException e) { + throw e; + } + return(getClient(truststore_path, truststore_password, keystore_path, keystore_password)); + } } diff --git a/aai-core/src/main/java/org/onap/aai/util/MapperUtil.java b/aai-core/src/main/java/org/onap/aai/util/MapperUtil.java deleted file mode 100644 index 47a937d4..00000000 --- a/aai-core/src/main/java/org/onap/aai/util/MapperUtil.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Modifications Copyright © 2018 IBM. - * ================================================================================ - * 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.util; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; - -import org.onap.aai.exceptions.AAIException; - -public class MapperUtil { - - /** - * Instantiates MapperUtil. - */ - private MapperUtil() { - // prevent instantiation - } - - /** - * Read as object of. - * - * @param <T> the generic type - * @param clazz the clazz - * @param value the value - * @return the t - * @throws AAIException the AAI exception - */ - public static <T> T readAsObjectOf(Class<T> clazz, String value) throws AAIException { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.readValue(value, clazz); - } catch (Exception e) { - throw new AAIException("AAI_4007", e); - } - } - - /** - * Read with dashes as object of. - * - * @param <T> the generic type - * @param clazz the clazz - * @param value the value - * @return the t - * @throws AAIException the AAI exception - */ - public static <T> T readWithDashesAsObjectOf(Class<T> clazz, String value) throws AAIException { - ObjectMapper mapper = new ObjectMapper(); - try { - mapper.registerModule(new JaxbAnnotationModule()); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false); - - return mapper.readValue(value, clazz); - } catch (Exception e) { - throw new AAIException("AAI_4007", e); - } - } - - /** - * Write as JSON string. - * - * @param obj the obj - * @return the string - * @throws AAIException the AAI exception - */ - public static String writeAsJSONString(Object obj) throws AAIException { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.writeValueAsString(obj); - } catch (Exception e) { - throw new AAIException("AAI_4008", e); - } - } - - /** - * Write as JSON string with dashes. - * - * @param obj the obj - * @return the string - * @throws AAIException the AAI exception - */ - public static String writeAsJSONStringWithDashes(Object obj) throws AAIException { - ObjectMapper mapper = new ObjectMapper(); - try { - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - mapper.configure(SerializationFeature.INDENT_OUTPUT, false); - mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false); - - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false); - - mapper.registerModule(new JaxbAnnotationModule()); - return mapper.writeValueAsString(obj); - } catch (Exception e) { - throw new AAIException("AAI_4008", e); - } - } -} diff --git a/aai-core/src/main/java/org/onap/aai/util/PojoUtils.java b/aai-core/src/main/java/org/onap/aai/util/PojoUtils.java index 218f0dd8..05cbf97c 100644 --- a/aai-core/src/main/java/org/onap/aai/util/PojoUtils.java +++ b/aai-core/src/main/java/org/onap/aai/util/PojoUtils.java @@ -129,11 +129,7 @@ public class PojoUtils { mapper.registerModule(new JaxbAnnotationModule()); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - mapper.writeValue(baos, clazz); - - return baos.toString(); + return mapper.writeValueAsString(clazz); } /** diff --git a/aai-core/src/main/java/org/onap/aai/util/RestController.java b/aai-core/src/main/java/org/onap/aai/util/RestController.java index a1419d14..8527ffe5 100644 --- a/aai-core/src/main/java/org/onap/aai/util/RestController.java +++ b/aai-core/src/main/java/org/onap/aai/util/RestController.java @@ -20,26 +20,30 @@ package org.onap.aai.util; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientResponse; +import java.io.IOException; import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.LoggingContext; public class RestController implements RestControllerInterface { private static final String TARGET_NAME = "AAI"; - private static EELFLogger LOGGER = EELFManager.getInstance().getLogger(RestController.class); + private static Logger LOGGER = LoggerFactory.getLogger(RestController.class); private static Client client = null; @@ -85,6 +89,9 @@ public class RestController implements RestControllerInterface { this.initRestClient(); } + public RestController(String truststorePath, String truststorePassword, String keystorePath, String keystorePassword) throws AAIException { + this.initRestClient(truststorePath, truststorePassword, keystorePath, keystorePassword); + } /** * Inits the rest client. * @@ -101,11 +108,29 @@ public class RestController implements RestControllerInterface { } } } + /** + * Inits the rest client. + * + * @throws AAIException the AAI exception + */ + public void initRestClient(String truststorePath, String truststorePassword, String keystorePath, String keystorePassword) throws AAIException { + if (client == null) { + try { + client = getHttpsAuthClient(truststorePath, truststorePassword, keystorePath, keystorePassword); + } catch (KeyManagementException e) { + throw new AAIException("AAI_7117", "KeyManagementException in REST call to DB: " + e.toString()); + } catch (Exception e) { + throw new AAIException("AAI_7117", " Exception in REST call to DB: " + e.toString()); + } + } + } + public Client getHttpsAuthClient(String truststorePath, String truststorePassword, String keystorePath, String keystorePassword) throws KeyManagementException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { + return HttpsAuthClient.getClient(truststorePath, truststorePassword, keystorePath, keystorePassword); + } - public Client getHttpsAuthClient() throws KeyManagementException { + public Client getHttpsAuthClient() throws KeyManagementException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, AAIException { return HttpsAuthClient.getClient(); } - /** * Sets the rest srvr base URL. * @@ -151,13 +176,6 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - LOGGER.debug(methodName + " start"); restObject.set(t); @@ -177,7 +195,6 @@ public class RestController implements RestControllerInterface { AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)) + path; } } - initRestClient(); LOGGER.debug(url + " for the get REST API"); ClientResponse cres = client.resource(url).accept("application/json").header("X-TransactionId", transId) .header("X-FromAppId", sourceID).header("Real-Time", "true").type("application/json") @@ -192,12 +209,9 @@ public class RestController implements RestControllerInterface { restObject.set(t); LOGGER.debug(methodName + "REST api GET was successfull!"); } else { - LoggingContext.restore(); // System.out.println(methodName + ": url=" + url + " failed with status=" + cres.getStatus()); throw new AAIException("AAI_7116", methodName + " with status=" + cres.getStatus() + ", url=" + url); } - - LoggingContext.restore(); } /** @@ -219,20 +233,12 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - LOGGER.debug(methodName + " start"); restObject.set(t); url = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE) + apiVersion + "/" + path; - initRestClient(); LOGGER.debug(url + " for the get REST API"); ClientResponse cres = client.resource(url).accept("application/json").header("X-TransactionId", transId) .header("X-FromAppId", sourceID).header("Real-Time", "true").type("application/json") @@ -247,12 +253,9 @@ public class RestController implements RestControllerInterface { restObject.set(t); LOGGER.debug(methodName + "REST api GET was successfull!"); } else { - LoggingContext.restore(); // System.out.println(methodName + ": url=" + url + " failed with status=" + cres.getStatus()); throw new AAIException("AAI_7116", methodName + " with status=" + cres.getStatus() + ", url=" + url); } - - LoggingContext.restore(); } /** @@ -320,17 +323,8 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - LOGGER.debug(methodName + " start"); - initRestClient(); - if (oldserver) { url = AAIConfig.get(AAIConstants.AAI_OLDSERVER_URL) + path; } else { @@ -356,9 +350,7 @@ public class RestController implements RestControllerInterface { int statuscode = cres.getStatus(); if (statuscode >= 200 && statuscode <= 299) { LOGGER.debug(methodName + ": url=" + url + ", request=" + path); - LoggingContext.restore(); } else { - LoggingContext.restore(); throw new AAIException("AAI_7116", methodName + " with status=" + statuscode + ", url=" + url + ", msg=" + cres.getEntity(String.class)); } @@ -381,16 +373,8 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - LOGGER.debug(methodName + " start"); - initRestClient(); String request = "{}"; if (overrideLocalHost == null) { overrideLocalHost = AAIConfig.get(AAIConstants.AAI_LOCAL_OVERRIDE, AAIConstants.AAI_LOCAL_OVERRIDE_DEFAULT); @@ -408,13 +392,10 @@ public class RestController implements RestControllerInterface { if (cres.getStatus() == 404) { // resource not found LOGGER.info("Resource does not exist...: " + cres.getStatus() + ":" + cres.getEntity(String.class)); - LoggingContext.restore(); } else if (cres.getStatus() == 200 || cres.getStatus() == 204) { LOGGER.info("Resource " + url + " deleted"); - LoggingContext.restore(); } else { LOGGER.error("Deleting Resource failed: " + cres.getStatus() + ":" + cres.getEntity(String.class)); - LoggingContext.restore(); throw new AAIException("AAI_7116", "Error during DELETE"); } } @@ -440,18 +421,10 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - LOGGER.debug(methodName + " start"); try { - initRestClient(); url = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE) + apiVersion + "/" + path; ClientResponse cres = client.resource(url).accept("application/json").header("X-TransactionId", transId) @@ -473,7 +446,6 @@ public class RestController implements RestControllerInterface { throw new AAIException("AAI_7116", methodName + " with url=" + url + ", Exception: " + e.toString()); } finally { - LoggingContext.restore(); } } @@ -558,13 +530,6 @@ public class RestController implements RestControllerInterface { String url = ""; transId += ":" + UUID.randomUUID().toString(); - LoggingContext.save(); - LoggingContext.partnerName(sourceID); - LoggingContext.targetEntity(TARGET_NAME); - LoggingContext.requestId(transId); - LoggingContext.serviceName(methodName); - LoggingContext.targetServiceName(methodName); - int numRetries = 5; ClientResponse cres = null; int statusCode = -1; @@ -582,7 +547,6 @@ public class RestController implements RestControllerInterface { AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)) + path; } - initRestClient(); do { cres = client.resource(url).accept("application/json").header("X-TransactionId", transId) @@ -613,7 +577,6 @@ public class RestController implements RestControllerInterface { throw new AAIException("AAI_7116", methodName + " with url=" + url + ", Exception: " + e.toString()); } finally { - LoggingContext.restore(); } } diff --git a/aai-core/src/main/java/org/onap/aai/util/StoreNotificationEvent.java b/aai-core/src/main/java/org/onap/aai/util/StoreNotificationEvent.java index 8b2bf50a..01d21ca7 100644 --- a/aai-core/src/main/java/org/onap/aai/util/StoreNotificationEvent.java +++ b/aai-core/src/main/java/org/onap/aai/util/StoreNotificationEvent.java @@ -20,8 +20,8 @@ package org.onap.aai.util; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.StringWriter; import java.util.Iterator; @@ -45,7 +45,7 @@ import org.springframework.core.env.Environment; public class StoreNotificationEvent { - private static final EELFLogger logger = EELFManager.getInstance().getLogger(StoreNotificationEvent.class); + private static final Logger LOGGER = LoggerFactory.getLogger(StoreNotificationEvent.class); private MessageProducer messageProducer; private String fromAppId = ""; @@ -81,7 +81,7 @@ public class StoreNotificationEvent { * @throws AAIException * the AAI exception */ - public String storeEvent(NotificationEvent.EventHeader eh, Object obj) throws AAIException { + public String storeEventAndSendToJms(NotificationEvent.EventHeader eh, Object obj) throws AAIException { if (obj == null) { throw new AAIException("AAI_7350"); @@ -235,7 +235,94 @@ public class StoreNotificationEvent { } } - public String storeEvent(Loader loader, Introspector eventHeader, Introspector obj) throws AAIException { + public String storeEventOnly(Loader loader, Introspector eventHeader, Introspector obj) throws AAIException { + if (obj == null) { + throw new AAIException("AAI_7350"); + } + + try { + final Introspector notificationEvent = loader.introspectorFromName("notification-event"); + + if (eventHeader.getValue("id") == null) { + eventHeader.setValue("id", genDate2() + "-" + UUID.randomUUID().toString()); + } + + if (eventHeader.getValue("timestamp") == null) { + eventHeader.setValue("timestamp", genDate()); + } + + if (eventHeader.getValue("entity-link") == null) { + eventHeader.setValue("entity-link", "UNK"); + } + + if (eventHeader.getValue("action") == null) { + eventHeader.setValue("action", "UNK"); + } + + if (eventHeader.getValue("event-type") == null) { + eventHeader.setValue("event-type", AAIConfig.get("aai.notificationEvent.default.eventType", "UNK")); + } + + if (eventHeader.getValue("domain") == null) { + eventHeader.setValue("domain", AAIConfig.get("aai.notificationEvent.default.domain", "UNK")); + } + + if (eventHeader.getValue("source-name") == null) { + eventHeader.setValue("source-name", AAIConfig.get("aai.notificationEvent.default.sourceName", "UNK")); + } + + if (eventHeader.getValue("sequence-number") == null) { + eventHeader.setValue("sequence-number", + AAIConfig.get("aai.notificationEvent.default.sequenceNumber", "UNK")); + } + + if (eventHeader.getValue("severity") == null) { + eventHeader.setValue("severity", AAIConfig.get("aai.notificationEvent.default.severity", "UNK")); + } + + if (eventHeader.getValue("version") == null) { + eventHeader.setValue("version", AAIConfig.get("aai.notificationEvent.default.version", "UNK")); + } + + if (notificationEvent.getValue("cambria-partition") == null) { + notificationEvent.setValue("cambria-partition", + AAIConfig.get("aai.notificationEvent.default.partition", AAIConstants.UEB_PUB_PARTITION_AAI)); + } + + notificationEvent.setValue("event-header", eventHeader.getUnderlyingObject()); + notificationEvent.setValue("entity", obj.getUnderlyingObject()); + + String entityJson = notificationEvent.marshal(false); + JSONObject entityJsonObject = new JSONObject(entityJson); + + JSONObject entityJsonObjectUpdated = new JSONObject(); + + JSONObject entityHeader = entityJsonObject.getJSONObject("event-header"); + String cambriaPartition = entityJsonObject.getString("cambria.partition"); + + entityJsonObject.remove("event-header"); + entityJsonObject.remove("cambria.partition"); + + entityJsonObjectUpdated.put("event-header", entityHeader); + entityJsonObjectUpdated.put("cambria.partition", cambriaPartition); + + Iterator<String> iter = entityJsonObject.keys(); + JSONObject entity = new JSONObject(); + if (iter.hasNext()) { + entity = entityJsonObject.getJSONObject(iter.next()); + } + + entityJsonObjectUpdated.put("entity", entity); + + return entityJsonObjectUpdated.toString(); + } catch (JSONException e) { + throw new AAIException("AAI_7350", e); + } catch (AAIUnknownObjectException e) { + throw new AAIException("AAI_7350", e); + } + } + + public String storeEventAndSendToJms(Loader loader, Introspector eventHeader, Introspector obj) throws AAIException { if (obj == null) { throw new AAIException("AAI_7350"); } diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/DeltaAction.java b/aai-core/src/main/java/org/onap/aai/util/delta/DeltaAction.java new file mode 100644 index 00000000..1b1e49ae --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/DeltaAction.java @@ -0,0 +1,30 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. 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.util.delta; + +public enum DeltaAction { + CREATE, + UPDATE, + DELETE, + CREATE_REL, + DELETE_REL, + STATIC +} diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/DeltaEvents.java b/aai-core/src/main/java/org/onap/aai/util/delta/DeltaEvents.java new file mode 100644 index 00000000..f240f4e8 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/DeltaEvents.java @@ -0,0 +1,136 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. 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.util.delta; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.gson.*; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dmaap.AAIDmaapEventJMSProducer; +import org.onap.aai.dmaap.MessageProducer; +import org.onap.aai.util.AAIConfig; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +public class DeltaEvents { + + private static final Logger LOGGER = LoggerFactory.getLogger(DeltaEvents.class); + + private static final Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) + .create(); + + private String transId; + private String sourceName; + private String eventVersion = "v1"; + private String schemaVersion; + private Map<String, ObjectDelta> objectDeltas; + + private MessageProducer messageProducer; + + public DeltaEvents(String transId, String sourceName, String schemaVersion, Map<String, ObjectDelta> objectDeltas) { + this(transId, sourceName, schemaVersion, objectDeltas, new AAIDmaapEventJMSProducer()); + } + + public DeltaEvents(String transId, String sourceName, String schemaVersion, Map<String, ObjectDelta> objectDeltas, MessageProducer messageProducer) { + this.transId = transId; + this.sourceName = sourceName; + this.schemaVersion = schemaVersion; + this.objectDeltas = objectDeltas; + this.messageProducer = messageProducer; + } + + public boolean triggerEvents() { + if (objectDeltas.isEmpty()) { + return false; + } + + JsonObject finalJson = new JsonObject(); + finalJson.addProperty("event-topic", "DELTA"); + finalJson.addProperty("transId", transId); + finalJson.addProperty("fromAppId", sourceName); + finalJson.addProperty("fullId", ""); + finalJson.add("aaiEventPayload", buildEvent()); + + this.messageProducer.sendMessageToDefaultDestination(finalJson.toString()); + return true; + } + + private JsonObject buildEvent() { + JsonObject event = new JsonObject(); + event.addProperty("cambria.partition", this.getPartition()); + event.add("event-header", getHeader()); + event.add("entities", gson.toJsonTree(objectDeltas.values())); + return event; + } + + private String getPartition() { + return "DELTA"; + } + + private JsonObject getHeader() { + ObjectDelta first = objectDeltas.values().iterator().next(); + JsonObject header = new JsonObject(); + header.addProperty("id", this.transId); + header.addProperty("timestamp", this.getTimeStamp(first.getTimestamp())); + header.addProperty("source-name", this.sourceName); + header.addProperty("domain", this.getDomain()); + header.addProperty("event-type", this.getEventType()); + header.addProperty("event-version", this.eventVersion); + header.addProperty("schema-version", this.schemaVersion); + header.addProperty("action", first.getAction().toString()); + header.addProperty("entity-type", this.getEntityType(first)); + header.addProperty("entity-link", first.getUri()); + header.addProperty("entity-uuid", this.getUUID(first)); + + return header; + } + + private String getUUID(ObjectDelta objectDelta) { + return (String) objectDelta.getPropertyDeltas().get(AAIProperties.AAI_UUID).getValue(); + } + + private String getEntityType(ObjectDelta objectDelta) { + return (String) objectDelta.getPropertyDeltas().get(AAIProperties.NODE_TYPE).getValue(); + } + + private String getEventType() { + return "DELTA"; + } + + private String getDomain() { + return AAIConfig.get("aai.notificationEvent.default.domain", "UNK"); + } + + /** + * Given Long timestamp convert to format YYYYMMdd-HH:mm:ss:SSS + * @param timestamp milliseconds since epoc + * @return long timestamp in format YYYYMMdd-HH:mm:ss:SSS + */ + private String getTimeStamp(long timestamp) { + //SimpleDateFormat is not thread safe new instance needed + DateFormat df = new SimpleDateFormat("YYYYMMdd-HH:mm:ss:SSS"); + return df.format(new Date(timestamp)); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/ObjectDelta.java b/aai-core/src/main/java/org/onap/aai/util/delta/ObjectDelta.java new file mode 100644 index 00000000..c560dbf6 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/ObjectDelta.java @@ -0,0 +1,126 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. 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.util.delta; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ObjectDelta { + + @SerializedName("uri") + private String uri; + + @SerializedName("action") + private DeltaAction action; + + @SerializedName("source-of-truth") + private String sourceOfTruth; + + @SerializedName("timestamp") + private long timestamp; + + @SerializedName("property-deltas") + private Map<String, PropertyDelta> propertyDeltas = new HashMap<>(); + + @SerializedName("relationship-deltas") + private List<RelationshipDelta> relationshipDeltas = new ArrayList<>(); + + public ObjectDelta(String uri, DeltaAction action, String sourceOfTruth, long timestamp) { + this.uri = uri; + this.action = action; + this.sourceOfTruth = sourceOfTruth; + this.timestamp = timestamp; + } + + public void addPropertyDelta(String prop, PropertyDelta propertyDelta) { + propertyDeltas.put(prop, propertyDelta); + } + + public void addRelationshipDelta(RelationshipDelta relationshipDelta) { + relationshipDeltas.add(relationshipDelta); + } + + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public DeltaAction getAction() { + return action; + } + + public void setAction(DeltaAction action) { + this.action = action; + } + + public String getSourceOfTruth() { + return sourceOfTruth; + } + + public void setSourceOfTruth(String sourceOfTruth) { + this.sourceOfTruth = sourceOfTruth; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public void setPropertyDeltas(Map<String, PropertyDelta> propertyDeltas) { + this.propertyDeltas = propertyDeltas; + } + + public void setRelationshipDeltas(List<RelationshipDelta> relationshipDeltas) { + this.relationshipDeltas = relationshipDeltas; + } + + public Map<String, PropertyDelta> getPropertyDeltas() { + return propertyDeltas; + } + + public List<RelationshipDelta> getRelationshipDeltas() { + return relationshipDeltas; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("uri", uri) + .append("action", action) + .append("sourceOfTruth", sourceOfTruth) + .append("timestamp", timestamp) + .append("propertyDeltas", propertyDeltas) + .append("relationshipDeltas", relationshipDeltas) + .toString(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDelta.java b/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDelta.java new file mode 100644 index 00000000..46a0072c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDelta.java @@ -0,0 +1,79 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. 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.util.delta; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class PropertyDelta { + + @SerializedName("action") + protected DeltaAction action; + + @SerializedName("value") + protected Object value; + + @SerializedName("old-value") + private Object oldValue; + + + public PropertyDelta(DeltaAction action, Object value) { + this.action = action; + this.value = value; + } + + public PropertyDelta(DeltaAction action, Object value, Object oldValue) { + this(action, value); + this.oldValue = oldValue; + } + + public DeltaAction getAction() { + return action; + } + + public void setAction(DeltaAction action) { + this.action = action; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public Object getOldValue() { + return oldValue; + } + + public void setOldValue(Object oldValue) { + this.oldValue = oldValue; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("action", action) + .append("value", value) + .append("oldValue", oldValue) + .toString(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDeltaFactory.java b/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDeltaFactory.java new file mode 100644 index 00000000..366d6886 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/PropertyDeltaFactory.java @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. 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.util.delta; + +public class PropertyDeltaFactory { + + public static PropertyDelta getDelta(DeltaAction action, Object value, Object oldValue) { + //TODO handle if action is not UPDATE + return new PropertyDelta(action, value, oldValue); + } + + public static PropertyDelta getDelta(DeltaAction action, Object value) { + //TODO handle if action is UPDATE + return new PropertyDelta(action, value); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/util/delta/RelationshipDelta.java b/aai-core/src/main/java/org/onap/aai/util/delta/RelationshipDelta.java new file mode 100644 index 00000000..1dcad1b3 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/util/delta/RelationshipDelta.java @@ -0,0 +1,133 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.util.delta; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.HashMap; +import java.util.Map; + +public class RelationshipDelta { + + @SerializedName("action") + private DeltaAction action; + + @SerializedName("in-v-uuid") + private String inVUuid; + + @SerializedName("out-v-uuid") + private String outVUuid; + + @SerializedName("in-v-uri") + private String inVUri; + + @SerializedName("out-v-uri") + private String outVUri; + + @SerializedName("label") + private String label; + + @SerializedName("props") + private Map<String, Object> props = new HashMap<>(); + + public RelationshipDelta(DeltaAction action, String inVUUID, String outVUUID, String inVUri, String outVUri, String label) { + this.action = action; + this.inVUuid = inVUUID; + this.outVUuid = outVUUID; + this.inVUri = inVUri; + this.outVUri = outVUri; + this.label = label; + } + + public DeltaAction getAction() { + return action; + } + + public void setAction(DeltaAction action) { + this.action = action; + } + + public String getInVUuid() { + return inVUuid; + } + + public void setInVUuid(String inVUuid) { + this.inVUuid = inVUuid; + } + + public String getOutVUuid() { + return outVUuid; + } + + public void setOutVUuid(String outVUuid) { + this.outVUuid = outVUuid; + } + + public String getInVUri() { + return inVUri; + } + + public void setInVUri(String inVUri) { + this.inVUri = inVUri; + } + + public String getOutVUri() { + return outVUri; + } + + public void setOutVUri(String outVUri) { + this.outVUri = outVUri; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Map<String, Object> getProps() { + return props; + } + + public void setProps(Map<String, Object> props) { + this.props = props; + } + + public void addProp(String key, String value) { + this.props.put(key, value); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("action", action) + .append("inVUuid", inVUuid) + .append("outVUuid", outVUuid) + .append("inVUri", inVUri) + .append("outVUri", outVUri) + .append("label", label) + .append("props", props) + .toString(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java b/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java index d552f231..c5629254 100644 --- a/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java +++ b/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java @@ -20,8 +20,8 @@ package org.onap.aai.web; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.util.Base64; @@ -37,7 +37,7 @@ import org.springframework.web.client.RestTemplate; @Configuration public class EventClientPublisher { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(EventClientPublisher.class); + private static final Logger LOGGER = LoggerFactory.getLogger(EventClientPublisher.class); @Value("${dmaap.ribbon.listOfServers:}") private String hosts; diff --git a/aai-core/src/main/resources/specs/transform-related-to-node.json b/aai-core/src/main/resources/specs/transform-related-to-node.json new file mode 100644 index 00000000..c7e018a8 --- /dev/null +++ b/aai-core/src/main/resources/specs/transform-related-to-node.json @@ -0,0 +1,21 @@ +[ + { + "operation": "shift", + "spec": { + "results": "result", + "*": "&" + } + }, + { + "operation": "shift", + "spec": { + "result": { + "*": { + "related-to": "result.[&(1,0)].&(0,0).node", + "*": "result.[&(1,0)].&(0,0)" + } + }, + "*": "&" + } + } +]
\ No newline at end of file diff --git a/aai-core/src/main/resources/swagger.html.ftl b/aai-core/src/main/resources/swagger.html.ftl new file mode 100644 index 00000000..1a2827f8 --- /dev/null +++ b/aai-core/src/main/resources/swagger.html.ftl @@ -0,0 +1,241 @@ +<#-- + + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2017-18 AT&T Intellectual Property. All rights reserved. + Copyright © 2018 Huawei Technologies (Australia) Pty Ltd. 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========================================================= + + ECOMP is a trademark and service mark of AT&T Intellectual Property. + +--> +<!DOCTYPE html> +<html> +<head> +<style>/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{p EdgeRules.ftl + ont-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover,a.text-primary:focus{color:#286090}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover,a.bg-primary:focus{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:800px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:900px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#337ab7}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#337ab7;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#fff;background-color:#337ab7;border-color:#337ab7;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.panel-definition{border-color:#a2a2a2}.panel-definition>.panel-heading{color:#000;background-color:#eee;border-color:#a2a2a2}.panel-definition>.panel-heading+.panel-collapse>.panel-body{border-top-color:#a2a2a2}.panel-definition>.panel-heading .badge{color:#eee;background-color:#000}.panel-definition>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#a2a2a2}.json-schema-description:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Description";padding-bottom:.5em;display:block}.json-schema-description:not(:last-child){padding-bottom:1.5em}.json-schema-properties:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Properties";padding-bottom:.5em;display:block}.json-schema-properties:not(:last-child){padding-bottom:1.5em}.json-schema-properties dd:not(:last-child){padding-bottom:1em}.json-schema-properties dl{margin:0}.json-schema-example:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Example";padding-bottom:.5em;display:block}.json-schema-example:not(:last-child){padding-bottom:1.5em}.json-schema-array-items:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Items";padding-bottom:.5em;display:block}.json-schema-array-items:not(:last-child){padding-bottom:1.5em}.json-schema-allOf-inherited:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Inherited";padding-bottom:.5em;display:block}.json-schema-allOf-inherited:not(:last-child){padding-bottom:1.5em}.json-schema-allOf-inherited ul{padding-left:0;list-style:none}.json-schema-anyOf>dl{border-left:2px solid #a2a2a2;padding-left:1em}.json-schema-anyOf>dl dt:not(:first-child):before{content:"or "}.json-schema-anyOf>dl dt:first-child:before{content:"either "}.json-schema-additionalProperties:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Additional properties";padding-bottom:.5em;display:block}.json-schema-additionalProperties:not(:last-child){padding-bottom:1.5em}.json-inner-schema .json-schema-properties,.json-inner-schema .json-schema-array-items,.json-inner-schema .json-schema-description,.json-inner-schema .json-schema-example{padding-left:1em;margin-top:.5em;padding-bottom:.5em;border-left:2px solid #a2a2a2}.json-property-discriminator:before{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em;background-color:#777;content:"discriminator"}a.json-property-discriminator:before:hover,a.json-property-discriminator:before:focus{color:#fff;text-decoration:none;cursor:pointer}.json-property-discriminator:before:empty{display:none}.btn .json-property-discriminator:before{position:relative;top:-1px}.json-property-discriminator:before[href]:hover,.json-property-discriminator:before[href]:focus{background-color:#5e5e5e}.json-property-required:before{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em;background-color:#777;content:"required"}a.json-property-required:before:hover,a.json-property-required:before:focus{color:#fff;text-decoration:none;cursor:pointer}.json-property-required:before:empty{display:none}.btn .json-property-required:before{position:relative;top:-1px}.json-property-required:before[href]:hover,.json-property-required:before[href]:focus{background-color:#5e5e5e}.json-property-read-only:before{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em;background-color:#777;content:"read only"}a.json-property-read-only:before:hover,a.json-property-read-only:before:focus{color:#fff;text-decoration:none;cursor:pointer}.json-property-read-only:before:empty{display:none}.btn .json-property-read-only:before{position:relative;top:-1px}.json-property-read-only:before[href]:hover,.json-property-read-only:before[href]:focus{background-color:#5e5e5e}.json-property-type{font-style:italic;font-weight:100}.json-property-format{font-size:smaller}.json-property-enum{font-weight:lighter;font-size:small}.json-property-default-value{font-weight:lighter;font-size:small}.json-property-default-value:before{content:'(default: "'}.json-property-default-value:after{content:'")'}.json-property-enum-item{font-weight:lighter;font-size:small}.json-property-enum-item:before,.json-property-enum-item:after{content:"\""}.json-schema--reference{font-size:90%}.table.swagger--summary>tbody>tr>td.swagger--summary-path{vertical-align:middle}.table.swagger--summary>tbody>tr>td p{margin:0}.swagger--panel-operation-post{border-color:#78cc94}.swagger--panel-operation-post>.panel-heading{color:#333;background-color:#e7f6ec;border-color:#78cc94}.swagger--panel-operation-post>.panel-heading+.panel-collapse>.panel-body{border-top-color:#78cc94}.swagger--panel-operation-post>.panel-heading .badge{color:#e7f6ec;background-color:#333}.swagger--panel-operation-post>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#78cc94}.swagger--panel-operation-post .operation-name{font-weight:bold}.swagger--panel-operation-post .operation-summary{float:right !important}.swagger--panel-operation-get{border-color:#74a8d1}.swagger--panel-operation-get>.panel-heading{color:#333;background-color:#e7f0f7;border-color:#74a8d1}.swagger--panel-operation-get>.panel-heading+.panel-collapse>.panel-body{border-top-color:#74a8d1}.swagger--panel-operation-get>.panel-heading .badge{color:#e7f0f7;background-color:#333}.swagger--panel-operation-get>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#74a8d1}.swagger--panel-operation-get .operation-name{font-weight:bold}.swagger--panel-operation-get .operation-summary{float:right !important}.swagger--panel-operation-put{border-color:#d8ab71}.swagger--panel-operation-put>.panel-heading{color:#333;background-color:#f9f2e9;border-color:#d8ab71}.swagger--panel-operation-put>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d8ab71}.swagger--panel-operation-put>.panel-heading .badge{color:#f9f2e9;background-color:#333}.swagger--panel-operation-put>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d8ab71}.swagger--panel-operation-put .operation-name{font-weight:bold}.swagger--panel-operation-put .operation-summary{float:right !important}.swagger--panel-operation-patch{border-color:#ed7c59}.swagger--panel-operation-patch>.panel-heading{color:#333;background-color:#FCE9E3;border-color:#ed7c59}.swagger--panel-operation-patch>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ed7c59}.swagger--panel-operation-patch>.panel-heading .badge{color:#FCE9E3;background-color:#333}.swagger--panel-operation-patch>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ed7c59}.swagger--panel-operation-patch .operation-name{font-weight:bold}.swagger--panel-operation-patch .operation-summary{float:right !important}.swagger--panel-operation-options{border-color:#74a8d1}.swagger--panel-operation-options>.panel-heading{color:#333;background-color:#e7f0f7;border-color:#74a8d1}.swagger--panel-operation-options>.panel-heading+.panel-collapse>.panel-body{border-top-color:#74a8d1}.swagger--panel-operation-options>.panel-heading .badge{color:#e7f0f7;background-color:#333}.swagger--panel-operation-options>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#74a8d1}.swagger--panel-operation-options .operation-name{font-weight:bold}.swagger--panel-operation-options .operation-summary{float:right !important}.swagger--panel-operation-delete{border-color:#c77d7d}.swagger--panel-operation-delete>.panel-heading{color:#333;background-color:#f5e8e8;border-color:#c77d7d}.swagger--panel-operation-delete>.panel-heading+.panel-collapse>.panel-body{border-top-color:#c77d7d}.swagger--panel-operation-delete>.panel-heading .badge{color:#f5e8e8;background-color:#333}.swagger--panel-operation-delete>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#c77d7d}.swagger--panel-operation-delete .operation-name{font-weight:bold}.swagger--panel-operation-delete .operation-summary{float:right !important}.swagger--panel-operation-head{border-color:#f3ff34}.swagger--panel-operation-head>.panel-heading{color:#333;background-color:#fcffcd;border-color:#f3ff34}.swagger--panel-operation-head>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f3ff34}.swagger--panel-operation-head>.panel-heading .badge{color:#fcffcd;background-color:#333}.swagger--panel-operation-head>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f3ff34}.swagger--panel-operation-head .operation-name{font-weight:bold}.swagger--panel-operation-head .operation-summary{float:right !important}.sw-operation-description:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Description";padding-bottom:.5em;display:block}.sw-operation-description:not(:last-child){padding-bottom:1.5em}.sw-request-params:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Request parameters";padding-bottom:.5em;display:block}.sw-request-params:not(:last-child){padding-bottom:1.5em}.sw-request-body:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Request body";padding-bottom:.5em;display:block}.sw-request-body:not(:last-child){padding-bottom:1.5em}.sw-responses:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Responses";padding-bottom:.5em;display:block}.sw-responses:not(:last-child){padding-bottom:1.5em}.swagger--global:before{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em;background-color:#777;content:'global'}a.swagger--global:before:hover,a.swagger--global:before:focus{color:#fff;text-decoration:none;cursor:pointer}.swagger--global:before:empty{display:none}.btn .swagger--global:before{position:relative;top:-1px}.swagger--global:before[href]:hover,.swagger--global:before[href]:focus{background-color:#5e5e5e}table.table th.sw-param-key{width:auto}table.table th.sw-param-key:before{content:"Key"}table.table th.sw-param-name{width:auto}table.table th.sw-param-name:before{content:"Name"}table.table th.sw-param-description{width:auto}table.table th.sw-param-description:before{content:"Description"}table.table th.sw-param-data-type{width:auto}table.table th.sw-param-data-type:before{content:"Data type"}table.table th.sw-param-type{width:auto}table.table th.sw-param-type:before{content:"Type"}table.table th.sw-request-security-schema{width:auto}table.table th.sw-request-security-schema:before{content:"Schema"}table.table th.sw-request-security-scopes{width:auto}table.table th.sw-request-security-scopes:before{content:"Scopes"}table.table th.sw-response-header-name{width:auto}table.table th.sw-response-header-name:before{content:"Header"}table.table th.sw-response-header-description{width:auto}table.table th.sw-response-header-description:before{content:"Description"}table.table th.sw-response-header-data-type{width:auto}table.table th.sw-response-header-data-type:before{content:"Data type"}.sw-response-name-value{font-weight:bold}.sw-response-description-text{padding-bottom:.5em}code.highlight{padding:0}.panel-security-definition{border-color:#a2a2a2}.panel-security-definition>.panel-heading{color:#000;background-color:#eee;border-color:#a2a2a2}.panel-security-definition>.panel-heading+.panel-collapse>.panel-body{border-top-color:#a2a2a2}.panel-security-definition>.panel-heading .badge{color:#eee;background-color:#000}.panel-security-definition>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#a2a2a2}.sw-request-security:before{font-weight:bold;color:#555;text-transform:uppercase;content:"Security";padding-bottom:.5em;display:block}.sw-request-security:not(:last-child){padding-bottom:1.5em}.sw-security-definition-basic:before{color:#555;font-size:smaller;content:"(HTTP Basic Authentication)"}span.sw-default-value-header{font-weight:bold}.sw-info-version{font-weight:bold}.sw-info-version span{font-family:monospace;font-weight:normal;font-size:1.1em}</style> + + <meta charset="UTF-8"> + <title>Active and Available Inventory REST API.</title> +</head> +<body> +<div class="container"> + <h1>Active and Available Inventory REST API.</h1> + <p class="sw-info-version">Version: <span>${version}</span></p> + <p><p>${description}</p> + </p> + + <div id="sw-schemes" class="sw-default-value"> + <span class="sw-default-value-header">Schemes:</span> + https + </div> + + <h2 id="swagger--summary-tags">Summary</h2> + <ol> + <#list aaiApis?keys as key> + <li><a href="#tag-${key}">Tag: ${key}</a> + </#list> + <li><a href="#Paths">Paths</a> + <li><a href="#SchemaDefinitions">Schema definitions</a> + </ol> + + <#list aaiApis?keys as key> + <h3 id="tag-${key}" class="swagger-summary-tag">Tag: ${key}</h3> + <table class="table table-bordered table-condensed swagger--summary"> + <thead><tr> + <th>Operation</th><th>Description</th> + </tr></thead> + <tbody> + <#list aaiApis[key] as api> + <#list api.getHttpMethods() as httpVerb> + <tr><td><a href="#operation-${api.getOperation()}${httpVerb.getType()}"> + ${httpVerb.getType()?upper_case} ${api.getPath()}</a></td> + <td><p>${httpVerb.getSummary()}</p></td> + </tr> + </#list> + </#list> + </tbody> + </table> + </#list> + + <h2 id="Paths">Paths</h2> + <#list sortedAaiApis?keys as key> + <#list sortedAaiApis[key] as api> + <#list api.getHttpMethods() as httpVerb> + <span id="path-${api.getOperation()}"></span> + <div id="operation-${api.getOperation()}${httpVerb.getType()}" class="swagger--panel-operation-${httpVerb.getType()} panel"> + <div class="panel-heading"> + <div class="operation-summary">${httpVerb.getSummary()}</div> + <h3 class="panel-title"><span class="operation-name">${httpVerb.getType()?upper_case}</span> + <strong>${api.getPath()}</strong></h3> + Tags: <a href="#tag-${api.getTag()}">${api.getTag()}</a> + </div> + <div class="panel-body"> + <section class="sw-operation-description"> + <p>${httpVerb.getSummary()}</p> + </section> + <#if httpVerb.isConsumerEnabled()> + <section class="sw-request-body"> + <p><span class="label label-default">application/json</span> <span class="label label-default">application/xml</span> </p> + <#if httpVerb.isBodyParametersEnabled()> + <div class="row"> + <div class="col-md-6"> + <p><p>${httpVerb.getBodyParameters()["description"]}</p></p> + </div> + <div class="col-md-6 sw-request-model"> + <div class="panel panel-definition"> + <div class="panel-body"> + <a class="json-schema-ref" href="${httpVerb.getSchemaLink()}">${httpVerb.getSchemaType()}</a> + </div></div></div></div> + </#if> + </section> + </#if> + <#if httpVerb.isParametersEnabled()> + <section class="sw-request-params"> + <table class="table"> + <thead><tr> + <th class="sw-param-name"></th> + <th class="sw-param-description"></th> + <th class="sw-param-type"></th> + <th class="sw-param-data-type"></th> + <th class="sw-param-annotation"></th> + </tr></thead> + <tbody> + <#list httpVerb.getParameters() as param> + <tr><td>${param["name"]}</td> + <td> + <#if param['description']??> + <p>${param["description"]}</p> + </#if> + </td> + <td>${param["in"]}</td> + <td> + <#if param['type']??> + <span class="json-property-type">${param["type"]}</span> + <span class="json-property-range" title="Value limits"></span> + </#if> + </td> + <td> + <#if param['required']> + <span class="json-property-required"></span> + </#if> + </td> + </tr> + </#list> + </tbody> + </table></section> + </#if> + + <section class="sw-responses"> + <p><span class="label label-default">application/json</span> <span class="label label-default">application/xml</span> </p> + <dl> + <#list httpVerb.getResponses() as response> + <dt class="sw-response-${response.getResponseCode()}"> + ${response.getResponseCode()} OK + </dt> + <dd class="sw-response-${response.getResponseCode()}"> + <div class="row"><div class="col-md-12"> + <p>successful operation</p> + </div></div> + <div class="row"> + <#if httpVerb.isHasReturnSchema()> + <div class="col-md-6 sw-response-model"> + <div class="panel panel-definition"> + <div class="panel-body"> + <a class="json-schema-ref" href="${httpVerb.getReturnSchemaLink()}">${httpVerb.getReturnSchemaObject()}</a> + </div></div></div> + </#if> + </div> + </dd> + </#list> + <dt class="sw-response-default">default</dt> + <dd class="sw-response-default"> + <div class="row"><div class="col-md-12"> + <p>Response codes found in <a href="${wikiLink}">response codes</a>.</p> + </div></div> + <div class="row"><div class="col-md-6 sw-response-model"></div></div> + </dd> + </dl> + </section> + </div> + </div> + + </#list> + </#list> + </#list> + + <h2 id="SchemaDefinitions">Schema definitions</h2> + <#list definitions as definition> + <div id="definition-${definition.getDefinitionName()}" class="panel panel-definition"> + <div class="panel-heading"><h3 class="panel-title"> + <a name="/definitions/${definition.getDefinitionName()}"></a>${definition.getDefinitionName()}: + <span class="json-property-type"><span class="json-property-type">object</span> + <span class="json-property-range" title="Value limits"></span></span> + </h3></div> + <div class="panel-body"> + <#if definition.isHasDescription()> + <section class="json-schema-description"> + ${definition.getDefinitionDescription()} + </section> + </#if> + <section class="json-schema-properties"><dl> + <#list definition.getRegularPropertyList() as definitionProperty> + <dt data-property-name="${definitionProperty.getPropertyName()}"> + <span class="json-property-name">${definitionProperty.getPropertyName()}:</span> + <#if definitionProperty.isHasType()> + <span class="json-property-type">${definitionProperty.getPropertyType()}</span> + </#if> + <span class="json-property-range" title="Value limits"></span> + <#if definitionProperty.isRequired()> + <span class="json-property-required"></span> + </#if> + </dt> + <dd> + <#if definitionProperty.isHasPropertyDescription()> + <p>${definitionProperty.getPropertyDescription()}</p> + </#if> + <div class="json-inner-schema"></div> + </dd> + </#list> + <#list definition.getSchemaPropertyList() as definitionProperty> + <dt data-property-name="${definitionProperty.getPropertyName()}"> + <span class="json-property-name">${definitionProperty.getPropertyName()}:</span> + <#if definitionProperty.isHasType()> + <span class="json-property-type">${definitionProperty.getPropertyType()}</span> + </#if> + <span class="json-property-range" title="Value limits"></span> + <#if definitionProperty.isRequired()> + <span class="json-property-required"></span> + </#if> + </dt> + <dd><div class="json-inner-schema"><section class="json-schema-array-items"> + <span class="json-property-type"> + <a class="json-schema-ref" href="${definitionProperty.getPropertyReference()}">${definitionProperty.getPropertyReferenceObjectName()}</a></span> + <span class="json-property-range" title="Value limits"></span> + <div class="json-inner-schema"></div> + </section></div></dd> + </#list> + </dl></section> + </div></div> + + </#list> +</div> +</body> +</html> |