| /* |
| * 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 |
| * |
| * https://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.core.settings.TimeoutConstraint; |
| 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 final TimeoutConstraint timeoutConstraint; |
| |
| private int logLevel = Message.MSG_INFO; |
| |
| public UpdateSiteLoader(final RepositoryCacheManager repositoryCacheManager, |
| final EventManager eventManager, final CacheResourceOptions options, |
| final TimeoutConstraint timeoutConstraint) { |
| this.repositoryCacheManager = repositoryCacheManager; |
| this.options = options; |
| this.timeoutConstraint = timeoutConstraint; |
| 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, this.timeoutConstraint); |
| |
| 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, this.timeoutConstraint); |
| |
| 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, SAXException { |
| URI siteUri = normalizeSiteUri(repoUri, null); |
| URL u = siteUri.resolve("site.xml").toURL(); |
| |
| final URLResource res = new URLResource(u, this.timeoutConstraint); |
| ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res, |
| "site", "updatesite", "xml", options, urlRepository); |
| if (report.getDownloadStatus() == DownloadStatus.FAILED) { |
| return null; |
| } |
| try (InputStream in = new FileInputStream(report.getLocalFile())) { |
| UpdateSite site = EclipseUpdateSiteParser.parse(in); |
| site.setUri(normalizeSiteUri(site.getUri(), siteUri)); |
| return site; |
| } |
| } |
| |
| 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, |
| 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); |
| |
| final URLResource res = new URLResource(digest, this.timeoutConstraint); |
| ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res, |
| "digest", "digest", "zip", options, urlRepository); |
| if (report.getDownloadStatus() == DownloadStatus.FAILED) { |
| return null; |
| } |
| try (InputStream in = new FileInputStream(report.getLocalFile())) { |
| ZipInputStream zipped = findEntry(in, "digest.xml"); |
| if (zipped == null) { |
| return null; |
| } |
| return UpdateSiteDigestParser.parse(zipped, site); |
| } |
| } |
| |
| private UpdateSiteDescriptor loadFromSite(UpdateSite site) throws IOException, SAXException { |
| UpdateSiteDescriptor repoDescriptor = new UpdateSiteDescriptor(site.getUri(), |
| ExecutionEnvironmentProfileProvider.getInstance()); |
| |
| for (EclipseFeature feature : site.getFeatures()) { |
| URL url = site.getUri().resolve(feature.getUrl()).toURL(); |
| |
| final URLResource res = new URLResource(url, this.timeoutConstraint); |
| ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res, |
| feature.getId(), "feature", "jar", options, urlRepository); |
| if (report.getDownloadStatus() == DownloadStatus.FAILED) { |
| return null; |
| } |
| try (InputStream in = new FileInputStream(report.getLocalFile())) { |
| ZipInputStream zipped = findEntry(in, "feature.xml"); |
| if (zipped == null) { |
| return null; |
| } |
| EclipseFeature f = FeatureParser.parse(zipped); |
| f.setURL(feature.getUrl()); |
| repoDescriptor.addFeature(f); |
| } |
| } |
| |
| 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; |
| } |
| } |