package cz.cuni.mff.d3s.been.swrepository;
import static cz.cuni.mff.d3s.been.swrepository.FSBasedStoreConfiguration.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cz.cuni.mff.d3s.been.bpk.ArtifactIdentifier;
import cz.cuni.mff.d3s.been.bpk.BpkIdentifier;
import cz.cuni.mff.d3s.been.bpk.BpkNames;
import cz.cuni.mff.d3s.been.datastore.SoftwareStore;
import cz.cuni.mff.d3s.been.datastore.StorePersister;
import cz.cuni.mff.d3s.been.datastore.StoreReader;
import cz.cuni.mff.d3s.been.util.PropertyReader;
/**
* @author darklight
*/
final class FSBasedStore implements SoftwareStore {
private static final Logger log = LoggerFactory.getLogger(FSBasedStore.class);
private static final String ARTIFACTS_ROOT_NAME = "artifacts";
private static final String BPKS_ROOT_NAME = "bpks";
private final File fsRoot;
private final File artifactFSRoot;
private final File bpkFSRoot;
/**
* Create the data store.
*/
private FSBasedStore(String fsRootName) {
fsRoot = new File(fsRootName);
artifactFSRoot = new File(fsRoot, ARTIFACTS_ROOT_NAME);
bpkFSRoot = new File(fsRoot, BPKS_ROOT_NAME);
}
static FSBasedStore createCache(Properties properties) {
final PropertyReader propReader = PropertyReader.on(properties);
final String rootDir = propReader.getString(CACHE_FS_ROOT, DEFAULT_CACHE_FS_ROOT);
// final Long cacheSizeString = propReader.getLong(SWCACHE_MAX_SIZE, DEFAULT_SWCACHE_MAX_SIZE);
// TODO infer a cache size control mechanism
return new FSBasedStore(rootDir);
}
static FSBasedStore createServer(Properties properties) {
final String rootDir = properties.getProperty(SERVER_FS_ROOT, DEFAULT_SERVER_FS_ROOT);
return new FSBasedStore(rootDir);
}
/**
* Initialize the FS store in the app's run directory.
*/
@Override
public void init() {
if (!fsRoot.exists()) {
fsRoot.mkdir();
}
if (!artifactFSRoot.exists()) {
artifactFSRoot.mkdir();
}
if (!bpkFSRoot.exists()) {
bpkFSRoot.mkdir();
}
}
@Override
public StoreReader getArtifactReader(ArtifactIdentifier artifactIdentifier) {
File item = getArtifactItem(artifactIdentifier);
if (item == null || !item.exists()) {
return null;
} else {
return new FSBasedStoreReader(item);
}
}
@Override
public boolean exists(BpkIdentifier bpkIdentifier) {
File item = getBpkItem(bpkIdentifier);
return (item != null && item.exists());
}
@Override
public StoreReader getBpkReader(BpkIdentifier bpkIdentifier) {
File item = getBpkItem(bpkIdentifier);
if (item == null || !item.exists()) {
return null;
}
return new FSBasedStoreReader(item);
}
@Override
public StorePersister getArtifactPersister(ArtifactIdentifier artifactIdentifier) {
File item = getArtifactItem(artifactIdentifier);
if (item == null) {
return null;
}
return new FSBasedStorePersister(artifactIdentifier.toString(), item);
}
@Override
public StorePersister getBpkPersister(BpkIdentifier bpkIdentifier) {
File item = getBpkItem(bpkIdentifier);
if (item == null) {
return null;
}
return new FSBasedStorePersister(bpkIdentifier.toString(), item);
}
/**
* Generically synthesize a stored file's directory in the persistence tree.
*
* @param itemRoot
* Root for the item's specific item type
* @param pathItems
* Identifiers for the item
* @return The file, may or may not exist
*/
public File getItemPath(File itemRoot, String... pathItems) {
Path itemPath = FileSystems.getDefault().getPath(itemRoot.getPath(), pathItems);
return itemPath.toFile();
}
/**
* Get a BPK file's path in the persistence tree.
*
* @param bpkIdentifier
* The BPK's identifier
* @return The path to the BPK
*/
public File getBpkItem(BpkIdentifier bpkIdentifier) {
if (bpkIdentifier == null || bpkIdentifier.getGroupId() == null || bpkIdentifier.getBpkId() == null || bpkIdentifier.getVersion() == null) {
log.error("Null or incomplete BPK identifier {}", bpkIdentifier);
return null;
}
final List<String> pathItems = new LinkedList();
pathItems.addAll(Arrays.asList(bpkIdentifier.getGroupId().split("\\.")));
pathItems.add(bpkIdentifier.getBpkId());
pathItems.add(bpkIdentifier.getVersion());
final File itemPath = getItemPath(bpkFSRoot, pathItems.toArray(new String[pathItems.size()]));
final String bpkFileName = String.format("%s-%s.bpk", bpkIdentifier.getBpkId(), bpkIdentifier.getVersion());
return new File(itemPath, bpkFileName);
}
/**
* Get an Artifact's path in the persistence tree
*
* @param artifactIdentifier
* A fully qualified identifier of the Maven artifact
* @return The path to the Artifact file
*/
public File getArtifactItem(ArtifactIdentifier artifactIdentifier) {
if (artifactIdentifier == null || artifactIdentifier.getGroupId() == null || artifactIdentifier.getArtifactId() == null || artifactIdentifier.getVersion() == null) {
log.error("Null or incomplete Artifact identifier {}", artifactIdentifier);
return null;
}
final List<String> pathItems = new ArrayList<>(Arrays.asList(artifactIdentifier.getGroupId().split("\\.")));
pathItems.add(artifactIdentifier.getArtifactId());
pathItems.add(artifactIdentifier.getVersion());
final String[] newPathItems = new String[pathItems.size()];
pathItems.toArray(newPathItems);
final File itemPath = getItemPath(artifactFSRoot, newPathItems);
final String artifactFileName = String.format(
"%s-%s.jar",
artifactIdentifier.getArtifactId(),
artifactIdentifier.getVersion());
return new File(itemPath, artifactFileName);
}
@Override
public List<BpkIdentifier> listBpks() {
String sep = Pattern.quote(File.separator);
String regexp = Pattern.quote(bpkFSRoot.getPath()) + // base
sep + //
"([a-zA-Z0-9-._" + sep + "]+)" + // groupId
sep + //
"([a-zA-Z0-9-._]+)" + // artifactId
sep + //
"([a-zA-Z0-9-._]+)" + // version
sep + //
"([a-zA-Z0-9-._]+)\\.bpk"; // file name
Pattern pattern = Pattern.compile(regexp);
List<BpkIdentifier> result = new ArrayList();
for (File f : FileUtils.listFiles(bpkFSRoot, new String[] { "bpk" }, true)) {
String path = f.getPath();
Matcher m = pattern.matcher(path);
if (m.find()) {
BpkIdentifier bpkIdentifier = new BpkIdentifier();
bpkIdentifier.setGroupId(m.group(1).replace(File.separator, "."));
bpkIdentifier.setBpkId(m.group(2));
bpkIdentifier.setVersion(m.group(3));
result.add(bpkIdentifier);
}
}
return result;
}
@Override
public Map<String, String> getTaskDescriptors(BpkIdentifier bpkIdentifier) throws IOException {
File bpkFile = getBpkItem(bpkIdentifier);
Map<String, String> descriptors = new HashMap<>();
ZipFile zipFile = new ZipFile(bpkFile);
Enumeration zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry element = (ZipEntry) zipEntries.nextElement();
String fileName = element.getName();
if (!element.isDirectory() && fileName.startsWith(BpkNames.TASK_DESCRIPTORS_DIR + "/")) {
InputStream inputStream = zipFile.getInputStream(element);
String xml = IOUtils.toString(inputStream);
descriptors.put(fileName, xml);
}
}
return descriptors;
}
@Override
public Map<String, String> getTaskContextDescriptors(BpkIdentifier bpkIdentifier) throws IOException {
File bpkFile = getBpkItem(bpkIdentifier);
Map<String, String> descriptors = new HashMap<>();
ZipFile zipFile = new ZipFile(bpkFile);
Enumeration zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry element = (ZipEntry) zipEntries.nextElement();
String fileName = element.getName();
if (!element.isDirectory() && fileName.startsWith(BpkNames.TASK_CONTEXT_DESCRIPTORS_DIR + "/")) {
InputStream inputStream = zipFile.getInputStream(element);
String xml = IOUtils.toString(inputStream);
descriptors.put(fileName, xml);
}
}
return descriptors;
}
}