// Copyright 2006 Google Inc. 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. package com.google.enterprise.connector.servlet; import com.google.enterprise.connector.common.PropertiesUtils; import com.google.enterprise.connector.instantiator.Configuration; import com.google.enterprise.connector.instantiator.InstantiatorException; import com.google.enterprise.connector.manager.Manager; import com.google.enterprise.connector.manager.MockManager; import com.google.enterprise.connector.persist.ConnectorExistsException; import com.google.enterprise.connector.persist.ConnectorNotFoundException; import com.google.enterprise.connector.persist.PersistentStoreException; import com.google.enterprise.connector.spi.ConfigureResponse; import junit.framework.TestCase; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.logging.Logger; /** * Tests {@link SetConnectorConfigHandler} for {@link SetConnectorConfig} * servlet. */ public class SetConnectorConfigHandlerTest extends TestCase { private static final Logger LOGGER = Logger.getLogger(SetConnectorConfigHandlerTest.class.getName()); private String language; private String connectorName; private String connectorType; private Map<String, String> configData; private String configXml; private String globalNamespace; private String localNamespace; private boolean update; private MockManager manager; @Override protected void setUp() { manager = new ConfigSavingManager(); globalNamespace = null; localNamespace = null; } /** Test invalid configure xml element. */ public void testInvalidRequest() throws Exception { SetConnectorConfigHandler hdl = new SetConnectorConfigHandler("", manager); assertEquals(ConnectorMessageCode.ERROR_PARSING_XML_REQUEST, hdl.getStatus().getMessageId()); } /** Test null connector name. */ public void testNullConnectorName() throws Exception { language = "en"; connectorName = ""; // Produces empty name element. connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueB1"); configData.put("name2", "valueB2"); configData.put("name3", "valueB3"); configXml = null; update = false; SetConnectorConfigHandler hdl = doTest(setXMLBody()); assertEquals(ConnectorMessageCode.RESPONSE_NULL_CONNECTOR, hdl.getStatus().getMessageId()); } /** Test empty config map. */ public void testEmptyConfig() throws Exception { language = "en"; connectorName = "ConnectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configXml = null; update = false; SetConnectorConfigHandler hdl = doTest(setXMLBody()); assertEquals(ConnectorMessageCode.RESPONSE_NULL_CONFIG_DATA, hdl.getStatus().getMessageId()); } /** Test set config - not update implies create new. */ public void testCreateConnector() throws Exception { language = "en"; connectorName = "connectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = null; update = false; doTest(); } /** Test set config with connectorInstance.xml */ public void testCreateConnectorWithConfigXml() throws Exception { language = "en"; connectorName = "connectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; update = false; doTest(); } /** Test set config with connectorInstance.xml */ public void testCreateConnectorWithConfigNamespaces() throws Exception { language = "en"; connectorName = "connectorA"; connectorType = "documentum"; globalNamespace = "ThinkGlobally"; localNamespace = "ActLocally"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; update = false; String xmlBody = setXMLBody(); configData.put(PropertiesUtils.GOOGLE_GLOBAL_NAMESPACE, globalNamespace); configData.put(PropertiesUtils.GOOGLE_LOCAL_NAMESPACE, localNamespace); SetConnectorConfigHandler hdl = doTest(xmlBody); ConnectorMessageCode status = hdl.getStatus(); ConfigureResponse response = hdl.getConfigRes(); assertTrue("Status: Code=" + status.getMessageId() + " Message=" + status.getMessage() + ((response == null) ? "" : " Response: Message=" + response.getMessage()), status.isSuccess()); } /** Test update configuration. */ public void testUpdate() throws Exception { language = "en"; connectorName = "connector_a"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); update = false; configXml = null; doTest(); configData.put("name1", "valueB1"); configData.put("name2", "valueB2"); update = true; doTest(); } /** Test update mixed case connector configuration. */ // GSA 5.2 and greater wants only lowercase connector names, // so force all new connector names to be lower case. // Unfortunately, we cannot do this for existing connectors. public void testUpdateMixedCase() throws Exception { language = "en"; connectorName = "connectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); update = false; configXml = null; manager.setConnectorConfiguration(connectorName, new Configuration(connectorType, configData, configXml), language, update); configData.put("name1", "valueB1"); configData.put("name2", "valueB2"); update = true; doTest(); } /** Test update configuration properties with connectorInstance.xml */ public void testUpdateWithConfigXml() throws Exception { language = "en"; connectorName = "connector_a"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; update = false; doTest(); configData.put("name1", "valueB1"); configData.put("name2", "valueB2"); update = true; doTest(); } /** Test update configuration with only connectorInstance.xml */ public void testUpdateOnlyConfigXml() throws Exception { language = "en"; connectorName = "connector_a"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = null; update = false; doTest(); update = true; configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; doTest(); } /** Test setConnectorConfiguration throwing InstantiatorException. */ public void testInstantiatorException() throws Exception { checkExceptionHandling(new InstantiatorException(getName()), ConnectorMessageCode.EXCEPTION_INSTANTIATOR); } /** Test setConnectorConfiguration throwing ConnectorNotFoundException. */ public void testConnectorNotFoundException() throws Exception { checkExceptionHandling(new ConnectorNotFoundException(getName()), ConnectorMessageCode.EXCEPTION_CONNECTOR_NOT_FOUND); } /** Test setConnectorConfiguration throwing ConnectorExistsException. */ public void testConnectorExistsException() throws Exception { checkExceptionHandling(new ConnectorExistsException(getName()), ConnectorMessageCode.EXCEPTION_CONNECTOR_EXISTS); } /** Test setConnectorConfiguration throwing PersistentStoreException. */ public void testPersistentStoreException() throws Exception { checkExceptionHandling(new PersistentStoreException(getName()), ConnectorMessageCode.EXCEPTION_PERSISTENT_STORE); } /** Test setConnectorConfiguration throwing RuntimeException. */ public void testRuntimeException() throws Exception { checkExceptionHandling(new RuntimeException(getName()), ConnectorMessageCode.EXCEPTION_THROWABLE); } private void checkExceptionHandling(Exception exception, int expectedCode) throws Exception { manager = new ExceptionalManager(exception); language = "en"; connectorName = "connectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "valueA3"); configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; update = false; SetConnectorConfigHandler hdl = doTest(setXMLBody()); assertEquals(expectedCode, hdl.getStatus().getMessageId()); // Avoid a bug in GSA that displays "No connector configuration // returned by the connector manager.", rather than the error status. assertNotNull(hdl.getConfigRes()); } /** Test setConnectorConfiguration returning a ConfigureResponse. */ public void testSetInvalidConfig() throws Exception { manager = new FailConfigurationManager(); language = "en"; connectorName = "connectorA"; connectorType = "documentum"; configData = new TreeMap<String, String>(); configData.put("name1", "valueA1"); configData.put("name2", "valueA2"); configData.put("name3", "InvalidValue"); configXml = "<?xml?><beans><bean id=\"NewConfigXML\"/></beans>"; update = false; SetConnectorConfigHandler hdl = doTest(setXMLBody()); assertEquals(ConnectorMessageCode.INVALID_CONNECTOR_CONFIG, hdl.getStatus().getMessageId()); assertNotNull(hdl.getConfigRes()); assertNotNull(hdl.getConfigRes().getMessage()); } /** Do test, expecting a successful return status. */ private void doTest() throws Exception { SetConnectorConfigHandler hdl = doTest(setXMLBody()); ConnectorMessageCode status = hdl.getStatus(); ConfigureResponse response = hdl.getConfigRes(); assertTrue("Status: Code=" + status.getMessageId() + " Message=" + status.getMessage() + ((response == null) ? "" : " Response: Message=" + response.getMessage()), status.isSuccess()); } /** Test the handler, returning the handler. */ private SetConnectorConfigHandler doTest(String xmlBody) throws Exception { LOGGER.info("xmlBody: " + xmlBody); String name = connectorName; Configuration origConfig = null; if (update) { origConfig = manager.getConnectorConfiguration(connectorName); } else { // GSA 5.2 wants connectorNames to be lower case. The handler // will lowercase for us, but only on new connectors; not on update. name = name.toLowerCase(); } SetConnectorConfigHandler hdl = new SetConnectorConfigHandler( xmlBody, manager); LOGGER.info("ConnectorName: " + hdl.getConnectorName() + " this: " + connectorName); LOGGER.info("ConnectorType: " + hdl.getConnectorType() + " this: " + connectorType); if (hdl.getStatus().isSuccess()) { assertEquals(language, hdl.getLanguage()); assertEquals(name, hdl.getConnectorName()); assertEquals(connectorType, hdl.getConnectorType()); assertEquals(update, hdl.isUpdate()); assertEquals(configData, hdl.getConfigData()); Configuration config = manager.getConnectorConfiguration(name); assertNotNull(config); assertEquals(connectorType, config.getTypeName()); assertEquals(configData, config.getMap()); if (configXml != null) { assertEquals(configXml, config.getXml()); assertEquals(configXml, hdl.getConfigXml()); } else if (origConfig != null) { assertEquals(origConfig.getXml(), config.getXml()); } else { assertEquals(manager.getConnectorInstancePrototype(connectorType), config.getXml()); } } return hdl; } private String setXMLBody() throws Exception { String name = connectorName; if (!update ) { // GSA 5.2 wants connectorNames to be lower case. // But we can only enforce it for new connectors, not existing ones. name = name.toLowerCase(); } String body = "<" + ServletUtil.XMLTAG_CONNECTOR_CONFIG + ">\n" + " <" + ServletUtil.QUERY_PARAM_LANG + ">" + language + "</" + ServletUtil.QUERY_PARAM_LANG + ">\n" + " <" + ServletUtil.XMLTAG_CONNECTOR_NAME + ">" + name + "</" + ServletUtil.XMLTAG_CONNECTOR_NAME + ">\n" + " <" + ServletUtil.XMLTAG_CONNECTOR_TYPE + ">" + connectorType + "</" + ServletUtil.XMLTAG_CONNECTOR_TYPE + ">\n" + " <" + ServletUtil.XMLTAG_UPDATE_CONNECTOR + ">" + update + "</" + ServletUtil.XMLTAG_UPDATE_CONNECTOR + ">\n"; if (globalNamespace != null) { body += " <" + ServletUtil.XMLTAG_GLOBAL_NAMESPACE + ">" + globalNamespace + "</" + ServletUtil.XMLTAG_GLOBAL_NAMESPACE+ ">\n"; } if (localNamespace != null) { body += " <" + ServletUtil.XMLTAG_LOCAL_NAMESPACE + ">" + localNamespace + "</" + ServletUtil.XMLTAG_LOCAL_NAMESPACE+ ">\n"; } for (Map.Entry<String, String> entry : configData.entrySet()) { body += " <" + ServletUtil.XMLTAG_PARAMETERS + " name=\"" + entry.getKey() + "\" value=\"" + entry.getValue() + "\"/>\n"; } if (configXml != null) { body += " <" + ServletUtil.XMLTAG_CONNECTOR_CONFIG_XML + "><![CDATA[" + configXml + "]]></" + ServletUtil.XMLTAG_CONNECTOR_CONFIG_XML + ">\n"; } return body + "</" + ServletUtil.XMLTAG_CONNECTOR_CONFIG + ">"; } /** A MockManager that saves Configurations and returns them. */ private class ConfigSavingManager extends MockManager implements Manager { private HashMap<String, Configuration> configurations; public ConfigSavingManager() { super(); configurations = new HashMap<String, Configuration>(); } @Override public ConfigureResponse setConnectorConfiguration(String connectorName, Configuration configuration, String language, boolean update) throws ConnectorNotFoundException, ConnectorExistsException, PersistentStoreException, InstantiatorException { ConfigureResponse response = super.setConnectorConfiguration( connectorName, configuration, language, update); if (response == null) { if (configuration.getXml() == null) { configuration = new Configuration(configuration, getConnectorInstancePrototype(configuration.getTypeName())); } configurations.put(connectorName, configuration); } return response; } @Override public Configuration getConnectorConfiguration(String connectorName) throws ConnectorNotFoundException { Configuration config = configurations.get(connectorName); if (config == null) { throw new ConnectorNotFoundException(connectorName); } return config; } } /** * Throws either a RuntimeException, ConnectorNotFoundException, * ConnectorExistsException, PersistentStoreException, InstantiatorException. */ private static void throwException(Exception exception) throws ConnectorNotFoundException, ConnectorExistsException, PersistentStoreException, InstantiatorException { if (exception instanceof ConnectorNotFoundException) { throw (ConnectorNotFoundException) exception; } else if (exception instanceof ConnectorExistsException) { throw (ConnectorExistsException) exception; } else if (exception instanceof InstantiatorException) { throw (InstantiatorException) exception; } else if (exception instanceof PersistentStoreException) { throw (PersistentStoreException) exception; } else if (exception instanceof RuntimeException) { // RuntimeExceptions don't need to be declared. throw (RuntimeException) exception; } } /** A MockManager that throws exception when setting configuration. */ private class ExceptionalManager extends MockManager { private Exception exception; public ExceptionalManager(Exception exception) { this.exception = exception; } @Override public ConfigureResponse setConnectorConfiguration(String connectorName, Configuration configuration, String language, boolean update) throws ConnectorNotFoundException, ConnectorExistsException, PersistentStoreException, InstantiatorException { throwException(exception); return null; } } /** A MockManager that returns a failed configuration. */ private class FailConfigurationManager extends MockManager { @Override public ConfigureResponse setConnectorConfiguration(String connectorName, Configuration configuration, String language, boolean update) { return new ConfigureResponse("setConnectorConfiguration: " + ((update) ? "update" : "add") + " " + connectorName + " " + configuration.toString(), null, null); } } }