• 企业搜索引擎开发之连接器connector(十二)


    本文接着分析连接器及其构造工厂相关设计及源码,先浏览一下下面的UML模型图:

    ConnectorFactory为Connector类型连接器接口,ConnectorInstanceFactor为工厂实现类(这里是反射工厂);Connector接口下面的实现类均为具体的连接器类;

    ConnectorFactory接口源码如下:

    /**
     * This factory provides a mechanism by which
     * {@link ConnectorType#validateConfig(Map, Locale, ConnectorFactory)
     * validateConfig} may create instances of the connector for the
     * purpose of validation. {@link Connector} instances created by the
     * factory are not added to the Connector Manager's list of running
     * connectors and do not have an on-disk representation.
     */
    public interface ConnectorFactory {
      /**
       * Make a {@link Connector} instance of the {@link ConnectorType}
       * that owns this ConnectorFactory instance using the supplied
       * {@link java.util.Map} of configuration properties.
       *
       * @param config a {@link java.util.Map} of configuration properties.
       *         If null, the Map that was passed to validateConfig is used.
       * @return a {@link Connector} instance, instantiated by the Connector
       *         Manager in exactly the same way as it would if this config
       *         were valid and persisted.
       * @throws RepositoryException if the Connector construction
       *         fails for any reason.
       */
      Connector makeConnector(Map<String, String> config)
          throws RepositoryException;
    }

    这里的方法签名就是根据map类型的参数构造连接器实例对象(不过貌似连接器应用本身没有用到这个工厂)

    实现类ConnectorInstanceFactor源码如下:

    /**
     * A {@link ConnectorFactory} implementation that creates transient
     * {@link Connector} instances on behalf of a {@link ConnectorType}
     * for the purposes of validating a supplied configuration.  The
     * {@code Connector} instances are temporary, have no on-disk
     * representation, and are not considered active instances.
     * <p>
     * Any {@code Connector} instances created by this factory will be destroyed
     * when {@code ConnectorType.validateConfig()} returns.
     * <p>
     * @see com.google.enterprise.connector.spi.ConnectorType#validateConfig(Map,
     *      Locale, ConnectorFactory)
     */
    class ConnectorInstanceFactory implements ConnectorFactory {
      private static final Logger LOGGER =
          Logger.getLogger(ConnectorInstanceFactory.class.getName());
    
      final String connectorName;
      final File connectorDir;
      final TypeInfo typeInfo;
      final Map<String, String> origConfig;
      final List<InstanceInfo> connectors;
    
      /**
       * Constructor takes the items needed by {@code InstanceInfo}, but not
       * provided via {@code makeConnector}.
       *
       * @param connectorName the name of this connector instance.
       * @param connectorDir the directory containing the connector prototype.
       * @param typeInfo the connector type.
       * @param config the configuration provided to {@code validateConfig}.
       */
      public ConnectorInstanceFactory(String connectorName, File connectorDir,
          TypeInfo typeInfo, Map<String, String> config) {
        this.connectorName = connectorName;
        this.connectorDir = connectorDir;
        this.typeInfo = typeInfo;
        this.origConfig = config;
        this.connectors = new LinkedList<InstanceInfo>();
      }
    
      /**
       * Create an instance of this {@code Connector} based upon the supplied
       * configuration data. If the supplied configuration {@code Map} is
       * {@code null}, use the original configuration.
       *
       * @see com.google.enterprise.connector.spi.ConnectorFactory#makeConnector(Map)
       */
      public Connector makeConnector(Map<String, String> config)
        throws RepositoryException {
        try {
          InstanceInfo info =
            InstanceInfo.fromNewConfig(connectorName, connectorDir, typeInfo,
                                       ((config == null) ? origConfig : config));
          if (info == null) {
            return null;
          }
          synchronized (this) {
            connectors.add(info);
          }
          return info.getConnector();
        } catch (InstantiatorException e) {
          throw new
              RepositoryException("ConnectorFactory failed to make connector.", e);
        }
      }
    
      /**
       * Shutdown any connector instances created by the factory.
       */
      synchronized void shutdown() {
        for (InstanceInfo info : connectors) {
          Connector connector = info.getConnector();
          if (connector instanceof ConnectorShutdownAware) {
            try {
              ((ConnectorShutdownAware) connector).shutdown();
            } catch (Exception e) {
              LOGGER.log(Level.WARNING, "Failed to shutdown connector "
                  + info.getName() + " created by validateConfig", e);
            }
          }
        }
        connectors.clear();
      }
    }

    可以看到,生成连接器实例方法makeConnector是通过先实例化InstanceInfo对象,然后从InstanceInfo对象获取连接器实例的

    InstanceInfo类的源码如下:

    /**
     * Container for info about a Connector Instance. Instantiable only through a
     * static factory that uses Spring.
     */
    final class InstanceInfo {
    
      private static final Logger LOGGER =
          Logger.getLogger(InstanceInfo.class.getName());
    
      private static ConnectorConfigStore configStore;
      private static ConnectorScheduleStore schedStore;
      private static ConnectorStateStore stateStore;
    
      private static Collection<ConnectorConfigStore> legacyConfigStores;
      private static Collection<ConnectorScheduleStore> legacyScheduleStores;
      private static Collection<ConnectorStateStore> legacyStateStores;
    
      private final TypeInfo typeInfo;
      private final File connectorDir;
      private final String connectorName;
      private final StoreContext storeContext;
    
      private Properties properties;
      private Connector connector;
    
    
      /** Private Constructor for use by Static Factory Methods, below. */
      private InstanceInfo(String connectorName, File connectorDir,
          TypeInfo typeInfo) throws InstanceInfoException {
        if (connectorName == null || connectorName.length() < 1) {
          throw new NullConnectorNameException();
        }
        if (connectorDir == null) {
          throw new NullDirectoryException();
        }
        if (typeInfo == null) {
          throw new NullTypeInfoException();
        }
    
        this.connectorName = connectorName;
        this.connectorDir = connectorDir;
        this.typeInfo = typeInfo;
        this.storeContext = new StoreContext(connectorName, connectorDir);
      }
    
    
      /* **** Getters and Setters **** */
    
      public static void setConnectorStores(ConnectorConfigStore configStore,
          ConnectorScheduleStore schedStore, ConnectorStateStore stateStore) {
        InstanceInfo.configStore = configStore;
        InstanceInfo.schedStore = schedStore;
        InstanceInfo.stateStore = stateStore;
      }
    
      public static void setLegacyStores(
          Collection<ConnectorConfigStore> configStores,
          Collection<ConnectorScheduleStore> schedStores,
          Collection<ConnectorStateStore> stateStores) {
        legacyConfigStores = configStores;
        legacyScheduleStores = schedStores;
        legacyStateStores = stateStores;
      }
    
      /**
       * @return the connector
       */
      Connector getConnector() {
        return connector;
      }
    
      /**
       * @return the name
       */
      String getName() {
        return connectorName;
      }
    
      /**
       * @return the typeInfo
       */
      TypeInfo getTypeInfo() {
        return typeInfo;
      }
    
      /**
       * @return the connectorDir
       */
      File getConnectorDir() {
        return connectorDir;
      }
    
    
      /* **** Static Factory Methods used to Create Instances. **** */
    
      /**
       * Factory Method that Constructs a new Connector Instance based
       * upon its on-disk persistently stored configuration.
       *
       * @param connectorName the name of the Connector instance.
       * @param connectorDir the Connector's on-disk directory.
       * @param typeInfo the Connector's prototype.
       * @return new InstanceInfo representing the Connector instance.
       * @throws InstanceInfoException
       */
      public static InstanceInfo fromDirectory(String connectorName,
          File connectorDir, TypeInfo typeInfo) throws InstanceInfoException {
        InstanceInfo info = new InstanceInfo(connectorName, connectorDir, typeInfo);
        info.properties = configStore.getConnectorConfiguration(info.storeContext);
    
        // Upgrade from Legacy Configuration Data Stores. This method is
        // called to instantiate Connectors that were created by some
        // other (possibly older) instance of the Connector Manager.
        // If the various stored instance data is not found in the
        // expected locations, the connector may have been previously
        // created by an older version of the Connector Manager and may
        // have its instance data stored in the older legacy locations.
        // Move the data from the legacy stores to the expected locations
        // before launching the connector instance.
        if (info.properties == null) {
          upgradeConfigStore(info);
          if (info.properties == null) {
            throw new InstanceInfoException("Configuration not found for connector "
                                            + connectorName);
          }
        }
        if (schedStore.getConnectorSchedule(info.storeContext) == null) {
          upgradeScheduleStore(info);
          if (info.getConnectorSchedule() == null) {
            // If there is no schedule, create a disabled schedule rather than
            // logging "schedule not found" once a second for eternity.
            LOGGER.warning("Traversal Schedule not found for connector "
                           + connectorName + ", disabling traversal.");
            Schedule schedule = new Schedule();
            schedule.setConnectorName(connectorName);
            info.setConnectorSchedule(schedule.toString());
          }
        }
        if (stateStore.getConnectorState(info.storeContext) == null) {
          upgradeStateStore(info);
        }
    
        info.connector = makeConnectorWithSpring(info);
        return info;
      }
    
      /**
       * Factory Method that Constructs a new Connector Instance based
       * upon the supplied configuration map.  This is typically done
       * when creating new connectors from scratch.  It is also used
       * by the ConnectorFactory.
       *
       * @param connectorName the name of the Connector instance.
       * @param connectorDir the Connector's working directory.
       * @param typeInfo the Connector's prototype.
       * @param configMap configuration properties.
       * @return new InstanceInfo representing the Connector instance.
       * @throws InstanceInfoException
       */
      public static InstanceInfo fromNewConfig(String connectorName,
          File connectorDir, TypeInfo typeInfo, Map<String, String> configMap)
          throws InstanceInfoException {
        InstanceInfo info = new InstanceInfo(connectorName, connectorDir, typeInfo);
        info.properties = PropertiesUtils.fromMap(configMap);
        // Don't write properties file to disk yet.
        info.connector = makeConnectorWithSpring(info);
        return info;
      }
    
      /**
       * Construct a new Connector Instance based upon the connectorInstance
       * and connectorDefaults bean definitions.
       *
       * @param info the InstanceInfo object under construction.
       */
      private static Connector makeConnectorWithSpring(InstanceInfo info)
          throws InstanceInfoException {
        Context context = Context.getInstance();
        String name = info.connectorName;
        Resource prototype = null;
        if (info.connectorDir != null) {
          // If this file exists, we use this it in preference to the default
          // prototype associated with the type. This allows customers to supply
          // their own per-instance config xml.
          File customPrototype =
              new File(info.connectorDir, TypeInfo.CONNECTOR_INSTANCE_XML);
          if (customPrototype.exists()) {
            prototype = new FileSystemResource(customPrototype);
            LOGGER.info("Using connector-specific xml config for connector "
                + name + " at path " + customPrototype.getPath());
          }
        }
        if (prototype == null) {
          prototype = info.typeInfo.getConnectorInstancePrototype();
        }
    
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(factory);
        Resource defaults = info.typeInfo.getConnectorDefaultPrototype();
        try {
          beanReader.loadBeanDefinitions(prototype);
        } catch (BeansException e) {
          throw new FactoryCreationFailureException(e, prototype, name);
        }
        // Seems non-intuitive to load these in this order, but we want newer
        // versions of the connectors to override any default bean definitions
        // specified in old-style monolithic connectorInstance.xml files.
        if (defaults != null) {
          try {
            beanReader.loadBeanDefinitions(defaults);
          } catch (BeansException e) {
            throw new FactoryCreationFailureException(e, defaults, name);
          }
        }
    
        EncryptedPropertyPlaceholderConfigurer cfg = null;
        try {
            cfg = (EncryptedPropertyPlaceholderConfigurer) context.getBean(
                factory, null, EncryptedPropertyPlaceholderConfigurer.class);
        } catch (BeansException e) {
          throw new BeanInstantiationFailureException(e, prototype, name,
              EncryptedPropertyPlaceholderConfigurer.class.getName());
        }
        if (cfg == null) {
          cfg = new EncryptedPropertyPlaceholderConfigurer();
        }
    
        try {
          cfg.setLocation(getPropertiesResource(info));
          cfg.postProcessBeanFactory(factory);
        } catch (BeansException e) {
          throw new PropertyProcessingFailureException(e, prototype, name);
        }
    
        Connector connector = null;
        try {
          connector = (Connector) context.getBean(factory, null, Connector.class);
        } catch (BeansException e) {
          throw new BeanInstantiationFailureException(e, prototype, name,
              Connector.class.getName());
        }
        if (connector == null) {
          throw new NoBeansFoundException(prototype, name, Connector.class);
        }
        return connector;
      }
    
      /**
       * Return a Spring Resource containing the InstanceInfo
       * configuration Properties.
       */
      private static Resource getPropertiesResource(InstanceInfo info)
          throws InstanceInfoException {
        Properties properties =
            (info.properties == null) ? new Properties() : info.properties;
        try {
          return new ByteArrayResourceHack(
              PropertiesUtils.storeToString(properties, null).getBytes());
        } catch (PropertiesException e) {
          throw new PropertyProcessingInternalFailureException(e,
              info.connectorName);
        }
      }
    
      /* This subclass of ByteArrayResource attempts to circumvent a bug in
       * org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties()
       * that tries to fetch the filename extension of the properties Resource
       * in an attempt to determine whether to parse the properties as XML or
       * traditional syntax.  ByteArrayResource throws an exception when
       * getFilename() is called because there is no associated filename.
       * This subclass returns a fake filename (without a .xml extension).
       * TODO: Remove this when Spring Framework SPR-5068 gets fixed:
       * http://jira.springframework.org/browse/SPR-5068
       */
      private static class ByteArrayResourceHack extends ByteArrayResource {
        public ByteArrayResourceHack(byte[] byteArray) {
          super(byteArray);
        }
        @Override
        public String getFilename() {
          return "ByteArrayResourceHasNoFilename";
        }
      }
    
    
      /* **** Manage the Connector Instance Persistent data store. **** */
    
      /**
       * Remove this Connector Instance's persistent store state.
       */
      public void removeConnector() {
        stateStore.removeConnectorState(storeContext);
        schedStore.removeConnectorSchedule(storeContext);
        configStore.removeConnectorConfiguration(storeContext);
      }
    
      /**
       * Get the configuration data for this connector instance.
       *
       * @return a Map&lt;String, String&gt; of its ConnectorType-specific
       * configuration data, or null if no configuration is stored.
       */
      public Map<String, String> getConnectorConfig() {
        if (properties == null) {
          properties = configStore.getConnectorConfiguration(storeContext);
        }
        return PropertiesUtils.toMap(properties);
      }
    
      /**
       * Set the configuration data for this connector instance.
       * Writes the supplied configuration through to the persistent store.
       *
       * @param configMap a Map&lt;String, String&gt; of its ConnectorType-specific
       *        configuration data, or null if no configuration is stored.
       */
      public void setConnectorConfig(Map<String, String> configMap) {
        properties = PropertiesUtils.fromMap(configMap);
        if (configMap == null) {
          configStore.removeConnectorConfiguration(storeContext);
        } else {
          configStore.storeConnectorConfiguration(storeContext, properties);
        }
      }
    
      /**
       * Sets the schedule for this connector instance.
       * Writes the modified schedule through to the persistent store.
       *
       * @param connectorSchedule String to store or null unset any existing
       * schedule.
       */
      public void setConnectorSchedule(String connectorSchedule) {
        if (connectorSchedule == null) {
          schedStore.removeConnectorSchedule(storeContext);
        } else {
          schedStore.storeConnectorSchedule(storeContext, connectorSchedule);
        }
      }
    
      /**
       * Gets the schedule for this connector instance.
       *
       * @return the schedule String, or null to erase any previously set schedule.
       * for this connector
       */
      public String getConnectorSchedule() {
        return schedStore.getConnectorSchedule(storeContext);
      }
    
      /**
       * Sets the remembered traversal state for this connector instance.
       * Writes the modified state through to the persistent store.
       *
       * @param connectorState String to store or null to erase any previously
       * saved traversal state.
       * @throws IllegalStateException if state store is disabled for this connector
       */
      public void setConnectorState(String connectorState) {
        if (connectorState == null) {
          stateStore.removeConnectorState(storeContext);
        } else {
          stateStore.storeConnectorState(storeContext, connectorState);
        }
      }
    
      /**
       * Gets the remembered traversal state for this connector instance.
       *
       * @return the state, or null if no state has been stored for this connector
       * @throws IllegalStateException if state store is disabled for this connector
       */
      public String getConnectorState() {
        return stateStore.getConnectorState(storeContext);
      }
    
      /**
       * Upgrade ConnectorConfigStore.  If the ConnectorConfigStore has
       * no stored configuration data for this connector, look in the
       * Legacy stores (those used in earlier versions of the product).
       * If a configuration was found in a Legacy store, move it to the
       * new store.
       *
       * @param info a partially constructed InstanceInfo describing the
       * connector.
       */
      private static void upgradeConfigStore(InstanceInfo info) {
        if (legacyConfigStores != null) {
          for (ConnectorConfigStore legacyStore : legacyConfigStores) {
            Properties properties =
                legacyStore.getConnectorConfiguration(info.storeContext);
            if (properties != null) {
              LOGGER.config("Migrating configuration information for connector "
                            + info.connectorName + " from legacy storage "
                            + legacyStore.getClass().getName() + " to "
                            + configStore.getClass().getName());
              info.properties = properties;
              configStore.storeConnectorConfiguration(info.storeContext,
                                                      properties);
              legacyStore.removeConnectorConfiguration(info.storeContext);
              return;
            }
          }
        }
        LOGGER.config("Connector " + info.connectorName
                      + " lacks saved configuration information, and none was"
                      + " found in any LegacyConnectorConfigStores.");
      }
    
      /**
       * Upgrade ConnectorScheduleStore.  If the ConnectorScheduleStore has
       * no stored schedule data for this connector, look in the
       * Legacy stores (those used in earlier versions of the product).
       * If a schedule was found in a Legacy store, move it to the
       * new store.
       *
       * @param info a partially constructed InstanceInfo describing the
       * connector.
       */
      private static void upgradeScheduleStore(InstanceInfo info) {
        if (legacyScheduleStores != null) {
          for (ConnectorScheduleStore legacyStore : legacyScheduleStores) {
            String schedule = legacyStore.getConnectorSchedule(info.storeContext);
            if (schedule != null) {
              LOGGER.config("Migrating traversal schedule information for connector "
                            + info.connectorName + " from legacy storage "
                            + legacyStore.getClass().getName() + " to "
                            + schedStore.getClass().getName());
              schedStore.storeConnectorSchedule(info.storeContext, schedule);
              legacyStore.removeConnectorSchedule(info.storeContext);
              return;
            }
          }
        }
        LOGGER.config("Connector " + info.connectorName
                      + " lacks saved traversal schedule information, and none"
                      + " was found in any LegacyConnectorScheduleStores.");
      }
    
      /**
       * Upgrade ConnectorStateStore.  If the ConnectorStateStore has
       * no stored traversal state data for this connector, look in the
       * Legacy stores (those used in earlier versions of the product).
       * If a traversal state was found in a Legacy store, move it to the
       * new store.
       *
       * @param info a partially constructed InstanceInfo describing the
       * connector.
       */
      private static void upgradeStateStore(InstanceInfo info) {
        if (legacyStateStores != null) {
          for (ConnectorStateStore legacyStore : legacyStateStores) {
            String state = legacyStore.getConnectorState(info.storeContext);
            if (state != null) {
              LOGGER.config("Migrating traversal state information for connector "
                            + info.connectorName + " from legacy storage "
                            + legacyStore.getClass().getName() + " to "
                            + stateStore.getClass().getName());
              stateStore.storeConnectorState(info.storeContext, state);
              legacyStore.removeConnectorState(info.storeContext);
              return;
            }
          }
        }
        LOGGER.config("Connector " + info.connectorName
                      + " lacks saved traversal state information, and none was"
                      + " found in any LegacyConnectorStateStores.");
      }
    
    
      /* **** InstanceInfoExcepetions **** */
    
      static class InstanceInfoException extends InstantiatorException {
        InstanceInfoException(String message, Throwable cause) {
          super(message, cause);
        }
        InstanceInfoException(String message) {
          super(message);
        }
      }
    
      static class NullConnectorNameException extends InstanceInfoException {
        NullConnectorNameException() {
          super("Attempt to instantiate a connector with a null or empty name");
        }
      }
    
      static class NullDirectoryException extends InstanceInfoException {
        NullDirectoryException() {
          super("Attempt to instantiate a connector with a null directory");
        }
      }
    
      static class NullTypeInfoException extends InstanceInfoException {
        NullTypeInfoException() {
          super("Attempt to instantiate a connector with a null TypeInfo");
        }
      }
    
      static class FactoryCreationFailureException extends InstanceInfoException {
        FactoryCreationFailureException(Throwable cause,
            Resource prototype, String connectorName) {
          super("Spring factory creation failure for connector " + connectorName
              + " using resource " + prototype.getDescription(),
              cause);
        }
      }
    
      static class NoBeansFoundException extends InstanceInfoException {
        NoBeansFoundException(Resource prototype,
            String connectorName, Class<?> clazz) {
          super("No beans found of type " + clazz.getName() + " for connector "
              + connectorName + " using resource "
              + prototype.getDescription());
        }
      }
    
      static class BeanInstantiationFailureException extends InstanceInfoException {
        BeanInstantiationFailureException(Throwable cause,
            Resource prototype, String connectorName, String beanName) {
          super("Spring failure while instantiating bean " + beanName
              + " for connector " + connectorName + " using resource "
              + prototype.getDescription(), cause);
        }
      }
    
      static class PropertyProcessingInternalFailureException extends
          InstanceInfoException {
        PropertyProcessingInternalFailureException(Throwable cause,
            String connectorName) {
          super("Spring internal failure while processing configuration properties"
                + " for connector " + connectorName, cause);
        }
      }
    
      static class PropertyProcessingFailureException extends InstanceInfoException {
        PropertyProcessingFailureException(Throwable cause, Resource prototype,
            String connectorName) {
          super("Problem while processing configuration properties for connector "
                + connectorName + " using resource "
                + prototype.getDescription(), cause);
        }
      }
    }

    InstanceInfo fromDirectory(String connectorName,File connectorDir, TypeInfo typeInfo)方法与InstanceInfo fromNewConfig(String connectorName,File connectorDir, TypeInfo typeInfo, Map<String, String> configMap)方法的区别在于前者基于目录构造InstanceInfo实例,后者基于configuration map对象构造InstanceInfo实例

    PropertiesUtils是工具类,用于从属性文件加载Properties及map类型与Properties类型的转换

    其源码如下:

    public class PropertiesUtils {
    
      private static final Logger LOGGER =
          Logger.getLogger(PropertiesUtils.class.getName());
    
      public static final String GOOGLE_CONNECTOR_NAME =
          "googleConnectorName";
      public static final String GOOGLE_CONNECTOR_WORK_DIR =
          "googleConnectorWorkDir";
      public static final String GOOGLE_WORK_DIR = "googleWorkDir";
      public static final String GOOGLE_PROPERTIES_VERSION =
          "googlePropertiesVersion";
      public static final int GOOGLE_PROPERTIES_VERSION_NUMBER = 3;
    
      // Non-XML format Properties files are by definition 8859-1 encoding.
      public static final String PROPERTIES_ENCODING = "ISO-8859-1";
    
      private PropertiesUtils() {
        // prevents instantiation
      }
    
      /**
       * Read Properties from a file.  Decrypt passwords.
       *
       * @param propertiesFile Properties File to read
       * @return Properties as read from file
       * @throws PropertiesException if error reading file
       */
      public static Properties loadFromFile(File propertiesFile)
        throws PropertiesException {
        try {
          InputStream is =
              new BufferedInputStream(new FileInputStream(propertiesFile));
          try {
            return loadProperties(is);
          } finally {
            is.close();
          }
        } catch (Exception e) {
          throw new PropertiesException("Unable to load Properties from file "
                                        + propertiesFile.getPath(), e);
        }
      }
    
      /**
       * Write the properties to a file.  Encrypt passwords,
       * version the properties.
       *
       * @param properties Properties to write
       * @param propertiesFile File to write properties to
       * @param comment optional comment String to pass to Properties.store()
       * @throws PropertiesException if error writing to file
       */
      public static void storeToFile(Properties properties, File propertiesFile,
          String comment) throws PropertiesException {
        try {
          FileOutputStream fos = new FileOutputStream(propertiesFile);
          try {
            storeProperties(properties, fos, comment);
          } finally {
            fos.close();
          }
        } catch (Exception e) {
          throw new PropertiesException("Unable to store Properties to file "
                                        + propertiesFile.getPath(), e);
        }
      }
    
      /**
       * Store a set of Properties to a String.  This is effectively
       * java.util.Properties.store(StringOutputStream), if there were
       * such a thing as StringOutputStream.  The returned string is
       * suitable for loading back into as set of Properties using
       * fromString(String).
       *
       * @param properties to encode into a String
       * @param comment optional comment string to pass to Properties.store()
       * @return a String object with containing the properties.
       * @throws PropertiesException
       */
      public static String storeToString(Properties properties, String comment)
          throws PropertiesException {
        try {
          ByteArrayOutputStream os = null;
          try {
            os = new ByteArrayOutputStream();
            storeProperties(properties, os, comment);
            return os.toString(PROPERTIES_ENCODING);
          } finally {
            os.close();
          }
        } catch (IOException e) {
          throw new PropertiesException("Unable to encode Properties to String", e);
        }
      }
    
      /**
       * Load a set of Properties from a String.  This is effectively
       * java.util.Properties.load(StringInputStream), if there were
       * such a thing as StringInputStream.  This should be able to
       * load properties from strings created by toString();
       *
       * @param propertiesString
       * @return a Properties object, or null if null string
       * @throws PropertiesException
       */
      public static Properties loadFromString(String propertiesString)
          throws PropertiesException {
        if (propertiesString != null) {
          try {
            ByteArrayInputStream is = null;
            try {
              is = new ByteArrayInputStream(
                 propertiesString.getBytes(PROPERTIES_ENCODING));
              return loadProperties(is);
            } finally {
              is.close();
            }
          } catch (IOException e) {
            throw new PropertiesException("Unable to decode Properties from String",
                                          e);
          }
        }
        return null;
      }
    
      /**
       * Read Properties from an InputStream.  Decrypt passwords.
       *
       * @param inputStream InputStream to read Properties from
       * @return Properties as read from inputStream
       * @throws PropertiesException
       */
      public static Properties loadProperties(InputStream inputStream)
          throws PropertiesException {
        if (inputStream == null) {
          return null;
        }
        Properties properties = new Properties();
        try {
          properties.load(inputStream);
        } catch (Exception e) {
          throw new PropertiesException("Error loading properties from stream", e);
        }
    
        // Decrypt stored passwords.
        decryptSensitiveProperties(properties);
    
        return properties;
      }
    
      /**
       * Write the properties to an OutputStream.  Encrypt passwords,
       * version the properties.
       *
       * @param properties Properties to write
       * @param outputStream OutputStream to write properties to
       * @param comment optional comment String
       * @throws PropertiesException if error writing to stream
       */
      public static void storeProperties(Properties properties,
          OutputStream outputStream, String comment) throws PropertiesException {
        if (properties == null) {
          return;
        }
        try {
          // Make a copy of the Properties before munging them.
          Properties props = copy(properties);
          stampPropertiesVersion(props);
          encryptSensitiveProperties(props);
          // If the comment contains embedded newlines, we must comment out each
          // subsequent line after the first, as Java Properties won't do it for us.
          if (comment != null && comment.indexOf('\n') > 0) {
            comment = comment.replaceAll("\n", "\n#");
          }
          props.store(outputStream, comment);
        } catch (Exception e) {
          throw new PropertiesException("Error storing properties to stream", e);
        }
      }
    
      /**
       * Make a Properties object from a Map, copying all the keys and values.
       *
       * @param sourceMap a Map representing properties key-value map
       * @return new Properties object that may be modified without altering
       *          the source properties.
       */
      public static Properties fromMap(Map<String, String> sourceMap) {
        if (sourceMap == null) {
          return null;
        }
        Properties properties = new Properties();
        properties.putAll(sourceMap);
        return properties;
      }
    
      /**
       * Make a Map&lt;String, String&gt; from the supplied Properties,
       * copying all the keys and values.
       *
       * @param sourceProperties Properties representing properties key-value map.
       * @return a Map&lt;String, String&gt; representation of the source
       *          Properties.
       */
      public static Map<String, String> toMap(Properties sourceProperties) {
        if (sourceProperties == null) {
          return null;
        }
        Map<String, String> configMap = new HashMap<String, String>();
        Iterator<?> iter = sourceProperties.keySet().iterator();
        while (iter.hasNext()) {
          String key = (String) iter.next();
          configMap.put(key, sourceProperties.getProperty(key));
        }
        return configMap;
      }
    
      /**
       * Make a deep copy of a Properties object - copying all
       * the keys and values.  This is in contrast to java.util.Propeties.copy(),
       * which makes a shallow copy.
       *
       * @param sourceProperties a source set of Properties.
       * @return new Properties object that may be modified without altering
       * the source properties.
       */
      public static Properties copy(Properties sourceProperties) {
        Properties props = new Properties();
        props.putAll(sourceProperties);
        return props;
      }
    
      /**
       * Encrypt Properties values that may be sensitive.  At this point,
       * any property that has the case-insensitive substring 'password'
       * in the key is considered sensitive.  Encrypting sensitive properties
       * is advisable when storing or transmitting properties in plain text.
       *
       * @param properties a set of Properties.
       */
      public static void encryptSensitiveProperties(Properties properties) {
        EncryptedPropertyPlaceholderConfigurer.encryptSensitiveProperties(properties);
      }
    
      /**
       * Decrypt Properties values that may be sensitive.  At this point,
       * any property that has the case-insensitive substring 'password'
       * in the key is considered sensitive.  This decrypts a set of
       * properties that was encrypted via encryptSensitiveProperties();
       *
       * @param properties a set of Properties.
       */
      public static void decryptSensitiveProperties(Properties properties) {
        EncryptedPropertyPlaceholderConfigurer.decryptSensitiveProperties(properties);
      }
    
      /**
       * Stamp the Properties set with the current Properties Version.
       *
       * @param properties a set of Properties.
       */
      public static void stampPropertiesVersion(Properties properties) {
        properties.put(GOOGLE_PROPERTIES_VERSION,
            Integer.toString(GOOGLE_PROPERTIES_VERSION_NUMBER));
      }
    
      /**
       * Retrieve the Properties Version stamp from this Properties set.
       *
       * @param properties a set of Properties.
       */
      public static int getPropertiesVersion(Properties properties) {
        String versionStr = properties.getProperty(
            GOOGLE_PROPERTIES_VERSION, "0");
        int version = 0;
        try {
          version = Integer.parseInt(versionStr);
          if (version > GOOGLE_PROPERTIES_VERSION_NUMBER) {
            LOGGER.warning("Properties appear to have been written by a newer "
                + "version of Connector Manager (" + version + ")");
          }
        } catch (NumberFormatException e) {
          LOGGER.warning("Invalid Properties Version: " + versionStr);
        }
        return version;
      }
    }

    本系列企业搜索引擎开发之连接器connector系本人原创

    转载请注明出处 博客园 刺猬的温驯

    本文链接http://www.cnblogs.com/chenying99/archive/2013/03/19/2970304.html

  • 相关阅读:
    【Office】将一个excel文件中的表移动至另一个excel中
    【 DB_Oracle】impdp/expdp导入导出dmp文件
    【DB_Oracle】设置Oracle的监听和服务随 Linux开机自启
    【DB_Oracle】Centos中安装oracle11g R2
    【 DB_Oracle】Linux下启动Oracle服务和监听程序
    【OS_Linux】VMware中给CentOS磁盘扩容
    【 OS_Linux】WinSCP实现Windows与Linux间文件的传输
    【DB_Oracle】windows下安装Oracle 11g
    鼠标键盘失灵对策(Windows8.1)
    UNIX 高手的另外 10 个习惯
  • 原文地址:https://www.cnblogs.com/chenying99/p/2970304.html
Copyright © 2020-2023  润新知