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 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.osgi.application;
023
024import java.io.File;
025import java.io.IOException;
026import java.util.Collection;
027import java.util.jar.JarFile;
028import java.util.jar.Manifest;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.common.utils.FileNamePattern;
033import org.nuxeo.common.utils.JarUtils;
034import org.nuxeo.osgi.BundleFile;
035import org.nuxeo.osgi.DirectoryBundleFile;
036import org.nuxeo.osgi.JarBundleFile;
037import org.osgi.framework.BundleException;
038import org.osgi.framework.Constants;
039
040/**
041 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
042 */
043public class BundleWalker extends FileWalker.Visitor {
044
045    private static final Log log = LogFactory.getLog(BundleWalker.class);
046
047    public static final FileNamePattern[] DEFAULT_PATTERNS = { new FileNamePattern("*.jar"),
048            new FileNamePattern("*.war"), new FileNamePattern("*.rar"), new FileNamePattern("*.sar"), // jboss sar
049            new FileNamePattern("*_jar"), new FileNamePattern("*_war"), new FileNamePattern("*_rar") };
050
051    private FileNamePattern[] patterns;
052
053    private final Callback callback;
054
055    public BundleWalker(Callback cb) {
056        this(cb, DEFAULT_PATTERNS);
057    }
058
059    public BundleWalker(Callback cb, String[] patterns) {
060        if (patterns != null) {
061            this.patterns = new FileNamePattern[patterns.length];
062            for (int i = 0; i < patterns.length; i++) {
063                this.patterns[i] = new FileNamePattern(patterns[i]);
064            }
065        }
066        callback = cb;
067    }
068
069    public BundleWalker(Callback cb, FileNamePattern[] patterns) {
070        this.patterns = patterns;
071        callback = cb;
072    }
073
074    public void visit(File root) {
075        new FileWalker().walk(root, this);
076    }
077
078    public void visit(Collection<File> files) {
079        for (File file : files) {
080            if (file.isFile()) {
081                if (file.isFile()) {
082                    visitFile(file);
083                } else if (file.isDirectory()) {
084                    visitDirectory(file);
085                }
086            }
087        }
088    }
089
090    public void visit(File... files) {
091        for (File file : files) {
092            if (file.isFile()) {
093                if (file.isFile()) {
094                    visitFile(file);
095                } else if (file.isDirectory()) {
096                    visitDirectory(file);
097                }
098            }
099        }
100    }
101
102    @Override
103    public int visitDirectory(File file) {
104        // System.out.println("###### Processing DIR: " + file);
105        // first check if this is a possible bundle
106        String fileName = file.getName();
107        if (patterns != null) {
108            if (!acceptFile(fileName, patterns)) {
109                return FileWalker.CONTINUE;
110            }
111        }
112        // check if this is an OSGi bundle
113        try {
114            Manifest mf = JarUtils.getDirectoryManifest(file);
115            if (mf == null) {
116                return FileWalker.CONTINUE;
117            }
118            String bundleName = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
119            if (bundleName != null) {
120                // notify the callback about the new bundle
121                callback.visitBundle(new DirectoryBundleFile(file, mf));
122                // assume that a directory OSGi bundle cannot contain other bundles so skip it
123                return FileWalker.BREAK;
124            }
125        } catch (IOException e) {
126            log.error(e, e);
127        }
128        return FileWalker.CONTINUE;
129    }
130
131    @Override
132    public int visitFile(File file) {
133        // System.out.println("###### Processing file: "+file);
134        // first check if this is a possible bundle
135        String fileName = file.getName();
136        if (patterns != null) {
137            if (!acceptFile(fileName, patterns)) {
138                // System.out.println("###### Ignoring file based on name: "+file);
139                return FileWalker.CONTINUE;
140            }
141        }
142        // check if this is an OSGi bundle
143        try {
144            JarFile jarFile = new JarFile(file);
145            if (jarFile.getManifest() == null) {
146                // System.out.println("###### No manifest found: "+file);
147                return FileWalker.CONTINUE;
148            }
149            BundleFile bundleFile = new JarBundleFile(jarFile);
150            if (bundleFile.getSymbolicName() != null) {
151                // System.out.println("###### Bundle symbolic name: "+bundleFile.getSymbolicName());
152
153                // notify the callback about the new bundle
154                callback.visitBundle(bundleFile);
155            } else {
156                // notify the callback about the new jar
157                callback.visitJar(bundleFile);
158            }
159        } catch (IOException e) {
160            // ignore
161        }
162        return FileWalker.CONTINUE;
163    }
164
165    protected boolean acceptFile(String fileName, FileNamePattern[] patterns) {
166        int i = 0;
167        for (; i < patterns.length; i++) {
168            if (patterns[i].match(fileName)) {
169                break;
170            }
171        }
172        return i < patterns.length;
173    }
174
175    public interface Callback {
176        void visitBundle(BundleFile bundleFile) throws IOException;
177
178        void visitJar(BundleFile bundleFile) throws IOException;
179    }
180
181}