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