// Copyright 2010 Google Inc. // // 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.importexport; import com.google.enterprise.connector.common.StringUtils; import com.google.enterprise.connector.instantiator.Configuration; import com.google.enterprise.connector.scheduler.Schedule; import com.google.enterprise.connector.util.SAXParseErrorHandler; import com.google.enterprise.connector.util.XmlParseUtil; import junit.framework.TestCase; import org.w3c.dom.Document; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; public class ImportExportConnectorTest extends TestCase { static final Map<String, String> CONFIG_MAP = new HashMap<String, String>() { { put("username", "name"); put("password", "pwd"); put("color", "red"); } }; static final String CONFIG_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<beans>\n" + " <bean id=\"test\" class=\"java.lang.String\">\n" + " <constructor-arg value=\"Hello World\"/>\n" + " </bean>\n" + "</beans>\n"; static final String CONFIG_XML_WITH_CDATA = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<beans>\n" + " <bean id=\"test\" class=\"java.lang.String\">\n" + " <![CDATA[\"Hello World\"/]]>\n" + " </bean>\n" + "</beans>\n"; static final String NEW_FORMAT_NULL_SCHEDULE = "<ConnectorInstance>\n" + " <ConnectorName>connector-01</ConnectorName>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; static final String NEW_FORMAT_EMPTY_SCHEDULE = "<ConnectorInstance>\n" + " <ConnectorName>connector-01</ConnectorName>\n" + " <ConnectorSchedules version=\"3\">" + "</ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; static final String NEW_FORMAT_WITH_SCHEDULE = "<ConnectorInstance>\n" + " <ConnectorName>connector-02</ConnectorName>\n" + " <ConnectorSchedules version=\"3\">connector-02:100:300000:0-0" + "</ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; static final String NEW_FORMAT_WITH_CHECKPOINT = "<ConnectorInstance>\n" + " <ConnectorName>connector-02</ConnectorName>\n" + " <ConnectorCheckpoint>checkpoint</ConnectorCheckpoint>\n" + " <ConnectorSchedules version=\"3\">\n" + " <load>100</load>\n" + " <RetryDelayMillis>300000</RetryDelayMillis>\n" + " <TimeIntervals>0-0</TimeIntervals>\n" + " </ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; private static final String NEW_FORMAT_WITH_CONFIG_XML = "<ConnectorInstance>\n" + " <ConnectorName>connector-02</ConnectorName>\n" + " <ConnectorSchedules version=\"3\">\n" + " <load>100</load>\n" + " <RetryDelayMillis>300000</RetryDelayMillis>\n" + " <TimeIntervals>0-0</TimeIntervals>\n" + " </ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + " <ConnectorConfigXml>\n<![CDATA[" + CONFIG_XML + "]]>\n" + " </ConnectorConfigXml>\n" + "</ConnectorInstance>\n"; private static final String NEW_FORMAT_WITH_CONFIG_XML_CDATA = "<ConnectorInstance>\n" + " <ConnectorName>connector-02</ConnectorName>\n" + " <ConnectorSchedules version=\"3\">\n" + " <load>100</load>\n" + " <RetryDelayMillis>300000</RetryDelayMillis>\n" + " <TimeIntervals>0-0</TimeIntervals>\n" + " </ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + " <ConnectorConfigXml>\n<![CDATA[" + CONFIG_XML_WITH_CDATA.replaceAll("]]>", "]]>") + "]]>\n" + " </ConnectorConfigXml>\n" + "</ConnectorInstance>\n"; private static final String NEW_FORMAT_WITH_EXPLODED_SCHEDULE = "<ConnectorInstance>\n" + " <ConnectorName>connector-02</ConnectorName>\n" + " <ConnectorSchedules version=\"3\">\n" + " <disabled>true</disabled>\n" + " <load>100</load>\n" + " <RetryDelayMillis>300000</RetryDelayMillis>\n" + " <TimeIntervals>0-0</TimeIntervals>\n" + " </ConnectorSchedules>\n" + " <ConnectorType>TestConnector</ConnectorType>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; static final String NEW_FORMAT_WITH_TYPE_VERSION = "<ConnectorInstance>\n" + " <ConnectorName>connector-01</ConnectorName>\n" + " <ConnectorType name=\"TestConnector\" version=\"1.0\"/>\n" + " <ConnectorConfig>\n" + " <Param name=\"color\" value=\"red\"/>\n" + " <Param name=\"password\" value=\"pwd\"/>\n" + " <Param name=\"username\" value=\"name\"/>\n" + " </ConnectorConfig>\n" + "</ConnectorInstance>\n"; // Test reading New GSA Export format. (modern Schedule format) public final void testReadNewFormatSchedule() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_SCHEDULE); assertEquals("name", "connector-02", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertNull("typeVersion", connector.getTypeVersion()); assertEquals("schedule", "connector-02:100:300000:0-0", connector.getScheduleString()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertNull("configXml", connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading a missing schedule. public final void testReadNullSchedule() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_NULL_SCHEDULE); assertEquals("name", "connector-01", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertNull("schedule", connector.getSchedule()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertNull("configXml", connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading an empty schedule. public final void testReadEmptySchedule() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_EMPTY_SCHEDULE); assertEquals("name", "connector-01", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertNull("schedule", connector.getSchedule()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertNull("configXml", connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading exploded schedule format. (easier to read) public final void testReadExplodedSchedule() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_EXPLODED_SCHEDULE); assertEquals("name", "connector-02", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertEquals("schedule", "#connector-02:100:300000:0-0", connector.getScheduleString()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertNull("configXml", connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading checkpoint. public final void testReadCheckpoint() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_CHECKPOINT); assertEquals("name", "connector-02", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertEquals("schedule", "connector-02:100:300000:0-0", connector.getScheduleString()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertEquals("checkpoint", "checkpoint", connector.getCheckpoint()); assertNull("configXml", connector.getConfigXml()); } // Test reading configuration with connectorInstance.xml. public final void testReadConfigXml() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_CONFIG_XML); assertEquals("name", "connector-02", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertEquals("schedule", "connector-02:100:300000:0-0", connector.getScheduleString()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertEquals("configXml", CONFIG_XML, connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading configuration with connectorInstance.xml with embedded CDATA. public final void testReadConfigXmlEmbeddedCdata() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_CONFIG_XML_CDATA); assertEquals("name", "connector-02", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertEquals("schedule", "connector-02:100:300000:0-0", connector.getScheduleString()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertEquals("configXml", CONFIG_XML_WITH_CDATA, connector.getConfigXml()); assertNull("checkpoint", connector.getCheckpoint()); } // Test reading versioned connector type. public final void testReadVersionedType() { ImportExportConnector connector = fromXmlString(NEW_FORMAT_WITH_TYPE_VERSION); assertEquals("name", "connector-01", connector.getName()); assertEquals("type", "TestConnector", connector.getTypeName()); assertEquals("typeVersion", "1.0", connector.getTypeVersion()); Map<String, String> config = connector.getConfigMap(); assertContains(config, "username", "name"); assertContains(config, "password", "pwd"); assertContains(config, "color", "red"); assertTrue(config.size() == 0); assertNull("configXml", connector.getConfigXml()); } // Test that schedules are written in exploded format. public final void testWriteExplodedSchedule() { Schedule schedule = new Schedule("connector-02", true, 100, 300000, "0-0"); ImportExportConnector connector = new ImportExportConnector("connector-02", new Configuration("TestConnector", CONFIG_MAP, null), schedule, null); String xmlResult = asXmlString(connector); assertEquals(NEW_FORMAT_WITH_EXPLODED_SCHEDULE, StringUtils.normalizeNewlines(xmlResult)); } // Test that checkpoints are included in the output. public final void testWriteCheckpoint() { Schedule schedule = new Schedule("connector-02", false, 100, 300000, "0-0"); ImportExportConnector connector = new ImportExportConnector("connector-02", new Configuration("TestConnector", CONFIG_MAP, null), schedule, "checkpoint"); String xmlResult = asXmlString(connector); System.out.println("testWriteCheckpoint:\n" + xmlResult); assertEquals(NEW_FORMAT_WITH_CHECKPOINT, StringUtils.normalizeNewlines(xmlResult)); } // Test that configuration XML is included in the output. public final void testWriteConfigXml() { Schedule schedule = new Schedule("connector-02", false, 100, 300000, "0-0"); ImportExportConnector connector = new ImportExportConnector("connector-02", new Configuration("TestConnector", CONFIG_MAP, CONFIG_XML), schedule, null); String xmlResult = asXmlString(connector); assertEquals(NEW_FORMAT_WITH_CONFIG_XML, StringUtils.normalizeNewlines(xmlResult)); } // Test that configuration XML with embedded CDATA is properly escaped. public final void testWriteConfigXmlCdata() { Schedule schedule = new Schedule("connector-02", false, 100, 300000, "0-0"); ImportExportConnector connector = new ImportExportConnector("connector-02", new Configuration("TestConnector", CONFIG_MAP, CONFIG_XML_WITH_CDATA), schedule, null); String xmlResult = asXmlString(connector); assertEquals(NEW_FORMAT_WITH_CONFIG_XML_CDATA, StringUtils.normalizeNewlines(xmlResult)); } // Test that property values are properly escaped. public final void testWriteEscapedAttrValues() { HashMap<String, String> config = new HashMap<String, String>(CONFIG_MAP); config.put("uglyvalue", "one&two<three>four'five\""); ImportExportConnector connector = new ImportExportConnector("connector-01", new Configuration("TestConnector", config, null), null, null); String xmlResult = asXmlString(connector); assertTrue("attribute values", xmlResult.contains("Param name=\"uglyvalue\"" + " value=\"one&two<three>four'five"\"")); } public void testWriteType() { String typeVersion = "1.0 \"codename\""; ImportExportConnector connector = new ImportExportConnector("connector-01", new Configuration("TestConnector", CONFIG_MAP, null), null, null); connector.setTypeVersion(typeVersion); String xmlResult = asXmlString(connector); connector = fromXmlString(xmlResult); assertEquals(typeVersion, connector.getTypeVersion()); } static void assertContains(Map<String, String> config, String key, String value) { for (Map.Entry<String, String> entry : config.entrySet()) { if (entry.getKey().equals(key) && entry.getValue().equals(value)) { config.remove(key); return; } } fail("Failed to find key=" + key + ", value=" + value); } static String asXmlString(ImportExportConnector connector) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); connector.toXml(pw, 0); return sw.toString(); } private static ImportExportConnector fromXmlString(String xmlString) { Document document = XmlParseUtil.parse(xmlString, new SAXParseErrorHandler(), XmlParseUtil.nonEntityResolver); assertNotNull(xmlString, document); ImportExportConnector connector = new ImportExportConnector(); connector.fromXml(document.getDocumentElement()); return connector; } }