Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [p2-dev] Executing a P2 traverse query

Hi Tomas,

On Fri, Dec 10, 2010 at 5:58 PM, Thomas Hallgren <thomas@xxxxxxx> wrote:
The traverse query was written with the intention to replace the slicer but we never got around to actually do that in the 3.6 time frame. As it stands today, it's not a clean cut replacement. The slicer (permissive or non-permissive) will give you specific control over the query that is applicable for a couple of use-cases that is utilized by the PDE target platform provisioning system. One is for instance to guess whether a requirement stems from a PDE-feature 'include' or a PDE-feature 'requires' by looking at the version range (exact or not) in the requirement.

Thanks for the clarification.
 
The traverse query is generic and not capable of performing special considerations like that. It is however proven to be both faster and more memory conservative than the slicer so I guess the answer to your question all depends on your requirements. It would be easier to give a good answer if we knew more about what it is you want to accomplish.

Sure; here's what's up (short version):

We have an headless installer/updater built on top of Update Manager that I'm trying to move to P2.  Basically right now I'm spiking various use-cases in P2 with a view to forming an opinion about the best way to reimplement this API on top of P2.

But that's not all.  At EclipseCon, we contributed our headless installer API to E4 with a view to make it easy for others to build RCP applications like we have:

(a) When an application starts up, it checks for updates and automatically downloads/installs them.

(b) Alternatively, when a user logs in, the application contacts a provisioning server and obtains a list of Features/Versions that the user is permitted to use.  We then call Update Manager and install/update ONLY those Features/Versions and appropriate dependencies.

So we'd love to have feedback/input from the community.

Detail:

We've got (a) above tackled using P2.  We're struggling a bit with the API to implement (b).

The API that implements (b) is:

public boolean update(URL[] updateSiteURLs, File downloadRootDir, Set<FeatureVersionedIdentifier> featuresRequested) throws InstallError 

In P2 terms, updateSiteURLs wind up being consolidated into a compoundQueryable. 

I'm not sure what P2 terms are yet for downloadRootDir, but I'm guessing that this has something to do with P2 Profiles?  In Update Manager, this specified a root directory where we would create your install site(s).  The purpose is to enable installations on Citrix or *nixes where the installation directory might not be writable; this we have to support putting updates somewhere else.

featuresRequested is the set of Features that the current user is allowed to use.  It obviously must be a subset of the features on updateSiteURLs

[[A FeatureVersionedIdentifier simply is a (String, String) pair where the first String is the Feature ID and the second is either the exact version, a version number without the qualifier, or "0.0.0" to specify the latest version.  (IFeatureReference doesn't implement equals/hashCode, so we needed something that would be usable in collections.  Plus we wanted to be independent of Update Manager so we could later move to P2.)  This class is very similar to an InstallableUnitDescription, but just for Features; maybe there's another even closer match in P2?]]

featuresRequested is basically our query API.

More or less current code can be found at:

http://git.eclipse.org/c/e4/org.eclipse.e4.installer.git/tree/bundles/org.eclipse.e4.enterprise.installer/src/org/eclipse/e4/enterprise/installer/BundleUpdater.java

Current questions:

My (spiked) update method looks like the following; (important lines bolded):

//Loading repos
Collection<IMetadataRepository> metadataReposList = new LinkedList<IMetadataRepository>();
metadataReposList.add(metadataManager.loadRepository(new URI(P2SITE), monitor));
metadataReposList.add(metadataManager.loadRepository(new URI(SECOND_P2SITE), monitor));
metadataReposList.add(metadataManager.loadRepository(new URI(THIRD_P2SITE), monitor));

artifactManager.loadRepository(new URI(P2SITE), monitor);
artifactManager.loadRepository(new URI(SECOND_P2SITE), monitor);
artifactManager.loadRepository(new URI(THIRD_P2SITE), monitor);

//Querying
final String traverseQuery = "$0.traverse(parent | parent.requirements.collect(rc | select(iu |iu ~= rc)).flatten())";
IQueryable<IInstallableUnit> allMetadataRepos = QueryUtil.compoundQueryable(metadataReposList);

Set<IInstallableUnit> toInstallOrUpdate = null;
ArrayList<IInstallableUnit> ius = getIUsToInstall();

try {
    if (ius.size() > 0) {
        toInstallOrUpdate = allMetadataRepos.query(
            QueryUtil.createQuery(traverseQuery, ius), monitor).toUnmodifiableSet();
    } else {
        toInstallOrUpdate = allMetadataRepos.query(
            QueryUtil.createIUAnyQuery(), monitor).toUnmodifiableSet();
    }
} catch (Throwable t) {
    IStatus result = error(t.getMessage(), t);
    log(result);
    return result;
}

// Perform the install
InstallOperation installOperation = new InstallOperation(session, toInstallOrUpdate);
resolveJob = installOperation.getResolveJob(monitor);
IStatus installStatus = syncRunJob(monitor, resolveJob);
if (null == resolveJob) {
    return log(error("Unexpected: installOperation#getResolveJob returned null!", new NullPointerException()));
}
log(info("Result of resolving install opearations: " + installStatus.getMessage()));
if (installStatus.isOK()) {
    log(info("Installing..."));
    ProvisioningJob provisioningJob = installOperation.getProvisioningJob(monitor);
    if (null == provisioningJob) {
        return log(error("Unexpected: installOperation#getProvisioningJob returned null!", new NullPointerException()));
    }
    installStatus = syncRunJob(monitor, provisioningJob);
    log(info("Install result: " + installStatus.getMessage()));
} else {
    log(info("Install status returned !ok: " + installStatus.getMessage()));
}


The key part is the "if" clause I bolded above. 

If there are no IUs in the list, we (in the spike) install everything, just to prove that the rest of the code works right.  When I go through that code path, everything works fine.

However, when I explicitly specify the .feature.group(s) to install, then the IUs I get back from the query are exactly the .feature.group(s) I used as input, and nothing else.  No plugins, no included/dependent features, nothing.  Naturally, this fails.

I'm not sure where to go next.

In your opinion, is this the right approach or should I be using the slicer?  (It seems fine to me for our API, but I might be missing something.)

I'll try to get a small example together that I can upload somewhere.

Thanks in advance.


Regards,

Dave Orme


Back to the top