001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 */
012package org.nuxeo.ecm.webengine.jaxrs;
013
014import java.lang.reflect.Array;
015import java.util.ArrayList;
016
017import org.osgi.framework.Bundle;
018
019/**
020 * Some helper methods for parsing configuration and loading contributed servlets or filters.
021 *
022 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
023 */
024public class Utils {
025
026    /**
027     * Load classes from a list of class references given as a comma separated string list.
028     *
029     * @param classRefs the string containing the list of class references
030     * @return an array of the loaded classes
031     * @throws ClassNotFoundException
032     * @throws BundleNotFoundException
033     */
034    public static Class<?>[] loadClasses(String classRefs) throws ClassNotFoundException, BundleNotFoundException {
035        return loadClasses(classRefs, ',');
036    }
037
038    /**
039     * Load classes from a list of class references given as a 'sep' separated string list.
040     *
041     * @param classRefs the string containing the list of class references
042     * @param sep the separator character used to separate class references in the string.
043     * @return an array of the loaded classes
044     * @throws ClassNotFoundException
045     * @throws BundleNotFoundException
046     */
047    public static Class<?>[] loadClasses(String classRefs, char sep) throws ClassNotFoundException,
048            BundleNotFoundException {
049        StringBuilder buf = null;
050        ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
051        char[] chars = classRefs.toCharArray();
052        for (int i = 0; i < chars.length; i++) {
053            char c = chars[i];
054            if (c <= ' ') {
055                continue;
056            } else if (c == sep) {
057                if (buf != null) {
058                    classes.add(loadClass(buf.toString()));
059                    buf = null;
060                }
061            } else {
062                if (buf == null) {
063                    buf = new StringBuilder();
064                }
065                buf.append(c);
066            }
067        }
068
069        if (buf != null) {
070            classes.add(loadClass(buf.toString()));
071        }
072
073        return classes.toArray(new Class<?>[classes.size()]);
074    }
075
076    /**
077     * Get class instances for the given class references string
078     *
079     * @param <T>
080     * @param componentType the type of the expected array component
081     * @param classRefs
082     * @return
083     * @see {@link #loadClasses(String)}
084     */
085    public static <T> T[] newInstances(Class<T> componentType, String classRefs) throws ReflectiveOperationException,
086            BundleNotFoundException {
087        return newInstances(componentType, classRefs, ',');
088    }
089
090    /**
091     * Get class instances for the given class references string
092     *
093     * @param <T>
094     * @param componentType
095     * @param classRefs
096     * @param sep
097     * @return
098     * @see {@link #loadClasses(String, char)}
099     */
100    @SuppressWarnings("unchecked")
101    public static <T> T[] newInstances(Class<T> componentType, String classRefs, char sep)
102            throws ReflectiveOperationException, BundleNotFoundException {
103        Class<?>[] classes = loadClasses(classRefs, sep);
104        T[] ar = (T[]) Array.newInstance(componentType, classes.length);
105        for (int i = 0; i < classes.length; i++) {
106            ar[i] = (T) classes[i].newInstance();
107        }
108        return ar;
109    }
110
111    /**
112     * Load a class from a class reference string. The class reference string is in the format:
113     * <code>bundleSymbolicName:className</code> or <code>className</code>. If no bundle symbolic name is given the
114     * class will be loaded using the class loader of the {@link Utils} class.
115     * <p>
116     * The bundle will be resolved to the last version of the bundle (in case when different bundle versions are found)
117     *
118     * @param classRef
119     * @return
120     */
121    public static Class<?> loadClass(String classRef) throws ClassNotFoundException, BundleNotFoundException {
122        int i = classRef.indexOf(':');
123        if (i == -1) {
124            // use the current bundle class loader
125            return Activator.getInstance().getContext().getBundle().loadClass(classRef.trim());
126        } else {
127            return loadClass(classRef.substring(0, i).trim(), classRef.substring(i + 1).trim());
128        }
129    }
130
131    /**
132     * Get a class proxy reference for the given class reference
133     *
134     * @param classRef
135     * @return
136     */
137    public static ClassRef getClassRef(String classRef) throws ClassNotFoundException, BundleNotFoundException {
138        return getClassRef(classRef, null);
139    }
140
141    public static ClassRef getClassRef(String classRef, Bundle bundle) throws ClassNotFoundException,
142            BundleNotFoundException {
143        int i = classRef.indexOf(':');
144        if (i == -1) {
145            // use the current bundle class loader
146            if (bundle == null) {
147                bundle = Activator.getInstance().getContext().getBundle();
148            }
149            return new ClassRef(bundle, bundle.loadClass(classRef.trim()));
150        } else {
151            String bundleId = classRef.substring(0, i).trim();
152            String className = classRef.substring(i + 1).trim();
153            Bundle[] bundles = Activator.getInstance().getPackageAdmin().getBundles(bundleId, null);
154            if (bundles != null) {
155                return new ClassRef(bundles[0], bundles[0].loadClass(className));
156            } else {
157                throw new BundleNotFoundException(bundleId);
158            }
159        }
160    }
161
162    /**
163     * Load a class given the owner bundle and the class name.
164     * <p>
165     * The bundle will be resolved to the last version of the bundle (in case when different bundle versions are found)
166     *
167     * @param bundleId
168     * @param className
169     * @return
170     * @throws ClassNotFoundException
171     * @throws BundleNotFoundException
172     */
173    public static Class<?> loadClass(String bundleId, String className) throws ClassNotFoundException,
174            BundleNotFoundException {
175        Bundle[] bundles = Activator.getInstance().getPackageAdmin().getBundles(bundleId, null);
176        if (bundles != null) {
177            return bundles[0].loadClass(className);
178        } else {
179            throw new BundleNotFoundException(bundleId);
180        }
181    }
182
183    /**
184     * Create a new object of the given class in the given bundle. The class should provide a no-args empty constructor.
185     * <p>
186     * The bundle will be resolved to the last version of the bundle (in case when different bundle versions are found)
187     *
188     * @param bundleId
189     * @param className
190     * @return
191     * @see {@link #loadClass(String, String)}
192     */
193    public static Object newInstance(String bundleId, String className) throws ReflectiveOperationException,
194            BundleNotFoundException {
195        return loadClass(bundleId, className).newInstance();
196    }
197
198    /**
199     * Create a new object of the given a class reference.
200     * <p>
201     * The bundle will be resolved to the last version of the bundle (in case when different bundle versions are found)
202     *
203     * @param classRef
204     * @return
205     * @see {@link #loadClass(String, String)}
206     */
207    public static Object newInstance(String classRef) throws ReflectiveOperationException, BundleNotFoundException {
208        return loadClass(classRef).newInstance();
209    }
210
211    public static class ClassRef {
212        protected Bundle bundle;
213
214        protected Class<?> clazz;
215
216        public ClassRef(Bundle bundle, Class<?> clazz) {
217            this.bundle = bundle;
218            this.clazz = clazz;
219        }
220
221        public Class<?> get() {
222            return clazz;
223        }
224
225        public Bundle bundle() {
226            return bundle;
227        }
228
229        public Object newInstance() throws ReflectiveOperationException {
230            return clazz.newInstance();
231        }
232
233        @Override
234        public String toString() {
235            if (bundle != null) {
236                return bundle.getSymbolicName() + ":" + clazz.getName();
237            }
238            return clazz.getName();
239        }
240
241    }
242
243}