/*
* 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.core.publish;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.ivy.core.IvyContext;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.cache.ResolutionCacheManager;
import org.apache.ivy.core.event.EventManager;
import org.apache.ivy.core.event.publish.EndArtifactPublishEvent;
import org.apache.ivy.core.event.publish.StartArtifactPublishEvent;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.MDArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.parser.xml.UpdateOptions;
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorUpdater;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.util.ConfigurationUtils;
import org.apache.ivy.util.Message;
import org.xml.sax.SAXException;
public class PublishEngine {
private PublishEngineSettings settings;
private EventManager eventManager;
public PublishEngine(PublishEngineSettings settings, EventManager eventManager) {
this.settings = settings;
this.eventManager = eventManager;
}
/**
* Publishes a module to the repository. The publish can update the ivy file to publish if
* update is set to true. In this case it will use the given pubrevision, pubdate and status. If
* pubdate is null it will default to the current date. If status is null it will default to the
* current ivy file status (which itself defaults to integration if none is found). If update is
* false, then if the revision is not the same in the ivy file than the one expected (given as
* parameter), this method will fail with an IllegalArgumentException. pubdate and status are
* not used if update is false. extra artifacts can be used to publish more artifacts than
* actually declared in the ivy file. This can be useful to publish additional metadata or
* reports. The extra artifacts array can be null (= no extra artifacts), and if non null only
* the name, type, ext url and extra attributes of the artifacts are really used. Other methods
* can return null safely.
*/
public Collection<Artifact> publish(ModuleRevisionId mrid,
Collection<String> srcArtifactPattern, String resolverName, PublishOptions options)
throws IOException {
Message.info(":: publishing :: " + mrid.getModuleId());
Message.verbose("\tvalidate = " + options.isValidate());
long start = System.currentTimeMillis();
options.setSrcIvyPattern(settings.substitute(options.getSrcIvyPattern()));
if (options.getPubBranch() == null) {
options.setPubbranch(mrid.getBranch());
}
if (options.getPubrevision() == null) {
options.setPubrevision(mrid.getRevision());
}
ModuleRevisionId pubmrid = ModuleRevisionId.newInstance(mrid, options.getPubBranch(),
options.getPubrevision());
// let's find the resolved module descriptor
ModuleDescriptor md = null;
if (options.getSrcIvyPattern() != null) {
File ivyFile = settings.resolveFile(IvyPatternHelper.substitute(
options.getSrcIvyPattern(), DefaultArtifact.newIvyArtifact(pubmrid, new Date())));
if (!ivyFile.exists()) {
throw new IllegalArgumentException("ivy file to publish not found for " + mrid
+ ": call deliver before (" + ivyFile + ")");
}
URL ivyFileURL = ivyFile.toURI().toURL();
try {
md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFileURL,
false);
if (options.isUpdate()) {
File tmp = File.createTempFile("ivy", ".xml");
tmp.deleteOnExit();
String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
Set<String> confsToRemove = new HashSet<String>(Arrays.asList(md
.getConfigurationsNames()));
confsToRemove.removeAll(Arrays.asList(confs));
try {
XmlModuleDescriptorUpdater.update(
ivyFileURL,
tmp,
new UpdateOptions()
.setSettings(settings)
.setStatus(
options.getStatus() == null ? md.getStatus() : options
.getStatus())
.setRevision(options.getPubrevision())
.setBranch(options.getPubBranch())
.setPubdate(
options.getPubdate() == null ? new Date() : options
.getPubdate())
.setMerge(options.isMerge())
.setMergedDescriptor(md)
.setConfsToExclude(
confsToRemove.toArray(new String[confsToRemove.size()])));
ivyFile = tmp;
// we parse the new file to get updated module descriptor
md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
ivyFile.toURI().toURL(), false);
options.setSrcIvyPattern(ivyFile.getAbsolutePath());
} catch (SAXException e) {
throw new IllegalStateException("bad ivy file for " + mrid + ": " + ivyFile
+ ": " + e);
}
} else if (!options.getPubrevision().equals(md.getModuleRevisionId().getRevision())) {
throw new IllegalArgumentException("cannot publish " + ivyFile + " as "
+ options.getPubrevision()
+ ": bad revision found in ivy file (Revision: "
+ md.getModuleRevisionId().getRevision()
+ "). Use forcedeliver or update.");
}
} catch (ParseException e) {
throw new IllegalStateException("bad ivy file for " + mrid + ": " + ivyFile + ": "
+ e);
}
} else {
ResolutionCacheManager cacheManager = settings.getResolutionCacheManager();
try {
md = cacheManager.getResolvedModuleDescriptor(mrid);
} catch (ParseException e) {
throw new IllegalStateException("bad ivy file in cache for " + mrid + ": " + e);
}
md.setResolvedModuleRevisionId(pubmrid);
}
DependencyResolver resolver = settings.getResolver(resolverName);
if (resolver == null) {
throw new IllegalArgumentException("unknown resolver " + resolverName);
}
// collect all declared artifacts of this module
Collection<Artifact> missing = publish(md, srcArtifactPattern, resolver, options);
Message.verbose("\tpublish done (" + (System.currentTimeMillis() - start) + "ms)");
return missing;
}
public Collection<Artifact> publish(ModuleDescriptor md, Collection<String> srcArtifactPattern,
DependencyResolver resolver, PublishOptions options) throws IOException {
Collection<Artifact> missing = new ArrayList<Artifact>();
Set<Artifact> artifactsSet = new LinkedHashSet<Artifact>();
String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
for (int i = 0; i < confs.length; i++) {
Artifact[] artifacts = md.getArtifacts(confs[i]);
for (int j = 0; j < artifacts.length; j++) {
artifactsSet.add(artifacts[j]);
}
}
Artifact[] extraArtifacts = options.getExtraArtifacts();
if (extraArtifacts != null) {
for (int i = 0; i < extraArtifacts.length; i++) {
artifactsSet.add(new MDArtifact(md, extraArtifacts[i].getName(), extraArtifacts[i]
.getType(), extraArtifacts[i].getExt(), extraArtifacts[i].getUrl(),
extraArtifacts[i].getQualifiedExtraAttributes()));
}
}
// now collects artifacts files
Map<Artifact, File> artifactsFiles = new LinkedHashMap<Artifact, File>();
for (Artifact artifact : artifactsSet) {
for (String pattern : srcArtifactPattern) {
File artifactFile = settings.resolveFile(IvyPatternHelper.substitute(
settings.substitute(pattern), artifact));
if (artifactFile.exists()) {
artifactsFiles.put(artifact, artifactFile);
break;
}
}
if (!artifactsFiles.containsKey(artifact)) {
StringBuffer sb = new StringBuffer();
sb.append("missing artifact " + artifact + ":\n");
for (String pattern : srcArtifactPattern) {
sb.append("\t"
+ settings.resolveFile(IvyPatternHelper.substitute(pattern, artifact))
+ " file does not exist\n");
}
if (options.isWarnOnMissing() || options.isHaltOnMissing()) {
Message.warn(sb.toString());
} else {
Message.verbose(sb.toString());
}
if (options.isHaltOnMissing()) {
throw new IOException("missing artifact " + artifact);
}
missing.add(artifact);
}
}
if (options.getSrcIvyPattern() != null) {
Artifact artifact = MDArtifact.newIvyArtifact(md);
File artifactFile = settings.resolveFile(IvyPatternHelper.substitute(
options.getSrcIvyPattern(), artifact));
if (!artifactFile.exists()) {
String msg = "missing ivy file for " + md.getModuleRevisionId() + ": \n"
+ artifactFile + " file does not exist";
if (options.isWarnOnMissing() || options.isHaltOnMissing()) {
Message.warn(msg);
} else {
Message.verbose(msg);
}
if (options.isHaltOnMissing()) {
throw new IOException("missing ivy artifact " + artifact);
}
missing.add(artifact);
} else {
artifactsFiles.put(artifact, artifactFile);
}
}
// and now do actual publishing
boolean successfullyPublished = false;
try {
resolver.beginPublishTransaction(md.getModuleRevisionId(), options.isOverwrite());
// for each declared published artifact in this descriptor, do:
for (Entry<Artifact, File> entry : artifactsFiles.entrySet()) {
Artifact artifact = entry.getKey();
File artifactFile = entry.getValue();
publish(artifact, artifactFile, resolver, options.isOverwrite());
}
resolver.commitPublishTransaction();
successfullyPublished = true;
} finally {
if (!successfullyPublished) {
resolver.abortPublishTransaction();
}
}
return missing;
}
private void publish(Artifact artifact, File src, DependencyResolver resolver, boolean overwrite)
throws IOException {
IvyContext.getContext().checkInterrupted();
// notify triggers that an artifact is about to be published
eventManager
.fireIvyEvent(new StartArtifactPublishEvent(resolver, artifact, src, overwrite));
boolean successful = false; // set to true once the publish succeeds
try {
if (src.exists()) {
resolver.publish(artifact, src, overwrite);
successful = true;
}
} finally {
// notify triggers that the publish is finished, successfully or not.
eventManager.fireIvyEvent(new EndArtifactPublishEvent(resolver, artifact, src,
overwrite, successful));
}
}
}