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