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