/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed 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.
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.nuxeo.osgi;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import org.nuxeo.common.utils.FileUtils;
import org.nuxeo.common.utils.StringUtils;
import org.nuxeo.osgi.util.CompoundEnumeration;
import org.nuxeo.osgi.util.EntryFilter;
import org.nuxeo.osgi.util.FileIterator;
import org.osgi.framework.Constants;
/**
* A {@link BundleFile} that is backed by a filesystem directory, for use in test settings from Eclipse or maven.
*/
public class DirectoryBundleFile implements BundleFile {
public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
protected final File file;
protected final List<File> files;
protected final Manifest mf;
public DirectoryBundleFile(File file) throws IOException {
this(file, null);
}
public DirectoryBundleFile(File file, Manifest mf) throws IOException {
this.file = file;
this.files = findFiles(file);
this.mf = mf == null ? findManifest() : mf;
}
protected List<File> findFiles(File file) {
List<File> files = new ArrayList<>(2);
files.add(file);
if (file.getPath().endsWith("/bin")) {
// hack for Eclipse PDE development
files.add(file.getParentFile());
} else if (file.getPath().endsWith("/target/classes")) {
// hack for maven/tycho development
files.add(file.getParentFile().getParentFile());
}
return files;
}
private Enumeration<URL> createEnumeration(File root, final EntryFilter efilter, final boolean recurse) {
FileIterator it = new FileIterator(root, pathname -> {
if (pathname.isDirectory()) {
return recurse;
}
return efilter.match(pathname.getName());
});
it.setSkipDirs(true);
return FileIterator.asUrlEnumeration(it);
}
@SuppressWarnings("unchecked")
@Override
public Enumeration<URL> findEntries(String name, String pattern, boolean recurse) {
EntryFilter efilter = EntryFilter.newFilter(pattern);
if (files.size() == 1) {
return createEnumeration(new File(file, name), efilter, recurse);
} else {
Enumeration<URL>[] enums = new Enumeration[files.size()];
int i = 0;
for (File f : files) {
enums[i++] = createEnumeration(new File(f, name), efilter, recurse);
}
return new CompoundEnumeration<>(enums);
}
}
@Override
public URL getEntry(String name) {
for (File file : files) {
File entry = new File(file, name);
if (entry.exists()) {
try {
return entry.toURI().toURL();
} catch (MalformedURLException e) {
// ignore
}
}
}
return null;
}
@Override
public Enumeration<String> getEntryPaths(String path) {
throw new UnsupportedOperationException("The operation BundleFile.geEntryPaths() is not yet implemented");
}
@Override
public File getFile() {
return file;
}
@Override
public String getFileName() {
return file.getName();
}
@Override
public String getLocation() {
return file.getPath();
}
@Override
public Manifest getManifest() {
return mf;
}
protected Manifest findManifest() throws IOException {
for (File file : files) {
File entry = new File(file, MANIFEST_PATH);
if (entry.exists()) {
try (FileInputStream fis = new FileInputStream(entry)) {
return new Manifest(fis);
}
}
}
String paths = files.stream().map(File::toString).collect(Collectors.joining(", "));
throw new IOException(String.format("Could not find a file '%s' in paths: %s", MANIFEST_PATH, paths));
}
@Override
public Collection<BundleFile> getNestedBundles(File tmpDir) throws IOException {
Attributes attrs = mf.getMainAttributes();
String cp = attrs.getValue(Constants.BUNDLE_CLASSPATH);
if (cp == null) {
cp = attrs.getValue("Class-Path");
}
if (cp == null) {
return null;
}
String[] paths = StringUtils.split(cp, ',', true);
List<BundleFile> nested = new ArrayList<>();
for (String path : paths) {
File nestedBundle = new File(file, path);
if (nestedBundle.isDirectory()) {
nested.add(new DirectoryBundleFile(nestedBundle));
} else {
nested.add(new JarBundleFile(nestedBundle));
}
}
return nested;
}
@Override
public Collection<BundleFile> findNestedBundles(File tmpDir) throws IOException {
List<BundleFile> nested = new ArrayList<>();
File[] files = FileUtils.findFiles(file, "*.jar", true);
for (File jar : files) {
if (jar.isDirectory()) {
nested.add(new DirectoryBundleFile(jar));
} else {
nested.add(new JarBundleFile(jar));
}
}
return nested;
}
@Override
public String getSymbolicName() {
String value = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
return value == null ? null : BundleManifestReader.removePropertiesFromHeaderValue(value);
}
@Override
public URL getURL() {
try {
return file.toURI().toURL();
} catch (MalformedURLException e) {
return null;
}
}
@Override
public boolean isDirectory() {
return true;
}
@Override
public boolean isJar() {
return false;
}
@Override
public String toString() {
return getLocation();
}
public static void main(String[] args) throws Exception {
DirectoryBundleFile bf = new DirectoryBundleFile(
new File("/Users/bstefanescu/work/org.eclipse.ecr/plugins/org.eclipse.ecr.application/bin"));
Enumeration<URL> urls = bf.findEntries("META-INF", "*.txt", false);
while (urls.hasMoreElements()) {
System.out.println(urls.nextElement());
}
}
@Override
public void close(OSGiAdapter osgi) throws IOException {
}
}