// Copyright 2006 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.servlet; import com.google.common.annotations.VisibleForTesting; 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.logging.NDC; import com.google.enterprise.connector.manager.Manager; 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 com.google.enterprise.connector.util.XmlParseUtil; import org.w3c.dom.Element; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Handler class for {@link SetConnectorConfig} servlet. */ public class SetConnectorConfigHandler { private static final Logger LOGGER = Logger.getLogger(SetConnectorConfigHandler.class.getName()); private ConnectorMessageCode status; private String language = null; private String connectorName; private String connectorType = null; private boolean update = false; private Map<String, String> configData = null; private String configXml = null; private ConfigureResponse configRes; /** * Reads configuration data from an input XML body string, then passes * the configuration change on to the Manager to process. * * @param xmlBody ConnectorConfig XML body string * @param manager Manager the Connector Manager */ public SetConnectorConfigHandler(String xmlBody, Manager manager) { status = new ConnectorMessageCode(); // Avoid a bug in GSA that displays "No connector configuration // returned by the connector manager.", rather than the error status. configRes = new ConfigureResponse(null, null, null); Element root = XmlParseUtil.parseAndGetRootElement( xmlBody, ServletUtil.XMLTAG_CONNECTOR_CONFIG); if (root == null) { status.setMessageId(ConnectorMessageCode.ERROR_PARSING_XML_REQUEST); return; } connectorName = XmlParseUtil.getFirstElementByTagName( root, ServletUtil.XMLTAG_CONNECTOR_NAME); if (connectorName == null) { status.setMessageId(ConnectorMessageCode.RESPONSE_NULL_CONNECTOR); return; } if (XmlParseUtil.getFirstElementByTagName(root, ServletUtil.XMLTAG_UPDATE_CONNECTOR).equalsIgnoreCase("true")) { update = true; } else { // 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. connectorName = connectorName.toLowerCase(); } NDC.append(connectorName); language = XmlParseUtil.getFirstElementByTagName( root, ServletUtil.QUERY_PARAM_LANG); connectorType = XmlParseUtil.getFirstElementByTagName( root, ServletUtil.XMLTAG_CONNECTOR_TYPE); configData = XmlParseUtil.getAllAttributes( root, ServletUtil.XMLTAG_PARAMETERS); // TODO (bmj): I am not convinced that this is an error. Nothing states // that a Connector *must* have configuration properties. if (configData.isEmpty()) { status.setMessageId(ConnectorMessageCode.RESPONSE_NULL_CONFIG_DATA); } // Extract Global and Local Namespaces and add them to the configuration // properties. String nameSpace = XmlParseUtil.getFirstElementByTagName( root, ServletUtil.XMLTAG_GLOBAL_NAMESPACE); if (nameSpace != null) { configData.put(PropertiesUtils.GOOGLE_GLOBAL_NAMESPACE, nameSpace); } nameSpace = XmlParseUtil.getFirstElementByTagName( root, ServletUtil.XMLTAG_LOCAL_NAMESPACE); if (nameSpace != null) { configData.put(PropertiesUtils.GOOGLE_LOCAL_NAMESPACE, nameSpace); } // Extract the connectorInstance.xml, if present. Element configXmlElement = (Element) root.getElementsByTagName( ServletUtil.XMLTAG_CONNECTOR_CONFIG_XML).item(0); if (configXmlElement != null) { configXml = XmlParseUtil.getCdata(configXmlElement); if (configXml != null) { configXml = ServletUtil.restoreEndMarkers(configXml); } } try { if (update) { Configuration previousConfig = manager.getConnectorConfiguration(connectorName); ServletUtil.replaceSensitiveData(configData, previousConfig.getMap()); if (configXml == null && connectorType.equals(previousConfig.getTypeName())) { configXml = previousConfig.getXml(); } } configRes = manager.setConnectorConfiguration(connectorName, new Configuration(connectorType, configData, configXml), language, update); // A non-null ConfigureResponse indicates a bad configuration. if (configRes != null) { status.setMessageId(ConnectorMessageCode.INVALID_CONNECTOR_CONFIG); } } catch (ConnectorNotFoundException e) { status = new ConnectorMessageCode( ConnectorMessageCode.EXCEPTION_CONNECTOR_NOT_FOUND, connectorName); LOGGER.log( Level.WARNING, ServletUtil.LOG_EXCEPTION_CONNECTOR_NOT_FOUND, e); } catch (ConnectorExistsException e) { status = new ConnectorMessageCode( ConnectorMessageCode.EXCEPTION_CONNECTOR_EXISTS, connectorName); LOGGER.log(Level.WARNING, ServletUtil.LOG_EXCEPTION_CONNECTOR_EXISTS, e); } catch (InstantiatorException e) { status.setMessageId(ConnectorMessageCode.EXCEPTION_INSTANTIATOR); LOGGER.log(Level.WARNING, ServletUtil.LOG_EXCEPTION_INSTANTIATOR, e); } catch (PersistentStoreException e) { status.setMessageId( ConnectorMessageCode.EXCEPTION_PERSISTENT_STORE); LOGGER.log(Level.WARNING, ServletUtil.LOG_EXCEPTION_PERSISTENT_STORE, e); } catch (Throwable t) { status.setMessageId(ConnectorMessageCode.EXCEPTION_THROWABLE); LOGGER.log(Level.WARNING, "", t); } } ConnectorMessageCode getStatus() { return status; } ConfigureResponse getConfigRes() { return configRes; } @VisibleForTesting String getConnectorName() { return connectorName; } @VisibleForTesting String getConnectorType() { return connectorType; } @VisibleForTesting Map<String, String> getConfigData() { return configData; } @VisibleForTesting String getConfigXml() { return configXml; } @VisibleForTesting String getLanguage() { return language; } @VisibleForTesting boolean isUpdate() { return update; } }