blob: 98dce008533e642705f964eeca919f17e45b30c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2009 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 API and implementation
* EclipseSource - ongoing development
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata.repository;
import org.eclipse.equinox.internal.provisional.p2.repository.ICompositeRepository;
import org.eclipse.equinox.internal.provisional.p2.repository.IRepository;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.persistence.CompositeRepositoryIO;
import org.eclipse.equinox.internal.p2.persistence.CompositeRepositoryState;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
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.*;
import org.eclipse.equinox.internal.provisional.spi.p2.metadata.repository.AbstractMetadataRepository;
import org.eclipse.osgi.util.NLS;
public class CompositeMetadataRepository extends AbstractMetadataRepository implements IMetadataRepository, ICompositeRepository {
static final public String REPOSITORY_TYPE = CompositeMetadataRepository.class.getName();
public static final String PI_REPOSITORY_TYPE = "compositeMetadataRepository"; //$NON-NLS-1$
static final private Integer REPOSITORY_VERSION = new Integer(1);
static final public String XML_EXTENSION = ".xml"; //$NON-NLS-1$
static final private String JAR_EXTENSION = ".jar"; //$NON-NLS-1$
// keep a list of the child URIs. they can be absolute or relative. they may or may not point
// to a valid reachable repo
private List childrenURIs = new ArrayList();
// keep a list of the repositories that we have successfully loaded
private List loadedRepos = new ArrayList();
/**
* Create a Composite repository in memory.
* @return the repository or null if unable to create one
*/
public static CompositeMetadataRepository createMemoryComposite() {
IMetadataRepositoryManager manager = getManager();
if (manager == null)
return null;
try {
//create a unique opaque URI
long time = System.currentTimeMillis();
URI repositoryURI = new URI("memory:" + String.valueOf(time)); //$NON-NLS-1$
while (manager.contains(repositoryURI))
repositoryURI = new URI("memory:" + String.valueOf(++time)); //$NON-NLS-1$
CompositeMetadataRepository result = (CompositeMetadataRepository) manager.createRepository(repositoryURI, repositoryURI.toString(), IMetadataRepositoryManager.TYPE_COMPOSITE_REPOSITORY, null);
manager.removeRepository(repositoryURI);
return result;
} catch (ProvisionException e) {
// just return null
LogHelper.log(e);
} catch (URISyntaxException e) {
// just return null
}
return null;
}
static private IMetadataRepositoryManager getManager() {
return (IMetadataRepositoryManager) ServiceHelper.getService(Activator.getContext(), IMetadataRepositoryManager.class.getName());
}
private boolean isLocal() {
return "file".equalsIgnoreCase(location.getScheme()); //$NON-NLS-1$
}
public boolean isModifiable() {
return isLocal();
}
public CompositeMetadataRepository(URI location, String name, Map properties) {
super(name == null ? (location != null ? location.toString() : "") : name, REPOSITORY_TYPE, REPOSITORY_VERSION.toString(), location, null, null, properties); //$NON-NLS-1$
//when creating a repository, we must ensure it exists on disk so a subsequent load will succeed
save();
}
/*
* This is only called by the parser when loading a repository.
*/
public CompositeMetadataRepository(CompositeRepositoryState state) {
super(state.getName(), state.getType(), state.getVersion(), state.getLocation(), state.getDescription(), state.getProvider(), state.getProperties());
for (int i = 0; i < state.getChildren().length; i++)
addChild(state.getChildren()[i], false);
}
/*
* Create and return a new repository state object which represents this repository.
* It will be used while persisting the repository to disk.
*/
public CompositeRepositoryState toState() {
CompositeRepositoryState result = new CompositeRepositoryState();
result.setName(getName());
result.setType(getType());
result.setVersion(getVersion());
result.setLocation(getLocation());
result.setDescription(getDescription());
result.setProvider(getProvider());
result.setProperties(getProperties());
// it is important to directly access the field so we have the relative URIs
result.setChildren((URI[]) childrenURIs.toArray(new URI[childrenURIs.size()]));
return result;
}
public Collector query(Query query, Collector collector, IProgressMonitor monitor) {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
// Query all the all the repositories this composite repo contains
CompoundQueryable queryable = new CompoundQueryable((IQueryable[]) loadedRepos.toArray(new IQueryable[loadedRepos.size()]));
collector = queryable.query(query, collector, monitor);
} finally {
if (monitor != null)
monitor.done();
}
return collector;
}
private void addChild(URI childURI, boolean save) {
URI absolute = URIUtil.makeAbsolute(childURI, location);
if (childrenURIs.contains(childURI) || childrenURIs.contains(absolute))
return;
// always add the URI to the list of child URIs (even if we can't load it later)
childrenURIs.add(childURI);
if (save)
save();
try {
boolean currentLoaded = getManager().contains(absolute);
IMetadataRepository currentRepo = getManager().loadRepository(absolute, null);
if (!currentLoaded) {
//set enabled to false so repositories do not polled twice
getManager().setEnabled(absolute, false);
//set repository to system to hide from users
getManager().setRepositoryProperty(absolute, IRepository.PROP_SYSTEM, String.valueOf(true));
}
// we successfully loaded the repo so remember it
loadedRepos.add(currentRepo);
} catch (ProvisionException e) {
//repository failed to load. fall through
LogHelper.log(e);
}
}
public void addChild(URI childURI) {
addChild(childURI, true);
}
public void removeChild(URI childURI) {
boolean removed = childrenURIs.remove(childURI);
// if the child wasn't there make sure and try the other permutation
// (absolute/relative) to see if it really is in the list.
URI other = childURI.isAbsolute() ? URIUtil.makeRelative(childURI, location) : URIUtil.makeAbsolute(childURI, location);
if (!removed)
removed = childrenURIs.remove(other);
if (removed) {
// we removed the child from the list so remove the associated repo object as well
IMetadataRepository found = null;
for (Iterator iter = loadedRepos.iterator(); found == null && iter.hasNext();) {
IMetadataRepository current = (IMetadataRepository) iter.next();
URI repoLocation = current.getLocation();
if (URIUtil.sameURI(childURI, repoLocation))
found = current;
else if (URIUtil.sameURI(other, repoLocation))
found = current;
}
if (found != null)
loadedRepos.remove(found);
save();
}
}
public void removeAllChildren() {
childrenURIs.clear();
loadedRepos.clear();
save();
}
public synchronized void addInstallableUnits(IInstallableUnit[] installableUnits) {
throw new UnsupportedOperationException("Cannot add IUs to a composite repository");
}
public synchronized void removeAll() {
throw new UnsupportedOperationException("Cannot remove IUs to a composite repository");
}
public synchronized boolean removeInstallableUnits(Query query, IProgressMonitor monitor) {
throw new UnsupportedOperationException("Cannot remove IUs to a composite repository");
}
private static File getActualLocation(URI location, String extension) {
File spec = URIUtil.toFile(location);
String path = spec.getAbsolutePath();
if (path.endsWith(CompositeMetadataRepositoryFactory.CONTENT_FILENAME + extension)) {
//todo this is the old code that doesn't look right
// return new File(spec + extension);
return spec;
}
if (path.endsWith("/")) //$NON-NLS-1$
path += CompositeMetadataRepositoryFactory.CONTENT_FILENAME;
else
path += "/" + CompositeMetadataRepositoryFactory.CONTENT_FILENAME; //$NON-NLS-1$
return new File(path + extension);
}
public static File getActualLocation(URI location) {
return getActualLocation(location, XML_EXTENSION);
}
public synchronized void addReference(URI repositoryLocation, int repositoryType, int options) {
throw new UnsupportedOperationException("Cannot add References to a composite repository");
}
// caller should be synchronized
private void save() {
if (!isModifiable())
return;
File file = getActualLocation(location);
File jarFile = getActualLocation(location, JAR_EXTENSION);
boolean compress = "true".equalsIgnoreCase((String) properties.get(PROP_COMPRESSED)); //$NON-NLS-1$
try {
OutputStream output = null;
if (!compress) {
if (jarFile.exists()) {
jarFile.delete();
}
if (!file.exists()) {
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
file.createNewFile();
}
output = new FileOutputStream(file);
} else {
if (file.exists()) {
file.delete();
}
if (!jarFile.exists()) {
if (!jarFile.getParentFile().exists())
jarFile.getParentFile().mkdirs();
jarFile.createNewFile();
}
JarEntry jarEntry = new JarEntry(file.getName());
JarOutputStream jOutput = new JarOutputStream(new FileOutputStream(jarFile));
jOutput.putNextEntry(jarEntry);
output = jOutput;
}
super.setProperty(IRepository.PROP_TIMESTAMP, Long.toString(System.currentTimeMillis()));
new CompositeRepositoryIO().write(toState(), output, PI_REPOSITORY_TYPE);
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_WRITE, NLS.bind(Messages.io_failedWrite, location), e));
}
}
public List getChildren() {
List result = new ArrayList();
for (Iterator iter = childrenURIs.iterator(); iter.hasNext();)
result.add(URIUtil.makeAbsolute((URI) iter.next(), location));
return result;
}
public static URI getActualLocationURI(URI base, String extension) {
if (extension == null)
extension = XML_EXTENSION;
return URIUtil.append(base, CompositeMetadataRepositoryFactory.CONTENT_FILENAME + extension);
}
//TODO this should never be called. What do we do?
public void initialize(RepositoryState state) {
this.name = state.Name;
this.type = state.Type;
this.version = state.Version.toString();
this.provider = state.Provider;
this.description = state.Description;
this.location = state.Location;
this.properties = state.Properties;
}
}