/guiced-jsf/src/main/java/org/openlogics/guice/jsf/core/GuiceJSFConfigurer.java
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}