PageRenderTime 3ms CodeModel.GetById 25ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/guiced-jsf/src/main/java/org/openlogics/guice/jsf/core/GuiceJSFConfigurer.java

https://bitbucket.org/miguelvega/openapi
Java | 513 lines | 283 code | 77 blank | 153 comment | 65 complexity | 1739f2181e04b983eea5873bbc33314d MD5 | raw file
  1/**
  2 *
  3 */
  4package org.openlogics.guice.jsf.core;
  5
  6import com.google.common.base.Strings;
  7import com.google.inject.*;
  8import com.google.inject.servlet.GuiceServletContextListener;
  9import org.openlogics.guice.jsf.scope.JSFScopeBuilder;
 10import org.openlogics.guice.jsf.scope.ScopeHandler;
 11import org.openlogics.utils.reflect.Reflections;
 12import org.slf4j.Logger;
 13import org.slf4j.LoggerFactory;
 14
 15import javax.faces.bean.*;
 16import javax.servlet.ServletContext;
 17import javax.servlet.ServletContextEvent;
 18import java.lang.annotation.Annotation;
 19import java.net.JarURLConnection;
 20import java.net.URL;
 21import java.util.ArrayList;
 22import java.util.Enumeration;
 23import java.util.List;
 24import java.util.Set;
 25import java.util.jar.JarEntry;
 26import java.util.jar.JarFile;
 27
 28/**
 29 * The Guice module context provider loads all the required modules for DI for
 30 * the entire application
 31 *
 32 * @author Miguel Vega
 33 */
 34public class GuiceJSFConfigurer extends GuiceServletContextListener {
 35    /**
 36     * the context parameter for configuring the managed bean root package, all
 37     * the classes under this root pacakage will be scanned to match the managed
 38     * bean classes
 39     */
 40    public static final String GUICE_JSF_MB_ROOT_PACKAGE_CONTEXT_PARAMETER = "guice.jsf.MANAGED_BEAN_ROOT_PACKAGE";
 41
 42    /**
 43     * the guice jsf modules that needs to be loaded, separated by ','
 44     */
 45    public static final String GUICE_JSF_MODULES_CONTEXT_PARAMETER = "guice.jsf.MODULES";
 46
 47    /**
 48     * the guice jsf jars defines the name of jars that need to be scanned for
 49     * managed beans, separated by ','
 50     */
 51    public static final String GUICE_JSF_MB_JARS_CONTEXT_PARAMETER = "guice.jsf.MANAGED_BEAN_JAR_FILES";
 52
 53    /**
 54     * the default WEB-INF classes folder
 55     */
 56    private static final String WEB_INF_CLASSES = "/WEB-INF/classes/";
 57
 58    /**
 59     * the default WEB-INF lib folder
 60     */
 61    private static final String WEB_INF_LIB = "/WEB-INF/lib/";
 62
 63    /*
 64     * the logger instance
 65     */
 66    private static Logger logger = LoggerFactory.getLogger(GuiceJSFConfigurer.class);
 67
 68    /**
 69     * the instance of the servlet context
 70     */
 71    private ServletContext servletContext;
 72
 73    /**
 74     * get the servlet context
 75     *
 76     * @return
 77     */
 78    final ServletContext getServletContext() {
 79        return this.servletContext;
 80    }
 81
 82    /**
 83     * override the super class to get the servlet context reference
 84     */
 85    @Override
 86    public final void contextInitialized(ServletContextEvent servletContextEvent) {
 87        if (logger.isDebugEnabled()) {
 88            logger
 89                    .debug("ServletContext initialized, set the servletContext referece.");
 90        }
 91
 92        // store the servlet context reference as the local variable
 93        this.servletContext = servletContextEvent.getServletContext();
 94
 95        // scan the managed bean
 96        this.scanManagedBeans();
 97
 98        // invoke the base method to enable injector creation
 99        super.contextInitialized(servletContextEvent);
100    }
101
102    /*
103     * @see com.google.inject.servlet.GuiceServletContextListener#getInjector()
104     */
105    @Override
106    protected final Injector getInjector() {
107        // load existing modules
108        List<Module> modules = loadModules();
109
110        final ManagedBeanConfig config = (ManagedBeanConfig) getServletContext()
111                .getAttribute(ManagedBeanConfig.KEY);
112
113        // create the scope builder
114        final JSFScopeBuilder builder = new JSFScopeBuilder();
115
116        // create the managed bean module
117        Module managedBeanModule = new AbstractModule() {
118            @Override
119            protected void configure() {
120                // create managed bean scope modules
121                List<ManagedBeanMetadata> allBeans = config
122                        .getManagedBeanMetadata();
123
124                // go through all the defined managed bean
125                for (ManagedBeanMetadata each : allBeans) {
126                    // create the custom scope instance
127                    Scope customScope = builder.build(each.getBeanName(), each
128                            .getScope());
129
130                    // bind this bean class to the custom scope instance
131                    bind(each.getBeanClass()).in(customScope);
132
133                    if (logger.isDebugEnabled()) {
134                        logger.debug("Prepare Guice Injector->Bind JSF bean [name:'" + each.getBeanName() + "',scope:'" + each.getScope().getName() + "',class:'" + each.getBeanClass().getName() + "'] in Guice custom Scope '" + customScope.getClass().getName());
135                    }
136                }
137            }
138        };
139
140        // add the managed bean module
141        modules.add(managedBeanModule);
142
143        // create the injector
144        Injector injector = Guice.createInjector(modules);
145
146        //create the eager managed bean in application scope
147        createEagerApplicationScopeManagedBean(config, injector);
148
149        //return the injector
150        return injector;
151    }
152
153    /**
154     * create all the managed beans in application scope which
155     * its eager attribute set to true
156     *
157     * @param config
158     * @param injector
159     */
160    private void createEagerApplicationScopeManagedBean(
161            final ManagedBeanConfig config, Injector injector) {
162        List<ManagedBeanMetadata> applicationBeans = config
163                .getManagedBeanMetadata(ApplicationScoped.class);
164
165        for (ManagedBeanMetadata each : applicationBeans) {
166            if (each.isEager() == true) {
167                if (logger.isDebugEnabled()) {
168                    logger.debug("eagerly create the bean '" + each.getBeanName()
169                            + "' in application scope.");
170                }
171
172                //create the bean instance
173                Object beanInstance = injector.getInstance(each.getBeanClass());
174
175                //add it into the application scope
176                getServletContext().setAttribute(each.getBeanName(),
177                        beanInstance);
178            }
179        }
180    }
181
182    /**
183     * subclass can override this method to configure extra modules in code
184     * rather than from the GUICE_JSF_MODULES_CONTEXT_PARAMETER context
185     * parameter
186     *
187     * @return
188     */
189    protected List<Module> configureModules() {
190        return new ArrayList<Module>();
191    }
192
193    /**
194     * scan the managed beans
195     */
196    private void scanManagedBeans() {
197        String contextScanPathValue = getServletContext().getInitParameter(
198                GUICE_JSF_MB_ROOT_PACKAGE_CONTEXT_PARAMETER);
199
200        String rootPackage = contextScanPathValue.replace(".", "/") + "/";
201
202        // create the managed bean config to store all the managed beans
203        ManagedBeanConfig config = new ManagedBeanConfig();
204
205        // scan the web info classes to find managed beans
206        scanClasses(getServletContext(), WEB_INF_CLASSES + rootPackage, config);
207
208        // scan the web info libs to find managed beans
209        scanJars(getServletContext(), rootPackage, config);
210
211        // save the managed bean config into the servlet context
212        getServletContext().setAttribute(ManagedBeanConfig.KEY, config);
213    }
214
215    /**
216     * scan the web info jar
217     *
218     * @param servletContext
219     * @param contextScanPackage
220     */
221    private void scanJars(ServletContext servletContext,
222                          String contextScanPackage, ManagedBeanConfig config) {
223        List<JarFile> jars = findJars(servletContext, WEB_INF_LIB);
224        if (!jars.isEmpty()) {
225            for (JarFile jarFile : jars) {
226                scanJarFile(jarFile, contextScanPackage, config);
227            }
228        }
229    }
230
231    /**
232     * scan each jar file
233     *
234     * @param jarFile
235     * @param beanPackage
236     */
237    private void scanJarFile(JarFile jarFile, String beanPackage,
238                             ManagedBeanConfig config) {
239        Enumeration<JarEntry> entries = jarFile.entries();
240
241        while (entries.hasMoreElements()) {
242            // go to the next jar entry
243            JarEntry entry = entries.nextElement();
244
245            // only check the jar matches the beanPackage value
246            if (!entry.getName().startsWith(beanPackage)) {
247                continue;
248            }
249
250            if (entry.getName().endsWith(".class")) {
251                Class<?> clazz = extractClassFromPath(entry.getName());
252
253                if (clazz != null) {
254                    loadManagedBean(config, clazz);
255                }
256            }
257        }
258    }
259
260    /**
261     * load a managed bean metadata and put it into the managed bean config
262     *
263     * @param config
264     * @param clazz
265     */
266    private void loadManagedBean(ManagedBeanConfig config, Class<?> clazz) {
267        if (logger.isDebugEnabled()) {
268            logger.debug("loading managed bean from class " + clazz);
269        }
270
271        // get the @ManagedBean annotation from the bean class
272        ManagedBean managedBean = clazz.getAnnotation(ManagedBean.class);
273
274        if (managedBean != null) {
275            // get the scope annotation
276            Class<? extends Annotation> scopeAnnotation = getScopeAnnotation(clazz);
277
278            String beanName = managedBean.name();
279            if (Strings.isNullOrEmpty(beanName) == true) {
280                beanName = clazz.getSimpleName();
281                beanName = formatBeanClass(beanName);
282            }
283
284            // add to the managed bean config
285            config.addManagedBean(beanName, clazz, scopeAnnotation, managedBean
286                    .eager());
287        }
288    }
289
290    /**
291     * get the scope annotation class
292     *
293     * @param clazz
294     * @return
295     */
296    private Class<? extends Annotation> getScopeAnnotation(Class<?> clazz) {
297        Class<? extends Annotation> scope = null;
298
299        if (clazz.getAnnotation(RequestScoped.class) != null) {
300            scope = RequestScoped.class;
301        } else if (clazz.getAnnotation(SessionScoped.class) != null) {
302            scope = SessionScoped.class;
303        } else if (clazz.getAnnotation(ApplicationScoped.class) != null) {
304            scope = ApplicationScoped.class;
305        } else if (clazz.getAnnotation(ViewScoped.class) != null) {
306            scope = ViewScoped.class;
307        } else {
308            // find the annotation which has a meta annotation ScopeResolver
309            scope = Reflections.findAnnotation(clazz, ScopeHandler.class);
310        }
311
312        // if non scope is provided, the scope should be request
313        if (scope == null) {
314            scope = RequestScoped.class;
315        }
316        return scope;
317    }
318
319    /**
320     * format the bean name from the class name, make the first letter as lower
321     * case
322     *
323     * @param beanName
324     * @return
325     */
326    private String formatBeanClass(String beanName) {
327        String firstChar = String.valueOf(beanName.charAt(0)).toLowerCase();
328        return firstChar + beanName.substring(1);
329    }
330
331    /**
332     * find a proper jar file
333     *
334     * @param servletContext
335     * @param rootPath
336     * @return
337     */
338    private List<JarFile> findJars(ServletContext servletContext,
339                                   String rootPath) {
340        // get the jars provided by the init parameter
341        String jarsInitParameter = servletContext
342                .getInitParameter(GUICE_JSF_MB_JARS_CONTEXT_PARAMETER);
343
344        String[] jarNames = new String[]{};
345        if (jarsInitParameter != null) {
346            jarNames = jarsInitParameter.split(",");
347        }
348
349        Set<String> paths = servletContext.getResourcePaths(rootPath);
350        List<JarFile> jars = new ArrayList<JarFile>();
351
352        if (paths != null && !paths.isEmpty()) {
353            for (String path : paths) {
354                if (path.endsWith(".jar")) {
355                    boolean included = jarNames.length > 0 ? false : true;
356
357                    // search for the name match
358                    for (String jarName : jarNames) {
359                        if (path.toLowerCase().endsWith(jarName.toLowerCase())) {
360                            included = true;
361                            break;
362                        }
363                    }
364
365                    if (included) {
366                        if (logger.isDebugEnabled()) {
367                            logger.debug("Scan library '" + path
368                                    + "' for managed beans");
369                        }
370
371                        try {
372                            String jarUrlPath = "jar:"
373                                    + servletContext.getResource(path) + "!/";
374                            URL url = new URL(jarUrlPath);
375                            JarFile jarFile = ((JarURLConnection) url
376                                    .openConnection()).getJarFile();
377
378                            jars.add(jarFile);
379                        } catch (Exception e) {
380                        }
381                    }
382                }
383            }
384        }
385
386        return jars;
387    }
388
389    /**
390     * scan the web inf classes directory
391     *
392     * @param servletContext
393     * @param rootPath
394     */
395    private void scanClasses(ServletContext servletContext, String rootPath,
396                             ManagedBeanConfig config) {
397        Set<String> paths = servletContext.getResourcePaths(rootPath);
398
399        for (String path : paths) {
400            if (path.endsWith("/")) {
401                scanClasses(servletContext, path, config);
402            } else {
403                if (path.endsWith(".class")) {
404                    Class<?> clazz = extractClassFromPath(path);
405                    if (clazz != null) {
406                        loadManagedBean(config, clazz);
407                    }
408                }
409            }
410        }
411    }
412
413    /**
414     * convert a package path to a class
415     *
416     * @param path
417     * @return
418     */
419    private Class<?> extractClassFromPath(String path) {
420        String classNamePath = null;
421
422        if (path.indexOf(WEB_INF_CLASSES) != -1)
423            classNamePath = path.substring(WEB_INF_CLASSES.length());
424        else
425            classNamePath = path;
426
427        classNamePath = classNamePath
428                .substring(0, (classNamePath.length() - 6));
429        classNamePath = classNamePath.replaceAll("/", ".");
430
431        try {
432            return Class.forName(classNamePath);
433        } catch (ClassNotFoundException e) {
434            e.printStackTrace();
435        }
436        return null;
437    }
438
439    /**
440     * load the modules from the context parameter
441     *
442     * @return
443     */
444    private List<Module> loadModules() {
445        // get the list of modules
446        List<Module> modules = new ArrayList<Module>();
447
448        // get the modules init parameter
449        String modulesInitParameter = servletContext
450                .getInitParameter(GUICE_JSF_MODULES_CONTEXT_PARAMETER);
451
452        if (logger.isDebugEnabled()) {
453            logger.debug("loading the guice modules from the "
454                    + GUICE_JSF_MODULES_CONTEXT_PARAMETER
455                    + " context parameter as '" + modulesInitParameter + "'");
456        }
457
458        if (modulesInitParameter != null) {
459            String[] moduleNames = modulesInitParameter.split(",");
460
461            for (String moduleName : moduleNames) {
462                String trimmedModuleName = moduleName.trim();
463
464                if (Strings.isNullOrEmpty(trimmedModuleName) == false) {
465                    if (logger.isDebugEnabled()) {
466                        logger.debug("parsing the guice modules for '"
467                                + trimmedModuleName + "'");
468                    }
469
470                    try {
471                        // get the module form the name
472                        Module module = (Module) Class.forName(
473                                trimmedModuleName).newInstance();
474
475                        // add the module
476                        modules.add(module);
477                    } catch (Exception e) {
478                        // error loading the modules
479                        if (logger.isDebugEnabled()) {
480                            logger.error("Skip module '" + trimmedModuleName
481                                    + "', could not load it due to "
482                                    + e.getMessage(), e);
483                        }
484
485                        throw new RuntimeException(e);
486                    }
487                } else {
488                    if (logger.isDebugEnabled()) {
489                        logger.debug("skip the empty module entry.");
490                    }
491                }
492            }
493
494            // load modules from the subclasses if possible
495            modules.addAll(configureModules());
496
497            // throw exception, no module has been provided
498            if (modules.size() == 0) {
499                throw new RuntimeException(
500                        "No guice module has been provided by context parameter '"
501                                + GUICE_JSF_MODULES_CONTEXT_PARAMETER
502                                + "', verify your settings in your application. If you want to disable Guice in JSF application, simply remove the GuiceServletContextListener in web.xml.");
503            }
504        } else {
505            // throw exception, no module has been provided
506            throw new RuntimeException(
507                    "No guice module has been provided by context parameter '"
508                            + GUICE_JSF_MODULES_CONTEXT_PARAMETER
509                            + "', verify your settings in your application. If you want to disable Guice in JSF application, simply remove the GuiceServletContextListener in web.xml.");
510        }
511        return modules;
512    }
513}