diff options
author | Venkata Harish K Kajur <vk250x@att.com> | 2018-01-30 13:52:34 -0500 |
---|---|---|
committer | Venkata Harish K Kajur <vk250x@att.com> | 2018-02-28 11:36:08 -0500 |
commit | 4f13d6cc64eed0ef75a31ec5f490853267447ab1 (patch) | |
tree | cb7066e12fc39038cc99b74bbeba879c9eb6ca8c /aai-resources/src/main/java | |
parent | 73539cae6d24e352b84a6411e523da734fbc19e2 (diff) |
Turn ajsc 2 to using ajsc 6 spring boot
Issue-ID: AAI-800
Change-Id: Id174ec5088ddea57f18e605d004c417bee8fbf33
Signed-off-by: Venkata Harish K Kajur <vk250x@att.com>
Diffstat (limited to 'aai-resources/src/main/java')
34 files changed, 3328 insertions, 1376 deletions
diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertiesListener.java b/aai-resources/src/main/java/org/onap/aai/Profiles.java index 38ea8c6..9f00466 100644 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertiesListener.java +++ b/aai-resources/src/main/java/org/onap/aai/Profiles.java @@ -19,20 +19,14 @@ * * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ -package org.onap.aai.ajsc_aai.filemonitor; +package org.onap.aai; -import java.io.File; +public final class Profiles { -//import com.att.ssf.filemonitor.FileChangedListener; + public static final String DMAAP = "dmaap"; -//public class ServicePropertiesListener implements FileChangedListener { + public static final String ONE_WAY_SSL = "one-way-ssl"; + public static final String TWO_WAY_SSL = "two-way-ssl"; - /** - * {@inheritDoc} - */ - //@Override - //public void update(File file) throws Exception - //{ - //ServicePropertiesMap.refresh(file); - //} -//} + private Profiles(){} +} diff --git a/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java b/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java new file mode 100644 index 0000000..c435053 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java @@ -0,0 +1,159 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.aai.config.PropertyPasswordConfiguration; +import org.onap.aai.dbmap.AAIGraph; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.ModelInjestor; +import org.onap.aai.logging.LoggingContext; +import org.onap.aai.migration.MigrationControllerInternal; +import org.onap.aai.util.AAIConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.env.Environment; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.UUID; + +@SpringBootApplication +// Component Scan provides a way to look for spring beans +// It only searches beans in the following packages +// Any method annotated with @Bean annotation or any class +// with @Component, @Configuration, @Service will be picked up +@ComponentScan(basePackages = { + "org.onap.aai.config", + "org.onap.aai.web", + "org.onap.aai.tasks", + "org.onap.aai.rest" +}) +@EnableAutoConfiguration(exclude = { + DataSourceAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + HibernateJpaAutoConfiguration.class +}) +@RibbonClient(name = "dmaap", configuration = AAIRibbonConfiguration.class) +public class ResourcesApp { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(ResourcesApp.class.getName()); + + private static final String APP_NAME = "aai-resources"; + + @Autowired + private Environment env; + + @PostConstruct + private void init() throws AAIException { + System.setProperty("org.onap.aai.serverStarted", "false"); + setDefaultProps(); + + LoggingContext.save(); + LoggingContext.component("init"); + LoggingContext.partnerName("NA"); + LoggingContext.targetEntity(APP_NAME); + LoggingContext.requestId(UUID.randomUUID().toString()); + LoggingContext.serviceName(APP_NAME); + LoggingContext.targetServiceName("contextInitialized"); + + logger.info("AAI Server initialization started..."); + + // Setting this property to allow for encoded slash (/) in the path parameter + // This is only needed for tomcat keeping this as temporary + System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); + + logger.info("Starting AAIGraph connections and the ModelInjestor"); + + if(env.acceptsProfiles(Profiles.TWO_WAY_SSL) && env.acceptsProfiles(Profiles.ONE_WAY_SSL)){ + logger.warn("You have seriously misconfigured your application"); + } + + AAIConfig.init(); + ModelInjestor.getInstance(); + AAIGraph.getInstance(); + } + + @PreDestroy + public void cleanup(){ + logger.info("Shutting down both realtime and cached connections"); + AAIGraph.getInstance().graphShutdown(); + } + + public static void main(String[] args) { + + setDefaultProps(); + SpringApplication app = new SpringApplication(ResourcesApp.class); + app.setRegisterShutdownHook(true); + app.addInitializers(new PropertyPasswordConfiguration()); + Environment env = app.run(args).getEnvironment(); + + logger.info( + "Application '{}' is running on {}!" , + env.getProperty("spring.application.name"), + env.getProperty("server.port") + ); + + if ("true".equals(AAIConfig.get("aai.run.migrations", "false"))) { + MigrationControllerInternal migrations = new MigrationControllerInternal(); + migrations.run(new String[]{"--commit"}); + } + + logger.info("Resources MicroService Started"); + logger.error("Resources MicroService Started"); + logger.debug("Resources MicroService Started"); + System.out.println("Resources Microservice Started"); + } + + public static void setDefaultProps(){ + + if (System.getProperty("file.separator") == null) { + System.setProperty("file.separator", "/"); + } + + String currentDirectory = System.getProperty("user.dir"); + + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + + if(currentDirectory.contains(APP_NAME)){ + if (System.getProperty("BUNDLECONFIG_DIR") == null) { + System.setProperty("BUNDLECONFIG_DIR", "src/main/resources"); + } + } else { + if (System.getProperty("BUNDLECONFIG_DIR") == null) { + System.setProperty("BUNDLECONFIG_DIR", "aai-resources/src/main/resources"); + } + } + + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/JaxrsErrorMessageLookupService.java b/aai-resources/src/main/java/org/onap/aai/ajsc_aai/JaxrsErrorMessageLookupService.java deleted file mode 100644 index f7b08d9..0000000 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/JaxrsErrorMessageLookupService.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.ajsc_aai; - -//import java.util.HashMap; -//import java.util.Map; - -//import javax.ws.rs.GET; -//import javax.ws.rs.HeaderParam; -//import javax.ws.rs.Path; -//import javax.ws.rs.PathParam; -//import javax.ws.rs.Produces; - -//import org.slf4j.Logger; -//import org.slf4j.LoggerFactory; -//import org.springframework.web.context.ContextLoader; -//import org.springframework.web.context.WebApplicationContext; - -//import ajsc.ErrorMessageLookupService; - -//@Path("/errormessage") -//public class JaxrsErrorMessageLookupService { - - //private final static Logger logger = LoggerFactory - //.getLogger(ErrorMessageLookupService.class); - - /** - * Gets the message. - * - * @param input the input - * @param errorCode the error code - * @param appId the app id - * @param operation the operation - * @param messageText the message text - * @param isRESTService the is REST service - * @param faultEntity the fault entity - * @param ConvID the conv ID - * @return the message - */ - //@GET - //@Path("/emls") - //@Produces("text/plain") - //public String getMessage(@PathParam("input") String input, - //@HeaderParam("errorCode") String errorCode, - //@HeaderParam("appId") String appId, - //@HeaderParam("operation") String operation, - //@HeaderParam("messageText") String messageText, - //@HeaderParam("isRESTService") String isRESTService, - //@HeaderParam("faultEntity") String faultEntity, - //@HeaderParam("ConvID") String ConvID) { - - //Map<String, String> headers = new HashMap<String, String>(); - //headers.put(errorCode, errorCode); - //headers.put(appId, appId); - //headers.put(operation, operation); - //headers.put(messageText, messageText); - //headers.put(isRESTService, isRESTService); - //headers.put(faultEntity, faultEntity); - //headers.put(ConvID, ConvID); - - //WebApplicationContext applicationContext = ContextLoader - //.getCurrentWebApplicationContext(); - - //ErrorMessageLookupService e = (ErrorMessageLookupService) applicationContext - //.getBean("errorMessageLookupService"); - - //String message = e.getExceptionDetails(appId, operation, errorCode, - //messageText,isRESTService, faultEntity, ConvID); - - //System.out.println("Error code = " + errorCode); - //System.out.println("appId = " + appId); - //System.out.println("operation = " + operation); - //System.out.println("messageText = " + messageText); - //System.out.println("isRESTService = " + isRESTService); - //System.out.println("faultEntity = " + faultEntity); - //System.out.println("ConvID = " + ConvID); - //return "The exception message is:\n " + message; - //} - -//} diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java b/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java deleted file mode 100644 index 7274c61..0000000 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.ajsc_aai.filemonitor; - -import java.io.File; -import java.io.FileInputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class ServicePropertiesMap -{ - private static HashMap<String, HashMap<String, String>> mapOfMaps = new HashMap<String, HashMap<String, String>>(); - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ServicePropertiesMap.class); - - /** - * Refresh. - * - * @param file the file - * @throws Exception the exception - */ - public static void refresh(File file) throws Exception - { - try - { - LOGGER.info("Loading properties - " + (file != null?file.getName():"")); - - //Store .json & .properties files into map of maps - String filePath = file.getPath(); - - if(filePath.lastIndexOf(".json")>0){ - - ObjectMapper om = new ObjectMapper(); - TypeReference<HashMap<String, String>> typeRef = new TypeReference<HashMap<String, String>>() {}; - HashMap<String, String> propMap = om.readValue(file, typeRef); - HashMap<String, String> lcasePropMap = new HashMap<String, String>(); - for (String key : propMap.keySet() ) - { - String lcaseKey = ifNullThenEmpty(key); - lcasePropMap.put(lcaseKey, propMap.get(key)); - } - - mapOfMaps.put(file.getName(), lcasePropMap); - - - }else if(filePath.lastIndexOf(".properties")>0){ - Properties prop = new Properties(); - FileInputStream fis = new FileInputStream(file); - prop.load(fis); - - @SuppressWarnings("unchecked") - HashMap<String, String> propMap = new HashMap<String, String>((Map)prop); - - mapOfMaps.put(file.getName(), propMap); - } - - LOGGER.info("File - " + file.getName() + " is loaded into the map and the corresponding system properties have been refreshed"); - } - catch (Exception e) - { - LOGGER.error("File " + (file != null?file.getName():"") + " cannot be loaded into the map ", e); - throw new Exception("Error reading map file " + (file != null?file.getName():""), e); - } - } - - /** - * Gets the property. - * - * @param fileName the file name - * @param propertyKey the property key - * @return the property - */ - public static String getProperty(String fileName, String propertyKey) - { - HashMap<String, String> propMap = mapOfMaps.get(fileName); - return propMap!=null?propMap.get(ifNullThenEmpty(propertyKey)):""; - } - - /** - * Gets the properties. - * - * @param fileName the file name - * @return the properties - */ - public static HashMap<String, String> getProperties(String fileName){ - return mapOfMaps.get(fileName); - } - - /** - * If null then empty. - * - * @param key the key - * @return the string - */ - private static String ifNullThenEmpty(String key) { - if (key == null) { - return ""; - } else { - return key; - } - } - -} diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertyService.java b/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertyService.java deleted file mode 100644 index 956d0e4..0000000 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/filemonitor/ServicePropertyService.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.ajsc_aai.filemonitor; - -//import java.io.File; -//import java.io.FileInputStream; -//import java.io.IOException; -//import java.lang.reflect.Method; -//import java.util.ArrayList; -//import java.util.List; -//import java.util.Properties; - -//import javax.annotation.PostConstruct; - -//import org.slf4j.Logger; -//import org.slf4j.LoggerFactory; - -//import com.att.ssf.filemonitor.FileChangedListener; -//import com.att.ssf.filemonitor.FileMonitor; - -//public class ServicePropertyService { - //private boolean loadOnStartup; - //private ServicePropertiesListener fileChangedListener; - //private ServicePropertiesMap filePropertiesMap; - //private String ssfFileMonitorPollingInterval; - //private String ssfFileMonitorThreadpoolSize; - //private List<File> fileList; - //private static final String FILE_CHANGE_LISTENER_LOC = System - //.getProperty("AJSC_CONF_HOME") + "/etc"; - //private static final String USER_CONFIG_FILE = "service-file-monitor.properties"; - //static final Logger logger = LoggerFactory - //.getLogger(ServicePropertyService.class); - - //// do not remove the postConstruct annotation, init method will not be - //// called after constructor - /** - * Inits the. - * - * @throws Exception the exception - */ - //@PostConstruct - //public void init() throws Exception { - - //try { - //getFileList(FILE_CHANGE_LISTENER_LOC); - - //for (File file : fileList) { - //try { - //FileChangedListener fileChangedListener = this.fileChangedListener; - //Object filePropertiesMap = this.filePropertiesMap; - //Method m = filePropertiesMap.getClass().getMethod( - //"refresh", File.class); - //m.invoke(filePropertiesMap, file); - //FileMonitor fm = FileMonitor.getInstance(); - //fm.addFileChangedListener(file, fileChangedListener, - //loadOnStartup); - //} catch (Exception ioe) { - //logger.error("Error in the file monitor block", ioe); - //} - //} - //} catch (Exception ex) { - //logger.error("Error creating property map ", ex); - //} - - //} - - /** - * Gets the file list. - * - * @param dirName the dir name - * @return the file list - * @throws IOException Signals that an I/O exception has occurred. - */ - //private void getFileList(String dirName) throws IOException { - //File directory = new File(dirName); - //FileInputStream fis = null; - - //if (fileList == null) - //fileList = new ArrayList<File>(); - - //// get all the files that are ".json" or ".properties", from a directory - //// & it's sub-directories - //File[] fList = directory.listFiles(); - - //for (File file : fList) { - //// read service property files from the configuration file - //if (file.isFile() && file.getPath().endsWith(USER_CONFIG_FILE)) { - //try { - //fis = new FileInputStream(file); - //Properties prop = new Properties(); - //prop.load(fis); - - //for (String filePath : prop.stringPropertyNames()) { - //fileList.add(new File(prop.getProperty(filePath))); - //} - //} catch (Exception ioe) { - //logger.error("Error reading the file stream ", ioe); - //} finally { - //fis.close(); - //} - //} else if (file.isDirectory()) { - //getFileList(file.getPath()); - //} - //} - - //} - - /** - * Sets the load on startup. - * - * @param loadOnStartup the new load on startup - */ - //public void setLoadOnStartup(boolean loadOnStartup) { - //this.loadOnStartup = loadOnStartup; - //} - - /** - * Sets the ssf file monitor polling interval. - * - * @param ssfFileMonitorPollingInterval the new ssf file monitor polling interval - */ - //public void setSsfFileMonitorPollingInterval( - //String ssfFileMonitorPollingInterval) { - //this.ssfFileMonitorPollingInterval = ssfFileMonitorPollingInterval; - //} - - /** - * Sets the ssf file monitor threadpool size. - * - * @param ssfFileMonitorThreadpoolSize the new ssf file monitor threadpool size - */ - //public void setSsfFileMonitorThreadpoolSize( - //String ssfFileMonitorThreadpoolSize) { - //this.ssfFileMonitorThreadpoolSize = ssfFileMonitorThreadpoolSize; - //} - - /** - * Gets the load on startup. - * - * @return the load on startup - */ - //public boolean getLoadOnStartup() { - //return loadOnStartup; - //} - - /** - * Gets the ssf file monitor polling interval. - * - * @return the ssf file monitor polling interval - */ - //public String getSsfFileMonitorPollingInterval() { - //return ssfFileMonitorPollingInterval; - //} - - /** - * Gets the ssf file monitor threadpool size. - * - * @return the ssf file monitor threadpool size - */ - //public String getSsfFileMonitorThreadpoolSize() { - //return ssfFileMonitorThreadpoolSize; - //} - - /** - * Gets the file changed listener. - * - * @return the file changed listener - */ - //public ServicePropertiesListener getFileChangedListener() { - //return fileChangedListener; - //} - - /** - * Sets the file changed listener. - * - * @param fileChangedListener the new file changed listener - */ - //public void setFileChangedListener( - //ServicePropertiesListener fileChangedListener) { - //this.fileChangedListener = fileChangedListener; - //} - - /** - * Gets the file properties map. - * - * @return the file properties map - */ - //public ServicePropertiesMap getFilePropertiesMap() { - //return filePropertiesMap; - //} - - /** - * Sets the file properties map. - * - * @param filePropertiesMap the new file properties map - */ - //public void setFilePropertiesMap(ServicePropertiesMap filePropertiesMap) { - //this.filePropertiesMap = filePropertiesMap; - //} -//} diff --git a/aai-resources/src/main/java/org/onap/aai/config/DmaapConfig.java b/aai-resources/src/main/java/org/onap/aai/config/JettyPasswordDecoder.java index c34ae0a..5aef2eb 100644 --- a/aai-resources/src/main/java/org/onap/aai/config/DmaapConfig.java +++ b/aai-resources/src/main/java/org/onap/aai/config/JettyPasswordDecoder.java @@ -21,23 +21,15 @@ */ package org.onap.aai.config; -import org.apache.activemq.broker.BrokerService; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.eclipse.jetty.util.security.Password; -@Configuration -public class DmaapConfig { +public class JettyPasswordDecoder implements PasswordDecoder { - @Bean(destroyMethod = "stop") - public BrokerService brokerService() throws Exception { - - BrokerService broker = new BrokerService(); - broker.addConnector("tcp://localhost:61447"); - broker.setPersistent(false); - broker.setUseJmx(false); - broker.setSchedulerSupport(false); - broker.start(); - - return broker; + @Override + public String decode(String input) { + if (input.startsWith("OBF:")) { + return Password.deobfuscate(input); + } + return Password.deobfuscate("OBF:" + input); } } diff --git a/aai-resources/src/main/java/org/onap/aai/config/PasswordDecoder.java b/aai-resources/src/main/java/org/onap/aai/config/PasswordDecoder.java new file mode 100644 index 0000000..8c199eb --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/config/PasswordDecoder.java @@ -0,0 +1,27 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.config; + +public interface PasswordDecoder { + + String decode(String input); +} diff --git a/aai-resources/src/main/java/org/onap/aai/config/PropertyPasswordConfiguration.java b/aai-resources/src/main/java/org/onap/aai/config/PropertyPasswordConfiguration.java new file mode 100644 index 0000000..623c7e9 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/config/PropertyPasswordConfiguration.java @@ -0,0 +1,83 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.config; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.stereotype.Component; + +public class PropertyPasswordConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> { + + private static final Pattern decodePasswordPattern = Pattern.compile("password\\((.*?)\\)"); + + private PasswordDecoder passwordDecoder = new JettyPasswordDecoder(); + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + for (PropertySource<?> propertySource : environment.getPropertySources()) { + Map<String, Object> propertyOverrides = new LinkedHashMap<>(); + decodePasswords(propertySource, propertyOverrides); + if (!propertyOverrides.isEmpty()) { + PropertySource<?> decodedProperties = new MapPropertySource("decoded "+ propertySource.getName(), propertyOverrides); + environment.getPropertySources().addBefore(propertySource.getName(), decodedProperties); + } + } + } + + private void decodePasswords(PropertySource<?> source, Map<String, Object> propertyOverrides) { + if (source instanceof EnumerablePropertySource) { + EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) source; + for (String key : enumerablePropertySource.getPropertyNames()) { + Object rawValue = source.getProperty(key); + if (rawValue instanceof String) { + String decodedValue = decodePasswordsInString((String) rawValue); + propertyOverrides.put(key, decodedValue); + } + } + } + } + + private String decodePasswordsInString(String input) { + if (input == null) return null; + StringBuffer output = new StringBuffer(); + Matcher matcher = decodePasswordPattern.matcher(input); + while (matcher.find()) { + String replacement = passwordDecoder.decode(matcher.group(1)); + matcher.appendReplacement(output, replacement); + } + matcher.appendTail(output); + return output.toString(); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/dbgen/DupeTool.java b/aai-resources/src/main/java/org/onap/aai/dbgen/DupeTool.java new file mode 100644 index 0000000..a19bc8e --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/dbgen/DupeTool.java @@ -0,0 +1,1900 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.dbgen; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.*; +import java.util.Map.Entry; + +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.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +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.dbmap.AAIGraphConfig; +import org.onap.aai.dbmap.AAIGraph; +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.logging.ErrorLogHelper; +import org.onap.aai.logging.LogFormatTools; +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.slf4j.MDC; + +import com.att.eelf.configuration.Configuration; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.thinkaurelius.titan.core.TitanFactory; +import com.thinkaurelius.titan.core.TitanGraph; + + + +public class DupeTool { + + private static final String FROMAPPID = "AAI-DB"; + private static final String TRANSID = UUID.randomUUID().toString(); + + private static String graphType = "realdb"; + + public static boolean SHOULD_EXIT_VM = true; + + public static int EXIT_VM_STATUS_CODE = -1; + + public static void exit(int statusCode){ + if(SHOULD_EXIT_VM){ + System.exit(1); + } + EXIT_VM_STATUS_CODE = statusCode; + } + + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(String[] args) { + System.setProperty("aai.service.name", DupeTool.class.getSimpleName()); + // Set the logging file properties to be used by EELFManager + Properties props = System.getProperties(); + props.setProperty(Configuration.PROPERTY_LOGGING_FILE_NAME, "dupeTool-logback.xml"); + props.setProperty(Configuration.PROPERTY_LOGGING_FILE_PATH, AAIConstants.AAI_HOME_ETC_APP_PROPERTIES); + EELFLogger logger = EELFManager.getInstance().getLogger(DupeTool.class.getSimpleName()); + MDC.put("logFilenameAppender", DupeTool.class.getSimpleName()); + + LoggingContext.init(); + LoggingContext.partnerName(FROMAPPID); + LoggingContext.serviceName(AAIConstants.AAI_RESOURCES_MS); + LoggingContext.component("dupeTool"); + LoggingContext.targetEntity(AAIConstants.AAI_RESOURCES_MS); + LoggingContext.targetServiceName("main"); + LoggingContext.requestId(TRANSID); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + + String defVersion = "v9"; + try { + defVersion = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + } + catch ( AAIException ae ){ + String emsg = "Error trying to get default API Version property \n"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + exit(0); + } + + Loader loader= null; + try { + loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST); + + } + catch (Exception ex){ + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.UNKNOWN_ERROR); + logger.error("ERROR - Could not do the moxyMod.init() " + LogFormatTools.getStackTop(ex)); + exit(1); + } + TitanGraph graph1 = null; + TitanGraph graph2 = null; + Graph gt1 = null; + Graph gt2 = null; + + boolean specialTenantRule = false; + + try { + AAIConfig.init(); + int maxRecordsToFix = AAIConstants.AAI_DUPETOOL_DEFAULT_MAX_FIX; + int sleepMinutes = AAIConstants.AAI_DUPETOOL_DEFAULT_SLEEP_MINUTES; + int timeWindowMinutes = 0; // A value of 0 means that we will not have a time-window -- we will look + // at all nodes of the passed-in nodeType. + long windowStartTime = 0; // Translation of the window into a starting timestamp + + try { + String maxFixStr = AAIConfig.get("aai.dupeTool.default.max.fix"); + if( maxFixStr != null && !maxFixStr.equals("") ){ + maxRecordsToFix = Integer.parseInt(maxFixStr); + } + String sleepStr = AAIConfig.get("aai.dupeTool.default.sleep.minutes"); + if( sleepStr != null && !sleepStr.equals("") ){ + sleepMinutes = Integer.parseInt(sleepStr); + } + } + catch ( Exception e ){ + // Don't worry, we'll just use the defaults that we got from AAIConstants + logger.warn("WARNING - could not pick up aai.dupeTool values from aaiconfig.properties file. Will use defaults. "); + } + + String nodeTypeVal = ""; + String userIdVal = ""; + String filterParams = ""; + Boolean skipHostCheck = false; + Boolean autoFix = false; + String argStr4Msg = ""; + Introspector obj = null; + + if (args != null && args.length > 0) { + // They passed some arguments in that will affect processing + for (int i = 0; i < args.length; i++) { + String thisArg = args[i]; + argStr4Msg = argStr4Msg + " " + thisArg; + + if (thisArg.equals("-nodeType")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(" No value passed with -nodeType option. "); + exit(0); + } + nodeTypeVal = args[i]; + argStr4Msg = argStr4Msg + " " + nodeTypeVal; + } + else if (thisArg.equals("-sleepMinutes")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("No value passed with -sleepMinutes option."); + exit(0); + } + String nextArg = args[i]; + try { + sleepMinutes = Integer.parseInt(nextArg); + } catch (Exception e) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("Bad value passed with -sleepMinutes option: [" + + nextArg + "]"); + exit(0); + } + argStr4Msg = argStr4Msg + " " + sleepMinutes; + } + else if (thisArg.equals("-maxFix")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("No value passed with -maxFix option."); + exit(0); + } + String nextArg = args[i]; + try { + maxRecordsToFix = Integer.parseInt(nextArg); + } catch (Exception e) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("Bad value passed with -maxFix option: [" + + nextArg + "]"); + exit(0); + } + argStr4Msg = argStr4Msg + " " + maxRecordsToFix; + } + else if (thisArg.equals("-timeWindowMinutes")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("No value passed with -timeWindowMinutes option."); + exit(0); + } + String nextArg = args[i]; + try { + timeWindowMinutes = Integer.parseInt(nextArg); + } catch (Exception e) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error("Bad value passed with -timeWindowMinutes option: [" + + nextArg + "]"); + exit(0); + } + argStr4Msg = argStr4Msg + " " + timeWindowMinutes; + } + else if (thisArg.equals("-skipHostCheck")) { + skipHostCheck = true; + } + else if (thisArg.equals("-specialTenantRule")) { + specialTenantRule = true; + } + else if (thisArg.equals("-autoFix")) { + autoFix = true; + } + else if (thisArg.equals("-userId")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(" No value passed with -userId option. "); + exit(0); + } + userIdVal = args[i]; + argStr4Msg = argStr4Msg + " " + userIdVal; + } + else if (thisArg.equals("-params4Collect")) { + i++; + if (i >= args.length) { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(" No value passed with -params4Collect option. "); + exit(0); + } + filterParams = args[i]; + argStr4Msg = argStr4Msg + " " + filterParams; + } + else { + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(" Unrecognized argument passed to DupeTool: [" + + thisArg + "]. "); + logger.error(" Valid values are: -action -userId -vertexId -edgeId -overRideProtection "); + exit(0); + } + } + } + + userIdVal = userIdVal.trim(); + if( (userIdVal.length() < 6) || userIdVal.toUpperCase().equals("AAIADMIN") ){ + String emsg = "userId parameter is required. [" + userIdVal + "] passed to DupeTool(). userId must be not empty and not aaiadmin \n"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(emsg); + exit(0); + } + + nodeTypeVal = nodeTypeVal.trim(); + if( nodeTypeVal.equals("") ){ + String emsg = " nodeType is a required parameter for DupeTool().\n"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); + logger.error(emsg); + exit(0); + } else { + obj = loader.introspectorFromName(nodeTypeVal); + } + + if (skipHostCheck) { + logger.info(" We will skip the HostCheck as requested. "); + } + + if( timeWindowMinutes > 0 ){ + // Translate the window value (ie. 30 minutes) into a unix timestamp like + // we use in the db - so we can select data created after that time. + windowStartTime = figureWindowStartTime( timeWindowMinutes ); + } + + String msg = ""; + msg = "DupeTool called with these params: [" + argStr4Msg + "]"; + System.out.println(msg); + logger.info(msg); + + // Determine what the key fields are for this nodeType (and we want them ordered) + ArrayList <String> keyPropNamesArr = new ArrayList<String>(obj.getKeys()); + + // Determine what kinds of nodes (if any) this nodeType is dependent on for uniqueness + ArrayList<String> depNodeTypeList = new ArrayList<String>(); + Collection<String> depNTColl = obj.getDependentOn(); + Iterator<String> ntItr = depNTColl.iterator(); + while( ntItr.hasNext() ){ + depNodeTypeList.add(ntItr.next()); + } + + // Based on the nodeType, window and filterData, figure out the vertices that we will be checking + System.out.println(" ---- NOTE --- about to open graph (takes a little while)--------\n"); + graph1 = setupGraph(logger); + gt1 = getGraphTransaction( graph1, logger ); + ArrayList<Vertex> verts2Check = new ArrayList<Vertex>(); + try { + verts2Check = figureOutNodes2Check( TRANSID, FROMAPPID, gt1, + nodeTypeVal, windowStartTime, filterParams, logger ); + } + catch ( AAIException ae ){ + String emsg = "Error trying to get initial set of nodes to check. \n"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + exit(0); + } + + if( verts2Check == null || verts2Check.size() == 0 ){ + msg = " No vertices found to check. Used nodeType = [" + nodeTypeVal + + "], windowMinutes = " + timeWindowMinutes + + ", filterData = [" + filterParams + "]."; + logger.info( msg ); + System.out.println( msg ); + exit(0); + } + else { + msg = " Found " + verts2Check.size() + " nodes of type " + nodeTypeVal + + " to check using passed filterParams and windowStartTime. "; + logger.info( msg ); + System.out.println( msg ); + } + + ArrayList <String> firstPassDupeSets = new ArrayList <String>(); + ArrayList <String> secondPassDupeSets = new ArrayList <String>(); + Boolean isDependentOnParent = false; + if( !obj.getDependentOn().isEmpty() ){ + isDependentOnParent = true; + } + + if( isDependentOnParent ){ + firstPassDupeSets = getDupeSets4DependentNodes( TRANSID, FROMAPPID, gt1, + defVersion, nodeTypeVal, verts2Check, keyPropNamesArr, loader, + specialTenantRule, logger ); + } + else { + firstPassDupeSets = getDupeSets4NonDepNodes( TRANSID, FROMAPPID, gt1, + defVersion, nodeTypeVal, verts2Check, keyPropNamesArr, + specialTenantRule, loader, logger ); + } + + msg = " Found " + firstPassDupeSets.size() + " sets of duplicates for this request. "; + logger.info( msg ); + System.out.println( msg ); + if( firstPassDupeSets.size() > 0 ){ + msg = " Here is what they look like: "; + logger.info( msg ); + System.out.println( msg ); + for( int x = 0; x < firstPassDupeSets.size(); x++ ){ + msg = " Set " + x + ": [" + firstPassDupeSets.get(x) +"] "; + logger.info( msg ); + System.out.println( msg ); + showNodeDetailsForADupeSet(gt1, firstPassDupeSets.get(x), logger); + } + } + + boolean didSomeDeletesFlag = false; + ArrayList <String> dupeSetsToFix = new ArrayList <String> (); + if( autoFix && firstPassDupeSets.size() == 0 ){ + msg = "AutoFix option is on, but no dupes were found on the first pass. Nothing to fix."; + logger.info( msg ); + System.out.println( msg ); + } + else if( autoFix ){ + // We will try to fix any dupes that we can - but only after sleeping for a + // time and re-checking the list of duplicates using a seperate transaction. + try { + msg = "\n\n----------- About to sleep for " + sleepMinutes + " minutes." + + " -----------\n\n"; + logger.info( msg ); + System.out.println( msg ); + int sleepMsec = sleepMinutes * 60 * 1000; + Thread.sleep(sleepMsec); + } catch (InterruptedException ie) { + msg = "\n >>> Sleep Thread has been Interrupted <<< "; + logger.info( msg ); + System.out.println( msg ); + exit(0); + } + + graph2 = setupGraph(logger); + gt2 = getGraphTransaction( graph2, logger ); + if( isDependentOnParent ){ + secondPassDupeSets = getDupeSets4DependentNodes( TRANSID, FROMAPPID, gt2, + defVersion, nodeTypeVal, verts2Check, keyPropNamesArr, loader, + specialTenantRule, logger ); + } + else { + secondPassDupeSets = getDupeSets4NonDepNodes( TRANSID, FROMAPPID, gt2, + defVersion, nodeTypeVal, verts2Check, keyPropNamesArr, + specialTenantRule, loader, logger ); + } + + dupeSetsToFix = figureWhichDupesStillNeedFixing( firstPassDupeSets, secondPassDupeSets, logger ); + msg = "\nAfter running a second pass, there were " + dupeSetsToFix.size() + + " sets of duplicates that we think can be deleted. "; + logger.info( msg ); + System.out.println( msg ); + if( dupeSetsToFix.size() > 0 ){ + msg = " Here is what the sets look like: "; + logger.info( msg ); + System.out.println( msg ); + for( int x = 0; x < dupeSetsToFix.size(); x++ ){ + msg = " Set " + x + ": [" + dupeSetsToFix.get(x) +"] "; + logger.info( msg ); + System.out.println( msg ); + showNodeDetailsForADupeSet(gt2, dupeSetsToFix.get(x), logger); + } + } + + if( dupeSetsToFix.size() > 0 ){ + if( dupeSetsToFix.size() > maxRecordsToFix ){ + String infMsg = " >> WARNING >> Dupe list size (" + + dupeSetsToFix.size() + + ") is too big. The maxFix we are using is: " + + maxRecordsToFix + + ". No nodes will be deleted. (use the" + + " -maxFix option to override this limit.)"; + System.out.println(infMsg); + logger.info(infMsg); + } + else { + // Call the routine that fixes known dupes + didSomeDeletesFlag = deleteNonKeepers( gt2, dupeSetsToFix, logger ); + } + } + if( didSomeDeletesFlag ){ + gt2.tx().commit(); + } + } + + } catch (AAIException e) { + logger.error("Caught AAIException while running the dupeTool: " + LogFormatTools.getStackTop(e)); + ErrorLogHelper.logException(e); + } catch (Exception ex) { + logger.error("Caught exception while running the dupeTool: "+ LogFormatTools.getStackTop(ex)); + ErrorLogHelper.logError("AAI_6128", ex.getMessage() + ", resolve and rerun the dupeTool. "); + } finally { + if (gt1 != null && gt1.tx().isOpen()) { + // We don't change any data with gt1 - so just roll it back so it knows we're done. + try { + gt1.tx().rollback(); + } + catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed + logger.warn("WARNING from final gt1.rollback() " + LogFormatTools.getStackTop(ex)); + } + } + + if (gt2 != null && gt2.tx().isOpen()) { + // Any changes that worked correctly should have already done + // their commits. + try { + gt2.tx().rollback(); + } catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed + logger.warn("WARNING from final gt2.rollback() " + LogFormatTools.getStackTop(ex)); + } + } + + try { + if( graph1 != null && graph1.isOpen() ){ + closeGraph(graph1, logger); + } + } catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed{ + logger.warn("WARNING from final graph1.shutdown() " + LogFormatTools.getStackTop(ex)); + } + + try { + if( graph2 != null && graph2.isOpen() ){ + closeGraph(graph2, logger); + } + } catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed{ + logger.warn("WARNING from final graph2.shutdown() " + LogFormatTools.getStackTop(ex)); + } + } + + exit(0); + + }// end of main() + + + /** + * Collect Duplicate Sets for nodes that are NOT dependent on parent nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param g the g + * @param version the version + * @param nType the n type + * @param passedVertList the passed vert list + * @param dbMaps the db maps + * @return the array list + */ + private static ArrayList<String> getDupeSets4NonDepNodes( String transId, + String fromAppId, Graph g, String version, String nType, + ArrayList<Vertex> passedVertList, + ArrayList <String> keyPropNamesArr, + Boolean specialTenantRule, Loader loader, EELFLogger logger ) { + + ArrayList<String> returnList = new ArrayList<String>(); + + // We've been passed a set of nodes that we want to check. + // They are all NON-DEPENDENT nodes meaning that they should be + // unique in the DB based on their KEY DATA alone. So, if + // we group them by their key data - if any key has more than one + // vertex mapped to it, those vertices are dupes. + // + // When we find duplicates, we return then as a String (there can be + // more than one duplicate for one set of key data): + // Each element in the returned arrayList might look like this: + // "1234|5678|keepVid=UNDETERMINED" (if there were 2 dupes, and we + // couldn't figure out which one to keep) + // or, "100017|200027|30037|keepVid=30037" (if there were 3 dupes and we + // thought the third one was the one that should survive) + + HashMap <String, ArrayList<String>> keyVals2VidHash = new HashMap <String, ArrayList<String>>(); + HashMap <String,Vertex> vtxHash = new HashMap <String,Vertex>(); + Iterator<Vertex> pItr = passedVertList.iterator(); + while (pItr.hasNext()) { + try { + Vertex tvx = pItr.next(); + String thisVid = tvx.id().toString(); + vtxHash.put(thisVid, tvx); + + // if there are more than one vertexId mapping to the same keyProps -- they are dupes + String hKey = getNodeKeyValString( tvx, keyPropNamesArr, logger ); + if( keyVals2VidHash.containsKey(hKey) ){ + // We've already seen this key + ArrayList <String> tmpVL = (ArrayList <String>)keyVals2VidHash.get(hKey); + tmpVL.add(thisVid); + keyVals2VidHash.put(hKey, tmpVL); + } + else { + // First time for this key + ArrayList <String> tmpVL = new ArrayList <String>(); + tmpVL.add(thisVid); + keyVals2VidHash.put(hKey, tmpVL); + } + } + catch (Exception e) { + logger.warn(" >>> Threw an error in getDupeSets4NonDepNodes - just absorb this error and move on. " + LogFormatTools.getStackTop(e)); + } + } + + for( Map.Entry<String, ArrayList<String>> entry : keyVals2VidHash.entrySet() ){ + ArrayList <String> vidList = entry.getValue(); + try { + if( !vidList.isEmpty() && vidList.size() > 1 ){ + // There are more than one vertex id's using the same key info + String dupesStr = ""; + ArrayList <Vertex> vertList = new ArrayList <Vertex> (); + for (int i = 0; i < vidList.size(); i++) { + String tmpVid = vidList.get(i); + dupesStr = dupesStr + tmpVid + "|"; + vertList.add(vtxHash.get(tmpVid)); + } + + if (dupesStr != "") { + Vertex prefV = getPreferredDupe(transId, fromAppId, + g, vertList, version, specialTenantRule, loader, logger); + if (prefV == null) { + // We could not determine which duplicate to keep + dupesStr = dupesStr + "KeepVid=UNDETERMINED"; + returnList.add(dupesStr); + } else { + dupesStr = dupesStr + "KeepVid=" + prefV.id(); + returnList.add(dupesStr); + } + } + } + } + catch (Exception e) { + logger.warn(" >>> Threw an error in getDupeSets4NonDepNodes - just absorb this error and move on. " + LogFormatTools.getStackTop(e)); + } + + } + return returnList; + + }// End of getDupeSets4NonDepNodes() + + + /** + * Collect Duplicate Sets for nodes that are dependent on parent nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param g the g + * @param version the version + * @param nType the n type + * @param passedVertList the passed vert list + * @param dbMaps the db maps + * @param keyPropNamesArr Array (ordered) of keyProperty names + * @param specialTenantRule flag + * @param EELFLogger the logger + * @return the array list + */ + private static ArrayList<String> getDupeSets4DependentNodes( String transId, + String fromAppId, Graph g, String version, String nType, + ArrayList<Vertex> passedVertList, + ArrayList <String> keyPropNamesArr, Loader loader, + Boolean specialTenantRule, EELFLogger logger ) { + + // This is for nodeTypes that DEPEND ON A PARENT NODE FOR UNIQUNESS + + ArrayList<String> returnList = new ArrayList<String>(); + ArrayList<String> alreadyFoundDupeVidArr = new ArrayList<String>(); + + // We've been passed a set of nodes that we want to check. These are + // all nodes that ARE DEPENDENT on a PARENT Node for uniqueness. + // The first thing to do is to identify the key properties for the node-type + // and pull from the db just using those properties. + // Then, we'll check those nodes with their parent nodes to see if there + // are any duplicates. + // + // When we find duplicates, we return then as a String (there can be + // more than one duplicate for one set of key data): + // Each element in the returned arrayList might look like this: + // "1234|5678|keepVid=UNDETERMINED" (if there were 2 dupes, and we + // couldn't figure out which one to keep) + // or, "100017|200027|30037|keepVid=30037" (if there were 3 dupes and we + // thought the third one was the one that should survive) + HashMap <String, Object> checkVertHash = new HashMap <String,Object> (); + try { + Iterator<Vertex> pItr = passedVertList.iterator(); + while (pItr.hasNext()) { + Vertex tvx = pItr.next(); + String passedId = tvx.id().toString(); + if( !alreadyFoundDupeVidArr.contains(passedId) ){ + // We haven't seen this one before - so we should check it. + HashMap <String,Object> keyPropValsHash = getNodeKeyVals( tvx, keyPropNamesArr, logger ); + ArrayList <Vertex> tmpVertList = getNodeJustUsingKeyParams( transId, fromAppId, g, + nType, keyPropValsHash, version, logger ); + + if( tmpVertList.size() <= 1 ){ + // Even without a parent node, this thing is unique so don't worry about it. + } + else { + for( int i = 0; i < tmpVertList.size(); i++ ){ + Vertex tmpVtx = (tmpVertList.get(i)); + String tmpVid = tmpVtx.id().toString(); + alreadyFoundDupeVidArr.add(tmpVid); + + String hKey = getNodeKeyValString( tmpVtx, keyPropNamesArr, logger ); + if( checkVertHash.containsKey(hKey) ){ + // add it to an existing list + ArrayList <Vertex> tmpVL = (ArrayList <Vertex>)checkVertHash.get(hKey); + tmpVL.add(tmpVtx); + checkVertHash.put(hKey, tmpVL); + } + else { + // First time for this key + ArrayList <Vertex> tmpVL = new ArrayList <Vertex>(); + tmpVL.add(tmpVtx); + checkVertHash.put(hKey, tmpVL); + } + } + } + } + } + + // More than one node have the same key fields since they may + // depend on a parent node for uniqueness. Since we're finding + // more than one, we want to check to see if any of the + // vertices that have this set of keys are also pointing at the + // same 'parent' node. + // Note: for a given set of key data, it is possible that there + // could be more than one set of duplicates. + for (Entry<String, Object> lentry : checkVertHash.entrySet()) { + ArrayList <Vertex> thisIdSetList = (ArrayList <Vertex>)lentry.getValue(); + if (thisIdSetList == null || thisIdSetList.size() < 2) { + // Nothing to check for this set. + continue; + } + + HashMap<String, ArrayList<Vertex>> vertsGroupedByParentHash = groupVertsByDepNodes( + transId, fromAppId, g, version, nType, + thisIdSetList, loader); + for (Map.Entry<String, ArrayList<Vertex>> entry : vertsGroupedByParentHash + .entrySet()) { + ArrayList<Vertex> thisParentsVertList = entry + .getValue(); + if (thisParentsVertList.size() > 1) { + // More than one vertex found with the same key info + // hanging off the same parent/dependent node + String dupesStr = ""; + for (int i = 0; i < thisParentsVertList.size(); i++) { + dupesStr = dupesStr + + ( (thisParentsVertList + .get(i))).id() + "|"; + } + if (dupesStr != "") { + Vertex prefV = getPreferredDupe(transId, + fromAppId, g, thisParentsVertList, + version, specialTenantRule, loader, logger); + + if (prefV == null) { + // We could not determine which duplicate to keep + dupesStr = dupesStr + "KeepVid=UNDETERMINED"; + returnList.add(dupesStr); + } + else { + dupesStr = dupesStr + "KeepVid=" + + prefV.id().toString(); + returnList.add(dupesStr); + } + } + } + } + } + + } catch (Exception e) { + logger.warn(" >>> Threw an error in checkAndProcessDupes - just absorb this error and move on. " + LogFormatTools.getStackTop(e)); + } + + return returnList; + + }// End of getDupeSets4DependentNodes() + + + private static Graph getGraphTransaction(TitanGraph graph, EELFLogger logger){ + + Graph gt = null; + try { + if( graph == null ){ + String emsg = "could not get graph object in DupeTool. \n"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR); + logger.error(emsg); + exit(0); + } + gt = graph.newTransaction(); + if (gt == null) { + String emsg = "null graphTransaction object in DupeTool. \n"; + throw new AAIException("AAI_6101", emsg); + } + + } + catch (AAIException e1) { + String msg = e1.getErrorObject().toString(); + System.out.println(msg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(msg); + exit(0); + } + catch (Exception e2) { + String msg = e2.toString(); + System.out.println(msg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.UNKNOWN_ERROR); + logger.error(msg); + exit(0); + } + + return gt; + + }// End of getGraphTransaction() + + + + public static void showNodeInfo(EELFLogger logger, Vertex tVert, Boolean displayAllVidsFlag ){ + + try { + Iterator<VertexProperty<Object>> pI = tVert.properties(); + String infStr = ">>> Found Vertex with VertexId = " + tVert.id() + ", properties: "; + System.out.println( infStr ); + logger.info(infStr); + while( pI.hasNext() ){ + VertexProperty<Object> tp = pI.next(); + infStr = " [" + tp.key() + "|" + tp.value() + "] "; + System.out.println( infStr ); + logger.info(infStr); + } + + ArrayList <String> retArr = collectEdgeInfoForNode( logger, tVert, displayAllVidsFlag ); + for( String infoStr : retArr ){ + System.out.println( infoStr ); + logger.info(infoStr); + } + } + catch (Exception e){ + String warnMsg = " -- Error -- trying to display edge info. [" + e.getMessage() + "]"; + System.out.println( warnMsg ); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.UNKNOWN_ERROR); + logger.warn(warnMsg); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + } + + }// End of showNodeInfo() + + + public static ArrayList <String> collectEdgeInfoForNode( EELFLogger logger, Vertex tVert, boolean displayAllVidsFlag ){ + ArrayList <String> retArr = new ArrayList <String> (); + Direction dir = Direction.OUT; + for ( int i = 0; i <= 1; i++ ){ + if( i == 1 ){ + // Second time through we'll look at the IN edges. + dir = Direction.IN; + } + Iterator <Edge> eI = tVert.edges(dir); + if( ! eI.hasNext() ){ + retArr.add("No " + dir + " edges were found for this vertex. "); + } + while( eI.hasNext() ){ + Edge ed = eI.next(); + String lab = ed.label(); + Vertex vtx = null; + if( dir == Direction.OUT ){ + // get the vtx on the "other" side + vtx = ed.inVertex(); + } + else { + // get the vtx on the "other" side + vtx = ed.outVertex(); + } + if( vtx == null ){ + retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< "); + } + else { + String nType = vtx.<String>property("aai-node-type").orElse(null); + if( displayAllVidsFlag ){ + // This should rarely be needed + String vid = vtx.id().toString(); + retArr.add("Found an " + dir + " edge (" + lab + ") between this vertex and a [" + nType + "] node with VtxId = " + vid ); + } + else { + // This is the normal case + retArr.add("Found an " + dir + " edge (" + lab + ") between this vertex and a [" + nType + "] node. "); + } + } + } + } + return retArr; + + }// end of collectEdgeInfoForNode() + + + private static long figureWindowStartTime( int timeWindowMinutes ){ + // Given a window size, calculate what the start-timestamp would be. + + if( timeWindowMinutes <= 0 ){ + // This just means that there is no window... + return 0; + } + long unixTimeNow = System.currentTimeMillis(); + long windowInMillis = timeWindowMinutes * 60 * 1000; + + long startTimeStamp = unixTimeNow - windowInMillis; + + return startTimeStamp; + } // End of figureWindowStartTime() + + + + /** + * Gets the node(s) just using key params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param keyPropsHash the key props hash + * @param apiVersion the api version + * @return the node just using key params + * @throws AAIException the AAI exception + */ + public static ArrayList <Vertex> getNodeJustUsingKeyParams( String transId, String fromAppId, Graph graph, String nodeType, + HashMap<String,Object> keyPropsHash, String apiVersion, EELFLogger logger ) throws AAIException{ + + ArrayList <Vertex> retVertList = new ArrayList <Vertex> (); + + // We assume that all NodeTypes have at least one key-property defined. + // Note - instead of key-properties (the primary key properties), a user could pass + // alternate-key values if they are defined for the nodeType. + ArrayList<String> kName = new ArrayList<String>(); + ArrayList<Object> kVal = new ArrayList<Object>(); + if( keyPropsHash == null || keyPropsHash.isEmpty() ) { + throw new AAIException("AAI_6120", " NO key properties passed for this getNodeJustUsingKeyParams() request. NodeType = [" + nodeType + "]. "); + } + + int i = -1; + for( Map.Entry<String, Object> entry : keyPropsHash.entrySet() ){ + i++; + kName.add(i, entry.getKey()); + kVal.add(i, entry.getValue()); + } + int topPropIndex = i; + Vertex tiV = null; + String propsAndValuesForMsg = ""; + Iterator<Vertex> verts = null; + GraphTraversalSource g = graph.traversal(); + try { + if( topPropIndex == 0 ){ + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") "; + verts= g.V().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType); + } + else if( topPropIndex == 1 ){ + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ") "; + verts = g.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has("aai-node-type",nodeType); + } + else if( topPropIndex == 2 ){ + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ") "; + verts= g.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has("aai-node-type",nodeType); + } + else if( topPropIndex == 3 ){ + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ", " + + kName.get(3) + " = " + kVal.get(3) + ") "; + verts= g.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).has("aai-node-type",nodeType); + } + else { + throw new AAIException("AAI_6114", " We only support 4 keys per nodeType for now \n"); + } + } + catch( Exception ex ){ + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error( " ERROR trying to get node for: [" + propsAndValuesForMsg + "] " + LogFormatTools.getStackTop(ex)); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + } + + if( verts != null ){ + while( verts.hasNext() ){ + tiV = verts.next(); + retVertList.add(tiV); + } + } + + if( retVertList.size() == 0 ){ + logger.debug("DEBUG No node found for nodeType = [" + nodeType + + "], propsAndVal = " + propsAndValuesForMsg ); + } + + return retVertList; + + }// End of getNodeJustUsingKeyParams() + + + + /** + * Gets the node(s) just using key params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param windowStartTime the window start time + * @param propsHash the props hash + * @param apiVersion the api version + * @return the nodes + * @throws AAIException the AAI exception + */ + public static ArrayList <Vertex> figureOutNodes2Check( String transId, String fromAppId, + Graph graph, String nodeType, long windowStartTime, + String propsString, EELFLogger logger ) throws AAIException{ + + ArrayList <Vertex> retVertList = new ArrayList <Vertex> (); + String msg = ""; + GraphTraversal<Vertex,Vertex> tgQ = graph.traversal().V().has("aai-node-type",nodeType); + String qStringForMsg = "graph.traversal().V().has(\"aai-node-type\"," + nodeType + ")"; + + if( propsString != null && !propsString.trim().equals("") ){ + propsString = propsString.trim(); + int firstPipeLoc = propsString.indexOf("|"); + if( firstPipeLoc <= 0 ){ + msg = "Bad props4Collect passed: [" + propsString + "]. \n Expecting a format like, 'propName1|propVal1,propName2|propVal2'"; + System.out.println(msg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(msg); + exit(0); + } + + // Note - if they're only passing on parameter, there won't be any commas + String [] paramArr = propsString.split(","); + for( int i = 0; i < paramArr.length; i++ ){ + int pipeLoc = paramArr[i].indexOf("|"); + if( pipeLoc <= 0 ){ + msg = "Bad propsString passed: [" + propsString + "]. \n Expecting a format like, 'propName1|propVal1,propName2|propVal2'"; + System.out.println(msg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(msg); + exit(0); + } + else { + String propName = paramArr[i].substring(0,pipeLoc); + String propVal = paramArr[i].substring(pipeLoc + 1); + tgQ = tgQ.has(propName,propVal); + qStringForMsg = qStringForMsg + ".has(" + propName + "," + propVal + ")"; + } + } + } + + if(tgQ == null){ + msg = "Bad TitanGraphQuery object. "; + System.out.println(msg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR); + logger.error(msg); + exit(0); + } + else { + Iterator<Vertex> vertItor = tgQ; + while( vertItor.hasNext() ){ + Vertex tiV = vertItor.next(); + if( windowStartTime <= 0 ){ + // We're not applying a time-window + retVertList.add(tiV); + } + else { + Object objTimeStamp = tiV.property("aai-created-ts").orElse(null); + if( objTimeStamp == null ){ + // No timestamp - so just take it + retVertList.add(tiV); + } + else { + long thisNodeCreateTime = (long)objTimeStamp; + if( thisNodeCreateTime > windowStartTime ){ + // It is in our window, so we can take it + retVertList.add(tiV); + } + } + } + } + } + + if( retVertList.size() == 0 ){ + logger.debug("DEBUG No node found for: [" + qStringForMsg + ", with aai-created-ts > " + windowStartTime ); + } + + return retVertList; + + }// End of figureOutNodes2Check() + + + /** + * Gets the preferred dupe. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param g the g + * @param dupeVertexList the dupe vertex list + * @param ver the ver + * @param EELFLogger the logger + * @return Vertex + * @throws AAIException the AAI exception + */ + public static Vertex getPreferredDupe( String transId, + String fromAppId, Graph g, + ArrayList<Vertex> dupeVertexList, String ver, + Boolean specialTenantRule, Loader loader, EELFLogger logger ) + throws AAIException { + + // This method assumes that it is being passed a List of vertex objects + // which violate our uniqueness constraints. + + Vertex nullVtx = null; + + if (dupeVertexList == null) { + return nullVtx; + } + int listSize = dupeVertexList.size(); + if (listSize == 0) { + return nullVtx; + } + if (listSize == 1) { + return ( dupeVertexList.get(0)); + } + + Vertex vtxPreferred = null; + Vertex currentFaveVtx = dupeVertexList.get(0); + for (int i = 1; i < listSize; i++) { + Vertex vtxB = dupeVertexList.get(i); + vtxPreferred = pickOneOfTwoDupes(transId, fromAppId, g, + currentFaveVtx, vtxB, ver, specialTenantRule, loader, logger); + if (vtxPreferred == null) { + // We couldn't choose one + return nullVtx; + } else { + currentFaveVtx = vtxPreferred; + } + } + + return (currentFaveVtx); + + } // end of getPreferredDupe() + + + /** + * Pick one of two dupes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param g the g + * @param vtxA the vtx A + * @param vtxB the vtx B + * @param ver the ver + * @param boolean specialTenantRuleFlag flag + * @param EELFLogger the logger + * @return Vertex + * @throws AAIException the AAI exception + */ + public static Vertex pickOneOfTwoDupes(String transId, + String fromAppId, Graph g, Vertex vtxA, + Vertex vtxB, String ver, Boolean specialTenantRule, Loader loader, EELFLogger logger) throws AAIException { + + Vertex nullVtx = null; + Vertex preferredVtx = null; + + Long vidA = new Long(vtxA.id().toString()); + Long vidB = new Long(vtxB.id().toString()); + + String vtxANodeType = ""; + String vtxBNodeType = ""; + Object obj = vtxA.<Object>property("aai-node-type").orElse(null); + if (obj != null) { + vtxANodeType = obj.toString(); + } + obj = vtxB.<Object>property("aai-node-type").orElse(null); + if (obj != null) { + vtxBNodeType = obj.toString(); + } + + if (vtxANodeType.equals("") || (!vtxANodeType.equals(vtxBNodeType))) { + // Either they're not really dupes or there's some bad data - so + // don't pick one + return nullVtx; + } + + // Check that node A and B both have the same key values (or else they + // are not dupes) + // (We'll check dep-node later) + Collection<String> keyProps = loader.introspectorFromName(vtxANodeType).getKeys(); + Iterator<String> keyPropI = keyProps.iterator(); + while (keyPropI.hasNext()) { + String propName = keyPropI.next(); + String vtxAKeyPropVal = ""; + obj = vtxA.<Object>property(propName).orElse(null); + if (obj != null) { + vtxAKeyPropVal = obj.toString(); + } + String vtxBKeyPropVal = ""; + obj = vtxB.<Object>property(propName).orElse(null); + if (obj != null) { + vtxBKeyPropVal = obj.toString(); + } + + if (vtxAKeyPropVal.equals("") + || (!vtxAKeyPropVal.equals(vtxBKeyPropVal))) { + // Either they're not really dupes or they are missing some key + // data - so don't pick one + return nullVtx; + } + } + + // Collect the vid's and aai-node-types of the vertices that each vertex + // (A and B) is connected to. + ArrayList<String> vtxIdsConn2A = new ArrayList<String>(); + ArrayList<String> vtxIdsConn2B = new ArrayList<String>(); + HashMap<String, String> nodeTypesConn2A = new HashMap<String, String>(); + HashMap<String, String> nodeTypesConn2B = new HashMap<String, String>(); + + ArrayList <String> retArr = new ArrayList <String> (); + Iterator <Edge> eAI = vtxA.edges(Direction.BOTH); + while( eAI.hasNext() ){ + Edge ed = eAI.next(); + Vertex tmpVtx; + if (vtxA.equals(ed.inVertex())) { + tmpVtx = ed.outVertex(); + } else { + tmpVtx = ed.inVertex(); + } + if( tmpVtx == null ){ + retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< "); + } + else { + String conVid = tmpVtx.id().toString(); + String nt = ""; + obj = tmpVtx.<Object>property("aai-node-type").orElse(null); + if (obj != null) { + nt = obj.toString(); + } + nodeTypesConn2A.put(nt, conVid); + vtxIdsConn2A.add(conVid); + } + } + + Iterator <Edge> eBI = vtxB.edges(Direction.BOTH); + while( eBI.hasNext() ){ + Edge ed = eBI.next(); + Vertex tmpVtx; + + if (vtxB.equals(ed.inVertex())) { + tmpVtx = ed.outVertex(); + } else { + tmpVtx = ed.inVertex(); + } + if( tmpVtx == null ){ + retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< "); + } + else { + String conVid = tmpVtx.id().toString(); + String nt = ""; + obj = tmpVtx.<Object>property("aai-node-type").orElse(null); + if (obj != null) { + nt = obj.toString(); + } + nodeTypesConn2B.put(nt, conVid); + vtxIdsConn2B.add(conVid); + } + } + + // 1 - If this kind of node needs a dependent node for uniqueness, then + // verify that they both nodes point to the same dependent + // node (otherwise they're not really duplicates) + // Note - there are sometimes more than one dependent node type since + // one nodeType can be used in different ways. But for a + // particular node, it will only have one dependent node that + // it's connected to. + Collection<String> depNodeTypes = loader.introspectorFromName(vtxANodeType).getDependentOn(); + if (depNodeTypes.isEmpty()) { + // This kind of node is not dependent on any other. That is ok. + } else { + String depNodeVtxId4A = ""; + String depNodeVtxId4B = ""; + Iterator<String> iter = depNodeTypes.iterator(); + while (iter.hasNext()) { + String depNodeType = iter.next(); + if (nodeTypesConn2A.containsKey(depNodeType)) { + // This is the dependent node type that vertex A is using + depNodeVtxId4A = nodeTypesConn2A.get(depNodeType); + } + if (nodeTypesConn2B.containsKey(depNodeType)) { + // This is the dependent node type that vertex B is using + depNodeVtxId4B = nodeTypesConn2B.get(depNodeType); + } + } + if (depNodeVtxId4A.equals("") + || (!depNodeVtxId4A.equals(depNodeVtxId4B))) { + // Either they're not really dupes or there's some bad data - so + // don't pick either one + return nullVtx; + } + } + + if (vtxIdsConn2A.size() == vtxIdsConn2B.size()) { + // 2 - If they both have edges to all the same vertices, then return + // the one with the lower vertexId. + + // OR (2b)-- if this is the SPECIAL case -- of + // "tenant|vserver vs. tenant|service-subscription" + // then we pick/prefer the one that's connected to + // the service-subscription. AAI-8172 + boolean allTheSame = true; + Iterator<String> iter = vtxIdsConn2A.iterator(); + while (iter.hasNext()) { + String vtxIdConn2A = iter.next(); + if (!vtxIdsConn2B.contains(vtxIdConn2A)) { + allTheSame = false; + break; + } + } + + if (allTheSame) { + if (vidA < vidB) { + preferredVtx = vtxA; + } else { + preferredVtx = vtxB; + } + } + else if ( specialTenantRule ){ + // They asked us to apply a special rule if it applies + if(vtxIdsConn2A.size() == 2 && vtxANodeType.equals("tenant") ){ + // We're dealing with two tenant nodes which each just have + // two connections. One must be the parent (cloud-region) + // which we check in step 1 above. If one connects to + // a vserver and the other connects to a service-subscription, + // our special rule is to keep the one connected + // to the + if( nodeTypesConn2A.containsKey("vserver") && nodeTypesConn2B.containsKey("service-subscription") ){ + String infMsg = " WARNING >>> we are using the special tenant rule to choose to " + + " delete tenant vtxId = " + vidA + ", and keep tenant vtxId = " + vidB ; + System.out.println(infMsg); + logger.info( infMsg ); + preferredVtx = vtxB; + } + else if( nodeTypesConn2B.containsKey("vserver") && nodeTypesConn2A.containsKey("service-subscription") ){ + String infMsg = " WARNING >>> we are using the special tenant rule to choose to " + + " delete tenant vtxId = " + vidB + ", and keep tenant vtxId = " + vidA ; + System.out.println(infMsg); + logger.info( infMsg ); + preferredVtx = vtxA; + } + } + } + } else if (vtxIdsConn2A.size() > vtxIdsConn2B.size()) { + // 3 - VertexA is connected to more things than vtxB. + // We'll pick VtxA if its edges are a superset of vtxB's edges. + boolean missingOne = false; + Iterator<String> iter = vtxIdsConn2B.iterator(); + while (iter.hasNext()) { + String vtxIdConn2B = iter.next(); + if (!vtxIdsConn2A.contains(vtxIdConn2B)) { + missingOne = true; + break; + } + } + if (!missingOne) { + preferredVtx = vtxA; + } + } else if (vtxIdsConn2B.size() > vtxIdsConn2A.size()) { + // 4 - VertexB is connected to more things than vtxA. + // We'll pick VtxB if its edges are a superset of vtxA's edges. + boolean missingOne = false; + Iterator<String> iter = vtxIdsConn2A.iterator(); + while (iter.hasNext()) { + String vtxIdConn2A = iter.next(); + if (!vtxIdsConn2B.contains(vtxIdConn2A)) { + missingOne = true; + break; + } + } + if (!missingOne) { + preferredVtx = vtxB; + } + } else { + preferredVtx = nullVtx; + } + + return (preferredVtx); + + } // end of pickOneOfTwoDupes() + + + /** + * Group verts by dep nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param g the g + * @param version the version + * @param nType the n type + * @param passedVertList the passed vert list + * @param dbMaps the db maps + * @return the hash map + * @throws AAIException the AAI exception + */ + private static HashMap<String, ArrayList<Vertex>> groupVertsByDepNodes( + String transId, String fromAppId, Graph g, String version, + String nType, ArrayList<Vertex> passedVertList, Loader loader) + throws AAIException { + + // Given a list of Titan Vertices, group them together by dependent + // nodes. Ie. if given a list of ip address nodes (assumed to all + // have the same key info) they might sit under several different + // parent vertices. + // Under Normal conditions, there would only be one per parent -- but + // we're trying to find duplicates - so we allow for the case + // where more than one is under the same parent node. + + HashMap<String, ArrayList<Vertex>> retHash = new HashMap<String, ArrayList<Vertex>>(); + + // Find out what types of nodes the passed in nodes can depend on + ArrayList<String> depNodeTypeL = new ArrayList<String>(); + Collection<String> depNTColl = loader.introspectorFromName(nType).getDependentOn(); + Iterator<String> ntItr = depNTColl.iterator(); + while (ntItr.hasNext()) { + depNodeTypeL.add(ntItr.next()); + } + // For each vertex they passed us, we want find the vertex it + // is dependent on so we can keep track of who-all is connected + // to that parent. + if (passedVertList != null) { + Iterator<Vertex> iter = passedVertList.iterator(); + while (iter.hasNext()) { + Vertex thisVert = iter.next(); + Iterator <String> depNtItr = depNTColl.iterator(); + while (depNtItr.hasNext()) { + GraphTraversal<Vertex, Vertex> modPipe = null; + // NOTE -- if we change the direction of parent/child edges, we will need + // the "in" below to become "out" + modPipe = g.traversal().V(thisVert).in().has("aai-node-type", depNtItr.next() ); + if( modPipe == null || !modPipe.hasNext() ){ + //System.out.println("DEBUG - didn't find any [" + targetStep + "] connected to this guy (which is ok)"); + } + else { + while( modPipe.hasNext() ){ + Vertex depVert = modPipe.next(); + String parentVid = depVert.id().toString(); + if (retHash.containsKey(parentVid)) { + // add this vert to the list for this parent key + retHash.get(parentVid).add(thisVert); + } else { + // This is the first one we found on this parent + ArrayList<Vertex> vList = new ArrayList<Vertex>(); + vList.add(thisVert); + retHash.put(parentVid, vList); + } + } + } + } + } + } + + return retHash; + + }// end of groupVertsByDepNodes() + + + /** + * Delete non keepers if appropriate. + * + * @param g the g + * @param dupeInfoList the dupe info string + * @param logger the EELFLogger + * @return the boolean + */ + private static Boolean deleteNonKeepers(Graph g, + ArrayList<String> dupeInfoList, EELFLogger logger ) { + + // This assumes that each dupeInfoString is in the format of + // pipe-delimited vid's followed by either "keepVid=xyz" or "keepVid=UNDETERMINED" + // ie. "3456|9880|keepVid=3456" + + boolean didADelFlag = false; + for( int n = 0; n < dupeInfoList.size(); n++ ){ + String dupeInfoString = dupeInfoList.get(n); + boolean tmpFlag = deleteNonKeeperForOneSet( g, dupeInfoString, logger ); + didADelFlag = tmpFlag | didADelFlag; + } + + return didADelFlag; + + }// end of deleteNonKeepers() + + + /** + * Delete non keepers if appropriate. + * + * @param g the g + * @param dupeSetStr the dupe string + * @param logger the EELFLogger + * @return the boolean + */ + private static Boolean deleteNonKeeperForOneSet(Graph g, + String dupeInfoString, EELFLogger logger ) { + + Boolean deletedSomething = false; + // This assumes that each dupeInfoString is in the format of + // pipe-delimited vid's followed by either "keepVid=xyz" or "keepVid=UNDETERMINED" + // ie. "3456|9880|keepVid=3456" + + + String[] dupeArr = dupeInfoString.split("\\|"); + ArrayList<String> idArr = new ArrayList<String>(); + int lastIndex = dupeArr.length - 1; + for (int i = 0; i <= lastIndex; i++) { + if (i < lastIndex) { + // This is not the last entry, it is one of the dupes, + String vidString = dupeArr[i]; + idArr.add(vidString); + } else { + // This is the last entry which should tell us if we have a + // preferred keeper + String prefString = dupeArr[i]; + if (prefString.equals("KeepVid=UNDETERMINED")) { + // They sent us a bad string -- nothing should be deleted if + // no dupe could be tagged as preferred. + return false; + } else { + // If we know which to keep, then the prefString should look + // like, "KeepVid=12345" + String[] prefArr = prefString.split("="); + if (prefArr.length != 2 || (!prefArr[0].equals("KeepVid"))) { + String emsg = "Bad format. Expecting KeepVid=999999"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + return false; + } else { + String keepVidStr = prefArr[1]; + if (idArr.contains(keepVidStr)) { + idArr.remove(keepVidStr); + // So now, the idArr should just contain the vid's + // that we want to remove. + for (int x = 0; x < idArr.size(); x++) { + boolean okFlag = true; + String thisVid = idArr.get(x); + try { + long longVertId = Long.parseLong(thisVid); + Vertex vtx = g.traversal().V(longVertId).next(); + String msg = "--->>> We will delete node with VID = " + thisVid + " <<<---"; + System.out.println(msg); + logger.info(msg); + vtx.remove(); + } + catch (Exception e) { + okFlag = false; + String emsg = "ERROR trying to delete VID = " + thisVid + ", [" + e + "]"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + } + if (okFlag) { + String infMsg = " DELETED VID = " + thisVid; + logger.info(infMsg); + System.out.println(infMsg); + deletedSomething = true; + } + } + } else { + String emsg = "ERROR - Vertex Id to keep not found in list of dupes. dupeInfoString = [" + + dupeInfoString + "]"; + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + System.out.println(emsg); + return false; + } + } + }// else we know which one to keep + }// else last entry + }// for each vertex in a group + + return deletedSomething; + + }// end of deleteNonKeeperForOneSet() + + + /** + * Get values of the key properties for a node. + * + * @param tvx the vertex to pull the properties from + * @param keyPropertyNames ArrayList (ordered) of key prop names + * @param logger the EELFLogger + * @return a hashMap of the propertyNames/values + */ + private static HashMap <String,Object> getNodeKeyVals( Vertex tvx, + ArrayList <String> keyPropNamesArr, EELFLogger logger ) { + + HashMap <String,Object> retHash = new HashMap <String,Object>(); + Iterator <String> propItr = keyPropNamesArr.iterator(); + while( propItr.hasNext() ){ + String propName = propItr.next(); + if( tvx != null ){ + Object propValObj = tvx.property(propName).orElse(null); + retHash.put(propName, propValObj); + } + } + return retHash; + + }// End of getNodeKeyVals() + + + /** + * Get values of the key properties for a node as a single string + * + * @param tvx the vertex to pull the properties from + * @param keyPropertyNames collection of key prop names + * @param logger the EELFLogger + * @return a String of concatenated values + */ + private static String getNodeKeyValString( Vertex tvx, + ArrayList <String> keyPropNamesArr, EELFLogger logger ) { + + // -- NOTE -- for what we're using this for, we would need to + // guarantee that the properties are always in the same order + + String retString = ""; + Iterator <String> propItr = keyPropNamesArr.iterator(); + while( propItr.hasNext() ){ + String propName = propItr.next(); + if( tvx != null ){ + Object propValObj = tvx.property(propName).orElse(null); + retString = " " + retString + propValObj.toString(); + } + } + return retString; + + }// End of getNodeKeyValString() + + + /** + * Find duplicate sets from two dupe runs. + * + * @param firstPassDupeSets from the first pass + * @param secondPassDupeSets from the second pass + * @param EELFLogger logger + * @return commonDupeSets that are common to both passes and have a determined keeper + */ + private static ArrayList <String> figureWhichDupesStillNeedFixing( ArrayList <String>firstPassDupeSets, + ArrayList <String> secondPassDupeSets, EELFLogger logger ){ + + ArrayList <String> common2BothSet = new ArrayList <String> (); + + // We just want to look for entries from the first set which have identical (almost) + // entries in the secondary set. I say "almost" because the order of the + // vid's to delete may be in a different order, but we only want to use it if + // they have all the same values. Note also - we're just looking for + // the sets where we have a candidate to delete. + + // The duplicate-set Strings are in this format: + // "1234|5678|keepVid=UNDETERMINED" (if there were 2 dupes, and we + // couldn't figure out which one to keep) + // or, "100017|200027|30037|keepVid=30037" (if there were 3 dupes and we + // thought the third one was the one that should survive) + + if( firstPassDupeSets == null || firstPassDupeSets.isEmpty() + || secondPassDupeSets == null || secondPassDupeSets.isEmpty() ){ + // If either set is empty, then our return list has to be empty too + return common2BothSet; + } + + boolean needToParse = false; + for( int x = 0; x < secondPassDupeSets.size(); x++ ){ + String secPassDupeSetStr = secondPassDupeSets.get(x); + if( secPassDupeSetStr.endsWith("UNDETERMINED") ){ + // This is a set of dupes where we could not pick one + // to delete - so don't include it on our list for + // fixing. + } + else if( firstPassDupeSets.contains(secPassDupeSetStr) ){ + // We have lucked out and do not even need to parse this since + // it was in the other array with any dupes listed in the same order + // This is actually the most common scenario since there is + // usually only one dupe, so order doesn't matter. + common2BothSet.add(secPassDupeSetStr); + } + else { + // We'll need to do some parsing to check this one + needToParse = true; + } + } + + if( needToParse ){ + // Make a hash from the first and second Pass data + // where the key is the vid to KEEP and the value is an + // array of (String) vids that would get deleted. + HashMap <String,ArrayList<String>> firstPassHash = makeKeeperHashOfDupeStrings( firstPassDupeSets, common2BothSet, logger ); + + HashMap <String,ArrayList<String>> secPassHash = makeKeeperHashOfDupeStrings( secondPassDupeSets, common2BothSet, logger ); + + // Loop through the secondPass data and keep the ones + // that check out against the firstPass set. + for( Map.Entry<String, ArrayList<String>> entry : secPassHash.entrySet() ){ + boolean skipThisOne = false; + String secKey = entry.getKey(); + ArrayList <String> secList = entry.getValue(); + if( !firstPassHash.containsKey(secKey) ){ + // The second pass found this delete candidate, but not the first pass + skipThisOne = true; + } + else { + // They both think they should keep this VID, check the associated deletes for it. + ArrayList <String> firstList = firstPassHash.get(secKey); + for( int z = 0; z < secList.size(); z++ ){ + if( !firstList.contains(secList.get(z)) ){ + // The first pass did not think this needed to be deleted + skipThisOne = true; + } + } + } + if( !skipThisOne ){ + // Put the string back together and pass it back + // Not beautiful, but no time to make it nice right now... + // Put it back in the format: "3456|9880|keepVid=3456" + String thisDelSetStr = ""; + for( int z = 0; z < secList.size(); z++ ){ + if( z == 0 ){ + thisDelSetStr = secList.get(z); + } + else { + thisDelSetStr = thisDelSetStr + "|" + secList.get(z); + } + } + thisDelSetStr = thisDelSetStr + "|keepVid=" + secKey; + common2BothSet.add(thisDelSetStr); + } + } + + } + return common2BothSet; + + }// figureWhichDupesStillNeedFixing + + + private static HashMap <String, ArrayList <String>> makeKeeperHashOfDupeStrings( ArrayList <String> dupeSets, + ArrayList <String> excludeSets, EELFLogger logger ){ + + HashMap <String,ArrayList<String>> keeperHash = new HashMap <String, ArrayList<String>>(); + + for( int x = 0; x < dupeSets.size(); x++ ){ + String tmpSetStr = dupeSets.get(x); + if( excludeSets.contains(tmpSetStr) ){ + // This isn't one of the ones we needed to parse. + continue; + } + + String[] dupeArr = tmpSetStr.split("\\|"); + ArrayList<String> delIdArr = new ArrayList<String>(); + int lastIndex = dupeArr.length - 1; + for (int i = 0; i <= lastIndex; i++) { + if (i < lastIndex) { + // This is not the last entry, it is one of the dupes + delIdArr.add(dupeArr[i]); + } + else { + // This is the last entry which should tell us if we + // have a preferred keeper and how many dupes we had + String prefString = dupeArr[i]; + if( i == 1 ){ + // There was only one dupe, so if we were gonna find + // it, we would have found it above with no parsing. + } + else if (prefString.equals("KeepVid=UNDETERMINED")) { + // This one had no determined keeper, so we don't + // want it. + } + else { + // If we know which to keep, then the prefString + // should look like, "KeepVid=12345" + String[] prefArr = prefString.split("="); + if( prefArr.length != 2 + || (!prefArr[0].equals("KeepVid")) ) { + String infMsg = "Bad format in figureWhichDupesStillNeedFixing(). Expecting " + + " KeepVid=999999 but string looks like: [" + tmpSetStr + "]"; + System.out.println(infMsg); + logger.info(infMsg); + } + else { + keeperHash.put(prefArr[0], delIdArr); + } + } + } + } + } + + return keeperHash; + + }// End makeHashOfDupeStrings() + + + /** + * Get values of the key properties for a node. + * + * @param g the g + * @param dupeInfoString + * @param logger the EELFLogger + * @return void + */ + static private void showNodeDetailsForADupeSet(Graph g, String dupeInfoString, EELFLogger logger) { + + // dang... parsing this string once again... + + String[] dupeArr = dupeInfoString.split("\\|"); + int lastIndex = dupeArr.length - 1; + for (int i = 0; i <= lastIndex; i++) { + if (i < lastIndex) { + // This is not the last entry, it is one of the dupes, + String vidString = dupeArr[i]; + long longVertId = Long.parseLong(vidString); + Vertex vtx = g.traversal().V(longVertId).next(); + showNodeInfo(logger, vtx, false); + } else { + // This is the last entry which should tell us if we have a + // preferred keeper + String prefString = dupeArr[i]; + if (prefString.equals("KeepVid=UNDETERMINED")) { + String msg = " Our algorithm cannot choose from among these, so they will all be kept. -------\n"; + System.out.println(msg); + logger.info(msg); + } else { + // If we know which to keep, then the prefString should look + // like, "KeepVid=12345" + String[] prefArr = prefString.split("="); + if (prefArr.length != 2 || (!prefArr[0].equals("KeepVid"))) { + String emsg = "Bad format. Expecting KeepVid=999999"; + System.out.println(emsg); + LoggingContext.statusCode(StatusCode.ERROR); + LoggingContext.responseCode(LoggingContext.DATA_ERROR); + logger.error(emsg); + LoggingContext.statusCode(StatusCode.COMPLETE); + LoggingContext.responseCode(LoggingContext.SUCCESS); + } else { + String keepVidStr = prefArr[1]; + String msg = " vid = " + keepVidStr + " is the one that we would KEEP. ------\n"; + System.out.println(msg); + logger.info(msg); + } + } + } + } + + }// End of showNodeDetailsForADupeSet() + + private static int graphIndex = 1; + + public static TitanGraph setupGraph(EELFLogger logger){ + + TitanGraph titanGraph = null; + + + try (InputStream inputStream = new FileInputStream(AAIConstants.REALTIME_DB_CONFIG);){ + + Properties properties = new Properties(); + properties.load(inputStream); + + if("inmemory".equals(properties.get("storage.backend"))){ + titanGraph = AAIGraph.getInstance().getGraph(); + graphType = "inmemory"; + } else { + titanGraph = TitanFactory.open(new AAIGraphConfig.Builder(AAIConstants.REALTIME_DB_CONFIG).forService(DupeTool.class.getSimpleName()).withGraphType("realtime" + graphIndex).buildConfiguration()); + graphIndex++; + } + } catch (Exception e) { + logger.error("Unable to open the graph", LogFormatTools.getStackTop(e)); + } + + return titanGraph; + } + + public static void closeGraph(TitanGraph graph, EELFLogger logger){ + + try { + if("inmemory".equals(graphType)) { + return; + } + if( graph != null && graph.isOpen() ){ + graph.tx().close(); + graph.close(); + } + } catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed{ + logger.warn("WARNING from final graph.shutdown()", ex); + } + } +} + + diff --git a/aai-resources/src/main/java/org/onap/aai/dbgen/ForceDeleteTool.java b/aai-resources/src/main/java/org/onap/aai/dbgen/ForceDeleteTool.java index 9a7fc39..7dc43d7 100644 --- a/aai-resources/src/main/java/org/onap/aai/dbgen/ForceDeleteTool.java +++ b/aai-resources/src/main/java/org/onap/aai/dbgen/ForceDeleteTool.java @@ -20,12 +20,16 @@ * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.dbgen; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; import java.util.Scanner; import java.util.UUID; +import org.apache.commons.configuration.ConfigurationException; 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; @@ -33,7 +37,9 @@ 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.dbmap.AAIGraphConfig; +import org.onap.aai.dbmap.AAIGraph; import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.LogFormatTools; import org.onap.aai.logging.LoggingContext; import org.onap.aai.logging.LoggingContext.StatusCode; import org.onap.aai.serialization.db.AAIDirection; @@ -53,6 +59,20 @@ import com.thinkaurelius.titan.core.TitanGraph; public class ForceDeleteTool { private static final String FROMAPPID = "AAI-DB"; private static final String TRANSID = UUID.randomUUID().toString(); + + private static String graphType = "realdb"; + + public static boolean SHOULD_EXIT_VM = true; + + public static int EXIT_VM_STATUS_CODE = -1; + + public static void exit(int statusCode){ + if(SHOULD_EXIT_VM){ + System.exit(1); + } + EXIT_VM_STATUS_CODE = statusCode; + } + /* * The main method. * @@ -101,7 +121,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(" No value passed with -action option. "); - System.exit(0); + exit(0); } actionVal = args[i]; argStr4Msg = argStr4Msg + " " + actionVal; @@ -112,7 +132,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(" No value passed with -userId option. "); - System.exit(0); + exit(0); } userIdVal = args[i]; argStr4Msg = argStr4Msg + " " + userIdVal; @@ -129,7 +149,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(" No value passed with -vertexId option. "); - System.exit(0); + exit(0); } String nextArg = args[i]; argStr4Msg = argStr4Msg + " " + nextArg; @@ -140,7 +160,7 @@ public class ForceDeleteTool { LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error("Bad value passed with -vertexId option: [" + nextArg + "]"); - System.exit(0); + exit(0); } } else if (thisArg.equals("-params4Collect")) { @@ -149,7 +169,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(" No value passed with -params4Collect option. "); - System.exit(0); + exit(0); } dataString = args[i]; argStr4Msg = argStr4Msg + " " + dataString; @@ -160,7 +180,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(" No value passed with -edgeId option. "); - System.exit(0); + exit(0); } String nextArg = args[i]; argStr4Msg = argStr4Msg + " " + nextArg; @@ -172,7 +192,7 @@ public class ForceDeleteTool { logger.error(" Unrecognized argument passed to ForceDeleteTool: [" + thisArg + "]. "); logger.error(" Valid values are: -action -userId -vertexId -edgeId -overRideProtection -params4Collect -DISPLAY_ALL_VIDS"); - System.exit(0); + exit(0); } } } @@ -183,7 +203,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(emsg); - System.exit(0); + exit(0); } if( actionVal.equals("DELETE_NODE") && vertexIdLong == 0 ){ @@ -192,7 +212,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(emsg); - System.exit(0); + exit(0); } else if( actionVal.equals("DELETE_EDGE") && edgeIdStr.equals("")){ String emsg = "ERROR: No edge ID passed on DELETE_EDGE request. \n"; @@ -200,7 +220,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(emsg); - System.exit(0); + exit(0); } @@ -211,7 +231,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(emsg); - System.exit(0); + exit(0); } String msg = ""; @@ -219,14 +239,14 @@ public class ForceDeleteTool { try { AAIConfig.init(); System.out.println(" ---- NOTE --- about to open graph (takes a little while)--------\n"); - graph = TitanFactory.open(new AAIGraphConfig.Builder(AAIConstants.REALTIME_DB_CONFIG).forService(ForceDelete.class.getSimpleName()).withGraphType("realtime1").buildConfiguration()); + graph = setupGraph(logger); if( graph == null ){ String emsg = "could not get graph object in ForceDeleteTool() \n"; System.out.println(emsg); LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR); logger.error(emsg); - System.exit(0); + exit(0); } } catch (AAIException e1) { @@ -235,7 +255,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.UNKNOWN_ERROR); logger.error(msg); - System.exit(0); + exit(0); } catch (Exception e2) { msg = e2.toString(); @@ -243,7 +263,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.UNKNOWN_ERROR); logger.error(msg); - System.exit(0); + exit(0); } msg = "ForceDelete called by: userId [" + userIdVal + "] with these params: [" + argStr4Msg + "]"; @@ -265,7 +285,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(msg); - System.exit(0); + exit(0); } GraphTraversal<Vertex, Vertex> g = graph.traversal().V(); String qStringForMsg = " graph.traversal().V()"; @@ -279,7 +299,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR); logger.error(msg); - System.exit(0); + exit(0); } else { String propName = paramArr[i].substring(0,pipeLoc); @@ -306,7 +326,7 @@ public class ForceDeleteTool { LoggingContext.statusCode(StatusCode.ERROR); LoggingContext.responseCode(LoggingContext.DATA_ERROR); logger.error(msg); - System.exit(0); + exit(0); } String infMsg = "\n\n Found: " + resCount + " nodes for this query: [" + qStringForMsg + "]\n"; @@ -360,7 +380,7 @@ public class ForceDeleteTool { String infMsg = ">>>>>>>>>> Edge with edgeId = " + edgeIdStr + " not found."; logger.info( infMsg ); System.out.println(infMsg); - System.exit(0); + exit(0); } if( fd.getEdgeDelConfirmation(logger, userIdVal, thisEdge, overRideProtection) ){ @@ -375,16 +395,17 @@ public class ForceDeleteTool { System.out.println(infMsg); logger.info( infMsg ); } - System.exit(0); + exit(0); } else { String emsg = "Unknown action parameter [" + actionVal + "] passed to ForceDeleteTool(). Valid values = COLLECT_DATA, DELETE_NODE or DELETE_EDGE \n"; System.out.println(emsg); logger.info( emsg ); - System.exit(0); + exit(0); } - System.exit(0); + closeGraph(graph, logger); + exit(0); }// end of main() @@ -792,7 +813,49 @@ public class ForceDeleteTool { } // End of getNodeDelConfirmation() } - + + public static TitanGraph setupGraph(EELFLogger logger){ + + TitanGraph titanGraph = null; + + try (InputStream inputStream = new FileInputStream(AAIConstants.REALTIME_DB_CONFIG);){ + + Properties properties = new Properties(); + properties.load(inputStream); + + if("inmemory".equals(properties.get("storage.backend"))){ + titanGraph = AAIGraph.getInstance().getGraph(); + graphType = "inmemory"; + } else { + titanGraph = TitanFactory.open( + new AAIGraphConfig.Builder(AAIConstants.REALTIME_DB_CONFIG) + .forService(ForceDeleteTool.class.getSimpleName()) + .withGraphType("realtime1") + .buildConfiguration() + ); + } + } catch (Exception e) { + logger.error("Unable to open the graph", LogFormatTools.getStackTop(e)); + } + + return titanGraph; + } + + public static void closeGraph(TitanGraph graph, EELFLogger logger){ + + try { + if("inmemory".equals(graphType)) { + return; + } + if( graph != null && graph.isOpen() ){ + graph.tx().close(); + graph.close(); + } + } catch (Exception ex) { + // Don't throw anything because Titan sometimes is just saying that the graph is already closed{ + logger.warn("WARNING from final graph.shutdown()", ex); + } + } } diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/util/ServicePropertiesMapBean.java b/aai-resources/src/main/java/org/onap/aai/interceptors/AAIContainerFilter.java index 71c290b..fdd3edb 100644 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/util/ServicePropertiesMapBean.java +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/AAIContainerFilter.java @@ -19,20 +19,25 @@ * * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ -package org.onap.aai.ajsc_aai.util; +package org.onap.aai.interceptors; -import org.onap.aai.ajsc_aai.filemonitor.ServicePropertiesMap; +import java.util.UUID; -public class ServicePropertiesMapBean { +import org.onap.aai.util.FormatDate; - /** - * Gets the property. - * - * @param propFileName the prop file name - * @param propertyKey the property key - * @return the property - */ - public static String getProperty(String propFileName, String propertyKey) { - return ServicePropertiesMap.getProperty(propFileName, propertyKey); +public abstract class AAIContainerFilter { + + protected String genDate() { + FormatDate fd = new FormatDate("YYMMdd-HH:mm:ss:SSS"); + return fd.getDateTime(); + } + + protected boolean isValidUUID(String transId) { + try { + UUID.fromString(transId); + } catch (IllegalArgumentException e) { + return false; + } + return true; } } diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/AAIHeaderProperties.java b/aai-resources/src/main/java/org/onap/aai/interceptors/AAIHeaderProperties.java index 733383a..8eca9b6 100644 --- a/aai-resources/src/main/java/org/onap/aai/interceptors/AAIHeaderProperties.java +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/AAIHeaderProperties.java @@ -21,7 +21,21 @@ */ package org.onap.aai.interceptors; -public class AAIHeaderProperties { - +public final class AAIHeaderProperties { + + private AAIHeaderProperties(){} + public static final String REQUEST_CONTEXT = "aai-request-context"; + + public static final String HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override"; + + public static final String TRANSACTION_ID = "X-TransactionId"; + + public static final String FROM_APP_ID = "X-FromAppId"; + + public static final String AAI_TX_ID = "X-AAI-TXID"; + + public static final String AAI_REQUEST = "X-REQUEST"; + + public static final String AAI_REQUEST_TS = "X-REQUEST-TS"; } diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSInInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSInInterceptor.java deleted file mode 100644 index 7d8112d..0000000 --- a/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSInInterceptor.java +++ /dev/null @@ -1,285 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.interceptors; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.ws.rs.core.MediaType; - -import org.apache.commons.io.IOUtils; -import org.apache.cxf.helpers.CastUtils; -import org.apache.cxf.interceptor.LoggingMessage; -import org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor; -import org.apache.cxf.message.Message; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.rest.util.EchoResponse; -import org.onap.aai.util.AAIConfig; -import org.onap.aai.util.AAIConstants; -import org.onap.aai.util.FormatDate; -import org.onap.aai.util.HbaseSaltPrefixer; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import org.slf4j.MDC; - -public class AAILogJAXRSInInterceptor extends JAXRSInInterceptor { - - protected final String COMPONENT = "aairest"; - protected final String CAMEL_REQUEST ="CamelHttpUrl"; - private static final Pattern uuidPattern = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"); - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAILogJAXRSInInterceptor.class); - - /** - * {@inheritDoc} - */ - public void handleMessage(Message message) { - - boolean go = false; - String uri = null; - String query = null; - try { - - uri = (String)message.get(CAMEL_REQUEST); - if (uri != null) { - query = (String)message.get(Message.QUERY_STRING); - } - - if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_INTERCEPTOR).equalsIgnoreCase("true") && - AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_ENABLED).equalsIgnoreCase("true")) { - go = true; - message.getExchange().put("AAI_LOGGING_HBASE_ENABLED", 1); - if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_LOGREQUEST).equalsIgnoreCase("true") ) { - message.getExchange().put("AAI_LOGGING_HBASE_LOGREQUEST", 1); - } - if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_LOGRESPONSE).equalsIgnoreCase("true") ) { - message.getExchange().put("AAI_LOGGING_HBASE_LOGRESPONSE", 1); - } - } - if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_ENABLED).equalsIgnoreCase("true") ) { - go = true; - message.getExchange().put("AAI_LOGGING_TRACE_ENABLED", 1); - if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_LOGREQUEST).equalsIgnoreCase("true") ) { - message.getExchange().put("AAI_LOGGING_TRACE_LOGREQUEST", 1); - } - if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_LOGRESPONSE).equalsIgnoreCase("true") ) { - message.getExchange().put("AAI_LOGGING_TRACE_LOGRESPONSE", 1); - } - } - } catch (AAIException e1) { - ErrorLogHelper.logException(e1); - } - - if (uri.contains(EchoResponse.echoPath)) { - // if it's a health check, we don't want to log ANYTHING if it's a lightweight one - if (query == null) { - if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED")) { - message.getExchange().remove("AAI_LOGGING_HBASE_ENABLED"); - } - if (message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { - message.getExchange().remove("AAI_LOGGING_TRACE_ENABLED"); - } - go = false; - } - } - else if (uri.contains("/translog/")) { - // if it's a translog query, we don't want to log the responses - if (message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGRESPONSE")) { - message.getExchange().remove("AAI_LOGGING_HBASE_LOGRESPONSE"); - } - if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGRESPONSE")) { - message.getExchange().remove("AAI_LOGGING_TRACE_LOGRESPONSE"); - } - } - - if (go == false) { // there's nothing to do - return; - } - - // DONE: get a TXID based on hostname, time (YYYYMMDDHHMMSSMILLIS, and LoggingMessage.nextId(); 20150326145301-1 - String now = genDate(); - - message.getExchange().put("AAI_RQST_TM", now); - - String id = (String)message.getExchange().get(LoggingMessage.ID_KEY); - - String fullId = null; - try { - if (id == null) { - id = LoggingMessage.nextId(); - } - fullId = AAIConfig.get(AAIConstants.AAI_NODENAME) + "-" + now + "-" + id; - fullId = HbaseSaltPrefixer.getInstance().prependSalt(fullId); - message.getExchange().put(LoggingMessage.ID_KEY, fullId); - } catch (AAIException e1) { - LOGGER.debug("config problem", e1); - } - - if (fullId == null) { - fullId = now + "-" + id; - fullId = HbaseSaltPrefixer.getInstance().prependSalt(fullId); - } - message.put(LoggingMessage.ID_KEY, fullId); - final LoggingMessage buffer = new LoggingMessage("Message", fullId); - - Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE); - if (responseCode != null) { - buffer.getResponseCode().append(responseCode); - } - - String encoding = (String)message.get(Message.ENCODING); - - if (encoding != null) { - buffer.getEncoding().append(encoding); - } - String httpMethod = (String)message.get(Message.HTTP_REQUEST_METHOD); - if (httpMethod != null) { - buffer.getHttpMethod().append(httpMethod); - } - - String ct = (String)message.get(Message.CONTENT_TYPE); - if (ct != null) { - if ("*/*".equals(ct)) { - message.put(Message.CONTENT_TYPE, MediaType.APPLICATION_JSON); - ct = MediaType.APPLICATION_JSON; - } - buffer.getContentType().append(ct); - - } - Object headers = message.get(Message.PROTOCOL_HEADERS); - if (headers != null) { - buffer.getHeader().append(headers); - - Map<String, List<String>> headersList = CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS)); - String transId = ""; - List<String> xt = headersList.get("X-TransactionId"); - String newTransId = transId; - boolean missingTransId = false; - boolean replacedTransId = false; - String logMsg = null; - if (xt != null) { - for (String transIdValue : xt) { - transId = transIdValue; - } - Matcher matcher = uuidPattern.matcher(transId); - if (!matcher.find()) { - replacedTransId = true; - // check if there's a colon, and check the first group? - if (transId.contains(":")) { - String[] uuidParts = transId.split(":"); - Matcher matcher2 = uuidPattern.matcher(uuidParts[0]); - if (matcher2.find()) { - newTransId = uuidParts[0]; - } else { - // punt, we tried to find it, it has a colon but no UUID-1 - newTransId = UUID.randomUUID().toString(); - } - } else { - newTransId = UUID.randomUUID().toString(); - } - } - } else { - newTransId = UUID.randomUUID().toString(); - missingTransId = true; - } - - if (missingTransId || replacedTransId) { - List<String> txList = new ArrayList<String>(); - txList.add(newTransId); - headersList.put("X-TransactionId", txList); - if (missingTransId) { - logMsg = "Missing requestID. Assigned " + newTransId; - } else if (replacedTransId) { - logMsg = "Replaced invalid requestID of " + transId + " Assigned " + newTransId; - } - MDC.put("RequestId",newTransId); - } - else { - MDC.put("RequestId",transId); - } - - List<String> fromAppIdList = headersList.get("X-FromAppId"); - if (fromAppIdList != null) { - String fromAppId = null; - for (String fromAppIdValue : fromAppIdList) { - fromAppId = fromAppIdValue; - } - MDC.put("PartnerName",fromAppId); - } - - List<String> contentType = headersList.get("Content-Type"); - if (contentType == null) { - ct = (String)message.get(Message.CONTENT_TYPE); - headersList.put(Message.CONTENT_TYPE, Collections.singletonList(ct)); - } - - LOGGER.auditEvent("REST " + httpMethod + " " + ((query != null)? uri+"?"+query : uri) + " HbaseTxId=" + fullId); - LOGGER.info(logMsg); - } - - - if (uri != null) { - buffer.getAddress().append(uri); - if (query != null) { - buffer.getAddress().append("?").append(query); - } - } - - InputStream is = message.getContent(InputStream.class); - if (is != null) { - try { - String currentPayload = IOUtils.toString(is, "UTF-8"); - IOUtils.closeQuietly(is); - buffer.getPayload().append(currentPayload); - is = IOUtils.toInputStream(currentPayload, "UTF-8"); - message.setContent(InputStream.class, is); - IOUtils.closeQuietly(is); - } catch (Exception e) { - // It's ok to not have request input content - // throw new Fault(e); - } - } - - // this will be saved in the message exchange, and can be pulled out later... - message.getExchange().put(fullId + "_REQUEST", buffer.toString()); - } - - /** - * Gen date. - * - * @param aaiLogger the aai logger - * @param logline the logline - * @return the string - */ - protected String genDate() { - FormatDate fd = new FormatDate( "YYMMdd-HH:mm:ss:SSS"); - return fd.getDateTime(); - } - -} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSOutInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSOutInterceptor.java deleted file mode 100644 index 3b1f50c..0000000 --- a/aai-resources/src/main/java/org/onap/aai/interceptors/AAILogJAXRSOutInterceptor.java +++ /dev/null @@ -1,303 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.interceptors; - -import java.io.OutputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.cxf.helpers.CastUtils; -import org.apache.cxf.interceptor.LoggingMessage; -import org.apache.cxf.io.CacheAndWriteOutputStream; -import org.apache.cxf.io.CachedOutputStream; -import org.apache.cxf.io.CachedOutputStreamCallback; -import org.apache.cxf.jaxrs.interceptor.JAXRSOutInterceptor; -import org.apache.cxf.message.Message; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.util.AAIConfig; -import org.onap.aai.util.AAIConstants; -import org.onap.aai.util.FormatDate; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -// right after the request is complete, there may be content -public class AAILogJAXRSOutInterceptor extends JAXRSOutInterceptor { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAILogJAXRSOutInterceptor.class); - - protected final String COMPONENT = "aairest"; - protected final String CAMEL_REQUEST = "CamelHttpUrl"; - - /** - * {@inheritDoc} - */ - public void handleMessage(Message message) { - - String fullId = (String) message.getExchange().get(LoggingMessage.ID_KEY); - - Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>) message.get(Message.PROTOCOL_HEADERS)); - if (headers == null) { - headers = new HashMap<String, List<String>>(); - } - - headers.put("X-AAI-TXID", Collections.singletonList(fullId)); - message.put(Message.PROTOCOL_HEADERS, headers); - - Message outMessage = message.getExchange().getOutMessage(); - final OutputStream os = outMessage.getContent(OutputStream.class); - if (os == null) { - return; - } - - // we only want to register the callback if there is good reason for it. - if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED") || message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { - - final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os); - message.setContent(OutputStream.class, newOut); - newOut.registerCallback(new LoggingCallback(message, os)); - } - - } - - class LoggingCallback implements CachedOutputStreamCallback { - - private final Message message; - private final OutputStream origStream; - - public LoggingCallback(final Message msg, final OutputStream os) { - this.message = msg; - this.origStream = os; - } - - public void onFlush(CachedOutputStream cos) { - - } - - public void onClose(CachedOutputStream cos) { - - String getValue = ""; - String postValue = ""; - String logValue = ""; - - try { - logValue = AAIConfig.get("aai.transaction.logging"); - getValue = AAIConfig.get("aai.transaction.logging.get"); - postValue = AAIConfig.get("aai.transaction.logging.post"); - } catch (AAIException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED") && !message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { - return; - } - - String fullId = (String) message.getExchange().get(LoggingMessage.ID_KEY); - - Message inMessage = message.getExchange().getInMessage(); - String transId = null; - String fromAppId = null; - - Map<String, List<String>> headersList = CastUtils.cast((Map<?, ?>) inMessage.get(Message.PROTOCOL_HEADERS)); - if (headersList != null) { - List<String> xt = headersList.get("X-TransactionId"); - if (xt != null) { - for (String transIdValue : xt) { - transId = transIdValue; - } - } - List<String> fa = headersList.get("X-FromAppId"); - if (fa != null) { - for (String fromAppIdValue : fa) { - - fromAppId = fromAppIdValue; - } - } - } - - String httpMethod = (String) inMessage.get(Message.HTTP_REQUEST_METHOD); - - String uri = (String) inMessage.get(CAMEL_REQUEST); - String fullUri = uri; - if (uri != null) { - String query = (String) message.get(Message.QUERY_STRING); - if (query != null) { - fullUri = uri + "?" + query; - } - } - - String request = (String) message.getExchange().get(fullId + "_REQUEST"); - - Message outMessage = message.getExchange().getOutMessage(); - - final LoggingMessage buffer = new LoggingMessage("OUTMessage", fullId); - - // should we check this, and make sure it's not an error? - Integer responseCode = (Integer) outMessage.get(Message.RESPONSE_CODE); - if (responseCode == null) { - responseCode = 200; // this should never happen, but just in - // case we don't get one - } - buffer.getResponseCode().append(responseCode); - - String encoding = (String) outMessage.get(Message.ENCODING); - - if (encoding != null) { - buffer.getEncoding().append(encoding); - } - - String ct = (String) outMessage.get(Message.CONTENT_TYPE); - if (ct != null) { - buffer.getContentType().append(ct); - } - - Object headers = outMessage.get(Message.PROTOCOL_HEADERS); - if (headers != null) { - buffer.getHeader().append(headers); - } - - Boolean ss = false; - if (responseCode >= 200 && responseCode <= 299) { - ss = true; - } - String response = buffer.toString(); - - // this should have been set by the in interceptor - String rqstTm = (String) message.getExchange().get("AAI_RQST_TM"); - - // just in case it wasn't, we'll put this here. not great, but it'll - // have a val. - if (rqstTm == null) { - rqstTm = genDate(); - } - - - String respTm = genDate(); - - try { - String actualRequest = request; - StringBuilder builder = new StringBuilder(); - cos.writeCacheTo(builder, 100000); - // here comes my xml: - String payload = builder.toString(); - - String actualResponse = response; - if (payload == null) { - - } else { - actualResponse = response + payload; - } - - // we only log to AAI log if it's eanbled in the config props - // file - if (message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { - - if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGREQUEST")) { - - // strip newlines from request - String traceRequest = actualRequest; - traceRequest = traceRequest.replace("\n", " "); - traceRequest = traceRequest.replace("\r", ""); - traceRequest = traceRequest.replace("\t", ""); - LOGGER.debug(traceRequest); - } - if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGRESPONSE")) { - // strip newlines from response - String traceResponse = actualResponse; - traceResponse = traceResponse.replace("\n", " "); - traceResponse = traceResponse.replace("\r", ""); - traceResponse = traceResponse.replace("\t", ""); - - LOGGER.debug(traceResponse); - } - } - - // we only log to HBASE if it's enabled in the config props file - // TODO: pretty print XML/JSON. we might need to get the payload - // and envelope seperately - if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED")) { - if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGREQUEST")) { - actualRequest = "loggingDisabled"; - } - if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGRESPONSE")) { - actualResponse = "loggingDisabled"; - } - - LOGGER.debug("action={}, urlin={}, HbTransId={}", httpMethod, fullUri, fullId); - - if (logValue.equals("false")) { - } else if (getValue.equals("false") && httpMethod.equals("GET")) { - } else if (postValue.equals("false") && httpMethod.equals("POST")) { - } else { - putTransaction(transId, responseCode.toString(), rqstTm, respTm, fromAppId + ":" + transId, fullUri, httpMethod, request, response, actualResponse); - - } - } - } catch (Exception ex) { - // ignore - } - - message.setContent(OutputStream.class, origStream); - - LOGGER.auditEvent("HTTP Response Code: {}", responseCode.toString()); - } - - } - - protected String genDate() { - FormatDate fd = new FormatDate( "YYMMdd-HH:mm:ss:SSS"); - return fd.getDateTime(); - } - - public String putTransaction(String tid, String status, String rqstTm, String respTm, String srcId, String rsrcId, String rsrcType, String rqstBuf, String respBuf, String actualResponse) { - String tm = null; - - if (tid == null || "".equals(tid)) { - tm = this.genDate(); - tid = tm + "-"; - } - - String htid = tid; - - if (rqstTm == null || "".equals(rqstTm)) { - rqstTm = tm; - } - - if (respTm == null || "".equals(respTm)) { - respTm = tm; - } - - try { - LOGGER.debug(" transactionId:" + tid + " status: " + status + " rqstDate: " + rqstTm + " respDate: " + respTm + " sourceId: " + srcId + " resourceId: " - + rsrcId + " resourceType: " + rsrcType + " payload rqstBuf: " + rqstBuf + " payload respBuf: " + respBuf + " Payload Error Messages: " + actualResponse); - return htid; - } catch (Exception e) { - ErrorLogHelper.logError("AAI_4000", "Exception updating HBase:"); - return htid; - } - - } -} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/PreAaiAjscInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/PreAaiAjscInterceptor.java deleted file mode 100644 index 360ebe4..0000000 --- a/aai-resources/src/main/java/org/onap/aai/interceptors/PreAaiAjscInterceptor.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.interceptors; - -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.onap.aai.logging.LoggingContext; - -import ajsc.beans.interceptors.AjscInterceptor; - -public class PreAaiAjscInterceptor implements AjscInterceptor { - private final static String TARGET_ENTITY = "aai-resources"; - private static class LazyAaiAjscInterceptor { - public static final PreAaiAjscInterceptor INSTANCE = new PreAaiAjscInterceptor(); - } - - public static PreAaiAjscInterceptor getInstance() { - return LazyAaiAjscInterceptor.INSTANCE; - } - - @Override - public boolean allowOrReject(HttpServletRequest req, HttpServletResponse resp, Map<?, ?> paramMap) - throws Exception { - - LoggingContext.init(); - String serviceName = req.getMethod() + " " + req.getRequestURI().toString(); - String queryStr = req.getQueryString(); - if ( queryStr != null ) { - serviceName = serviceName + "?" + queryStr; - } - LoggingContext.partnerName(req.getHeader("X-FromAppId")); - LoggingContext.serviceName(serviceName); - LoggingContext.targetEntity(TARGET_ENTITY); - LoggingContext.targetServiceName(serviceName); - LoggingContext.requestId(req.getHeader("X-TransactionId")); - - return true; - } -} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/package-info.java b/aai-resources/src/main/java/org/onap/aai/interceptors/package-info.java new file mode 100644 index 0000000..0af4afd --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/package-info.java @@ -0,0 +1,38 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +/** + * <b>Interceptors</b> package is subdivided to pre and post interceptors + * If you want to add an additional interceptor you would need to add + * the priority level to AAIRequestFilterPriority or AAIResponsePriority + * to give a value which indicates the order in which the interceptor + * will be triggered and also you will add that value like here + * + * <pre> + * <code> + * @Priority(AAIRequestFilterPriority.YOUR_PRIORITY) + * public class YourInterceptor extends AAIContainerFilter implements ContainerRequestFilter { + * + * } + * </code> + * </pre> + */ +package org.onap.aai.interceptors; diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/post/AAIResponseFilterPriority.java b/aai-resources/src/main/java/org/onap/aai/interceptors/post/AAIResponseFilterPriority.java new file mode 100644 index 0000000..db05b30 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/post/AAIResponseFilterPriority.java @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.post; + +public final class AAIResponseFilterPriority { + + private AAIResponseFilterPriority() {} + + public static final int HEADER_MANIPULATION = 1000; + + public static final int RESPONSE_TRANS_LOGGING = 2000; + + public static final int RESET_LOGGING_CONTEXT = 3000; + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/PostAaiAjscInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResetLoggingContext.java index 2a34774..592c3fc 100644 --- a/aai-resources/src/main/java/org/onap/aai/interceptors/PostAaiAjscInterceptor.java +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResetLoggingContext.java @@ -19,47 +19,53 @@ * * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ -package org.onap.aai.interceptors; +package org.onap.aai.interceptors.post; -import java.util.Map; +import java.io.IOException; +import javax.annotation.Priority; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import org.onap.aai.interceptors.AAIContainerFilter; import org.onap.aai.logging.LoggingContext; import org.onap.aai.logging.LoggingContext.StatusCode; +import org.springframework.beans.factory.annotation.Autowired; + import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; -import ajsc.beans.interceptors.AjscInterceptor; +@Priority(AAIResponseFilterPriority.RESET_LOGGING_CONTEXT) +public class ResetLoggingContext extends AAIContainerFilter implements ContainerResponseFilter { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ResetLoggingContext.class); -public class PostAaiAjscInterceptor implements AjscInterceptor { + @Autowired + private HttpServletRequest httpServletRequest; + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(PostAaiAjscInterceptor.class); + this.cleanLoggingContext(); - private static class LazyAaiAjscInterceptor { - public static final PostAaiAjscInterceptor INSTANCE = new PostAaiAjscInterceptor(); } - public static PostAaiAjscInterceptor getInstance() { - return LazyAaiAjscInterceptor.INSTANCE; - } + private void cleanLoggingContext() { + final String responseCode = LoggingContext.responseCode(); + String url = httpServletRequest.getRequestURL().toString(); - @Override - public boolean allowOrReject(HttpServletRequest req, HttpServletResponse resp, Map<?, ?> paramMap) - throws Exception { - - final int httpStatusCode = resp.getStatus(); - LoggingContext.responseCode(Integer.toString(httpStatusCode)); - if ( httpStatusCode < 200 || httpStatusCode > 299 ) { + if (responseCode != null && responseCode.startsWith("ERR.")) { LoggingContext.statusCode(StatusCode.ERROR); - LOGGER.error(req.getRequestURL() + " call failed with responseCode=" + httpStatusCode); - } - else { + LOGGER.error(url + " call failed with responseCode=" + responseCode); + } else { LoggingContext.statusCode(StatusCode.COMPLETE); - LOGGER.info(req.getRequestURL() + " call succeeded"); + LOGGER.info(url + " call succeeded"); } + LoggingContext.clear(); - return true; } + } diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseHeaderManipulation.java b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseHeaderManipulation.java new file mode 100644 index 0000000..e3cb35b --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseHeaderManipulation.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.post; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; + +@Priority(AAIResponseFilterPriority.HEADER_MANIPULATION) +public class ResponseHeaderManipulation extends AAIContainerFilter implements ContainerResponseFilter { + + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + + updateResponseHeaders(requestContext, responseContext); + + } + + private void updateResponseHeaders(ContainerRequestContext requestContext, + ContainerResponseContext responseContext) { + responseContext.getHeaders().add(AAIHeaderProperties.AAI_TX_ID, requestContext.getProperty(AAIHeaderProperties.AAI_TX_ID)); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java new file mode 100644 index 0000000..964c436 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/post/ResponseTransactionLogging.java @@ -0,0 +1,127 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.post; + +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; + +import javax.annotation.Priority; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.util.AAIConfig; +import org.springframework.beans.factory.annotation.Autowired; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.gson.JsonObject; + +@Priority(AAIResponseFilterPriority.RESPONSE_TRANS_LOGGING) +public class ResponseTransactionLogging extends AAIContainerFilter implements ContainerResponseFilter { + + private static final EELFLogger TRANSACTION_LOGGER = EELFManager.getInstance().getLogger(ResponseTransactionLogging.class); + + @Autowired + private HttpServletResponse httpServletResponse; + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + + this.transLogging(requestContext, responseContext); + + } + + private void transLogging(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { + + String logValue; + String getValue; + String postValue; + + try { + logValue = AAIConfig.get("aai.transaction.logging"); + getValue = AAIConfig.get("aai.transaction.logging.get"); + postValue = AAIConfig.get("aai.transaction.logging.post"); + } catch (AAIException e) { + return; + } + + String transId = requestContext.getHeaderString(AAIHeaderProperties.TRANSACTION_ID); + String fromAppId = requestContext.getHeaderString(AAIHeaderProperties.FROM_APP_ID); + String fullUri = requestContext.getUriInfo().getRequestUri().toString(); + String requestTs = (String)requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST_TS); + + String httpMethod = requestContext.getMethod(); + + String status = Integer.toString(responseContext.getStatus()); + + String request = (String)requestContext.getProperty(AAIHeaderProperties.AAI_REQUEST); + String response = this.getResponseString(responseContext); + + if (!Boolean.parseBoolean(logValue)) { + } else if (!Boolean.parseBoolean(getValue) && "GET".equals(httpMethod)) { + } else if (!Boolean.parseBoolean(postValue) && "POST".equals(httpMethod)) { + } else { + + JsonObject logEntry = new JsonObject(); + logEntry.addProperty("transactionId", transId); + logEntry.addProperty("status", status); + logEntry.addProperty("rqstDate", requestTs); + logEntry.addProperty("respDate", this.genDate()); + logEntry.addProperty("sourceId", fromAppId + ":" + transId); + logEntry.addProperty("resourceId", fullUri); + logEntry.addProperty("resourceType", httpMethod); + logEntry.addProperty("rqstBuf", Objects.toString(request, "")); + logEntry.addProperty("respBuf", Objects.toString(response, "")); + + try { + TRANSACTION_LOGGER.debug(logEntry.toString()); + } catch (Exception e) { + ErrorLogHelper.logError("AAI_4000", "Exception writing transaction log."); + } + } + + } + + private String getResponseString(ContainerResponseContext responseContext) { + JsonObject response = new JsonObject(); + response.addProperty("ID", responseContext.getHeaderString(AAIHeaderProperties.AAI_TX_ID)); + response.addProperty("Content-Type", this.httpServletResponse.getContentType()); + response.addProperty("Response-Code", responseContext.getStatus()); + response.addProperty("Headers", responseContext.getHeaders().toString()); + Optional<Object> entityOptional = Optional.ofNullable(responseContext.getEntity()); + if(entityOptional.isPresent()){ + response.addProperty("Entity", entityOptional.get().toString()); + } else { + response.addProperty("Entity", ""); + } + return response.toString(); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/JaxrsUserService.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/AAIRequestFilterPriority.java index a1cc2ca..823a5e6 100644 --- a/aai-resources/src/main/java/org/onap/aai/ajsc_aai/JaxrsUserService.java +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/AAIRequestFilterPriority.java @@ -19,37 +19,21 @@ * * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ -package org.onap.aai.ajsc_aai; +package org.onap.aai.interceptors.pre; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import java.util.Map; -import java.util.HashMap; - -@Path("/user") -public class JaxrsUserService { +public final class AAIRequestFilterPriority { + + private AAIRequestFilterPriority() {} - private static final Map<String,String> userIdToNameMap; - static { - userIdToNameMap = new HashMap<String,String>(); - userIdToNameMap.put("userID1","Name1"); - userIdToNameMap.put("userID2","Name2"); - } + public static final int REQUEST_TRANS_LOGGING = 1000; - /** - * Lookup user. - * - * @param userId the user id - * @return the string - */ - @GET - @Path("/{userId}") - @Produces("text/plain") - public String lookupUser(@PathParam("userId") String userId) { - String name = userIdToNameMap.get(userId); - return name != null ? name : "unknown id"; - } - + public static final int HEADER_VALIDATION = 2000; + + public static final int SET_LOGGING_CONTEXT = 3000; + + public static final int AUTHORIZATION = 4000; + + public static final int HEADER_MANIPULATION = 5000; + + public static final int REQUEST_MODIFICATION = 6000; } diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/HeaderValidation.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/HeaderValidation.java new file mode 100644 index 0000000..4a7e10a --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/HeaderValidation.java @@ -0,0 +1,89 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.pre; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import javax.annotation.Priority; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; +import org.onap.aai.logging.ErrorLogHelper; + +@PreMatching +@Priority(AAIRequestFilterPriority.HEADER_VALIDATION) +public class HeaderValidation extends AAIContainerFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + Optional<Response> oResp; + + String transId = requestContext.getHeaderString(AAIHeaderProperties.TRANSACTION_ID); + String fromAppId = requestContext.getHeaderString(AAIHeaderProperties.FROM_APP_ID); + + List<MediaType> acceptHeaderValues = requestContext.getAcceptableMediaTypes(); + + oResp = this.validateHeaderValuePresence(fromAppId, "AAI_4009", acceptHeaderValues); + if (oResp.isPresent()) { + requestContext.abortWith(oResp.get()); + return; + } + oResp = this.validateHeaderValuePresence(transId, "AAI_4010", acceptHeaderValues); + if (oResp.isPresent()) { + requestContext.abortWith(oResp.get()); + return; + } + + if (!this.isValidUUID(transId)) { + transId = UUID.randomUUID().toString(); + requestContext.getHeaders().get(AAIHeaderProperties.TRANSACTION_ID).clear(); + requestContext.getHeaders().add(AAIHeaderProperties.TRANSACTION_ID, transId); + } + + } + + private Optional<Response> validateHeaderValuePresence(String value, String errorCode, + List<MediaType> acceptHeaderValues) { + Response response = null; + AAIException aaie; + if (value == null) { + aaie = new AAIException(errorCode); + return Optional.of(Response.status(aaie.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(acceptHeaderValues, aaie, new ArrayList<>())) + .build()); + } + + return Optional.ofNullable(response); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestHeaderManipulation.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestHeaderManipulation.java new file mode 100644 index 0000000..3d3e6e0 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestHeaderManipulation.java @@ -0,0 +1,72 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.pre; + +import java.io.IOException; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Priority; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MultivaluedMap; + +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; +import org.springframework.beans.factory.annotation.Autowired; + +@PreMatching +@Priority(AAIRequestFilterPriority.HEADER_MANIPULATION) +public class RequestHeaderManipulation extends AAIContainerFilter implements ContainerRequestFilter { + + @Autowired + private HttpServletRequest httpServletRequest; + + private static final Pattern versionedEndpoint = Pattern.compile("^/aai/(v\\d+)"); + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + String uri = httpServletRequest.getRequestURI(); + this.addRequestContext(uri, requestContext.getHeaders()); + + } + + private void addRequestContext(String uri, MultivaluedMap<String, String> requestHeaders) { + + String rc = ""; + + Matcher match = versionedEndpoint.matcher(uri); + if (match.find()) { + rc = match.group(1); + } + + if (requestHeaders.containsKey(AAIHeaderProperties.REQUEST_CONTEXT)) { + requestHeaders.remove(AAIHeaderProperties.REQUEST_CONTEXT); + } + requestHeaders.put(AAIHeaderProperties.REQUEST_CONTEXT, Collections.singletonList(rc)); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestModification.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestModification.java new file mode 100644 index 0000000..812bf1b --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestModification.java @@ -0,0 +1,78 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.pre; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.annotation.Priority; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriBuilder; + +import org.onap.aai.interceptors.AAIContainerFilter; + +@PreMatching +@Priority(AAIRequestFilterPriority.HEADER_VALIDATION) +public class RequestModification extends AAIContainerFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + this.cleanDME2QueryParams(requestContext); + + } + + private void cleanDME2QueryParams(ContainerRequestContext request) { + UriBuilder builder = request.getUriInfo().getRequestUriBuilder(); + MultivaluedMap<String, String> queries = request.getUriInfo().getQueryParameters(); + + String[] blacklist = { "version", "envContext", "routeOffer" }; + Set<String> blacklistSet = Arrays.stream(blacklist).collect(Collectors.toSet()); + + boolean remove = true; + + for (String param : blacklistSet) { + if (!queries.containsKey(param)) { + remove = false; + break; + } + } + + if (remove) { + for (Map.Entry<String, List<String>> query : queries.entrySet()) { + String key = query.getKey(); + if (blacklistSet.contains(key)) { + builder.replaceQueryParam(key); + } + } + } + request.setRequestUri(builder.build()); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java new file mode 100644 index 0000000..75103f5 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/RequestTransactionLogging.java @@ -0,0 +1,106 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.pre; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; +import java.util.UUID; + +import javax.annotation.Priority; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; + +import org.glassfish.jersey.message.internal.ReaderWriter; +import org.glassfish.jersey.server.ContainerException; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; +import org.onap.aai.util.AAIConfig; +import org.onap.aai.util.AAIConstants; +import org.onap.aai.util.HbaseSaltPrefixer; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.gson.JsonObject; + +@PreMatching +@Priority(AAIRequestFilterPriority.REQUEST_TRANS_LOGGING) +public class RequestTransactionLogging extends AAIContainerFilter implements ContainerRequestFilter { + + @Autowired + private HttpServletRequest httpServletRequest; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + String currentTimeStamp = genDate(); + String fullId = this.getAAITxIdToHeader(currentTimeStamp); + this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_TX_ID, fullId); + this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_REQUEST, this.getRequest(requestContext, fullId)); + this.addToRequestContext(requestContext, AAIHeaderProperties.AAI_REQUEST_TS, currentTimeStamp); + } + + private void addToRequestContext(ContainerRequestContext requestContext, String name, String aaiTxIdToHeader) { + requestContext.setProperty(name, aaiTxIdToHeader); + } + + private String getAAITxIdToHeader(String currentTimeStamp) { + String txId = UUID.randomUUID().toString(); + try { + txId = HbaseSaltPrefixer.getInstance().prependSalt(AAIConfig.get(AAIConstants.AAI_NODENAME) + "-" + + currentTimeStamp + "-" + new Random(System.currentTimeMillis()).nextInt(99999)); + } catch (AAIException e) { + } + + return txId; + } + + private String getRequest(ContainerRequestContext requestContext, String fullId) { + + JsonObject request = new JsonObject(); + request.addProperty("ID", fullId); + request.addProperty("Http-Method", requestContext.getMethod()); + request.addProperty("Content-Type", httpServletRequest.getContentType()); + request.addProperty("Headers", requestContext.getHeaders().toString()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream in = requestContext.getEntityStream(); + + try { + if (in.available() > 0) { + ReaderWriter.writeTo(in, out); + byte[] requestEntity = out.toByteArray(); + request.addProperty("Payload", new String(requestEntity, "UTF-8")); + requestContext.setEntityStream(new ByteArrayInputStream(requestEntity)); + } + } catch (IOException ex) { + throw new ContainerException(ex); + } + + return request.toString(); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/SetLoggingContext.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/SetLoggingContext.java new file mode 100644 index 0000000..5c6a5e0 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/SetLoggingContext.java @@ -0,0 +1,71 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.interceptors.pre; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; + +import org.onap.aai.interceptors.AAIContainerFilter; +import org.onap.aai.interceptors.AAIHeaderProperties; +import org.onap.aai.logging.LoggingContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +@PreMatching +@Priority(AAIRequestFilterPriority.SET_LOGGING_CONTEXT) +public class SetLoggingContext extends AAIContainerFilter implements ContainerRequestFilter { + + @Autowired + private Environment environment; + + @Autowired + private HttpServletRequest httpServletRequest; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + String uri = httpServletRequest.getRequestURI(); + String queryString = httpServletRequest.getQueryString(); + + if(queryString != null && !queryString.isEmpty()){ + uri = uri + "?" + queryString; + } + + String httpMethod = requestContext.getMethod(); + String transId = requestContext.getHeaderString(AAIHeaderProperties.TRANSACTION_ID); + String fromAppId = requestContext.getHeaderString(AAIHeaderProperties.FROM_APP_ID); + + LoggingContext.init(); + LoggingContext.requestId(transId); + LoggingContext.partnerName(fromAppId); + LoggingContext.targetEntity(environment.getProperty("spring.application.name")); + LoggingContext.component(fromAppId); + LoggingContext.serviceName(httpMethod + " " + uri); + LoggingContext.targetServiceName(httpMethod + " " + uri); + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java index 0c2ef9c..fbfa59e 100644 --- a/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java +++ b/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java @@ -24,9 +24,12 @@ package org.onap.aai.rest; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; import java.util.concurrent.Callable; import javax.servlet.http.HttpServletRequest; @@ -43,12 +46,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import org.apache.cxf.jaxrs.ext.PATCH; +import io.swagger.jaxrs.PATCH; import org.javatuples.Pair; import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.exceptions.AAIException; @@ -240,7 +244,7 @@ public class LegacyMoxyConsumer extends RESTAPI { @Path("/{uri: .+}") @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getLegacy (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("all") @QueryParam("depth") String depthParam, @DefaultValue("false") @QueryParam("cleanup") String cleanUp, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + public Response getLegacy (String content, @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex, @DefaultValue("-1") @QueryParam("resultSize") String resultSize, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("all") @QueryParam("depth") String depthParam, @DefaultValue("false") @QueryParam("cleanup") String cleanUp, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { return runner(AAIConstants.AAI_CRUD_TIMEOUT_ENABLED, AAIConstants.AAI_CRUD_TIMEOUT_APP, AAIConstants.AAI_CRUD_TIMEOUT_LIMIT, @@ -250,7 +254,7 @@ public class LegacyMoxyConsumer extends RESTAPI { new Callable<Response>() { @Override public Response call() { - return getLegacy(content, versionParam, uri, depthParam, cleanUp, headers, info, req, new HashSet<String>()); + return getLegacy(content, versionParam, uri, depthParam, cleanUp, headers, info, req, new HashSet<String>(), resultIndex, resultSize); } } ); @@ -270,7 +274,7 @@ public class LegacyMoxyConsumer extends RESTAPI { * @param removeQueryParams * @return */ - public Response getLegacy(String content, String versionParam, String uri, String depthParam, String cleanUp, HttpHeaders headers, UriInfo info, HttpServletRequest req, Set<String> removeQueryParams) { + public Response getLegacy(String content, String versionParam, String uri, String depthParam, String cleanUp, HttpHeaders headers, UriInfo info, HttpServletRequest req, Set<String> removeQueryParams, String resultIndex, String resultSize) { String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); String realTime = headers.getRequestHeaders().getFirst("Real-Time"); @@ -288,7 +292,7 @@ public class LegacyMoxyConsumer extends RESTAPI { LoggingContext.serviceName(serviceName); LoggingContext.targetEntity(TARGET_ENTITY); LoggingContext.targetServiceName(serviceName); - + try { validateRequest(info); Version version = Version.valueOf(versionParam); @@ -298,37 +302,27 @@ public class LegacyMoxyConsumer extends RESTAPI { loader = httpEntry.getLoader(); MultivaluedMap<String, String> params = info.getQueryParameters(); - RemoveDME2QueryParams dme2Workaround = new RemoveDME2QueryParams(); - //clear out all params not used for filtering - params.remove("depth"); - params.remove("cleanup"); - params.remove("nodes-only"); - for (String queryParam : removeQueryParams) { - params.remove(queryParam); - } - if (dme2Workaround.shouldRemoveQueryParams(params)) { - dme2Workaround.removeQueryParams(params); - } + params = removeNonFilterableParams(params); uri = uri.split("\\?")[0]; - + URI uriObject = UriBuilder.fromPath(uri).build(); QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject, params); String objType = ""; - if (!uriQuery.getContainerType().equals("")) { - objType = uriQuery.getContainerType(); - } else { - objType = uriQuery.getResultType(); - } - Introspector obj = loader.introspectorFromName(objType); - DBRequest request = + if (!uriQuery.getContainerType().equals("")) { + objType = uriQuery.getContainerType(); + } else { + objType = uriQuery.getResultType(); + } + Introspector obj = loader.introspectorFromName(objType); + DBRequest request = new DBRequest.Builder(HttpMethod.GET, uriObject, uriQuery, obj, headers, info, transId).build(); List<DBRequest> requests = new ArrayList<>(); requests.add(request); - Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); - + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + response = responsesTuple.getValue1().get(0).getValue1(); } catch (AAIException e) { @@ -349,6 +343,21 @@ public class LegacyMoxyConsumer extends RESTAPI { return response; } + + private MultivaluedMap<String, String> removeNonFilterableParams(MultivaluedMap<String, String> params) { + + String[] toRemove = { "depth", "cleanup", "nodes-only", "format", "resultIndex", "resultSize"}; + Set<String> toRemoveSet = Arrays.stream(toRemove).collect(Collectors.toSet()); + + MultivaluedMap<String, String> cleanedParams = new MultivaluedHashMap<>(); + params.keySet().stream().forEach(k -> { + if (!toRemoveSet.contains(k)) { + cleanedParams.addAll(k, params.get(k)); + } + }); + + return cleanedParams; + } /** * Delete. * @@ -584,7 +593,7 @@ public class LegacyMoxyConsumer extends RESTAPI { LoggingContext.requestId(transId); LoggingContext.partnerName(sourceOfTruth); LoggingContext.targetEntity(TARGET_ENTITY); - + try { validateRequest(info); diff --git a/aai-resources/src/main/java/org/onap/aai/rest/bulk/BulkUriInfo.java b/aai-resources/src/main/java/org/onap/aai/rest/bulk/BulkUriInfo.java index 2bff084..3153a1c 100644 --- a/aai-resources/src/main/java/org/onap/aai/rest/bulk/BulkUriInfo.java +++ b/aai-resources/src/main/java/org/onap/aai/rest/bulk/BulkUriInfo.java @@ -3,16 +3,12 @@ package org.onap.aai.rest.bulk; import java.net.URI;
import java.util.List;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.PathSegment;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.core.*;
-import com.sun.jersey.core.util.MultivaluedMapImpl;
public class BulkUriInfo implements UriInfo {
- private MultivaluedMap<String, String> queryParams= new MultivaluedMapImpl();
+ private MultivaluedMap<String, String> queryParams= new MultivaluedHashMap<>();
@Override
public String getPath() {
@@ -98,7 +94,17 @@ public class BulkUriInfo implements UriInfo { public List<Object> getMatchedResources() {
return null;
}
-
+
+ @Override
+ public URI resolve(URI uri) {
+ return null;
+ }
+
+ @Override
+ public URI relativize(URI uri) {
+ return null;
+ }
+
public void addParams(String key, List<String> list) {
this.queryParams.put(key, list);
}
diff --git a/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java index 0188142..6a3b0a5 100644 --- a/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java +++ b/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java @@ -35,8 +35,7 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.apache.cxf.jaxrs.ext.PATCH; - +import io.swagger.jaxrs.PATCH; import org.onap.aai.exceptions.AAIException; import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.restcore.RESTAPI; diff --git a/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java index 55a07e4..b1e156c 100644 --- a/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java +++ b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java @@ -21,8 +21,9 @@ */ package org.onap.aai.rest.util; -import java.util.ArrayList; -import java.util.HashMap; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.restcore.RESTAPI; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; @@ -34,14 +35,13 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; - -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.restcore.RESTAPI; +import java.util.ArrayList; +import java.util.HashMap; /** * The Class EchoResponse. */ +@Path("/util") public class EchoResponse extends RESTAPI { protected static String authPolicyFunctionName = "util"; @@ -60,7 +60,7 @@ public class EchoResponse extends RESTAPI { */ @GET @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - @Path(echoPath) + @Path("/echo") public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req, @QueryParam("action") String myAction) { Response response = null; diff --git a/aai-resources/src/main/java/org/onap/aai/util/AAIAppServletContextListener.java b/aai-resources/src/main/java/org/onap/aai/util/AAIAppServletContextListener.java deleted file mode 100644 index 0fcce0b..0000000 --- a/aai-resources/src/main/java/org/onap/aai/util/AAIAppServletContextListener.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017 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========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.util; - -import java.io.IOException; -import java.util.UUID; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -import org.onap.aai.dbmap.AAIGraph; -import org.onap.aai.exceptions.AAIException; -import org.onap.aai.introspection.ModelInjestor; -import org.onap.aai.logging.ErrorLogHelper; -import org.onap.aai.logging.LogFormatTools; -import org.onap.aai.logging.LoggingContext; -import org.onap.aai.logging.LoggingContext.StatusCode; -import org.onap.aai.migration.MigrationControllerInternal; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; - -public class AAIAppServletContextListener implements ServletContextListener { - - private static final String ACTIVEMQ_TCP_URL = "tcp://localhost:61447"; - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIAppServletContextListener.class.getName()); - - /** - * Destroys Context - * - * @param arg0 the ServletContextEvent - */ - public void contextDestroyed(ServletContextEvent arg0) { - } - - /** - * Initializes Context - * - * @param arg0 the ServletContextEvent - */ - public void contextInitialized(ServletContextEvent arg0) { - System.setProperty("org.onap.aai.serverStarted", "false"); - System.setProperty("aai.service.name", "resources"); - - LoggingContext.save(); - LoggingContext.component("init"); - LoggingContext.partnerName("NA"); - LoggingContext.targetEntity("aai-resources"); - LoggingContext.requestId(UUID.randomUUID().toString()); - LoggingContext.serviceName("aai-resources"); - LoggingContext.targetServiceName("contextInitialized"); - LoggingContext.statusCode(StatusCode.COMPLETE); - - LOGGER.info("AAI Server initialization started..."); - try { - LOGGER.info("Loading aaiconfig.properties"); - AAIConfig.init(); - - LOGGER.info("Loading error.properties"); - ErrorLogHelper.loadProperties(); - - LOGGER.info("Loading graph database"); - - AAIGraph.getInstance(); - ModelInjestor.getInstance(); - - // Jsm internal broker for aai events - - LOGGER.info("A&AI Server initialization succcessful."); - System.setProperty("activemq.tcp.url", ACTIVEMQ_TCP_URL); - System.setProperty("org.onap.aai.serverStarted", "true"); - if ("true".equals(AAIConfig.get("aai.run.migrations", "false"))) { - MigrationControllerInternal migrations = new MigrationControllerInternal(); - migrations.run(new String[]{"--commit"}); - } - - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - LOGGER.info("AAIGraph shutting down"); - AAIGraph.getInstance().graphShutdown(); - LOGGER.info("AAIGraph shutdown"); - System.out.println("Shutdown hook triggered."); - } - }); - - } catch (AAIException e) { - ErrorLogHelper.logException(e); - throw new RuntimeException("AAIException caught while initializing A&AI server", e); - } catch (IOException e) { - ErrorLogHelper.logError("AAI_4000", e.getMessage()); - throw new RuntimeException("IOException caught while initializing A&AI server", e); - } catch (Exception e) { - LOGGER.error("Unknown failure while initializing A&AI Server " + LogFormatTools.getStackTop(e)); - throw new RuntimeException("Unknown failure while initializing A&AI server", e); - } - - LOGGER.info("Resources MicroService Started"); - LOGGER.debug("Resources MicroService Started"); - LoggingContext.restore(); - } -} diff --git a/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java b/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java new file mode 100644 index 0000000..8863d79 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java @@ -0,0 +1,151 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.web; + +import org.glassfish.jersey.filter.LoggingFilter; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletProperties; +import org.onap.aai.rest.*; +import org.onap.aai.rest.retired.V7V8Models; +import org.onap.aai.rest.retired.V7V8NamedQueries; +import org.onap.aai.rest.tools.ModelVersionTransformer; +import org.onap.aai.rest.util.EchoResponse; +import org.reflections.Reflections; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import javax.annotation.Priority; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseFilter; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +@Component +@ApplicationPath("/aai") +public class JerseyConfiguration extends ResourceConfig { + + private static final Logger log = Logger.getLogger(JerseyConfiguration.class.getName()); + + private Environment env; + + @Autowired + public JerseyConfiguration(Environment env) { + + this.env = env; + + register(EchoResponse.class); + register(VertexIdConsumer.class); + register(ExampleConsumer.class); + register(BulkAddConsumer.class); + register(BulkProcessConsumer.class); + register(LegacyMoxyConsumer.class); + register(URLFromVertexIdConsumer.class); + register(V7V8Models.class); + register(V7V8NamedQueries.class); + register(ModelVersionTransformer.class); + + //Request Filters + registerFiltersForRequests(); + // Response Filters + registerFiltersForResponses(); + + property(ServletProperties.FILTER_FORWARD_ON_404, true); + + // Following registers the request headers and response headers + // If the LoggingFilter second argument is set to true, it will print response value as well + if ("true".equalsIgnoreCase(env.getProperty("aai.request.logging.enabled"))) { + register(new LoggingFilter(log, false)); + } + } + + public void registerFiltersForRequests() { + + // Find all the classes within the interceptors package + Reflections reflections = new Reflections("org.onap.aai.interceptors"); + // Filter them based on the clazz that was passed in + Set<Class<? extends ContainerRequestFilter>> filters = reflections.getSubTypesOf(ContainerRequestFilter.class); + + + // Check to ensure that each of the filter has the @Priority annotation and if not throw exception + for (Class filterClass : filters) { + if (filterClass.getAnnotation(Priority.class) == null) { + throw new RuntimeException("Container filter " + filterClass.getName() + " does not have @Priority annotation"); + } + } + + // Turn the set back into a list + List<Class<? extends ContainerRequestFilter>> filtersList = filters + .stream() + .filter(f -> { + if (f.isAnnotationPresent(Profile.class) + && !env.acceptsProfiles(f.getAnnotation(Profile.class).value())) { + return false; + } + return true; + }) + .collect(Collectors.toList()); + + // Sort them by their priority levels value + filtersList.sort((c1, c2) -> Integer.valueOf(c1.getAnnotation(Priority.class).value()).compareTo(c2.getAnnotation(Priority.class).value())); + + // Then register this to the jersey application + filtersList.forEach(this::register); + } + + public void registerFiltersForResponses() { + + // Find all the classes within the interceptors package + Reflections reflections = new Reflections("org.onap.aai.interceptors"); + // Filter them based on the clazz that was passed in + Set<Class<? extends ContainerResponseFilter>> filters = reflections.getSubTypesOf(ContainerResponseFilter.class); + + + // Check to ensure that each of the filter has the @Priority annotation and if not throw exception + for (Class filterClass : filters) { + if (filterClass.getAnnotation(Priority.class) == null) { + throw new RuntimeException("Container filter " + filterClass.getName() + " does not have @Priority annotation"); + } + } + + // Turn the set back into a list + List<Class<? extends ContainerResponseFilter>> filtersList = filters.stream() + .filter(f -> { + if (f.isAnnotationPresent(Profile.class) + && !env.acceptsProfiles(f.getAnnotation(Profile.class).value())) { + return false; + } + return true; + }) + .collect(Collectors.toList()); + + // Sort them by their priority levels value + filtersList.sort((c1, c2) -> Integer.valueOf(c1.getAnnotation(Priority.class).value()).compareTo(c2.getAnnotation(Priority.class).value())); + + // Then register this to the jersey application + filtersList.forEach(this::register); + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/web/LocalHostAccessLog.java b/aai-resources/src/main/java/org/onap/aai/web/LocalHostAccessLog.java new file mode 100644 index 0000000..4201a79 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/web/LocalHostAccessLog.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.web; + +import ch.qos.logback.access.jetty.RequestLogImpl; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.server.handler.RequestLogHandler; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; + +@Configuration +public class LocalHostAccessLog { + + @Bean + public EmbeddedServletContainerFactory jettyConfigBean(){ + JettyEmbeddedServletContainerFactory jef = new JettyEmbeddedServletContainerFactory(); + jef.addServerCustomizers((JettyServerCustomizer) server -> { + + HandlerCollection handlers = new HandlerCollection(); + + Arrays.stream(server.getHandlers()).forEach(handlers::addHandler); + + RequestLogHandler requestLogHandler = new RequestLogHandler(); + requestLogHandler.setServer(server); + + RequestLogImpl requestLogImpl = new RequestLogImpl(); + requestLogImpl.setResource("/localhost-access-logback.xml"); + requestLogImpl.start(); + + requestLogHandler.setRequestLog(requestLogImpl); + handlers.addHandler(requestLogHandler); + server.setHandler(handlers); + }); + return jef; + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/web/WebConfiguration.java b/aai-resources/src/main/java/org/onap/aai/web/WebConfiguration.java new file mode 100644 index 0000000..aaa3998 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/web/WebConfiguration.java @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.web; + +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +//import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +// +//@Configuration +//public class WebConfiguration { +// +// @Bean +// public WebMvcConfigurerAdapter forwardToIndex() { +// return new WebMvcConfigurerAdapter() { +// @Override +// public void addViewControllers(ViewControllerRegistry registry) { +// registry.addViewController("/swagger").setViewName( +// "redirect:/swagger/index.html"); +// registry.addViewController("/swagger/").setViewName( +// "redirect:/swagger/index.html"); +// registry.addViewController("/docs").setViewName( +// "redirect:/docs/html/index.html"); +// registry.addViewController("/docs/").setViewName( +// "redirect:/docs/html/index.html"); +// } +// }; +// } +//} |