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.List;
028
029import org.nuxeo.common.utils.FileNamePattern;
030import org.nuxeo.osgi.BundleFile;
031import org.nuxeo.osgi.NestedJarBundleFile;
032import org.nuxeo.osgi.OSGiAdapter;
033import org.osgi.framework.BundleException;
034
035/**
036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
037 */
038public abstract class ApplicationLoader {
039
040    protected final OSGiAdapter osgi;
041
042    protected boolean extractNestedJARs = false;
043
044    protected boolean scanForNestedJARs = false;
045
046    private FileNamePattern[] patterns = BundleWalker.DEFAULT_PATTERNS;
047
048    private final File tmpDir;
049
050    protected ApplicationLoader(OSGiAdapter osgi) {
051        this.osgi = osgi;
052        tmpDir = new File(osgi.getDataDir(), "nested-bundles");
053        tmpDir.mkdirs();
054    }
055
056    public abstract void installBundle(BundleFile bundleFile) throws BundleException;
057
058    public abstract void loadBundle(BundleFile bundleFile);
059
060    public abstract void loadJAR(BundleFile bundleFile);
061
062    public File getNestedBundleDirectory() {
063        return tmpDir;
064    }
065
066    public OSGiAdapter getOSGi() {
067        return osgi;
068    }
069
070    public void setExtractNestedJARs(boolean extractNestedJARs) {
071        this.extractNestedJARs = extractNestedJARs;
072    }
073
074    public boolean getExtractNestedJARs() {
075        return extractNestedJARs;
076    }
077
078    public void setScanForNestedJARs(boolean scanForNestedJARs) {
079        this.scanForNestedJARs = scanForNestedJARs;
080    }
081
082    public boolean getScanForNestedJARs() {
083        return scanForNestedJARs;
084    }
085
086    public void setPatterns(FileNamePattern[] patterns) {
087        this.patterns = patterns;
088    }
089
090    public FileNamePattern[] getPatterns() {
091        return patterns;
092    }
093
094    /**
095     * Scans and loads the given directory for OSGi bundles and regular JARs and fills the given lists appropriately.
096     * <p>
097     * Loading means registering with the given shared class loader each bundle found.
098     *
099     * @param root the directory to recursively scan
100     * @param bundles the list to fill with found bundles
101     * @param jars the list to fill with found jars
102     */
103    public void load(File root, List<BundleFile> bundles, List<BundleFile> jars) {
104        BundleFileLoader callback = new BundleFileLoader(bundles, jars);
105        BundleWalker visitor = new BundleWalker(callback, patterns);
106        visitor.visit(root);
107    }
108
109    /**
110     * Installs all given bundle deployments.
111     */
112    public void installAll(Collection<BundleFile> bundleFiles) throws BundleException {
113        for (BundleFile bundleFile : bundleFiles) {
114            installBundle(bundleFile);
115        }
116    }
117
118    /**
119     * Installs all bundles found in the given directory.
120     * <p>
121     * The directory is recursively searched for bundles.
122     *
123     * @param root the tree root
124     */
125    public void install(File root) {
126        BundleInstaller callback = new BundleInstaller();
127        BundleWalker visitor = new BundleWalker(callback, patterns);
128        visitor.visit(root);
129    }
130
131    /**
132     * Scans the given directory for OSGi bundles and regular JARs and fills the given lists appropriately.
133     *
134     * @param root the directory to recursively scan
135     * @param bundles the list to fill with found bundles
136     * @param ljars the list to fill with found jars
137     */
138    public void scan(File root, List<BundleFile> bundles, List<BundleFile> ljars) {
139        BundleFileScanner callback = new BundleFileScanner(bundles, ljars);
140        BundleWalker visitor = new BundleWalker(callback, patterns);
141        visitor.visit(root);
142    }
143
144    /**
145     * Installs bundles as they are discovered by the bundle visitor.
146     *
147     * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
148     */
149    public class BundleInstaller extends DefaultCallback {
150
151        @Override
152        public void visitBundle(BundleFile bundleFile) throws IOException {
153            loadBundle(bundleFile);
154            visitNestedBundles(bundleFile);
155        }
156
157        @Override
158        public void visitJar(BundleFile bundleFile) throws IOException {
159            loadJAR(bundleFile);
160            visitNestedBundles(bundleFile);
161        }
162
163    }
164
165    public class BundleFileScanner extends DefaultCallback {
166
167        final List<BundleFile> bundles;
168
169        final List<BundleFile> jars;
170
171        public BundleFileScanner(List<BundleFile> bundles, List<BundleFile> jars) {
172            this.bundles = bundles;
173            this.jars = jars;
174        }
175
176        @Override
177        public void visitBundle(BundleFile bundleFile) throws IOException {
178            bundles.add(bundleFile);
179            visitNestedBundles(bundleFile);
180        }
181
182        @Override
183        public void visitJar(BundleFile bundleFile) throws IOException {
184            jars.add(bundleFile);
185            visitNestedBundles(bundleFile);
186        }
187
188        public List<BundleFile> getBundles() {
189            return bundles;
190        }
191
192        public List<BundleFile> getJARs() {
193            return jars;
194        }
195
196    }
197
198    public class BundleFileLoader extends DefaultCallback {
199
200        final List<BundleFile> bundles;
201
202        final List<BundleFile> jars;
203
204        public BundleFileLoader(List<BundleFile> bundles, List<BundleFile> jars) {
205            this.bundles = bundles;
206            this.jars = jars;
207        }
208
209        @Override
210        public void visitBundle(BundleFile bundleFile) throws IOException {
211            // System.out.println(">>>> FOUND BUNDLE: "+bundleFile.getFileName());
212            loadBundle(bundleFile);
213            bundles.add(bundleFile);
214            visitNestedBundles(bundleFile);
215        }
216
217        @Override
218        public void visitJar(BundleFile bundleFile) throws IOException {
219            // System.out.println(">>>> FOUND JAR: "+bundleFile.getFileName());
220            loadJAR(bundleFile);
221            jars.add(bundleFile);
222            visitNestedBundles(bundleFile);
223        }
224
225        public List<BundleFile> getBundles() {
226            return bundles;
227        }
228
229        public List<BundleFile> getJARs() {
230            return jars;
231        }
232
233    }
234
235    public abstract class DefaultCallback implements BundleWalker.Callback {
236
237        @Override
238        public void visitBundle(BundleFile bundleFile) throws IOException {
239            visitNestedBundles(bundleFile);
240        }
241
242        @Override
243        public void visitJar(BundleFile bundleFile) throws IOException {
244            visitNestedBundles(bundleFile);
245        }
246
247        public void visitNestedBundles(BundleFile bundleFile) throws IOException {
248            if (bundleFile instanceof NestedJarBundleFile) {
249                return; // do not allows more than one level of nesting
250            }
251            if (extractNestedJARs) {
252                Collection<BundleFile> bundles;
253                if (scanForNestedJARs) {
254                    bundles = bundleFile.findNestedBundles(tmpDir);
255                } else { // use manifest to find nested jars
256                    bundles = bundleFile.getNestedBundles(tmpDir);
257                }
258                if (bundles == null || bundles.isEmpty()) {
259                    return;
260                }
261                for (BundleFile bundle : bundles) {
262                    if (bundle.getSymbolicName() != null) {
263                        visitBundle(bundle);
264                    } else {
265                        visitJar(bundle);
266                    }
267                }
268            }
269        }
270    }
271
272}