You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

advanced-global.asciidoc 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. ---
  2. title: Accessing Session-Global Data
  3. order: 15
  4. layout: page
  5. ---
  6. [[advanced.global]]
  7. = Accessing Session-Global Data
  8. __This section is mostly up-to-date with Vaadin 7, but has some information
  9. which still needs to be updated.__
  10. Applications typically need to access some objects from practically all user
  11. interface code, such as a user object, a business data model, or a database
  12. connection. This data is typically initialized and managed in the UI class of
  13. the application, or in the session or servlet.
  14. For example, you could hold it in the UI class as follows:
  15. [source, java]
  16. ----
  17. class MyUI extends UI {
  18. UserData userData;
  19. public void init() {
  20. userData = new UserData();
  21. }
  22. public UserData getUserData() {
  23. return userData;
  24. }
  25. }
  26. ----
  27. Vaadin offers two ways to access the UI object: with [methodname]#getUI()#
  28. method from any component and the global [methodname]#UI.getCurrent()# method.
  29. The [methodname]#getUI()# works as follows:
  30. [source, java]
  31. ----
  32. data = ((MyUI)component.getUI()).getUserData();
  33. ----
  34. This does not, however work in many cases, because it requires that the
  35. components are attached to the UI. That is not the case most of the time when
  36. the UI is still being built, such as in constructors.
  37. [source, java]
  38. ----
  39. class MyComponent extends CustomComponent {
  40. public MyComponent() {
  41. // This fails with NullPointerException
  42. Label label = new Label("Country: " +
  43. getUI().getLocale().getCountry());
  44. setCompositionRoot(label);
  45. }
  46. }
  47. ----
  48. The global access methods for the currently served servlet, session, and UI
  49. allow an easy way to access the data:
  50. [source, java]
  51. ----
  52. data = ((MyUI) UI.getCurrent()).getUserData();
  53. ----
  54. [[advanced.global.passing.problem]]
  55. == The Problem
  56. The basic problem in accessing session-global data is that the
  57. [methodname]#getUI()# method works only after the component has been attached to
  58. the application. Before that, it returns [parameter]#null#. This is the case in
  59. constructors of components, such as a [classname]#CustomComponent#:
  60. Using a static variable or a singleton implemented with such to give a global
  61. access to user session data is not possible, because static variables are global
  62. in the entire web application, not just the user session. This can be handy for
  63. communicating data between the concurrent sessions, but creates a problem within
  64. a session.
  65. The data would be shared by all users and be reinitialized every time a new user
  66. opens the application.
  67. [[advanced.global.passing.solutions-overview]]
  68. == Overview of Solutions
  69. To get the application object or any other global data, you have the following
  70. solutions:
  71. * Pass a reference to the global data as a parameter
  72. * Initialize components in [methodname]#attach()# method
  73. * Initialize components in the [methodname]#enter()# method of the navigation view
  74. (if using navigation)
  75. * Store a reference to global data using the __ThreadLocal Pattern__
  76. Each solution is described in the following sections.
  77. [[advanced.global.passing]]
  78. == Passing References Around
  79. You can pass references to objects as parameters. This is the normal way in
  80. object-oriented programming.
  81. [source, java]
  82. ----
  83. class MyApplication extends Application {
  84. UserData userData;
  85. public void init() {
  86. Window mainWindow = new Window("My Window");
  87. setMainWindow(mainWindow);
  88. userData = new UserData();
  89. mainWindow.addComponent(new MyComponent(this));
  90. }
  91. public UserData getUserData() {
  92. return userData;
  93. }
  94. }
  95. class MyComponent extends CustomComponent {
  96. public MyComponent(MyApplication app) {
  97. Label label = new Label("Name: " +
  98. app.getUserData().getName());
  99. setCompositionRoot(label);
  100. }
  101. }
  102. ----
  103. If you need the reference in other methods, you either have to pass it again as
  104. a parameter or store it in a member variable.
  105. The problem with this solution is that practically all constructors in the
  106. application need to get a reference to the application object, and passing it
  107. further around in the classes is another hard task.
  108. [[advanced.global.attach]]
  109. == Overriding [methodname]#attach()#
  110. The [methodname]#attach()# method is called when the component is attached to
  111. the UI through containment hierarchy. The [methodname]#getUI()# method always
  112. works.
  113. [source, java]
  114. ----
  115. class MyComponent extends CustomComponent {
  116. public MyComponent() {
  117. // Must set a dummy root in constructor
  118. setCompositionRoot(new Label(""));
  119. }
  120. @Override
  121. public void attach() {
  122. Label label = new Label("Name: " +
  123. ((MyUI)component.getUI())
  124. .getUserData().getName());
  125. setCompositionRoot(label);
  126. }
  127. }
  128. ----
  129. While this solution works, it is slightly messy. You may need to do some
  130. initialization in the constructor, but any construction requiring the global
  131. data must be done in the [methodname]#attach()# method. Especially,
  132. [classname]#CustomComponent# requires that the
  133. [methodname]#setCompositionRoot()# method is called in the constructor. If you
  134. can't create the actual composition root component in the constructor, you need
  135. to use a temporary dummy root, as is done in the example above.
  136. Using [methodname]#getUI()# also needs casting if you want to use methods
  137. defined in your UI class.
  138. [[advanced.global.threadlocal]]
  139. == ThreadLocal Pattern
  140. ((("ThreadLocal pattern", id="term.advanced.global.threadlocal", range="startofrange")))
  141. Vaadin uses the ThreadLocal pattern for allowing global access to the
  142. [classname]#UI#, and [classname]#Page# objects of the currently processed server
  143. request with a static [methodname]#getCurrent()# method in all the respective
  144. classes. This section explains why the pattern is used in Vaadin and how it
  145. works. You may also need to reimplement the pattern for some purpose.
  146. The ThreadLocal pattern gives a solution to the global access problem by solving
  147. two sub-problems of static variables.
  148. As the first problem, assume that the servlet container processes requests for
  149. many users (sessions) sequentially. If a static variable is set in a request
  150. belonging one user, it could be read or re-set by the next incoming request
  151. belonging to another user. This can be solved by setting the global reference at
  152. the beginning of each HTTP request to point to data of the current user, as
  153. illustrated in Figure <<figure.advanced.global.threadlocal.sequentiality>>.
  154. [[figure.advanced.global.threadlocal.sequentiality]]
  155. .Switching a static (or ThreadLocal) reference during sequential processing of requests
  156. image::img/threadlocal-sequentiality-hi.png[]
  157. The second problem is that servlet containers typically do thread pooling with
  158. multiple worker threads that process requests. Therefore, setting a static
  159. reference would change it in all threads running concurrently, possibly just
  160. when another thread is processing a request for another user. The solution is to
  161. store the reference in a thread-local variable instead of a static. You can do
  162. so by using the [classname]#ThreadLocal# class in Java for the switch reference.
  163. [[figure.advanced.global.threadlocal.concurrency]]
  164. .Switching [classname]#ThreadLocal# references during concurrent processing of requests
  165. image::img/threadlocal-concurrency-hi.png[]
  166. (((range="endofrange", startref="term.advanced.global.threadlocal")))