001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    package org.granite.osgi.classloader;
021    
022    import java.util.Enumeration;
023    import java.util.HashSet;
024    import java.util.Iterator;
025    import java.util.Set;
026    
027    import org.granite.logging.Logger;
028    import org.granite.messaging.service.annotations.RemoteDestination;
029    import org.osgi.framework.Bundle;
030    
031    /**
032     * Granite DataService classloader
033     * scan packages then load qualified GDS classes.
034     * @author <a href="mailto:gembin@gmail.com">gembin@gmail.com</a>
035     * @since 1.1.0
036     */
037    public class ServiceClassLoader {
038            private static final Logger log=Logger.getLogger(ServiceClassLoader.class);
039            private static final String CLASS_SUFFIX = ".class";
040            private Set<String> classesSet = new HashSet<String>();
041            /**
042             * a Bundle which is used to load classes
043             */
044            private Bundle bundle;
045            public void setBundle(Bundle bundle) {
046                    this.bundle = bundle;
047            }
048            /**
049             * @param className
050             * @return path of a class
051             */
052            private static String packageForPath(String className) {
053                    return className.replace('.', '/');
054            }
055            /**
056             * @param path
057             * @return package of a class
058             */
059            private static String pathForPackage(String path) {
060                    return path.replace('/', '.').replace('\\', '.');
061            }
062            /**
063             * resolve package to find all the classes in a specified package of a bundle
064             * @param packageNamePath
065             * @param recursive
066             */
067            private void resolvePackage(String packageNamePath,boolean recursive){
068                    //if(log.isInfoEnabled())
069                    //  log.info("Resolving..."+packageNamePath);
070                    @SuppressWarnings("unchecked")
071                    Enumeration<String> en = bundle.getEntryPaths(packageNamePath);
072                    if (en != null) {
073                            while (en.hasMoreElements()) {
074                                    String entryPath = en.nextElement();
075                                    //recursive subpackages if wildcard is presented
076                                    if(recursive && entryPath.endsWith("/")){ 
077                                            resolvePackage(entryPath,recursive);
078                                    }else if(entryPath.endsWith(CLASS_SUFFIX)){
079                                            String className = entryPath.substring(0,entryPath.length()- CLASS_SUFFIX.length());
080                                            classesSet.add(pathForPackage(className));
081                                    }
082                            }
083                    } 
084            }
085            /**
086             * @param className
087             * @return valid Service class annotated with @RemoteDestination
088             */
089            public Class<?> loadClass(String className){
090                    try {
091                            Class<?> clazz = bundle.loadClass(className);
092                            if (clazz.isAnnotationPresent(RemoteDestination.class)) {
093                                    if(log.isInfoEnabled())
094                                      log.info(clazz.toString() + " is a valid GDS Service");
095                                    return clazz;
096                            } 
097                    } catch (ClassNotFoundException e) {
098                            e.printStackTrace();
099                    }
100                    return null;
101            }
102            /**
103             * Scan the packages and load all the qualified GraniteDS classes
104             * @param packages
105             * @return a set of valid Service classes annotated with @RemoteDestination
106             */
107            public Set<Class<?>> loadClasses(String[] packages) {
108                    Set<Class<?>> classes = new HashSet<Class<?>>();
109                    if (packages != null){
110                            for (int i = 0; i < packages.length; i++) {
111                                    String packageName = packages[i];
112                                    if (bundle != null) {
113                                            boolean recursive=packageName.endsWith("*");
114                                            if(recursive)
115                                                    packageName=packageName.substring(0, packageName.length()-2);//remove wildcard '*'
116                                            resolvePackage(packageForPath(packageName),recursive);
117                                            Iterator<String> it=classesSet.iterator();
118                                            while(it.hasNext()){
119                                                    Class<?> clazz=null;
120                                                    try {
121                                                            clazz = bundle.loadClass(it.next());
122                                                            if (clazz!=null && clazz.isAnnotationPresent(RemoteDestination.class)) {
123                                                                    if(log.isInfoEnabled())
124                                                                            log.info(clazz.toString() + " is a valid GDS Service");
125                                                                    classes.add(clazz);
126                                                            } 
127                                                    } catch (ClassNotFoundException e) {
128                                                            log.error("Service class not found", e);
129                                                    }
130                                            }
131                                    } else {
132                                            if(log.isInfoEnabled()) 
133                                                    log.info("Bundle is not specified, cannot load classes!!");
134                                    }
135                            }
136                    }
137                    return classes;
138            }
139    }