// 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.persist;
import com.google.common.collect.ImmutableMap;
import com.google.enterprise.connector.instantiator.Configuration;
import com.google.enterprise.connector.scheduler.Schedule;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Copies the objects from zero or more legacy persistent stores to
* the production persistent store. Existing objects in the production
* store are not overwritten.
*/
// TODO: We could optionally support overwriting existing objects in
// the target and copying objects from the legacy stores rather than
// moving them. For manual migration, we might want options to delete
// the existing production store and then migrate the legacy stores
// (so that nulls overwrite objects, for example), or to selectively
// overwrite objects, or even to prompt for each object copied.
public class StoreMigrator {
private static final Logger LOGGER =
Logger.getLogger(StoreMigrator.class.getName());
private final PersistentStore store;
private final List<PersistentStore> legacyStores;
public StoreMigrator(PersistentStore store,
List<PersistentStore> legacyStores) {
this.store = store;
this.legacyStores = (legacyStores == null)
? Collections.<PersistentStore>emptyList() : legacyStores;
}
/**
* Migrates data from all legacy stores to the configured PersistentStore.
*/
public void migrate() {
for (PersistentStore legacyStore : legacyStores) {
migrate(legacyStore, store, null, false);
}
checkMissing(store, null);
}
/**
* Migrates data from the {@code sourceStore} to the {@code destStore}.
*
* @param sourceStore source {@link PersistentStore}
* @param destStore destination {@link PersistentStore}
* @param connectorNames Collection of connector names to migrate.
* @param force if {@code true} overwrite existing data in the
* {@code destStore}.
*/
public static void migrate(PersistentStore sourceStore,
PersistentStore destStore,
Collection<String> connectorNames,
boolean force) {
ImmutableMap<StoreContext, ConnectorStamps> inventory =
sourceStore.getInventory();
for (StoreContext context : inventory.keySet()) {
if (connectorNames != null &&
!connectorNames.contains(context.getConnectorName())) {
continue;
}
// This double assignment ensures that we check the same
// object type that we're storing.
Configuration config = destStore.getConnectorConfiguration(context);
if (force || config == null) {
config = sourceStore.getConnectorConfiguration(context);
if (config != null) {
logMigration(sourceStore, destStore, context, "configuration");
destStore.storeConnectorConfiguration(context, config);
}
}
Schedule sched = destStore.getConnectorSchedule(context);
if (force || sched == null) {
sched = sourceStore.getConnectorSchedule(context);
if (sched != null) {
logMigration(sourceStore, destStore, context, "traversal schedule");
destStore.storeConnectorSchedule(context, sched);
}
}
String state = destStore.getConnectorState(context);
if (force || state == null) {
state = sourceStore.getConnectorState(context);
if (state != null) {
logMigration(sourceStore, destStore, context, "traversal state");
destStore.storeConnectorState(context, state);
}
}
}
}
/**
* Checks for missing persistently stored data in the
* {@code persistentStore}. If items are missing from the store,
* log a message.
*
* @param persistentStore PersistentStore to check.
* @param connectorNames Collection of connector names to check.
*/
public static void checkMissing(PersistentStore persistentStore,
Collection<String> connectorNames) {
// Check for missing objects in the specified store.
ImmutableMap<StoreContext, ConnectorStamps> inventory =
persistentStore.getInventory();
for (StoreContext context : inventory.keySet()) {
if (connectorNames != null &&
!connectorNames.contains(context.getConnectorName())) {
continue;
}
if (persistentStore.getConnectorConfiguration(context) == null) {
logMissing(context, "configuration");
}
if (persistentStore.getConnectorSchedule(context) == null) {
logMissing(context, "traversal schedule");
}
if (persistentStore.getConnectorState(context) == null) {
logMissing(context, "traversal state");
}
}
}
private static void logMigration(
PersistentStore sourceStore, PersistentStore destStore,
StoreContext context, String objectType) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Migrating " + objectType + " information for connector "
+ context.getConnectorName() + " from "
+ sourceStore.getClass().getName() + " to "
+ destStore.getClass().getName());
}
}
private static void logMissing(StoreContext context,
String objectType) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Connector " + context.getConnectorName()
+ " lacks saved " + objectType + ".");
}
}
}