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 *     bstefanescu
018 */
019package org.nuxeo.osgi.application.client;
020
021import java.io.File;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.StringTokenizer;
030
031import org.nuxeo.common.Environment;
032import org.nuxeo.osgi.BundleFile;
033import org.nuxeo.osgi.BundleImpl;
034import org.nuxeo.osgi.DirectoryBundleFile;
035import org.nuxeo.osgi.JarBundleFile;
036import org.nuxeo.osgi.OSGiAdapter;
037import org.nuxeo.osgi.SystemBundle;
038import org.nuxeo.osgi.SystemBundleFile;
039import org.osgi.framework.BundleException;
040import org.osgi.framework.FrameworkEvent;
041
042/**
043 * Nuxeo Runtime launcher.
044 * <p>
045 * This launcher assumes all bundles are already on the classpath.
046 *
047 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
048 */
049public class NuxeoApp {
050
051    protected final ClassLoader loader;
052
053    protected final Environment env;
054
055    private OSGiAdapter osgi;
056
057    public static ClassLoader getDefaultClassLoader() {
058        ClassLoader cl = Thread.currentThread().getContextClassLoader();
059        return cl == null ? NuxeoApp.class.getClassLoader() : cl;
060    }
061
062    public NuxeoApp() {
063        this(new File("."), getDefaultClassLoader());
064    }
065
066    public NuxeoApp(File home) {
067        this(home, getDefaultClassLoader());
068    }
069
070    public NuxeoApp(File home, ClassLoader loader) {
071        this.loader = loader;
072        env = new Environment(home);
073        Environment.setDefault(env);
074    }
075
076    public ClassLoader getLoader() {
077        return loader;
078    }
079
080    public Environment getEnvironment() {
081        return env;
082    }
083
084    public void deployBundles(String bundlePath) throws BundleException, IOException {
085        deployBundles(getBundleFiles(new File("."), bundlePath, ":"));
086    }
087
088    public void deployBundles(File baseDir, String bundlePath) throws BundleException, IOException {
089        deployBundles(getBundleFiles(baseDir, bundlePath, ":"));
090    }
091
092    public synchronized void deployBundles(Collection<File> files) throws BundleException, IOException {
093        if (!isStarted()) {
094            throw new IllegalStateException("Framework not started");
095        }
096        for (File file : files) {
097            deployBundle(file);
098        }
099    }
100
101    public synchronized void deployBundle(File file) throws BundleException, IOException {
102        if (!isStarted()) {
103            throw new IllegalStateException("Framework not started");
104        }
105        if (!file.getPath().endsWith(".jar")) {
106            return; // not a valid bundle
107        }
108        BundleFile bf = file.isDirectory() ? new DirectoryBundleFile(file) : new JarBundleFile(file);
109        try {
110            BundleImpl bundle = new BundleImpl(osgi, bf, loader);
111            if (bundle.getSymbolicName() != null) {
112                osgi.install(bundle);
113            }
114        } catch (NullPointerException t) {
115            // do nothing: may happen with non OSGi manifests
116            // System.out.println("Ignore: "+file);
117        }
118    }
119
120    public synchronized void start() {
121        if (osgi != null) {
122            throw new IllegalStateException("Nuxeo Runtime already started");
123        }
124        osgi = new OSGiAdapter(env.getHome(), env.getData(), env.getProperties());
125    }
126
127    public synchronized boolean isStarted() {
128        return osgi != null;
129    }
130
131    public synchronized OSGiAdapter getOsgi() {
132        return osgi;
133    }
134
135    public synchronized void shutdown() throws IOException {
136        if (osgi == null) {
137            throw new IllegalStateException("Nuxeo Runtime not started");
138        }
139        osgi.shutdown();
140        osgi = null;
141    }
142
143    public static Collection<File> getBundleFiles(File baseDir, String bundles, String delim) throws IOException {
144        Collection<File> result = new LinkedHashSet<File>();
145        StringTokenizer tokenizer = new StringTokenizer(bundles, delim == null ? " \t\n\r\f" : delim);
146        while (tokenizer.hasMoreTokens()) {
147            String tok = tokenizer.nextToken();
148            List<File> files = expandFiles(baseDir, tok);
149            for (File file : files) {
150                result.add(file.getCanonicalFile());
151            }
152        }
153        return result;
154    }
155
156    public static File makeFile(File baseDir, String path) {
157        if (path.startsWith("/")) {
158            return new File(path);
159        }
160        return new File(baseDir, path);
161    }
162
163    public static List<File> expandFiles(File baseDir, String line) {
164        int p = line.lastIndexOf("/");
165        String fileName = null;
166        if (p > -1) {
167            fileName = line.substring(p + 1);
168            baseDir = makeFile(baseDir, line.substring(0, p));
169        } else {
170            fileName = line;
171        }
172        if (fileName.length() == 0) {
173            return Arrays.asList(baseDir.listFiles());
174        }
175        p = fileName.indexOf("*");
176        if (p == -1) {
177            return Collections.singletonList(makeFile(baseDir, fileName));
178        } else if (p == 0) {
179            String suffix = fileName.substring(p + 1);
180            List<File> result = new ArrayList<File>();
181            String[] names = baseDir.list();
182            if (names != null) {
183                for (String name : names) {
184                    if (name.endsWith(suffix)) {
185                        result.add(makeFile(baseDir, name));
186                    }
187                }
188            }
189            return result;
190        } else if (p == fileName.length() - 1) {
191            String prefix = fileName.substring(0, p);
192            List<File> result = new ArrayList<File>();
193            String[] names = baseDir.list();
194            if (names != null) {
195                for (String name : baseDir.list()) {
196                    if (name.startsWith(prefix)) {
197                        result.add(makeFile(baseDir, name));
198                    }
199                }
200            }
201            return result;
202        } else {
203            String prefix = fileName.substring(0, p);
204            String suffix = fileName.substring(p + 1);
205            List<File> result = new ArrayList<File>();
206            String[] names = baseDir.list();
207            if (names != null) {
208                for (String name : names) {
209                    if (name.startsWith(prefix) && name.endsWith(suffix)) {
210                        result.add(makeFile(baseDir, name));
211                    }
212                }
213            }
214            return result;
215        }
216    }
217
218    public void fireFrameworkStarted() throws BundleException {
219        if (osgi.getSystemBundle() == null) {
220            SystemBundleFile file;
221            try {
222                file = new SystemBundleFile(osgi.getWorkingDir());
223            } catch (IOException e) {
224                throw new BundleException("Cannot create system bundle file", e);
225            }
226            osgi.setSystemBundle(new SystemBundle(osgi, file, loader));
227        }
228        osgi.fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, osgi.getSystemBundle(), null));
229    }
230
231}