001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Bogdan Stefanescu 018 * Florent Guillaume 019 */ 020package org.nuxeo.osgi; 021 022import java.io.File; 023import java.io.FileInputStream; 024import java.io.IOException; 025import java.net.MalformedURLException; 026import java.net.URL; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Enumeration; 030import java.util.List; 031import java.util.jar.Attributes; 032import java.util.jar.Manifest; 033import java.util.stream.Collectors; 034 035import org.nuxeo.common.utils.FileUtils; 036import org.nuxeo.common.utils.StringUtils; 037import org.nuxeo.osgi.util.CompoundEnumeration; 038import org.nuxeo.osgi.util.EntryFilter; 039import org.nuxeo.osgi.util.FileIterator; 040import org.osgi.framework.Constants; 041 042/** 043 * A {@link BundleFile} that is backed by a filesystem directory, for use in test settings from Eclipse or maven. 044 */ 045public class DirectoryBundleFile implements BundleFile { 046 047 public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; 048 049 protected final File file; 050 051 protected final List<File> files; 052 053 protected final Manifest mf; 054 055 public DirectoryBundleFile(File file) throws IOException { 056 this(file, null); 057 } 058 059 public DirectoryBundleFile(File file, Manifest mf) throws IOException { 060 this.file = file; 061 this.files = findFiles(file); 062 this.mf = mf == null ? findManifest() : mf; 063 } 064 065 protected List<File> findFiles(File file) { 066 List<File> files = new ArrayList<>(2); 067 files.add(file); 068 if (file.getPath().endsWith("/bin")) { 069 // hack for Eclipse PDE development 070 files.add(file.getParentFile()); 071 } else if (file.getPath().endsWith("/target/classes")) { 072 // hack for maven/tycho development 073 files.add(file.getParentFile().getParentFile()); 074 } 075 return files; 076 } 077 078 private Enumeration<URL> createEnumeration(File root, final EntryFilter efilter, final boolean recurse) { 079 FileIterator it = new FileIterator(root, pathname -> { 080 if (pathname.isDirectory()) { 081 return recurse; 082 } 083 return efilter.match(pathname.getName()); 084 }); 085 it.setSkipDirs(true); 086 return FileIterator.asUrlEnumeration(it); 087 } 088 089 @SuppressWarnings("unchecked") 090 @Override 091 public Enumeration<URL> findEntries(String name, String pattern, boolean recurse) { 092 EntryFilter efilter = EntryFilter.newFilter(pattern); 093 if (files.size() == 1) { 094 return createEnumeration(new File(file, name), efilter, recurse); 095 } else { 096 Enumeration<URL>[] enums = new Enumeration[files.size()]; 097 int i = 0; 098 for (File f : files) { 099 enums[i++] = createEnumeration(new File(f, name), efilter, recurse); 100 } 101 return new CompoundEnumeration<>(enums); 102 } 103 } 104 105 @Override 106 public URL getEntry(String name) { 107 for (File file : files) { 108 File entry = new File(file, name); 109 if (entry.exists()) { 110 try { 111 return entry.toURI().toURL(); 112 } catch (MalformedURLException e) { 113 // ignore 114 } 115 } 116 } 117 return null; 118 } 119 120 @Override 121 public Enumeration<String> getEntryPaths(String path) { 122 throw new UnsupportedOperationException("The operation BundleFile.geEntryPaths() is not yet implemented"); 123 } 124 125 @Override 126 public File getFile() { 127 return file; 128 } 129 130 @Override 131 public String getFileName() { 132 return file.getName(); 133 } 134 135 @Override 136 public String getLocation() { 137 return file.getPath(); 138 } 139 140 @Override 141 public Manifest getManifest() { 142 return mf; 143 } 144 145 protected Manifest findManifest() throws IOException { 146 for (File file : files) { 147 File entry = new File(file, MANIFEST_PATH); 148 if (entry.exists()) { 149 try (FileInputStream fis = new FileInputStream(entry)) { 150 return new Manifest(fis); 151 } 152 } 153 } 154 String paths = files.stream().map(File::toString).collect(Collectors.joining(", ")); 155 throw new IOException(String.format("Could not find a file '%s' in paths: %s", MANIFEST_PATH, paths)); 156 } 157 158 @Override 159 public Collection<BundleFile> getNestedBundles(File tmpDir) throws IOException { 160 Attributes attrs = mf.getMainAttributes(); 161 String cp = attrs.getValue(Constants.BUNDLE_CLASSPATH); 162 if (cp == null) { 163 cp = attrs.getValue("Class-Path"); 164 } 165 if (cp == null) { 166 return null; 167 } 168 String[] paths = StringUtils.split(cp, ',', true); 169 List<BundleFile> nested = new ArrayList<>(); 170 for (String path : paths) { 171 File nestedBundle = new File(file, path); 172 if (nestedBundle.isDirectory()) { 173 nested.add(new DirectoryBundleFile(nestedBundle)); 174 } else { 175 nested.add(new JarBundleFile(nestedBundle)); 176 } 177 } 178 return nested; 179 } 180 181 @Override 182 public Collection<BundleFile> findNestedBundles(File tmpDir) throws IOException { 183 List<BundleFile> nested = new ArrayList<>(); 184 File[] files = FileUtils.findFiles(file, "*.jar", true); 185 for (File jar : files) { 186 if (jar.isDirectory()) { 187 nested.add(new DirectoryBundleFile(jar)); 188 } else { 189 nested.add(new JarBundleFile(jar)); 190 } 191 } 192 return nested; 193 } 194 195 @Override 196 public String getSymbolicName() { 197 String value = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME); 198 return value == null ? null : BundleManifestReader.removePropertiesFromHeaderValue(value); 199 } 200 201 @Override 202 public URL getURL() { 203 try { 204 return file.toURI().toURL(); 205 } catch (MalformedURLException e) { 206 return null; 207 } 208 } 209 210 @Override 211 public boolean isDirectory() { 212 return true; 213 } 214 215 @Override 216 public boolean isJar() { 217 return false; 218 } 219 220 @Override 221 public String toString() { 222 return getLocation(); 223 } 224 225 public static void main(String[] args) throws Exception { 226 DirectoryBundleFile bf = new DirectoryBundleFile( 227 new File("/Users/bstefanescu/work/org.eclipse.ecr/plugins/org.eclipse.ecr.application/bin")); 228 Enumeration<URL> urls = bf.findEntries("META-INF", "*.txt", false); 229 while (urls.hasMoreElements()) { 230 System.out.println(urls.nextElement()); 231 } 232 } 233 234 @Override 235 public void close(OSGiAdapter osgi) throws IOException { 236 } 237 238}