// 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.instantiator; import com.google.common.annotations.VisibleForTesting; import com.google.enterprise.connector.spi.ConnectorType; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.Resource; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; /** * Container for info about a Connector Type. Instantiable only through a static * factory that uses Spring. */ public class TypeInfo { public static final String CONNECTOR_INSTANCE_XML = "connectorInstance.xml"; private static final String CONNECTOR_DEFAULTS_XML = "connectorDefaults.xml"; private static final Logger LOGGER = Logger.getLogger(TypeInfo.class.getName()); private final String connectorTypeName; private final ConnectorType connectorType; private final Resource connectorInstancePrototype; private final Resource connectorDefaultPrototype; private File connectorTypeDir = null; /** * @return the connectorInstancePrototype */ Resource getConnectorInstancePrototype() { return connectorInstancePrototype; } /** * @return the connectorDefaultPrototype */ Resource getConnectorDefaultPrototype() { return connectorDefaultPrototype; } /** * @return the connectorType */ ConnectorType getConnectorType() { return connectorType; } /** * @return the connectorTypeName */ String getConnectorTypeName() { return connectorTypeName; } /** * @return the connectorTypeDir */ File getConnectorTypeDir() { return connectorTypeDir; } /** * @param connectorTypeDir the connectorTypeDir to set */ void setConnectorTypeDir(File connectorTypeDir) { this.connectorTypeDir = connectorTypeDir; } /* @VisibleForTesting */ TypeInfo(String connectorTypeName, ConnectorType connectorType, Resource connectorInstancePrototype, Resource connectorDefaultPrototype) { this.connectorTypeName = connectorTypeName; this.connectorType = connectorType; this.connectorInstancePrototype = connectorInstancePrototype; this.connectorDefaultPrototype = connectorDefaultPrototype; } public static TypeInfo fromSpringResource(Resource r) { TypeInfo result = null; try { result = fromSpringResourceAndThrow(r); } catch (TypeInfoException e) { LOGGER.log(Level.WARNING, "", e); } return result; } /** * Produces info about a connector type from a Spring resource. Throughout, we * catch runtime exceptions, since Spring is known to throw them. We want to * try to recover by moving on to the next resource if needed. * * @param r A spring resource pointing to xml bean definitions. * @return TypeInfo extracted from that resource * @throws FactoryCreationFailureException * @throws BeanListFailureException * @throws NoBeansFoundException * @throws BeanInstantiationFailureException * @throws InstanceXmlFailureException * @throws InstanceXmlMissingException */ static TypeInfo fromSpringResourceAndThrow(Resource r) throws FactoryCreationFailureException, BeanListFailureException, NoBeansFoundException, BeanInstantiationFailureException, InstanceXmlFailureException, InstanceXmlMissingException { String connectorTypeName = null; ConnectorType connectorType = null; Resource connectorInstancePrototype = null; Resource connectorDefaultPrototype = null; // Make a bean factory from the resource. XmlBeanFactory factory; try { factory = new XmlBeanFactory(r); } catch (RuntimeException e) { throw new FactoryCreationFailureException(r, e); } // Get the list of Connector Types defined in the bean factory. String beanList[]; try { beanList = factory.getBeanNamesForType(ConnectorType.class); } catch (Exception e) { throw new BeanListFailureException(r, e); } // Make sure there is at least one Connector Type. if (beanList.length < 1) { throw new NoBeansFoundException(r); } // Remember the name of the first one found, and instantiate it. connectorTypeName = beanList[0]; LOGGER.fine("Constructing connector of type " + connectorTypeName + " from resource " + r.getDescription()); try { connectorType = (ConnectorType) factory.getBean(connectorTypeName); } catch (Exception e) { throw new BeanInstantiationFailureException(r, e, connectorTypeName); } // Find the instance prototype. try { connectorInstancePrototype = r.createRelative(CONNECTOR_INSTANCE_XML); } catch (Exception e) { throw new InstanceXmlFailureException(r, e, connectorTypeName, CONNECTOR_INSTANCE_XML); } if (!connectorInstancePrototype.exists()) { throw new InstanceXmlMissingException(r, connectorTypeName); } // Find the default prototype. try { connectorDefaultPrototype = r.createRelative(CONNECTOR_DEFAULTS_XML); } catch (Exception e) { throw new InstanceXmlFailureException(r, e, connectorTypeName, CONNECTOR_DEFAULTS_XML); } if (!connectorDefaultPrototype.exists()) { connectorDefaultPrototype = null; } TypeInfo result = new TypeInfo(connectorTypeName, connectorType, connectorInstancePrototype, connectorDefaultPrototype); // If more Connector Types were found, issue a warning. if (beanList.length > 1) { StringBuilder buf = new StringBuilder(); for (int i = 1; i < beanList.length; i++) { buf.append(" "); buf.append(beanList[i]); } LOGGER.log(Level.WARNING, "Resource " + r.getDescription() + "contains multiple Connector Type definitions. Using the first: " + connectorTypeName + ". Skipping:" + buf.toString()); } return result; } static class TypeInfoException extends InstantiatorException { TypeInfoException(String message, Exception cause) { super(message, cause); } TypeInfoException(String message) { super(message); } } static class FactoryCreationFailureException extends TypeInfoException { FactoryCreationFailureException(Resource resource, Exception cause) { super("Exception from Spring while creating bean " + "factory from resource " + resource.getDescription(), cause); } } static class BeanListFailureException extends TypeInfoException { BeanListFailureException(Resource resource, Exception cause) { super("Exception from Spring while listing beans " + " from factory from resource " + resource.getDescription(), cause); } } static class NoBeansFoundException extends TypeInfoException { NoBeansFoundException(Resource resource) { super("Resource " + resource.getDescription() + " contains no definitions for ConnectorType"); } } static class BeanInstantiationFailureException extends TypeInfoException { BeanInstantiationFailureException(Resource resource, Exception cause, String connectorTypeName) { super("Exception from Spring while instantiating " + " connector type " + connectorTypeName + " from resource " + resource.getDescription(), cause); } } static class InstanceXmlFailureException extends TypeInfoException { InstanceXmlFailureException(Resource resource, Exception cause, String connectorTypeName, String xmlResourceName) { super("Exception from Spring while creating " + xmlResourceName + " sibling resource for " + connectorTypeName + " from resource " + resource.getDescription(), cause); } } static class InstanceXmlMissingException extends TypeInfoException { InstanceXmlMissingException(Resource resource, String connectorTypeName) { super("Can't find " + CONNECTOR_INSTANCE_XML + " sibling resource for " + connectorTypeName + " from resource " + resource.getDescription()); } } }