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.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     * @param bundleFiles
113     * @throws BundleException
114     */
115    public void installAll(Collection<BundleFile> bundleFiles) throws BundleException {
116        for (BundleFile bundleFile : bundleFiles) {
117            installBundle(bundleFile);
118        }
119    }
120
121    /**
122     * Installs all bundles found in the given directory.
123     * <p>
124     * The directory is recursively searched for bundles.
125     *
126     * @param root the tree root
127     */
128    public void install(File root) {
129        BundleInstaller callback = new BundleInstaller();
130        BundleWalker visitor = new BundleWalker(callback, patterns);
131        visitor.visit(root);
132    }
133
134    /**
135     * Scans the given directory for OSGi bundles and regular JARs and fills the given lists appropriately.
136     *
137     * @param root the directory to recursively scan
138     * @param bundles the list to fill with found bundles
139     * @param ljars the list to fill with found jars
140     */
141    public void scan(File root, List<BundleFile> bundles, List<BundleFile> ljars) {
142        BundleFileScanner callback = new BundleFileScanner(bundles, ljars);
143        BundleWalker visitor = new BundleWalker(callback, patterns);
144        visitor.visit(root);
145    }
146
147    /**
148     * Installs bundles as they are discovered by the bundle visitor.
149     *
150     * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
151     */
152    public class BundleInstaller extends DefaultCallback {
153
154        @Override
155        public void visitBundle(BundleFile bundleFile) throws IOException {
156            loadBundle(bundleFile);
157            visitNestedBundles(bundleFile);
158        }
159
160        @Override
161        public void visitJar(BundleFile bundleFile) throws IOException {
162            loadJAR(bundleFile);
163            visitNestedBundles(bundleFile);
164        }
165
166    }
167
168    public class BundleFileScanner extends DefaultCallback {
169
170        final List<BundleFile> bundles;
171
172        final List<BundleFile> jars;
173
174        public BundleFileScanner(List<BundleFile> bundles, List<BundleFile> jars) {
175            this.bundles = bundles;
176            this.jars = jars;
177        }
178
179        @Override
180        public void visitBundle(BundleFile bundleFile) throws IOException {
181            bundles.add(bundleFile);
182            visitNestedBundles(bundleFile);
183        }
184
185        @Override
186        public void visitJar(BundleFile bundleFile) throws IOException {
187            jars.add(bundleFile);
188            visitNestedBundles(bundleFile);
189        }
190
191        public List<BundleFile> getBundles() {
192            return bundles;
193        }
194
195        public List<BundleFile> getJARs() {
196            return jars;
197        }
198
199    }
200
201    public class BundleFileLoader extends DefaultCallback {
202
203        final List<BundleFile> bundles;
204
205        final List<BundleFile> jars;
206
207        public BundleFileLoader(List<BundleFile> bundles, List<BundleFile> jars) {
208            this.bundles = bundles;
209            this.jars = jars;
210        }
211
212        @Override
213        public void visitBundle(BundleFile bundleFile) throws IOException {
214            // System.out.println(">>>> FOUND BUNDLE: "+bundleFile.getFileName());
215            loadBundle(bundleFile);
216            bundles.add(bundleFile);
217            visitNestedBundles(bundleFile);
218        }
219
220        @Override
221        public void visitJar(BundleFile bundleFile) throws IOException {
222            // System.out.println(">>>> FOUND JAR: "+bundleFile.getFileName());
223            loadJAR(bundleFile);
224            jars.add(bundleFile);
225            visitNestedBundles(bundleFile);
226        }
227
228        public List<BundleFile> getBundles() {
229            return bundles;
230        }
231
232        public List<BundleFile> getJARs() {
233            return jars;
234        }
235
236    }
237
238    public abstract class DefaultCallback implements BundleWalker.Callback {
239
240        @Override
241        public void visitBundle(BundleFile bundleFile) throws IOException {
242            visitNestedBundles(bundleFile);
243        }
244
245        @Override
246        public void visitJar(BundleFile bundleFile) throws IOException {
247            visitNestedBundles(bundleFile);
248        }
249
250        public void visitNestedBundles(BundleFile bundleFile) throws IOException {
251            if (bundleFile instanceof NestedJarBundleFile) {
252                return; // do not allows more than one level of nesting
253            }
254            if (extractNestedJARs) {
255                Collection<BundleFile> bundles;
256                if (scanForNestedJARs) {
257                    bundles = bundleFile.findNestedBundles(tmpDir);
258                } else { // use manifest to find nested jars
259                    bundles = bundleFile.getNestedBundles(tmpDir);
260                }
261                if (bundles == null || bundles.isEmpty()) {
262                    return;
263                }
264                for (BundleFile bundle : bundles) {
265                    if (bundle.getSymbolicName() != null) {
266                        visitBundle(bundle);
267                    } else {
268                        visitJar(bundle);
269                    }
270                }
271            }
272        }
273    }
274
275}