
com.android.repository.api.RepoManager Maven / Gradle / Ivy
Show all versions of repository Show documentation
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.repository.api;
import static com.android.repository.impl.meta.TypeDetails.GenericType;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.repository.impl.manager.RepoManagerImpl;
import com.android.repository.impl.meta.CommonFactory;
import com.android.repository.impl.meta.GenericFactory;
import com.android.repository.impl.meta.RepositoryPackages;
import com.android.repository.io.FileOp;
import com.android.repository.io.FileOpUtils;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.w3c.dom.ls.LSResourceResolver;
/**
* Primary interface for interacting with repository packages.
*
* To set up an {@code RepoManager}:
*
* -
* Register the {@link SchemaModule}s used to parse the package.xml files and
* remote repositories used by this repo using {@link
* #registerSchemaModule(SchemaModule)}
*
* -
* Set the path where the repo is installed locally using {@link #setLocalPath(File)}.
*
* -
* If your local repo might contain packages created by a previous system, set a
* {@link FallbackLocalRepoLoader} that can recognize and convert those packages using
* {@link #setFallbackLocalRepoLoader(FallbackLocalRepoLoader)}.
*
* -
* Add {@link RepositorySourceProvider}s to provide URLs for remotely-available packages.
*
* -
* If some sources might be in a format used by a previous system, set a {@link
* FallbackRemoteRepoLoader} that can read and convert them.
*
*
*
* To load the local and remote packages, use {@link #load(long, List, List, List, ProgressRunner,
* Downloader, SettingsController, boolean)}
*
* TODO: it would be nice if this could be redesigned such that load didn't need to be called
* explicitly, or there was a better way to know if packages were or need to be loaded.
*
* To use the loaded packages, get an {@link RepositoryPackages} object from {@link #getPackages()}.
*/
public abstract class RepoManager {
/**
* After loading the repository, this is the amount of time that must pass before we consider it
* to be stale and need to be reloaded.
*/
public static final long DEFAULT_EXPIRATION_PERIOD_MS = TimeUnit.DAYS.toMillis(1);
/**
* Pattern for name of the xsd file used in {@link #sCommonModule}.
*/
private static final String COMMON_XSD_PATTERN = "repo-common-%02d.xsd";
/**
* Pattern for fully-qualified name of the {@code ObjectFactory} used in {@link
* #sCommonModule}.
*/
private static final String COMMON_OBJECT_FACTORY_PATTERN
= "com.android.repository.impl.generated.v%d.ObjectFactory";
/**
* Pattern for name of the xsd file used in {@link #sCommonModule}.
*/
private static final String GENERIC_XSD_PATTERN = "generic-%02d.xsd";
/**
* Pattern for fully-qualified name of the {@code ObjectFactory} used in {@link
* #sCommonModule}.
*/
private static final String GENERIC_OBJECT_FACTORY_PATTERN
= "com.android.repository.impl.generated.generic.v%d.ObjectFactory";
/**
* The base {@link SchemaModule} that is created by {@code RepoManagerImpl} itself.
*/
private static SchemaModule sCommonModule;
static {
try {
sCommonModule = new SchemaModule<>(COMMON_OBJECT_FACTORY_PATTERN, COMMON_XSD_PATTERN,
RepoManager.class);
} catch (Exception e) {
// This should never happen unless there's something wrong with the common repo schema.
assert false : "Failed to create SchemaModule: " + e;
}
}
/**
* The {@link SchemaModule} that contains an implementation of {@link GenericType}.
*/
private static SchemaModule sGenericModule;
static {
try {
sGenericModule = new SchemaModule<>(GENERIC_OBJECT_FACTORY_PATTERN, GENERIC_XSD_PATTERN,
RepoManager.class);
} catch (Exception e) {
// This should never happen unless there's something wrong with the generic repo schema.
assert false : "Failed to create SchemaModule: " + e;
}
}
/**
* @param fop The {@link FileOp} to use for local filesystem operations. Probably {@link
* FileOpUtils#create()} unless part of a unit test.
* @return A new {@code RepoManager}.
*/
@NonNull
public static RepoManager create(@NonNull FileOp fop) {
return new RepoManagerImpl(fop);
}
/**
* Register an {@link SchemaModule} that can be used when parsing XML for this repo.
*/
public abstract void registerSchemaModule(@NonNull SchemaModule module);
/**
* Gets the currently-registered {@link SchemaModule}s. This probably shouldn't be used except
* by code within the RepoManager or unit tests.
*/
@NonNull
public abstract Set> getSchemaModules();
/**
* Gets the core {@link SchemaModule} created by the RepoManager itself. Contains the base
* definition of repository, package, revision, etc.
*/
@NonNull
public static SchemaModule getCommonModule() {
return sCommonModule;
}
/**
* Gets the {@link SchemaModule} created by the RepoManager that includes the trivial generic
* {@code typeDetails} type.
*/
@NonNull
public static SchemaModule getGenericModule() {
return sGenericModule;
}
/**
* Sets the path to the local repository root.
*/
public abstract void setLocalPath(@Nullable File path);
/**
* Gets the path to the local repository root. This probably shouldn't be needed except by the
* repository manager and unit tests.
*/
@Nullable
public abstract File getLocalPath();
/**
* Sets the {@link FallbackLocalRepoLoader} to use when scanning the local repository for
* packages.
*/
public abstract void setFallbackLocalRepoLoader(@Nullable FallbackLocalRepoLoader local);
/**
* Adds a {@link RepositorySourceProvider} from which to get {@link RepositorySource}s from
* which to download lists of available repository packages.
*/
public abstract void registerSourceProvider(@NonNull RepositorySourceProvider provider);
/**
* Gets the currently registered {@link RepositorySourceProvider}s. Should only be needed for
* testing.
*/
@NonNull
@VisibleForTesting
public abstract Set getSourceProviders();
/**
* Gets the actual {@link RepositorySource}s from the registered {@link
* RepositorySourceProvider}s.
*
* Probably should only be needed by a repository UI.
*
* @param downloader The {@link Downloader} to use for downloading source lists, if needed.
* @param progress A {@link ProgressIndicator} for source providers to use to show their
* progress and for logging.
* @param forceRefresh Individual {@link RepositorySourceProvider}s may cache their results. If
* {@code forceRefresh} is true, specifies that they should reload rather
* than returning cached results.
* @return The {@link RepositorySource}s obtained from the providers.
*/
public abstract Set getSources(@Nullable Downloader downloader,
@NonNull ProgressIndicator progress, boolean forceRefresh);
/**
* Sets the {@link FallbackRemoteRepoLoader} to try when we encounter a remote xml file that the
* RepoManger can't read.
*/
public abstract void setFallbackRemoteRepoLoader(@Nullable FallbackRemoteRepoLoader remote);
/**
* Load the local and remote repositories.
*
* In callbacks, be careful of invoking tasks synchronously on other threads (e.g. the swing ui
* thread), since they might also be used by the {@link ProgressRunner) passed in.
*
* @param cacheExpirationMs How long must have passed since the last load for us to reload.
* Specify {@code 0} to reload immediately.
* @param onLocalComplete When loading, the local repo load happens first, and should be
* relatively fast. When complete, the {@code onLocalComplete} {@link
* RepoLoadedCallback}s are run. Will be called with a {@link
* RepositoryPackages} that contains only the local packages.
* @param onSuccess Callbacks that are run when the entire load (local and remote) has
* completed successfully. Called with an {@link RepositoryPackages}
* containing both the local and remote packages.
* @param onError Callbacks that are run when there's an error at some point during
* the load.
* @param runner The {@link ProgressRunner} to use for any tasks started during the
* load, including running the callbacks.
* @param downloader The {@link Downloader} to use for downloading remote files,
* including any remote list of repo sources and the remote
* repositories themselves.
* @param settings The settings to use during the load, including for example proxy
* settings used when fetching remote files.
* @param sync If true, load synchronously. If false, load asynchronously (this
* method should return quickly, and the {@code onSuccess} callbacks
* can be used to process the completed results).
*
* TODO: throw exception if cancelled
*/
public abstract void load(long cacheExpirationMs,
@Nullable List onLocalComplete,
@Nullable List onSuccess,
@Nullable List onError,
@NonNull ProgressRunner runner,
@Nullable Downloader downloader,
@Nullable SettingsController settings,
boolean sync);
/**
* Load the local and remote repositories synchronously.
*
* @param cacheExpirationMs How long must have passed since the last load for us to reload.
* Specify {@code 0} to reload immediately.
* @param progress The {@link ProgressIndicator} to use for showing progress and
* logging.
* @param downloader The {@link Downloader} to use for downloading remote files,
* including any remote list of repo sources and the remote
* repositories themselves.
* @param settings The settings to use during the load, including for example proxy
* settings used when fetching remote files.
* @return {@code true} if the load was successful (including if cached results were returned),
* false otherwise.
*/
public final boolean loadSynchronously(long cacheExpirationMs,
@NonNull final ProgressIndicator progress,
@Nullable Downloader downloader, @Nullable SettingsController settings) {
final AtomicBoolean result = new AtomicBoolean(true);
load(cacheExpirationMs, null, null, ImmutableList.of(
() -> result.set(false)), new DummyProgressRunner(progress),
downloader, settings, true);
return result.get();
}
/**
* Causes cached results to be considered expired. The next time {@link #load(long, List, List,
* List, ProgressRunner, Downloader, SettingsController, boolean)} is called, a complete load
* will be done.
*/
public abstract void markInvalid();
/**
* Causes the cached results of the local repositories to be considered expired. The next time
* {@link #load(long, List, List, List, ProgressRunner, Downloader, SettingsController,
* boolean)} is called, the load will be done only for the local repositories, the remotes being
* loaded from the cache if possible.
*/
public abstract void markLocalCacheInvalid();
/**
* Check to see if there have been any changes to the local repo since the last load. This
* includes scanning the local repo for packages, but does not involve any reading or parsing of
* package metadata files. If there have been any changes, or if the cache is older than the
* default timeout, the local packages will be reloaded.
*
* @return {@code true} if the load was successful, {@code false} otherwise}.
*/
public abstract boolean reloadLocalIfNeeded(@NonNull ProgressIndicator progress);
/**
* Gets the currently-loaded {@link RepositoryPackages}.
*/
@NonNull
public abstract RepositoryPackages getPackages();
/**
* Gets an {@link LSResourceResolver} that can find the XSDs for all versions of the
* currently-registered {@link SchemaModule}s by namespace. Returns null if there is an error.
*/
@Nullable
public abstract LSResourceResolver getResourceResolver(@NonNull ProgressIndicator progress);
/**
* Registers a listener that will be called whenever the local packages are reloaded and have
* changed. The {@link RepositoryPackages} instance passed to the callback will contain only the
* local packages.
*/
public abstract void registerLocalChangeListener(@NonNull RepoLoadedCallback listener);
/**
* Register a listener that will be called whenever the remote packages are reloaded and have
* changed. The {@link RepositoryPackages} instance will contain the remote and local packages.
*/
public abstract void registerRemoteChangeListener(@NonNull RepoLoadedCallback listener);
/**
* Record that the given package is in the process of being installed by the given installer.
*/
public abstract void installBeginning(@NonNull RepoPackage repoPackage,
@NonNull PackageOperation installer);
/**
* Record that the given package is no longer in the process of being installed (that is,
* install completed either successfully or unsuccessfully).
*/
public abstract void installEnded(@NonNull RepoPackage repoPackage);
/**
* Gets the previously-registered installer that is currently installing the given package, or
* {@code null} if there is none.
*/
@Nullable
public abstract PackageOperation getInProgressInstallOperation(
@NonNull RepoPackage remotePackage);
/**
* Callback for when repository load is completed/partially completed.
*/
public interface RepoLoadedCallback {
/**
* @param packages The packages that have been loaded so far. When this callback is used in
* the {@code onLocalComplete} argument to {@link #load(long, List, List,
* List, ProgressRunner, Downloader, SettingsController, boolean)} {@code
* packages} will only include local packages.
*/
void doRun(@NonNull RepositoryPackages packages);
}
protected static class DummyProgressRunner implements ProgressRunner {
private final ProgressIndicator mProgress;
public DummyProgressRunner(@NonNull ProgressIndicator progress) {
mProgress = progress;
}
@Override
public void runAsyncWithProgress(@NonNull ProgressRunnable r) {
r.run(mProgress, this);
}
@Override
public void runSyncWithProgress(@NonNull ProgressRunnable r) {
r.run(mProgress, this);
}
@Override
public void runSyncWithoutProgress(@NonNull Runnable r) {
r.run();
}
}
}