/**
* Copyright 2005-2016 Red Hat, Inc.
*
* Red Hat 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 io.fabric8.tooling.migration.profile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.ProviderMismatchException;
import java.nio.file.ReadOnlyFileSystemException;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.felix.utils.properties.Properties;
public class FabricProfileFileSystemProvider extends FileSystemProvider {
final Map<Path, FabricProfileFileSystem> fileSystems = new HashMap<>();
@Override
public String getScheme() {
return "fabric-profile";
}
@Override
public FabricProfileFileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
Path path = uriToPath(uri);
return newFileSystem(path, env);
}
public FabricProfileFileSystem newFileSystem(Path path, Map<String, ?> env) throws IOException {
if (!Files.isDirectory(path)) {
throw new UnsupportedOperationException();
}
synchronized (fileSystems) {
Path localPath = path.toRealPath();
if (this.fileSystems.containsKey(localPath)) {
throw new FileSystemAlreadyExistsException();
}
FabricProfileFileSystem fileSystem = buildFileSystem(path);
fileSystems.put(localPath, fileSystem);
return fileSystem;
}
}
private FabricProfileFileSystem buildFileSystem(final Path path) throws IOException {
final Map<String, Object> contents = new HashMap<>();
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
addChild(dir, new ArrayList<String>());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
byte[] content = Files.readAllBytes(file);
if (file.getFileName().toString().equals("io.fabric8.agent.properties")) {
file = file.resolveSibling("profile.cfg");
} else if (file.getFileName().toString().contains(".properties")) {
file = file.resolveSibling(file.getFileName().toString().replace(".properties", ".cfg"));
}
if (file.getFileName().toString().contains(".cfg")) {
Properties props = new Properties(false);
props.load(new ByteArrayInputStream(content));
for (Map.Entry<String, String> entry : props.entrySet()) {
String val = entry.getValue();
val = val.replace("${profile:io.fabric8.agent/", "${profile:profile/");
val = val.replace("${version:", "${profile:io.fabric8.version/");
val = val.replace("${runtime.", "${karaf.");
Matcher matcher = Pattern.compile(".*\\$\\{(.*?):.*?\\}.*").matcher(val);
if (matcher.matches()) {
String scheme = matcher.group(1);
if (!"profile".equals(scheme)) {
System.out.println("Unsupported scheme: " + entry.getKey() + " = " + val + " in " + path.relativize(file));
}
}
entry.setValue(val);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
props.save(baos);
baos.flush();
content = baos.toByteArray();
}
addChild(file, content);
return FileVisitResult.CONTINUE;
}
private void addChild(Path child, Object content) {
String par = "/" + path.relativize(child.getParent()).toString();
String str = "/" + path.relativize(child).toString();
if (!"/".equals(str)) {
if (str.endsWith("/")) {
str = str.substring(0, str.length() - 1);
}
((List<String>) contents.get(par)).add(str);
}
contents.put(str, content);
}
});
return new FabricProfileFileSystem(this, contents);
}
@Override
public FabricProfileFileSystem getFileSystem(URI uri) {
synchronized (fileSystems) {
FabricProfileFileSystem fileSystem = null;
try {
fileSystem = fileSystems.get(uriToPath(uri).toRealPath());
} catch (IOException ignore) {
}
if (fileSystem == null) {
throw new FileSystemNotFoundException();
}
return fileSystem;
}
}
protected Path uriToPath(URI uri) {
String scheme = uri.getScheme();
if ((scheme == null) || (!scheme.equalsIgnoreCase(getScheme()))) {
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
}
try {
String root = uri.getRawSchemeSpecificPart();
int i = root.lastIndexOf("!/");
if (i != -1) {
root = root.substring(0, i);
}
URI rootUri = new URI(root);
try {
return Paths.get(rootUri).toAbsolutePath();
} catch (FileSystemNotFoundException e) {
try {
FileSystem fs = FileSystems.newFileSystem(rootUri, new HashMap<String, Object>(), FabricProfileFileSystemProvider.class.getClassLoader());
return fs.provider().getPath(rootUri).toAbsolutePath();
} catch (IOException e2) {
e.addSuppressed(e2);
throw e;
}
}
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
@Override
public Path getPath(URI uri) {
String str = uri.getSchemeSpecificPart();
int i = str.lastIndexOf("!/");
if (i == -1) {
throw new IllegalArgumentException("URI: " + uri + " does not contain path info ex. github:apache/karaf#master!/");
}
return getFileSystem(uri).getPath(str.substring(i + 1));
}
@Override
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
if (!(path instanceof FabricProfilePath)) {
throw new ProviderMismatchException();
}
return ((FabricProfilePath) path).getFileSystem().newInputStream(path, options);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
if (!(dir instanceof FabricProfilePath)) {
throw new ProviderMismatchException();
}
return ((FabricProfilePath) dir).getFileSystem().newDirectoryStream(dir, filter);
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
if (!(path instanceof FabricProfilePath)) {
throw new ProviderMismatchException();
}
return ((FabricProfilePath) path).getFileSystem().newByteChannel(path, options, attrs);
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
throw new ReadOnlyFileSystemException();
}
@Override
public void delete(Path path) throws IOException {
throw new ReadOnlyFileSystemException();
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
throw new ReadOnlyFileSystemException();
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
throw new ReadOnlyFileSystemException();
}
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {
return path.toAbsolutePath().equals(path2.toAbsolutePath());
}
@Override
public boolean isHidden(Path path) throws IOException {
return false;
}
@Override
public FileStore getFileStore(Path path) throws IOException {
return null;
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
return null;
}
@Override
public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
if (!(path instanceof FabricProfilePath)) {
throw new ProviderMismatchException();
}
return ((FabricProfilePath) path).getFileSystem().readAttributes(path, type, options);
}
@Override
public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
return null;
}
@Override
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
throw new ReadOnlyFileSystemException();
}
}