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