blob: 04132b7db794837b9da6ae954694648ada1aaa8b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial implementation and ideas
******************************************************************************/
package org.eclipse.equinox.internal.provisional.p2.directorywatcher;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.metadata.generator.features.FeatureParser;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.*;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus;
import org.eclipse.equinox.internal.provisional.p2.core.repository.IRepository;
import org.eclipse.equinox.internal.provisional.p2.core.repository.RepositoryEvent;
import org.eclipse.equinox.internal.provisional.p2.metadata.IArtifactKey;
import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.internal.provisional.p2.metadata.generator.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepository;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepositoryManager;
import org.eclipse.equinox.internal.provisional.p2.query.Collector;
import org.eclipse.equinox.internal.provisional.p2.query.Query;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
public class RepositoryListener extends DirectoryChangeListener {
private static final String ARTIFACT_FOLDER = "artifact.folder"; //$NON-NLS-1$
private static final String ARTIFACT_REFERENCE = "artifact.reference"; //$NON-NLS-1$
private static final String FILE_LAST_MODIFIED = "file.lastModified"; //$NON-NLS-1$
private static final String FILE_NAME = "file.name"; //$NON-NLS-1$
private final IMetadataRepository metadataRepository;
private final IArtifactRepository artifactRepository;
private final BundleDescriptionFactory bundleDescriptionFactory;
private final Map currentFiles = new HashMap();
private Collection polledSeenFiles = new HashSet();
private Collection polledIUsToAdd = new ArrayList();
private Collection polledArtifactsToAdd = new ArrayList();
/**
* Create a repository listener that watches the specified folder and generates repositories
* for its content.
* @param context the bundle context
* @param repositoryName the repository name to use for the repository
* @param repositoryFolder the target folder for the repository, or <code>null</code> if a folder based on the
* bundle's data location should be used.
* @param hidden <code>true</code> if the repository should be hidden, <code>false</code> if not.
*/
public RepositoryListener(BundleContext context, String repositoryName, File repositoryFolder, boolean hidden) {
File stateDir;
if (repositoryFolder == null) {
String stateDirName = "listener_" + repositoryName.hashCode(); //$NON-NLS-1$
stateDir = context.getDataFile(stateDirName);
stateDir.mkdirs();
} else {
stateDir = repositoryFolder;
}
URL stateDirURL;
try {
stateDirURL = stateDir.toURL();
} catch (MalformedURLException e) {
throw new IllegalStateException(e.getMessage());
}
metadataRepository = initializeMetadataRepository(context, repositoryName, stateDirURL, hidden);
artifactRepository = initializeArtifactRepository(context, repositoryName, stateDirURL, hidden);
bundleDescriptionFactory = initializeBundleDescriptionFactory(context);
synchronizeCurrentFiles();
}
public RepositoryListener(BundleContext context, IMetadataRepository metadataRepository, IArtifactRepository artifactRepository) {
this.artifactRepository = artifactRepository;
this.metadataRepository = metadataRepository;
bundleDescriptionFactory = initializeBundleDescriptionFactory(context);
synchronizeCurrentFiles();
}
/**
* Broadcast events for any discovery sites associated with the feature
* so the repository managers add them to their list of known repositories.
*/
private void publishSites(Feature feature) {
IProvisioningEventBus bus = (IProvisioningEventBus) ServiceHelper.getService(Activator.getContext(), IProvisioningEventBus.SERVICE_NAME);
if (bus == null)
return;
URLEntry[] discoverySites = feature.getDiscoverySites();
for (int i = 0; i < discoverySites.length; i++)
publishSite(feature, bus, discoverySites[i].getURL(), false);
String updateSite = feature.getUpdateSiteURL();
if (updateSite != null)
publishSite(feature, bus, updateSite, true);
}
/**
* Broadcast a discovery event for the given repository location.
*/
private void publishSite(Feature feature, IProvisioningEventBus bus, String locationString, boolean isEnabled) {
try {
URL location = new URL(locationString);
bus.publishEvent(new RepositoryEvent(location, IRepository.TYPE_METADATA, RepositoryEvent.DISCOVERED, isEnabled));
bus.publishEvent(new RepositoryEvent(location, IRepository.TYPE_ARTIFACT, RepositoryEvent.DISCOVERED, isEnabled));
} catch (MalformedURLException e) {
LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Feature references invalid site: " + feature.getId(), e)); //$NON-NLS-1$
}
}
private BundleDescriptionFactory initializeBundleDescriptionFactory(BundleContext context) {
ServiceReference reference = context.getServiceReference(PlatformAdmin.class.getName());
if (reference == null)
throw new IllegalStateException(Messages.platformadmin_not_registered);
PlatformAdmin platformAdmin = (PlatformAdmin) context.getService(reference);
if (platformAdmin == null)
throw new IllegalStateException(Messages.platformadmin_not_registered);
try {
StateObjectFactory stateObjectFactory = platformAdmin.getFactory();
return new BundleDescriptionFactory(stateObjectFactory, null);
} finally {
context.ungetService(reference);
}
}
private IArtifactRepository initializeArtifactRepository(BundleContext context, String repositoryName, URL stateDirURL, boolean hidden) {
ServiceReference reference = context.getServiceReference(IArtifactRepositoryManager.class.getName());
IArtifactRepositoryManager manager = null;
if (reference != null)
manager = (IArtifactRepositoryManager) context.getService(reference);
if (manager == null)
throw new IllegalStateException(Messages.artifact_repo_manager_not_registered);
try {
try {
return manager.loadRepository(stateDirURL, null);
} catch (ProvisionException e) {
//fall through and create a new repository
}
try {
String name = repositoryName;
Map properties = new HashMap(1);
if (hidden) {
properties.put(IRepository.PROP_SYSTEM, Boolean.TRUE.toString());
name = "artifact listener " + repositoryName; //$NON-NLS-1$
}
return manager.createRepository(stateDirURL, name, IArtifactRepositoryManager.TYPE_SIMPLE_REPOSITORY, properties);
} catch (ProvisionException e) {
LogHelper.log(e);
throw new IllegalStateException(NLS.bind(Messages.failed_create_artifact_repo, stateDirURL));
}
} finally {
context.ungetService(reference);
}
}
private IMetadataRepository initializeMetadataRepository(BundleContext context, String repositoryName, URL stateDirURL, boolean hidden) {
ServiceReference reference = context.getServiceReference(IMetadataRepositoryManager.class.getName());
IMetadataRepositoryManager manager = null;
if (reference != null)
manager = (IMetadataRepositoryManager) context.getService(reference);
if (manager == null)
throw new IllegalStateException(Messages.metadata_repo_manager_not_registered);
try {
try {
return manager.loadRepository(stateDirURL, null);
} catch (ProvisionException e) {
//fall through and create new repository
}
String name = repositoryName;
Map properties = new HashMap(1);
if (hidden) {
properties.put(IRepository.PROP_SYSTEM, Boolean.TRUE.toString());
name = "metadata listener " + repositoryName; //$NON-NLS-1$
}
return manager.createRepository(stateDirURL, name, IMetadataRepositoryManager.TYPE_SIMPLE_REPOSITORY, properties);
} catch (ProvisionException e) {
LogHelper.log(e);
throw new IllegalStateException(NLS.bind(Messages.failed_create_metadata_repo, stateDirURL));
} finally {
context.ungetService(reference);
}
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#added(java.io.File)
*/
public boolean added(File file) {
return process(file);
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#changed(java.io.File)
*/
public boolean changed(File file) {
// this sequence will trigger removal and then addition during stopPoll
polledSeenFiles.remove(file);
return process(file);
}
public boolean removed(File file) {
// this file will get removed in stopPoll
return currentFiles.containsKey(file);
}
private boolean process(File file) {
boolean isDirectory = file.isDirectory();
// is it a feature ?
if (isDirectory && file.getParentFile() != null && file.getParentFile().getName().equals("features") && new File(file, "feature.xml").exists()) //$NON-NLS-1$ //$NON-NLS-2$)
return processFeature(file);
// is it a bundle ?
if (isDirectory || file.getName().endsWith(".jar")) //$NON-NLS-1$
return processBundle(file, isDirectory);
return false;
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#removed(java.io.File)
*/
private boolean processBundle(File file, boolean isDirectory) {
BundleDescription bundleDescription = bundleDescriptionFactory.getBundleDescription(file);
if (bundleDescription == null)
return false;
String fileName = file.getAbsolutePath();
String lastModified = Long.toString(file.lastModified());
// Add Bundle IU
Properties props = new Properties();
props.setProperty(FILE_NAME, fileName);
props.setProperty(FILE_LAST_MODIFIED, lastModified);
IArtifactKey key = MetadataGeneratorHelper.createBundleArtifactKey(bundleDescription.getSymbolicName(), bundleDescription.getVersion().toString());
IInstallableUnit[] ius = MetadataGeneratorHelper.createEclipseIU(bundleDescription, (Map) bundleDescription.getUserObject(), isDirectory, key, props);
// see bug 222370
// we only want to return the bundle IU so must exclude all fragment IUs
IInstallableUnit bundleIU = null;
for (int i = 0; i < ius.length; i++) {
if (!ius[i].isFragment()) {
bundleIU = ius[i];
break;
}
}
if (bundleIU == null) {
if (ius.length == 0)
return false;
throw new IllegalStateException(Messages.multiple_bundle_ius);
}
polledIUsToAdd.add(bundleIU);
// Add Bundle Artifact
ArtifactDescriptor descriptor = new ArtifactDescriptor(MetadataGeneratorHelper.createArtifactDescriptor(key, file, true, false));
try {
descriptor.setRepositoryProperty(ARTIFACT_REFERENCE, file.toURL().toExternalForm());
} catch (MalformedURLException e) {
// unexpected
e.printStackTrace();
return false;
}
if (isDirectory)
descriptor.setRepositoryProperty(ARTIFACT_FOLDER, Boolean.TRUE.toString());
descriptor.setRepositoryProperty(FILE_NAME, fileName);
descriptor.setRepositoryProperty(FILE_LAST_MODIFIED, lastModified);
polledArtifactsToAdd.add(descriptor);
return true;
}
private boolean processFeature(File file) {
FeatureParser parser = new FeatureParser();
Feature feature = parser.parse(file);
if (feature == null)
return false;
publishSites(feature);
String fileName = file.getAbsolutePath();
String lastModified = Long.toString(file.lastModified());
// Add Feature IUs
Properties props = new Properties();
props.setProperty(FILE_NAME, fileName);
props.setProperty(FILE_LAST_MODIFIED, lastModified);
IInstallableUnit featureIU = MetadataGeneratorHelper.createFeatureJarIU(feature, true, props);
IInstallableUnit groupIU = MetadataGeneratorHelper.createGroupIU(feature, featureIU, props);
polledIUsToAdd.add(featureIU);
polledIUsToAdd.add(groupIU);
// Add Feature Artifact
IArtifactKey featureKey = MetadataGeneratorHelper.createFeatureArtifactKey(feature.getId(), feature.getVersion());
ArtifactDescriptor descriptor = new ArtifactDescriptor(featureKey);
try {
descriptor.setRepositoryProperty(ARTIFACT_REFERENCE, file.toURL().toExternalForm());
} catch (MalformedURLException e) {
// unexpected
e.printStackTrace();
return false;
}
descriptor.setRepositoryProperty(ARTIFACT_FOLDER, Boolean.TRUE.toString());
descriptor.setRepositoryProperty(FILE_NAME, fileName);
descriptor.setRepositoryProperty(FILE_LAST_MODIFIED, lastModified);
polledArtifactsToAdd.add(descriptor);
return true;
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.DirectoryChangeListener#isInterested(java.io.File)
*/
public boolean isInterested(File file) {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#getSeenFile(java.io.File)
*/
public Long getSeenFile(File file) {
Long lastSeen = (Long) currentFiles.get(file);
if (lastSeen != null)
polledSeenFiles.add(file);
return lastSeen;
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#startPoll()
*/
public void startPoll() {
// do nothing
}
/* (non-Javadoc)
* @see org.eclipse.equinox.internal.provisional.p2.directorywatcher.IDirectoryChangeListener#stopPoll()
*/
public void stopPoll() {
final Set removedFiles = new HashSet(currentFiles.keySet());
removedFiles.removeAll(polledSeenFiles);
polledSeenFiles.clear();
if (removedFiles.isEmpty() && polledIUsToAdd.isEmpty() && polledArtifactsToAdd.isEmpty())
return;
if (metadataRepository != null)
synchronizeMetadataRepository(removedFiles);
if (artifactRepository != null)
synchronizeArtifactRepository(removedFiles);
synchronizeCurrentFiles();
polledIUsToAdd.clear();
polledArtifactsToAdd.clear();
}
private void synchronizeMetadataRepository(final Set removedFiles) {
Query removeQuery = new Query() {
public boolean isMatch(Object candidate) {
if (!(candidate instanceof IInstallableUnit))
return false;
IInstallableUnit iu = (IInstallableUnit) candidate;
File iuFile = new File(iu.getProperty(FILE_NAME));
return removedFiles.contains(iuFile);
}
};
metadataRepository.removeInstallableUnits(removeQuery, null);
if (!polledIUsToAdd.isEmpty())
metadataRepository.addInstallableUnits((IInstallableUnit[]) polledIUsToAdd.toArray(new IInstallableUnit[polledIUsToAdd.size()]));
}
private void synchronizeArtifactRepository(final Set removedFiles) {
final List keys = new ArrayList(Arrays.asList(artifactRepository.getArtifactKeys()));
for (Iterator it = keys.iterator(); it.hasNext();) {
IArtifactKey key = (IArtifactKey) it.next();
IArtifactDescriptor[] descriptors = artifactRepository.getArtifactDescriptors(key);
for (int i = 0; i < descriptors.length; i++) {
ArtifactDescriptor descriptor = (ArtifactDescriptor) descriptors[i];
File artifactFile = new File(descriptor.getRepositoryProperty(FILE_NAME));
if (removedFiles.contains(artifactFile))
artifactRepository.removeDescriptor(descriptor);
}
}
if (!polledArtifactsToAdd.isEmpty())
artifactRepository.addDescriptors((IArtifactDescriptor[]) polledArtifactsToAdd.toArray(new IArtifactDescriptor[polledArtifactsToAdd.size()]));
}
private void synchronizeCurrentFiles() {
currentFiles.clear();
if (metadataRepository != null) {
Collector ius = metadataRepository.query(InstallableUnitQuery.ANY, new Collector(), null);
for (Iterator it = ius.iterator(); it.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) it.next();
File iuFile = new File(iu.getProperty(FILE_NAME));
Long iuLastModified = new Long(iu.getProperty(FILE_LAST_MODIFIED));
currentFiles.put(iuFile, iuLastModified);
}
}
if (artifactRepository != null) {
final List keys = new ArrayList(Arrays.asList(artifactRepository.getArtifactKeys()));
for (Iterator it = keys.iterator(); it.hasNext();) {
IArtifactKey key = (IArtifactKey) it.next();
IArtifactDescriptor[] descriptors = artifactRepository.getArtifactDescriptors(key);
for (int i = 0; i < descriptors.length; i++) {
ArtifactDescriptor descriptor = (ArtifactDescriptor) descriptors[i];
File artifactFile = new File(descriptor.getRepositoryProperty(FILE_NAME));
Long artifactLastModified = new Long(descriptor.getRepositoryProperty(FILE_LAST_MODIFIED));
currentFiles.put(artifactFile, artifactLastModified);
}
}
}
}
public IMetadataRepository getMetadataRepository() {
return metadataRepository;
}
public IArtifactRepository getArtifactRepository() {
return artifactRepository;
}
}