/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ivy.osgi.updatesite;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.ivy.core.cache.CacheResourceOptions;
import org.apache.ivy.core.cache.RepositoryCacheManager;
import org.apache.ivy.core.event.EventManager;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.ivy.core.report.DownloadStatus;
import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
import org.apache.ivy.osgi.p2.P2ArtifactParser;
import org.apache.ivy.osgi.p2.P2CompositeParser;
import org.apache.ivy.osgi.p2.P2Descriptor;
import org.apache.ivy.osgi.p2.P2MetadataParser;
import org.apache.ivy.osgi.p2.XMLInputParser;
import org.apache.ivy.osgi.repo.RepoDescriptor;
import org.apache.ivy.osgi.updatesite.xml.EclipseFeature;
import org.apache.ivy.osgi.updatesite.xml.EclipseUpdateSiteParser;
import org.apache.ivy.osgi.updatesite.xml.FeatureParser;
import org.apache.ivy.osgi.updatesite.xml.UpdateSite;
import org.apache.ivy.osgi.updatesite.xml.UpdateSiteDigestParser;
import org.apache.ivy.plugins.repository.url.URLRepository;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.util.Message;
import org.xml.sax.SAXException;
public class UpdateSiteLoader {
private final RepositoryCacheManager repositoryCacheManager;
private final URLRepository urlRepository = new URLRepository();
private final CacheResourceOptions options;
private int logLevel = Message.MSG_INFO;
public UpdateSiteLoader(RepositoryCacheManager repositoryCacheManager,
EventManager eventManager, CacheResourceOptions options) {
this.repositoryCacheManager = repositoryCacheManager;
this.options = options;
if (eventManager != null) {
urlRepository.addTransferListener(eventManager);
}
}
public void setLogLevel(int logLevel) {
this.logLevel = logLevel;
}
public RepoDescriptor load(URI repoUri) throws IOException, ParseException, SAXException {
if (!repoUri.toString().endsWith("/")) {
try {
repoUri = new URI(repoUri.toString() + "/");
} catch (URISyntaxException e) {
throw new RuntimeException("Cannot make an uri for the repo");
}
}
Message.info("Loading the update site " + repoUri);
// first look for a p2 repository
RepoDescriptor repo = loadP2(repoUri);
if (repo != null) {
return repo;
}
Message.verbose("\tNo P2 artifacts, falling back on the old fashioned updatesite");
// then try the old update site
UpdateSite site = loadSite(repoUri);
if (site == null) {
return null;
}
repo = loadFromDigest(site);
if (repo != null) {
return repo;
}
return loadFromSite(site);
}
private P2Descriptor loadP2(URI repoUri) throws IOException, ParseException, SAXException {
P2Descriptor p2Descriptor = new P2Descriptor(repoUri,
ExecutionEnvironmentProfileProvider.getInstance());
p2Descriptor.setLogLevel(logLevel);
if (!populateP2Descriptor(repoUri, p2Descriptor)) {
return null;
}
p2Descriptor.finish();
return p2Descriptor;
}
private boolean populateP2Descriptor(URI repoUri, P2Descriptor p2Descriptor)
throws IOException, ParseException, SAXException {
Message.verbose("Loading P2 repository " + repoUri);
boolean contentExists = readContent(repoUri, p2Descriptor);
boolean artifactExists = readArtifacts(repoUri, p2Descriptor);
return artifactExists || contentExists;
}
private boolean readContent(URI repoUri, P2Descriptor p2Descriptor) throws IOException,
ParseException, SAXException {
boolean contentExists = readCompositeContent(repoUri, "compositeContent", p2Descriptor);
if (!contentExists) {
P2MetadataParser metadataParser = new P2MetadataParser(p2Descriptor);
metadataParser.setLogLevel(logLevel);
contentExists = readJarOrXml(repoUri, "content", metadataParser);
}
return contentExists;
}
private boolean readArtifacts(URI repoUri, P2Descriptor p2Descriptor) throws IOException,
ParseException, SAXException {
boolean artifactExists = readCompositeArtifact(repoUri, "compositeArtifacts", p2Descriptor);
if (!artifactExists) {
artifactExists = readJarOrXml(repoUri, "artifacts", new P2ArtifactParser(p2Descriptor,
repoUri.toURL().toExternalForm()));
}
return artifactExists;
}
private boolean readCompositeContent(URI repoUri, String name, P2Descriptor p2Descriptor)
throws IOException, ParseException, SAXException {
P2CompositeParser p2CompositeParser = new P2CompositeParser();
boolean exist = readJarOrXml(repoUri, name, p2CompositeParser);
if (exist) {
for (String childLocation : p2CompositeParser.getChildLocations()) {
if (!childLocation.endsWith("/")) {
childLocation += "/";
}
URI childUri = repoUri.resolve(childLocation);
readContent(childUri, p2Descriptor);
}
}
return exist;
}
private boolean readCompositeArtifact(URI repoUri, String name, P2Descriptor p2Descriptor)
throws IOException, ParseException, SAXException {
P2CompositeParser p2CompositeParser = new P2CompositeParser();
boolean exist = readJarOrXml(repoUri, name, p2CompositeParser);
if (exist) {
for (String childLocation : p2CompositeParser.getChildLocations()) {
if (!childLocation.endsWith("/")) {
childLocation += "/";
}
URI childUri = repoUri.resolve(childLocation);
readArtifacts(childUri, p2Descriptor);
}
}
return exist;
}
private boolean readJarOrXml(URI repoUri, String baseName, XMLInputParser reader)
throws IOException, ParseException, SAXException {
InputStream readIn = null; // the input stream from which the xml should be read
URL contentUrl = repoUri.resolve(baseName + ".jar").toURL();
URLResource res = new URLResource(contentUrl);
ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
baseName, baseName, "jar", options, urlRepository);
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
// no jar file, try the xml one
contentUrl = repoUri.resolve(baseName + ".xml").toURL();
res = new URLResource(contentUrl);
report = repositoryCacheManager.downloadRepositoryResource(res, baseName, baseName,
"xml", options, urlRepository);
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
// no xml either
return false;
}
readIn = new FileInputStream(report.getLocalFile());
} else {
InputStream in = new FileInputStream(report.getLocalFile());
try {
// compressed, let's get the pointer on the actual xml
readIn = findEntry(in, baseName + ".xml");
if (readIn == null) {
in.close();
return false;
}
} catch (IOException e) {
in.close();
throw e;
}
}
try {
reader.parse(readIn);
} finally {
readIn.close();
}
return true;
}
private UpdateSite loadSite(URI repoUri) throws IOException, ParseException, SAXException {
URI siteUri = normalizeSiteUri(repoUri, null);
URL u = siteUri.resolve("site.xml").toURL();
URLResource res = new URLResource(u);
ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
"site", "updatesite", "xml", options, urlRepository);
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
return null;
}
InputStream in = new FileInputStream(report.getLocalFile());
try {
UpdateSite site = EclipseUpdateSiteParser.parse(in);
site.setUri(normalizeSiteUri(site.getUri(), siteUri));
return site;
} finally {
in.close();
}
}
private URI normalizeSiteUri(URI uri, URI defaultValue) {
if (uri == null) {
return defaultValue;
}
String uriString = uri.toString();
if (uriString.endsWith("site.xml")) {
try {
return new URI(uriString.substring(0, uriString.length() - 8));
} catch (URISyntaxException e) {
throw new RuntimeException("Illegal uri", e);
}
}
if (!uriString.endsWith("/")) {
try {
return new URI(uriString + "/");
} catch (URISyntaxException e) {
throw new RuntimeException("Illegal uri", e);
}
}
return uri;
}
private UpdateSiteDescriptor loadFromDigest(UpdateSite site) throws IOException,
ParseException, SAXException {
URI digestBaseUri = site.getDigestUri();
if (digestBaseUri == null) {
digestBaseUri = site.getUri();
} else if (!digestBaseUri.isAbsolute()) {
digestBaseUri = site.getUri().resolve(digestBaseUri);
}
URL digest = digestBaseUri.resolve("digest.zip").toURL();
Message.verbose("\tReading " + digest);
URLResource res = new URLResource(digest);
ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
"digest", "digest", "zip", options, urlRepository);
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
return null;
}
InputStream in = new FileInputStream(report.getLocalFile());
try {
ZipInputStream zipped = findEntry(in, "digest.xml");
if (zipped == null) {
return null;
}
return UpdateSiteDigestParser.parse(zipped, site);
} finally {
in.close();
}
}
private UpdateSiteDescriptor loadFromSite(UpdateSite site) throws IOException, ParseException,
SAXException {
UpdateSiteDescriptor repoDescriptor = new UpdateSiteDescriptor(site.getUri(),
ExecutionEnvironmentProfileProvider.getInstance());
for (EclipseFeature feature : site.getFeatures()) {
URL url = site.getUri().resolve(feature.getUrl()).toURL();
URLResource res = new URLResource(url);
ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
feature.getId(), "feature", "jar", options, urlRepository);
if (report.getDownloadStatus() == DownloadStatus.FAILED) {
return null;
}
InputStream in = new FileInputStream(report.getLocalFile());
try {
ZipInputStream zipped = findEntry(in, "feature.xml");
if (zipped == null) {
return null;
}
EclipseFeature f = FeatureParser.parse(zipped);
f.setURL(feature.getUrl());
repoDescriptor.addFeature(f);
} finally {
in.close();
}
}
return repoDescriptor;
}
private ZipInputStream findEntry(InputStream in, String entryName) throws IOException {
ZipInputStream zipped = new ZipInputStream(in);
ZipEntry zipEntry = zipped.getNextEntry();
while (zipEntry != null && !zipEntry.getName().equals(entryName)) {
zipEntry = zipped.getNextEntry();
}
if (zipEntry == null) {
return null;
}
return zipped;
}
}