001/*
002 * (C) Copyright 2006-2011 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 *     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        // first check if this is a possible bundle
134        String fileName = file.getName();
135        if (patterns != null) {
136            if (!acceptFile(fileName, patterns)) {
137                return FileWalker.CONTINUE;
138            }
139        }
140        // check if this is an OSGi bundle
141        try (JarFile jarFile = new JarFile(file)) {
142            if (jarFile.getManifest() == null) {
143                return FileWalker.CONTINUE;
144            }
145            BundleFile bundleFile = new JarBundleFile(jarFile);
146            if (bundleFile.getSymbolicName() != null) {
147                // notify the callback about the new bundle
148                callback.visitBundle(bundleFile);
149            } else {
150                // notify the callback about the new jar
151                callback.visitJar(bundleFile);
152            }
153        } catch (IOException e) {
154            // ignore
155        }
156        return FileWalker.CONTINUE;
157    }
158
159    protected boolean acceptFile(String fileName, FileNamePattern[] patterns) {
160        int i = 0;
161        for (; i < patterns.length; i++) {
162            if (patterns[i].match(fileName)) {
163                break;
164            }
165        }
166        return i < patterns.length;
167    }
168
169    public interface Callback {
170        void visitBundle(BundleFile bundleFile) throws IOException;
171
172        void visitJar(BundleFile bundleFile) throws IOException;
173    }
174
175}