// 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.util.database; import com.google.common.annotations.VisibleForTesting; import com.google.enterprise.connector.spi.ConnectorType; import com.google.enterprise.connector.spi.DatabaseResourceBundle; import com.google.enterprise.connector.spi.LocalDatabase; import com.google.enterprise.connector.spi.SpiConstants.DatabaseType; import java.util.logging.Logger; import javax.sql.DataSource; /** * An implemenation of {@link LocalDatabase}, built as a thin wrapper upon * {@link JdbcDatabase} and {@link DatabaseResourceBundleManager}. * * @since 2.8 */ public class LocalDatabaseImpl implements LocalDatabase { private static final Logger LOGGER = Logger.getLogger(LocalDatabaseImpl.class.getName()); private final JdbcDatabase database; private final ClassLoader classLoader; private final DatabaseResourceBundleManager resourceBundleManager; /* Automatically generate the ResourceBundle baseName, * based upon the ConnectorType name and an arbitary * prefix and suffix. This is so much easier for the * Connector developer than allowing them to specify * the baseName of their choice. Note that the use * of a baseName suffix (containing an underscore) deviates * considerably from the typical use of ResourceBundles * for natural languages, while providing no benefit to * the developer or user. */ private static final String RESOURCE_BUNDLE_PREFIX = "config."; private static final String RESOURCE_BUNDLE_SUFFIX = "_sql"; @VisibleForTesting protected String resourceBundleBaseName; /** * Constructs a {@link LocalDatabase} implementation for Connectors * of type {@code connectorTypeName}. The {@code connectorTypeName} * is used to select {@link DatabaseResourceBundle}s for use by the * Connector. * * @param jdbcDatabase backing database for this LocalDatabase * @param connectorTypeName the Connector's ConnectorType name * @param connectorType a ConnectorType whose ClassLoader to use to locate * DatabaseResourceBundles. This may be specific to Connector type. * If {@code null}, the default ClassLoader will be used. */ public LocalDatabaseImpl(JdbcDatabase jdbcDatabase, String connectorTypeName, ConnectorType connectorType) { // TODO: Does this restrict resource lookup to the JAR file containing // the ConnectorType? This may or may not be desirable. this(jdbcDatabase, connectorTypeName, (connectorType == null) ? null : connectorType.getClass().getClassLoader()); } /** * Constructs a {@link LocalDatabase} implementation for Connectors * of type {@code connectorTypeName}. The {@code connectorTypeName} * is used to select {@link DatabaseResourceBundle}s for use by the * Connector. * * @param jdbcDatabase backing database for this LocalDatabase * @param connectorTypeName the Connector's ConnectorType name. * @param classLoader ClassLoader to use to locate DatabaseResourceBundles. * If {@code null}, the default ClassLoader will be used. */ @VisibleForTesting public LocalDatabaseImpl(JdbcDatabase jdbcDatabase, String connectorTypeName, ClassLoader classLoader) { this.database = jdbcDatabase; this.classLoader = classLoader; this.resourceBundleManager = new DatabaseResourceBundleManager(); // If the connectorTypeName contains periods, replace them so as // not to look like a resource package name. resourceBundleBaseName = RESOURCE_BUNDLE_PREFIX + connectorTypeName.replace('.', '_') + RESOURCE_BUNDLE_SUFFIX; } /** * Gets a {@link DataSource} which the connector implementer can use for any * purpose. * * @return a {@link DataSource} */ @Override public DataSource getDataSource() { DataSource dataSource = database.getDataSource(); LOGGER.finest("Got DataSource: " + dataSource); return dataSource; } /** * Gets a {@link DatabaseResourceBundle} through which the connector * implementor can get database-specific resources, such as SQL. The {@code * DatabaseResourceBundle} returned will be constructed by the Connector * Manager to return resources specific to this connector's type and to the * specific database version currently in use. See the Developer's guide for * details on how implementors can supply resources to the installation. * * @return a {@link DatabaseResourceBundle} */ @Override public DatabaseResourceBundle getDatabaseResourceBundle() { LOGGER.finest("Fetching DatabaseResourceBundle: baseName = " + resourceBundleBaseName + ", extension = " + database.getResourceBundleExtension()); return resourceBundleManager.getResourceBundle(resourceBundleBaseName, database.getResourceBundleExtension(), classLoader); } /** * Returns a {@link DatabaseType} enum identifying the database * implementation. * * @return a non-null {@link DatabaseType} enum identifying the database * implementation. */ @Override public DatabaseType getDatabaseType() { LOGGER.finest("Fetching DatabaseType: " + database.getDatabaseType()); return database.getDatabaseType(); } /** * Returns a String giving a description of the database. * <p> * For now, the form of this string is intentionally under-specified, * for flexibility. The SPI only guarantees that, if this object's database * type is not {@link DatabaseType#OTHER}, then this string begins with the * string-value of that {@link DatabaseType}. More formally, for any * {@code LocalDatabase} object {@code db} then the following is true: * {@code (db.getDescription().startsWith(db.getDatabaseType().toString())) || * (db.getDatabaseType() == DatabaseType.OTHER)}. If the database type is * "other", then the string should start with a simple name of the * database (rather than the {@code "unsupported"}). * <p> * The remainder of the string is reserved to hold additional information, * such as version. * * @return a non-{@code null} String description of the database */ @Override public String getDescription() { LOGGER.finest("Fetching Database description: " + database.getProductName() + " (" + database.getDescription() + ")"); // Return a not-really-descriptive description. return database.getProductName(); // + " (" + database.getDescription() + ")"; } }