blob: 23fd1c4a2f67ccdda3c0271188fce54f13390908 [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 API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.p2.artifact.repository.simple;
import java.io.*;
import java.net.URL;
import java.util.*;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.artifact.repository.Activator;
import org.eclipse.equinox.internal.p2.artifact.repository.Messages;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.p2.persistence.XMLParser;
import org.eclipse.equinox.internal.p2.persistence.XMLWriter;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.ArtifactDescriptor;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.IArtifactRepository;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStepDescriptor;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.metadata.IArtifactKey;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Version;
import org.xml.sax.*;
/**
* This class reads and writes artifact repository metadata
* (e.g. table of contents files);
*
* This class is not used for reading or writing the actual artifacts.
*/
// TODO: Should a registration/factory mechanism be supported
// for getting a repository reader/writer given a repository type
public class SimpleArtifactRepositoryIO {
/**
* Writes the given artifact repository to the stream.
* This method performs buffering, and closes the stream when finished.
*/
public void write(SimpleArtifactRepository repository, OutputStream output) {
OutputStream bufferedOutput = null;
try {
try {
bufferedOutput = new BufferedOutputStream(output);
Writer repositoryWriter = new Writer(bufferedOutput);
repositoryWriter.write(repository);
} finally {
if (bufferedOutput != null) {
bufferedOutput.close();
}
}
} catch (IOException ioe) {
// TODO shouldn't this throw a core exception?
ioe.printStackTrace();
}
}
/**
* Reads the artifact repository from the given stream,
* and returns the contained array of abstract artifact repositories.
*
* This method performs buffering, and closes the stream when finished.
*/
public IArtifactRepository read(URL location, InputStream input, IProgressMonitor monitor) throws ProvisionException {
BufferedInputStream bufferedInput = null;
try {
try {
bufferedInput = new BufferedInputStream(input);
Parser repositoryParser = new Parser(Activator.getContext(), Activator.ID);
repositoryParser.parse(input);
IStatus result = repositoryParser.getStatus();
switch (result.getSeverity()) {
case IStatus.CANCEL :
throw new OperationCanceledException();
case IStatus.ERROR :
throw new ProvisionException(result);
case IStatus.WARNING :
case IStatus.INFO :
LogHelper.log(result);
}
return repositoryParser.getRepository();
} finally {
if (bufferedInput != null)
bufferedInput.close();
}
} catch (IOException ioe) {
String msg = NLS.bind(Messages.io_failedRead, location);
throw new ProvisionException(new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, msg, ioe));
}
}
private interface XMLConstants extends org.eclipse.equinox.internal.p2.persistence.XMLConstants {
// Constants defining the structure of the XML for a SimpleArtifactRepository
// A format version number for simple artifact repository XML.
public static final Version CURRENT_VERSION = new Version(1, 0, 0);
public static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, new Version(2, 0, 0), false);
// Constants for processing instructions
public static final String PI_REPOSITORY_TARGET = "artifactRepository"; //$NON-NLS-1$
public static XMLWriter.ProcessingInstruction[] PI_DEFAULTS = new XMLWriter.ProcessingInstruction[] {XMLWriter.ProcessingInstruction.makeClassVersionInstruction(PI_REPOSITORY_TARGET, SimpleArtifactRepository.class, CURRENT_VERSION)};
// Constants for artifact repository elements
public static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$
public static final String REPOSITORY_PROPERTIES_ELEMENT = "repositoryProperties"; //$NON-NLS-1$
public static final String MAPPING_RULES_ELEMENT = "mappings"; //$NON-NLS-1$
public static final String MAPPING_RULE_ELEMENT = "rule"; //$NON-NLS-1$
public static final String ARTIFACTS_ELEMENT = "artifacts"; //$NON-NLS-1$
public static final String ARTIFACT_ELEMENT = "artifact"; //$NON-NLS-1$
public static final String PROCESSING_STEPS_ELEMENT = "processing"; //$NON-NLS-1$
public static final String PROCESSING_STEP_ELEMENT = "step"; //$NON-NLS-1$
// Constants for attributes of artifact repository elements
public static final String VERIFY_SIGNATURE_ATTRIBUTE = "verify"; //$NON-NLS-1$
public static final String MAPPING_RULE_FILTER_ATTRIBUTE = "filter"; //$NON-NLS-1$
public static final String MAPPING_RULE_OUTPUT_ATTRIBUTE = "output"; //$NON-NLS-1$
public static final String ARTIFACT_CLASSIFIER_ATTRIBUTE = CLASSIFIER_ATTRIBUTE;
public static final String STEP_DATA_ATTRIBUTE = "data"; //$NON-NLS-1$
public static final String STEP_REQUIRED_ATTRIBUTE = "required"; //$NON-NLS-1$
}
// XML writer for a SimpleArtifactRepository
protected class Writer extends XMLWriter implements XMLConstants {
public Writer(OutputStream output) throws IOException {
super(output, PI_DEFAULTS);
}
/**
* Write the given artifact repository to the output stream.
*/
public void write(SimpleArtifactRepository repository) {
start(REPOSITORY_ELEMENT);
attribute(NAME_ATTRIBUTE, repository.getName());
attribute(TYPE_ATTRIBUTE, repository.getType());
attribute(VERSION_ATTRIBUTE, repository.getVersion());
attributeOptional(PROVIDER_ATTRIBUTE, repository.getProvider());
attributeOptional(DESCRIPTION_ATTRIBUTE, repository.getDescription()); // TODO: could be cdata?
attribute(VERIFY_SIGNATURE_ATTRIBUTE, repository.getSignatureVerification(), false);
writeProperties(repository.getProperties());
writeMappingRules(repository.getRules());
writeArtifacts(repository.getDescriptors());
end(REPOSITORY_ELEMENT);
flush();
}
private void writeMappingRules(String[][] rules) {
if (rules.length > 0) {
start(MAPPING_RULES_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, rules.length);
for (int i = 0; i < rules.length; i++) {
start(MAPPING_RULE_ELEMENT);
attribute(MAPPING_RULE_FILTER_ATTRIBUTE, rules[i][0]);
attribute(MAPPING_RULE_OUTPUT_ATTRIBUTE, rules[i][1]);
end(MAPPING_RULE_ELEMENT);
}
end(MAPPING_RULES_ELEMENT);
}
}
private void writeArtifacts(Set artifactDescriptors) {
start(ARTIFACTS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, artifactDescriptors.size());
for (Iterator iter = artifactDescriptors.iterator(); iter.hasNext();) {
ArtifactDescriptor descriptor = (ArtifactDescriptor) iter.next();
IArtifactKey key = descriptor.getArtifactKey();
start(ARTIFACT_ELEMENT);
attribute(ARTIFACT_CLASSIFIER_ATTRIBUTE, key.getClassifier());
attribute(ID_ATTRIBUTE, key.getId());
attribute(VERSION_ATTRIBUTE, key.getVersion());
writeProcessingSteps(descriptor.getProcessingSteps());
writeProperties(descriptor.getProperties());
writeProperties(REPOSITORY_PROPERTIES_ELEMENT, descriptor.getRepositoryProperties());
end(ARTIFACT_ELEMENT);
}
end(ARTIFACTS_ELEMENT);
}
private void writeProcessingSteps(ProcessingStepDescriptor[] processingSteps) {
if (processingSteps.length > 0) {
start(PROCESSING_STEPS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, processingSteps.length);
for (int i = 0; i < processingSteps.length; i++) {
start(PROCESSING_STEP_ELEMENT);
attribute(ID_ATTRIBUTE, processingSteps[i].getProcessorId());
attribute(STEP_DATA_ATTRIBUTE, processingSteps[i].getData());
attribute(STEP_REQUIRED_ATTRIBUTE, processingSteps[i].isRequired());
end(PROCESSING_STEP_ELEMENT);
}
end(PROCESSING_STEPS_ELEMENT);
}
}
}
/*
* Parser for the contents of a SimpleArtifactRepository,
* as written by the Writer class.
*/
private class Parser extends XMLParser implements XMLConstants {
private SimpleArtifactRepository theRepository = null;
public Parser(BundleContext context, String bundleId) {
super(context, bundleId);
}
public void parse(File file) throws IOException {
parse(new FileInputStream(file));
}
public synchronized void parse(InputStream stream) throws IOException {
this.status = null;
try {
// TODO: currently not caching the parser since we make no assumptions
// or restrictions on concurrent parsing
getParser();
RepositoryHandler repositoryHandler = new RepositoryHandler();
xmlReader.setContentHandler(new RepositoryDocHandler(REPOSITORY_ELEMENT, repositoryHandler));
xmlReader.parse(new InputSource(stream));
if (isValidXML()) {
theRepository = repositoryHandler.getRepository();
}
} catch (SAXException e) {
throw new IOException(e.getMessage());
} catch (ParserConfigurationException e) {
throw new IOException(e.getMessage());
} finally {
stream.close();
}
}
public SimpleArtifactRepository getRepository() {
return theRepository;
}
protected Object getRootObject() {
return theRepository;
}
private final class RepositoryDocHandler extends DocHandler {
public RepositoryDocHandler(String rootName, RootHandler rootHandler) {
super(rootName, rootHandler);
}
public void processingInstruction(String target, String data) throws SAXException {
if (PI_REPOSITORY_TARGET.equals(target)) {
// TODO: should the root handler be constructed based on class
// via an extension registry mechanism?
// String clazz = extractPIClass(data);
// TODO: version tolerance by extension
Version repositoryVersion = extractPIVersion(target, data);
if (!XML_TOLERANCE.isIncluded(repositoryVersion)) {
throw new SAXException(NLS.bind(Messages.io_incompatibleVersion, repositoryVersion, XML_TOLERANCE));
}
}
}
}
private final class RepositoryHandler extends RootHandler {
private final String[] required = new String[] {NAME_ATTRIBUTE, TYPE_ATTRIBUTE, VERSION_ATTRIBUTE};
private final String[] optional = new String[] {DESCRIPTION_ATTRIBUTE, PROVIDER_ATTRIBUTE, VERIFY_SIGNATURE_ATTRIBUTE};
private String[] attrValues = new String[required.length + optional.length];
private MappingRulesHandler mappingRulesHandler = null;
private PropertiesHandler propertiesHandler = null;
private ArtifactsHandler artifactsHandler = null;
private SimpleArtifactRepository repository = null;
public RepositoryHandler() {
super();
}
public SimpleArtifactRepository getRepository() {
return repository;
}
protected void handleRootAttributes(Attributes attributes) {
attrValues = parseAttributes(attributes, required, optional);
attrValues[2] = checkVersion(REPOSITORY_ELEMENT, VERSION_ATTRIBUTE, attrValues[2]).toString();
attrValues[5] = checkBoolean(REPOSITORY_ELEMENT, VERIFY_SIGNATURE_ATTRIBUTE, attrValues[5], false).toString();
}
public void startElement(String name, Attributes attributes) {
if (MAPPING_RULES_ELEMENT.equals(name)) {
if (mappingRulesHandler == null) {
mappingRulesHandler = new MappingRulesHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else if (ARTIFACTS_ELEMENT.equals(name)) {
if (artifactsHandler == null) {
artifactsHandler = new ArtifactsHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else if (PROPERTIES_ELEMENT.equals(name)) {
if (propertiesHandler == null) {
propertiesHandler = new PropertiesHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else {
invalidElement(name, attributes);
}
}
protected void finished() {
if (isValidXML()) {
String[][] mappingRules = (mappingRulesHandler == null ? new String[0][0] //
: mappingRulesHandler.getMappingRules());
Map properties = (propertiesHandler == null ? new OrderedProperties(0) //
: propertiesHandler.getProperties());
Set artifacts = (artifactsHandler == null ? new HashSet(0) //
: artifactsHandler.getArtifacts());
boolean verifySignature = (attrValues[5] == null ? false //
: new Boolean(attrValues[5]).booleanValue());
repository = new SimpleArtifactRepository(attrValues[0], attrValues[1], attrValues[2], attrValues[3], //
attrValues[4], verifySignature, artifacts, mappingRules, properties);
}
}
}
protected class MappingRulesHandler extends AbstractHandler {
private List mappingRules;
public MappingRulesHandler(AbstractHandler parentHandler, Attributes attributes) {
super(parentHandler, MAPPING_RULES_ELEMENT);
String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE);
mappingRules = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4));
}
public String[][] getMappingRules() {
String[][] rules = new String[mappingRules.size()][2];
for (int index = 0; index < mappingRules.size(); index++) {
String[] ruleAttributes = (String[]) mappingRules.get(index);
rules[index] = ruleAttributes;
}
return rules;
}
public void startElement(String name, Attributes attributes) {
if (name.equals(MAPPING_RULE_ELEMENT)) {
new MappingRuleHandler(this, attributes, mappingRules);
} else {
invalidElement(name, attributes);
}
}
}
protected class MappingRuleHandler extends AbstractHandler {
private final String[] required = new String[] {MAPPING_RULE_FILTER_ATTRIBUTE, MAPPING_RULE_OUTPUT_ATTRIBUTE};
public MappingRuleHandler(AbstractHandler parentHandler, Attributes attributes, List mappingRules) {
super(parentHandler, MAPPING_RULE_ELEMENT);
mappingRules.add(parseRequiredAttributes(attributes, required));
}
public void startElement(String name, Attributes attributes) {
invalidElement(name, attributes);
}
}
protected class ArtifactsHandler extends AbstractHandler {
private Set artifacts;
public ArtifactsHandler(AbstractHandler parentHandler, Attributes attributes) {
super(parentHandler, ARTIFACTS_ELEMENT);
String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE);
artifacts = (size != null ? new LinkedHashSet(new Integer(size).intValue()) : new LinkedHashSet(4));
}
public Set getArtifacts() {
return artifacts;
}
public void startElement(String name, Attributes attributes) {
if (name.equals(ARTIFACT_ELEMENT)) {
new ArtifactHandler(this, attributes, artifacts);
} else {
invalidElement(name, attributes);
}
}
}
protected class ArtifactHandler extends AbstractHandler {
private final String[] required = new String[] {ARTIFACT_CLASSIFIER_ATTRIBUTE, ID_ATTRIBUTE, VERSION_ATTRIBUTE};
private Set artifacts;
ArtifactDescriptor currentArtifact = null;
private PropertiesHandler propertiesHandler = null;
private PropertiesHandler repositoryPropertiesHandler = null;
private ProcessingStepsHandler processingStepsHandler = null;
public ArtifactHandler(AbstractHandler parentHandler, Attributes attributes, Set artifacts) {
super(parentHandler, ARTIFACT_ELEMENT);
this.artifacts = artifacts;
String[] values = parseRequiredAttributes(attributes, required);
Version version = checkVersion(ARTIFACT_ELEMENT, VERSION_ATTRIBUTE, values[2]);
// TODO: resolve access restriction on ArtifactKey construction
currentArtifact = new ArtifactDescriptor(new ArtifactKey(values[0], values[1], version));
}
public ArtifactDescriptor getArtifact() {
return currentArtifact;
}
public void startElement(String name, Attributes attributes) {
if (PROCESSING_STEPS_ELEMENT.equals(name)) {
if (processingStepsHandler == null) {
processingStepsHandler = new ProcessingStepsHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else if (PROPERTIES_ELEMENT.equals(name)) {
if (propertiesHandler == null) {
propertiesHandler = new PropertiesHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else if (REPOSITORY_PROPERTIES_ELEMENT.equals(name)) {
if (repositoryPropertiesHandler == null) {
repositoryPropertiesHandler = new PropertiesHandler(this, attributes);
} else {
duplicateElement(this, name, attributes);
}
} else {
invalidElement(name, attributes);
}
}
protected void finished() {
if (isValidXML() && currentArtifact != null) {
Map properties = (propertiesHandler == null ? new OrderedProperties(0) : propertiesHandler.getProperties());
currentArtifact.addProperties(properties);
properties = (repositoryPropertiesHandler == null ? new OrderedProperties(0) : repositoryPropertiesHandler.getProperties());
currentArtifact.addRepositoryProperties(properties);
ProcessingStepDescriptor[] processingSteps = (processingStepsHandler == null ? new ProcessingStepDescriptor[0] //
: processingStepsHandler.getProcessingSteps());
currentArtifact.setProcessingSteps(processingSteps);
artifacts.add(currentArtifact);
}
}
}
protected class ProcessingStepsHandler extends AbstractHandler {
private List processingSteps;
public ProcessingStepsHandler(AbstractHandler parentHandler, Attributes attributes) {
super(parentHandler, PROCESSING_STEPS_ELEMENT);
String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE);
processingSteps = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4));
}
public ProcessingStepDescriptor[] getProcessingSteps() {
return (ProcessingStepDescriptor[]) processingSteps.toArray(new ProcessingStepDescriptor[processingSteps.size()]);
}
public void startElement(String name, Attributes attributes) {
if (name.equals(PROCESSING_STEP_ELEMENT)) {
new ProcessingStepHandler(this, attributes, processingSteps);
} else {
invalidElement(name, attributes);
}
}
}
protected class ProcessingStepHandler extends AbstractHandler {
private final String[] required = new String[] {ID_ATTRIBUTE, STEP_REQUIRED_ATTRIBUTE};
private final String[] optional = new String[] {STEP_DATA_ATTRIBUTE};
public ProcessingStepHandler(AbstractHandler parentHandler, Attributes attributes, List processingSteps) {
super(parentHandler, PROCESSING_STEP_ELEMENT);
String[] attributeValues = parseAttributes(attributes, required, optional);
processingSteps.add(new ProcessingStepDescriptor(attributeValues[0], attributeValues[1], checkBoolean(PROCESSING_STEP_ELEMENT, STEP_REQUIRED_ATTRIBUTE, attributeValues[2]).booleanValue()));
}
public void startElement(String name, Attributes attributes) {
invalidElement(name, attributes);
}
}
protected String getErrorMessage() {
return Messages.io_parseError;
}
public String toString() {
// TODO:
return null;
}
}
}