blob: bf4e6d4f327c1be54c07bba26f133ccfcb38831d [file] [log] [blame]
/*
* 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.plugins.resolver;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.settings.IvyPattern;
import org.apache.ivy.plugins.repository.file.FileRepository;
import org.apache.ivy.util.Checks;
import org.apache.ivy.util.Message;
/**
*/
public class FileSystemResolver extends RepositoryResolver {
private static final String TRANSACTION_DESTINATION_SUFFIX = ".part";
private static final Pattern TRANSACTION_PATTERN = Pattern
.compile("(.*[/\\\\]\\[revision\\])([/\\\\].+)");
/**
* Transactional mode.
*
* auto: use transaction if possible, only log verbose message if not true: always use
* transaction, fail if not supported false: never use transactions
*/
private String transactional = "auto"; // one of 'auto', 'true' or 'false'
/**
* When set indicates if this resolver supports transaction
*/
private Boolean supportTransaction;
/**
* The pattern leading to the directory where files are published before being moved at the end
* of a transaction
*/
private String baseTransactionPattern;
/**
* Map between actual patterns and patterns used during the transaction to put files in a
* temporary directory
*/
private Map<String, String> fullTransactionPatterns = new HashMap<>();
/**
* Location where files are published during the transaction
*/
private File transactionTempDir;
/**
* Location where files should end up at the end of the transaction
*/
private File transactionDestDir;
public FileSystemResolver() {
setRepository(new FileRepository());
}
@Override
public String getTypeName() {
return "file";
}
public boolean isLocal() {
return getFileRepository().isLocal();
}
public void setLocal(boolean local) {
getFileRepository().setLocal(local);
}
private FileRepository getFileRepository() {
return (FileRepository) getRepository();
}
@Override
protected String getDestination(String pattern, Artifact artifact, ModuleRevisionId mrid) {
if (supportTransaction() && isTransactionStarted()) {
String destPattern = fullTransactionPatterns.get(pattern);
if (destPattern == null) {
throw new IllegalArgumentException(
"unsupported pattern for publish destination pattern: " + pattern
+ ". supported patterns: " + fullTransactionPatterns.keySet());
}
return IvyPatternHelper.substitute(destPattern, mrid, artifact);
} else {
return super.getDestination(pattern, artifact, mrid);
}
}
private boolean isTransactionStarted() {
return transactionTempDir != null;
}
@Override
public void abortPublishTransaction() throws IOException {
if (supportTransaction()) {
if (isTransactionStarted()) {
try {
getFileRepository().delete(transactionTempDir);
Message.info("\tpublish aborted: deleted " + transactionTempDir);
} finally {
closeTransaction();
}
} else {
Message.info("\tpublish aborted: nothing was started");
}
}
}
@Override
public void commitPublishTransaction() throws IOException {
if (supportTransaction()) {
if (!isTransactionStarted()) {
throw new IllegalStateException("no current transaction!");
}
if (transactionDestDir.exists()) {
throw new IOException(
"impossible to commit transaction: transaction destination directory "
+ "already exists: "
+ transactionDestDir
+ "\npossible cause: usage of identifying tokens after the revision token");
}
try {
getFileRepository().move(transactionTempDir, transactionDestDir);
Message.info("\tpublish committed: moved " + transactionTempDir + " \n\t\tto "
+ transactionDestDir);
} catch (IOException ex) {
String message;
try {
getFileRepository().delete(transactionTempDir);
message = "publish transaction commit error for "
+ transactionDestDir + ": rolled back";
} catch (IOException deleteEx) {
message = "publish transaction commit error for "
+ transactionDestDir + ": rollback impossible either, "
+ "please remove " + transactionTempDir + " manually";
}
throw new IOException(message, ex);
} finally {
closeTransaction();
}
}
}
@Override
public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
throws IOException {
if (supportTransaction()) {
if (isTransactionStarted()) {
throw new IllegalStateException("a transaction is already started and not closed!");
}
if (overwrite) {
unsupportedTransaction("overwrite transaction not supported yet");
} else {
initTransaction(module);
if (transactionDestDir.exists()) {
unsupportedTransaction("transaction destination directory already exists: "
+ transactionDestDir
+ "\npossible cause: usage of identifying tokens after the revision token");
closeTransaction();
} else {
Message.verbose("\tstarting transaction: publish during transaction will be done in \n\t\t"
+ transactionTempDir
+ "\n\tand on commit moved to \n\t\t"
+ transactionDestDir);
}
}
}
}
@Override
protected Collection<String> filterNames(Collection<String> values) {
if (supportTransaction()) {
values = super.filterNames(values);
Iterator<String> iterator = values.iterator();
while (iterator.hasNext()) {
if (iterator.next().endsWith(TRANSACTION_DESTINATION_SUFFIX)) {
iterator.remove();
}
}
return values;
} else {
return super.filterNames(values);
}
}
public boolean supportTransaction() {
if ("false".equals(transactional)) {
return false;
}
checkSupportTransaction();
return supportTransaction;
}
private void closeTransaction() {
transactionTempDir = null;
transactionDestDir = null;
}
private void checkSupportTransaction() {
if (supportTransaction == null) {
supportTransaction = Boolean.FALSE;
List<String> ivyPatterns = getIvyPatterns();
List<String> artifactPatterns = getArtifactPatterns();
if (ivyPatterns.size() > 0) {
String pattern = ivyPatterns.get(0);
Matcher m = TRANSACTION_PATTERN.matcher(pattern);
if (!m.matches()) {
unsupportedTransaction("ivy pattern does not use revision as a directory");
return;
} else {
baseTransactionPattern = m.group(1);
fullTransactionPatterns.put(pattern, m.group(1)
+ TRANSACTION_DESTINATION_SUFFIX + m.group(2));
}
}
if (artifactPatterns.size() > 0) {
String pattern = artifactPatterns.get(0);
Matcher m = TRANSACTION_PATTERN.matcher(pattern);
if (!m.matches()) {
unsupportedTransaction("artifact pattern does not use revision as a directory");
return;
} else if (baseTransactionPattern != null) {
if (!baseTransactionPattern.equals(m.group(1))) {
unsupportedTransaction("ivy pattern and artifact pattern "
+ "do not use the same directory for revision");
return;
} else {
fullTransactionPatterns.put(pattern, m.group(1)
+ TRANSACTION_DESTINATION_SUFFIX + m.group(2));
}
} else {
baseTransactionPattern = m.group(1);
fullTransactionPatterns.put(pattern, m.group(1)
+ TRANSACTION_DESTINATION_SUFFIX + m.group(2));
}
}
supportTransaction = Boolean.TRUE;
}
}
private void unsupportedTransaction(String msg) {
String fullMsg = getName() + " do not support transaction. " + msg;
if ("true".equals(transactional)) {
throw new IllegalStateException(fullMsg
+ ". Set transactional attribute to 'auto' or 'false' or fix the problem.");
} else {
Message.verbose(fullMsg);
supportTransaction = Boolean.FALSE;
}
}
private void initTransaction(ModuleRevisionId module) {
ModuleRevisionId mrid = module;
if (isM2compatible()) {
mrid = convertM2IdForResourceSearch(module);
}
transactionTempDir = Checks.checkAbsolute(
IvyPatternHelper.substitute(
baseTransactionPattern,
ModuleRevisionId.newInstance(mrid, mrid.getRevision()
+ TRANSACTION_DESTINATION_SUFFIX)), "baseTransactionPattern");
transactionDestDir = Checks.checkAbsolute(
IvyPatternHelper.substitute(baseTransactionPattern, mrid), "baseTransactionPattern");
}
public String getTransactional() {
return transactional;
}
public void setTransactional(String transactional) {
this.transactional = transactional;
}
@Override
public void addConfiguredIvy(IvyPattern p) {
File file = Checks.checkAbsolute(p.getPattern(), "ivy pattern");
p.setPattern(file.getAbsolutePath());
super.addConfiguredIvy(p);
}
@Override
public void addIvyPattern(String pattern) {
File file = Checks.checkAbsolute(pattern, "ivy pattern");
super.addIvyPattern(file.getAbsolutePath());
}
@Override
public void addConfiguredArtifact(IvyPattern p) {
File file = Checks.checkAbsolute(p.getPattern(), "artifact pattern");
p.setPattern(file.getAbsolutePath());
super.addConfiguredArtifact(p);
}
@Override
public void addArtifactPattern(String pattern) {
File file = Checks.checkAbsolute(pattern, "artifact pattern");
super.addArtifactPattern(file.getAbsolutePath());
}
}