PageRenderTime 92ms CodeModel.GetById 25ms RepoModel.GetById 4ms app.codeStats 1ms

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