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 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.osgi.application;
023
024import java.io.BufferedReader;
025import java.io.BufferedWriter;
026import java.io.File;
027import java.io.FileReader;
028import java.io.FileWriter;
029import java.io.IOException;
030import java.util.ArrayList;
031import java.util.List;
032import java.util.jar.JarFile;
033
034import org.nuxeo.osgi.BundleFile;
035import org.nuxeo.osgi.DirectoryBundleFile;
036import org.nuxeo.osgi.JarBundleFile;
037import org.osgi.framework.BundleException;
038
039/**
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042public class ApplicationBundleLoader {
043
044    protected StandaloneBundleLoader bundleLoader;
045
046    protected final StandaloneApplication app;
047
048    protected boolean useCache = false;
049
050    protected boolean extractNestedJARs = true;
051
052    protected boolean scanForNestedJARs = true;
053
054    public ApplicationBundleLoader(StandaloneApplication app) {
055        this(app, false);
056    }
057
058    public ApplicationBundleLoader(StandaloneApplication app, boolean useCache) {
059        this.app = app;
060        bundleLoader = new StandaloneBundleLoader(app);
061        this.useCache = useCache;
062    }
063
064    public void setScanForNestedJARs(boolean scanForNestedJARs) {
065        this.scanForNestedJARs = scanForNestedJARs;
066    }
067
068    public boolean getScanForNestedJARs() {
069        return scanForNestedJARs;
070    }
071
072    public void setExtractNestedJARs(boolean extractNestedJARs) {
073        this.extractNestedJARs = extractNestedJARs;
074    }
075
076    public boolean getExtractNestedJARs() {
077        return extractNestedJARs;
078    }
079
080    public void setUseCache(boolean useCache) {
081        this.useCache = useCache;
082    }
083
084    public boolean getUseCache() {
085        return useCache;
086    }
087
088    public StandaloneBundleLoader getBundleLoader() {
089        return bundleLoader;
090    }
091
092    public File getCacheFile() {
093        return new File(app.getDataDir(), "bundles.cache");
094    }
095
096    public ClassLoader loadBundles(List<File> classPath) throws IOException, BundleException {
097        // create the standalone loader
098        bundleLoader = new StandaloneBundleLoader(app, app.getSharedClassLoader());
099        Thread.currentThread().setContextClassLoader(bundleLoader.getSharedClassLoader().getLoader());
100
101        aboutToStartRuntime();
102        boolean scan = true;
103        File file = getCacheFile();
104        if (useCache) {
105            if (file.isFile()) { // use the cache
106                scan = false;
107                try {
108                    fastLoad(file);
109                } catch (IOException e) {
110                    scan = true;
111                } catch (BundleException e) {
112                    scan = true;
113                }
114            }
115        }
116        if (scan) {
117            List<BundleFile> bundles = new ArrayList<>();
118            List<BundleFile> jars = new ArrayList<>();
119            scanAndLoad(classPath, bundles, jars);
120            writeCache(file, bundles, jars);
121            app.installAll(bundles);
122        }
123        // that's all
124        runtimeStarted();
125        return bundleLoader.getSharedClassLoader().getLoader();
126    }
127
128    public void scanAndLoad(List<File> classPath, List<BundleFile> bundles, List<BundleFile> jars) {
129        bundleLoader.setScanForNestedJARs(scanForNestedJARs);
130        bundleLoader.setExtractNestedJARs(extractNestedJARs);
131
132        for (File file : classPath) {
133            if (file.isFile()) { // a JAR file
134                String name = file.getName();
135                if (!name.endsWith(".jar") || name.endsWith(".rar") || name.endsWith(".zip") || name.endsWith(".sar")) {
136                    continue;
137                }
138                try {
139                    JarFile jar = new JarFile(file);
140                    JarBundleFile bf = new JarBundleFile(jar);
141                    if (bf.getSymbolicName() != null) {
142                        bundles.add(bf);
143                    } else {
144                        jars.add(bf);
145                    }
146                } catch (IOException e) { // may be not a JAR
147                    continue;
148                }
149            } else if (file.isDirectory()) { // a file directory
150                try {
151                    DirectoryBundleFile bf = new DirectoryBundleFile(file);
152                    if (bf.getSymbolicName() != null) {
153                        bundles.add(bf);
154                    } else {
155                        jars.add(bf);
156                    }
157                } catch (IOException e) {
158                    continue;
159                }
160            }
161        }
162    }
163
164    public static void writeCache(File file, List<BundleFile> bundles, List<BundleFile> jars) throws IOException {
165        // write loaded bundles to the cache
166        BufferedWriter writer = null;
167        try {
168            writer = new BufferedWriter(new FileWriter(file));
169            for (BundleFile bf : bundles) {
170                writer.append(bf.getFile().getAbsolutePath());
171                writer.newLine();
172            }
173            writer.append("#");
174            writer.newLine();
175            for (BundleFile bf : jars) {
176                writer.append(bf.getFile().getAbsolutePath());
177                writer.newLine();
178            }
179        } finally {
180            if (writer != null) {
181                writer.close();
182            }
183        }
184    }
185
186    public void fastLoad(File file) throws IOException, BundleException {
187        BufferedReader reader = null;
188        List<BundleFile> bundles = new ArrayList<>();
189        try {
190            reader = new BufferedReader(new FileReader(file));
191            List<BundleFile> list = bundles;
192            while (true) {
193                String line = reader.readLine();
194                if (line == null) {
195                    break;
196                }
197                if (line.startsWith("#")) {
198                    list = null;
199                    continue;
200                }
201                BundleFile bf = null;
202                File f = new File(line.trim());
203                if (f.isDirectory()) {
204                    bf = new DirectoryBundleFile(f);
205                } else {
206                    bf = new JarBundleFile(f);
207                }
208                bundleLoader.loadJAR(bf);
209                if (list != null) {
210                    list.add(bf);
211                }
212            }
213        } finally {
214            if (reader != null) {
215                reader.close();
216            }
217        }
218        // install found bundles
219        app.installAll(bundles);
220    }
221
222    protected void aboutToStartRuntime() {
223        // do nothing
224    }
225
226    protected void runtimeStarted() {
227        // do nothing
228    }
229
230}