/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.commands.catalog; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.apache.karaf.shell.api.action.Command; import org.apache.karaf.shell.api.action.Option; import org.apache.karaf.shell.api.action.lifecycle.Service; import org.codice.ddf.commands.catalog.facade.CatalogFacade; import org.codice.ddf.commands.catalog.facade.Provider; import org.opengis.filter.Filter; import org.opengis.filter.sort.SortOrder; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.catalog.data.Metacard; import ddf.catalog.federation.FederationException; import ddf.catalog.filter.impl.SortByImpl; import ddf.catalog.operation.QueryRequest; import ddf.catalog.operation.SourceProcessingDetails; import ddf.catalog.operation.SourceResponse; import ddf.catalog.operation.impl.QueryImpl; import ddf.catalog.operation.impl.QueryRequestImpl; import ddf.catalog.source.CatalogProvider; import ddf.catalog.source.SourceUnavailableException; import ddf.catalog.source.UnsupportedQueryException; import ddf.catalog.util.impl.ServiceComparator; @Service @Command(scope = CatalogCommands.NAMESPACE, name = "migrate", description = "Migrates Metacards from one Provider to another Provider.") public class MigrateCommand extends DuplicateCommands { private static final Logger LOGGER = LoggerFactory.getLogger(MigrateCommand.class); @Option(name = "--list", required = false, aliases = { "-list"}, multiValued = false, description = "Print a list of Providers.") boolean listProviders = false; @Option(name = "--from", required = false, aliases = { "-from"}, multiValued = false, description = "The Source ID of the Provider to migrate from.") String fromProviderId; @Option(name = "--to", required = false, aliases = { "-to"}, multiValued = false, description = "The Source ID of the Provider to migrate to.") String toProviderId; @Override protected Object executeWithSubject() throws Exception { final List<CatalogProvider> providers = getCatalogProviders(); if (listProviders) { if (providers.size() == 0) { console.println("There are no available Providers."); return null; } console.println("Available Providers:"); providers.stream() .map(p -> p.getClass() .getSimpleName()) .forEach(id -> console.println("\t" + id)); return null; } if (batchSize > MAX_BATCH_SIZE || batchSize < 1) { console.println("Batch Size must be between 1 and " + MAX_BATCH_SIZE + "."); return null; } if (providers.isEmpty() || providers.size() < 2) { console.println("Not enough CatalogProviders installed to migrate."); return null; } final CatalogProvider fromProvider = promptForProvider("FROM", fromProviderId, providers); if (fromProvider == null) { console.println("Invalid \"FROM\" Provider id."); return null; } console.println("FROM Provider ID: " + fromProvider.getClass() .getSimpleName()); final CatalogProvider toProvider = promptForProvider("TO", toProviderId, providers); if (toProvider == null) { console.println("Invalid \"TO\" Provider id."); return null; } console.println("TO Provider ID: " + toProvider.getClass() .getSimpleName()); CatalogFacade queryProvider = new Provider(fromProvider); CatalogFacade ingestProvider = new Provider(toProvider); start = System.currentTimeMillis(); console.println("Starting migration."); duplicateInBatches(queryProvider, ingestProvider, getFilter()); console.println(); long end = System.currentTimeMillis(); String completed = String.format( " %d record(s) migrated; %d record(s) failed; completed in %3.3f seconds.", ingestedCount.get(), failedCount.get(), (end - start) / MS_PER_SECOND); LOGGER.debug("Migration Complete: {}", completed); console.println(completed); return null; } private CatalogProvider promptForProvider(String whichProvider, String id, List<CatalogProvider> providers) throws IOException { List<String> providersIdList = providers.stream() .map(p -> p.getClass() .getSimpleName()) .collect(Collectors.toList()); while (true) { if (StringUtils.isBlank(id) || !providersIdList.contains(id)) { console.println( "Please enter the Source ID of the \"" + whichProvider + "\" Provider:"); } else { break; } id = getInput(whichProvider + " Provider ID: "); } final String providerId = id; final CatalogProvider provider = providers.stream() .filter(p -> p.getClass() .getSimpleName() .equals(providerId)) .findFirst() .orElse(null); return provider; } @Override protected SourceResponse query(CatalogFacade framework, Filter filter, int startIndex, long querySize) { QueryImpl query = new QueryImpl(filter); query.setRequestsTotalResultsCount(true); query.setPageSize((int) querySize); query.setSortBy(new SortByImpl(Metacard.MODIFIED, SortOrder.DESCENDING)); QueryRequest queryRequest = new QueryRequestImpl(query); query.setStartIndex(startIndex); SourceResponse response; try { LOGGER.debug("Querying with startIndex: {}", startIndex); response = framework.query(queryRequest); } catch (UnsupportedQueryException | SourceUnavailableException | FederationException e) { printErrorMessage(String.format("Received error from Frameworks: %s%n", e.getMessage())); return null; } if (response.getProcessingDetails() != null && !response.getProcessingDetails() .isEmpty()) { for (SourceProcessingDetails details : response.getProcessingDetails()) { LOGGER.debug("Got Issues: {}", details.getWarnings()); } return null; } return response; } private List<CatalogProvider> getCatalogProviders() { ServiceTracker st = new ServiceTracker(bundleContext, CatalogProvider.class.getName(), null); st.open(); ServiceReference<CatalogProvider>[] serviceRefs = st.getServiceReferences(); Map<ServiceReference<CatalogProvider>, CatalogProvider> map = new TreeMap<>(new ServiceComparator()); if (null != serviceRefs) { for (ServiceReference<CatalogProvider> serviceReference : serviceRefs) { map.put(serviceReference, (CatalogProvider) st.getService(serviceReference)); } } return new ArrayList<>(map.values()); } }