本文接着分析连接器及其构造工厂相关设计及源码,先浏览一下下面的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<String, String> 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<String, String> 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<String, String> from the supplied Properties, * copying all the keys and values. * * @param sourceProperties Properties representing properties key-value map. * @return a Map<String, String> 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