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.

VaadinPortletSession.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.LinkedHashSet;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import javax.portlet.ActionRequest;
  24. import javax.portlet.ActionResponse;
  25. import javax.portlet.EventRequest;
  26. import javax.portlet.EventResponse;
  27. import javax.portlet.MimeResponse;
  28. import javax.portlet.PortletConfig;
  29. import javax.portlet.PortletMode;
  30. import javax.portlet.PortletModeException;
  31. import javax.portlet.PortletResponse;
  32. import javax.portlet.PortletSession;
  33. import javax.portlet.PortletURL;
  34. import javax.portlet.RenderRequest;
  35. import javax.portlet.RenderResponse;
  36. import javax.portlet.ResourceRequest;
  37. import javax.portlet.ResourceResponse;
  38. import javax.portlet.StateAwareResponse;
  39. import javax.servlet.http.HttpSessionBindingListener;
  40. import javax.xml.namespace.QName;
  41. import com.vaadin.server.communication.PortletListenerNotifier;
  42. import com.vaadin.shared.Registration;
  43. import com.vaadin.ui.UI;
  44. import com.vaadin.util.CurrentInstance;
  45. /**
  46. * An implementation of {@link VaadinSession} for JSR-286 portlet environments.
  47. *
  48. * This is automatically registered as a {@link HttpSessionBindingListener} when
  49. * {@link PortletSession#setAttribute()} is called with the context as value.
  50. *
  51. * Only the documented parts of this class should be considered as stable public
  52. * API.
  53. *
  54. * Note also that some methods and/or nested interfaces might move to
  55. * {@link VaadinPortletService} in future minor or major versions of Vaadin. In
  56. * these cases, a deprecated redirection for backwards compatibility will be
  57. * used in VaadinPortletSession for a transition period.
  58. *
  59. * @since 7.0
  60. */
  61. @SuppressWarnings("serial")
  62. public class VaadinPortletSession extends VaadinSession {
  63. private final Set<PortletListener> portletListeners = new LinkedHashSet<>();
  64. private final Map<String, QName> eventActionDestinationMap = new HashMap<>();
  65. private final Map<String, Serializable> eventActionValueMap = new HashMap<>();
  66. private final Map<String, String> sharedParameterActionNameMap = new HashMap<>();
  67. private final Map<String, String> sharedParameterActionValueMap = new HashMap<>();
  68. /**
  69. * Create a portlet service session for the given portlet service.
  70. *
  71. * @param service
  72. * the portlet service to which the new session belongs
  73. */
  74. public VaadinPortletSession(VaadinPortletService service) {
  75. super(service);
  76. }
  77. /**
  78. * Returns the underlying portlet session.
  79. *
  80. * @return portlet session
  81. */
  82. public PortletSession getPortletSession() {
  83. WrappedSession wrappedSession = getSession();
  84. PortletSession session = ((WrappedPortletSession) wrappedSession)
  85. .getPortletSession();
  86. return session;
  87. }
  88. private PortletResponse getCurrentResponse() {
  89. VaadinPortletResponse currentResponse = (VaadinPortletResponse) CurrentInstance
  90. .get(VaadinResponse.class);
  91. if (currentResponse != null) {
  92. return currentResponse.getPortletResponse();
  93. } else {
  94. return null;
  95. }
  96. }
  97. /**
  98. * Returns the JSR-286 portlet configuration that provides access to the
  99. * portlet context and init parameters.
  100. *
  101. * @return portlet configuration
  102. */
  103. public PortletConfig getPortletConfig() {
  104. VaadinPortletResponse response = (VaadinPortletResponse) CurrentInstance
  105. .get(VaadinResponse.class);
  106. return response.getService().getPortlet().getPortletConfig();
  107. }
  108. /**
  109. * Adds a listener for various types of portlet requests.
  110. *
  111. * @param listener
  112. * to add
  113. * @since 8.0
  114. */
  115. public Registration addPortletListener(PortletListener listener) {
  116. portletListeners.add(listener);
  117. return () -> portletListeners.remove(listener);
  118. }
  119. /**
  120. * Removes a portlet request listener registered with
  121. * {@link #addPortletListener(PortletListener)}.
  122. *
  123. * @param listener
  124. * to remove
  125. * @deprecated Use a {@link Registration} object returned by
  126. * {@link #addPortletListener(PortletListener)} to remove a
  127. * listener
  128. */
  129. @Deprecated
  130. public void removePortletListener(PortletListener listener) {
  131. portletListeners.remove(listener);
  132. }
  133. /**
  134. * For internal use by the framework only - API subject to change.
  135. */
  136. public void firePortletRenderRequest(UI uI, RenderRequest request,
  137. RenderResponse response) {
  138. for (PortletListener l : new ArrayList<>(portletListeners)) {
  139. l.handleRenderRequest(request,
  140. new RestrictedRenderResponse(response), uI);
  141. }
  142. }
  143. /**
  144. * For internal use by the framework only - API subject to change.
  145. */
  146. public void firePortletActionRequest(UI uI, ActionRequest request,
  147. ActionResponse response) {
  148. String key = request.getParameter(ActionRequest.ACTION_NAME);
  149. if (eventActionDestinationMap.containsKey(key)) {
  150. // this action request is only to send queued portlet events
  151. response.setEvent(eventActionDestinationMap.get(key),
  152. eventActionValueMap.get(key));
  153. // cleanup
  154. eventActionDestinationMap.remove(key);
  155. eventActionValueMap.remove(key);
  156. } else if (sharedParameterActionNameMap.containsKey(key)) {
  157. // this action request is only to set shared render parameters
  158. response.setRenderParameter(sharedParameterActionNameMap.get(key),
  159. sharedParameterActionValueMap.get(key));
  160. // cleanup
  161. sharedParameterActionNameMap.remove(key);
  162. sharedParameterActionValueMap.remove(key);
  163. } else {
  164. // normal action request, notify listeners
  165. for (PortletListener l : new ArrayList<>(portletListeners)) {
  166. l.handleActionRequest(request, response, uI);
  167. }
  168. }
  169. }
  170. /**
  171. * For internal use by the framework only - API subject to change.
  172. */
  173. public void firePortletEventRequest(UI uI, EventRequest request,
  174. EventResponse response) {
  175. for (PortletListener l : new ArrayList<>(portletListeners)) {
  176. l.handleEventRequest(request, response, uI);
  177. }
  178. }
  179. /**
  180. * For internal use by the framework only - API subject to change.
  181. */
  182. public void firePortletResourceRequest(UI uI, ResourceRequest request,
  183. ResourceResponse response) {
  184. for (PortletListener l : new ArrayList<>(portletListeners)) {
  185. l.handleResourceRequest(request, response, uI);
  186. }
  187. }
  188. /**
  189. * Listener interface for the various types of JSR-286 portlet requests. The
  190. * listener methods are called by the request handler
  191. * {@link PortletListenerNotifier} after the session is locked and the
  192. * corresponding UI has been found (if already created) but before other
  193. * request processing takes place.
  194. *
  195. * Direct rendering of output is not possible in a portlet listener and the
  196. * JSR-286 limitations on allowed operations in each phase or portlet
  197. * request processing must be respected by the listeners.
  198. *
  199. * Note that internal action requests used by the framework to trigger
  200. * events or set shared parameters do not call the action request listener
  201. * but will result in a later event or render request that will trigger the
  202. * corresponding listener.
  203. */
  204. public interface PortletListener extends Serializable {
  205. public void handleRenderRequest(RenderRequest request,
  206. RenderResponse response, UI uI);
  207. public void handleActionRequest(ActionRequest request,
  208. ActionResponse response, UI uI);
  209. public void handleEventRequest(EventRequest request,
  210. EventResponse response, UI uI);
  211. public void handleResourceRequest(ResourceRequest request,
  212. ResourceResponse response, UI uI);
  213. }
  214. /**
  215. * Creates a new action URL.
  216. *
  217. * Creating an action URL is only supported when processing a suitable
  218. * request (render or resource request, including normal Vaadin UIDL
  219. * processing) and will return null if not processing a suitable request.
  220. *
  221. * @param action
  222. * the action parameter (javax.portlet.action parameter value in
  223. * JSR-286)
  224. * @return action URL or null if called outside a MimeRequest (outside a
  225. * UIDL request or similar)
  226. */
  227. public PortletURL generateActionURL(String action) {
  228. PortletURL url = null;
  229. PortletResponse response = getCurrentResponse();
  230. if (response instanceof MimeResponse) {
  231. url = ((MimeResponse) response).createActionURL();
  232. url.setParameter("javax.portlet.action", action);
  233. } else {
  234. return null;
  235. }
  236. return url;
  237. }
  238. /**
  239. * Sends a portlet event to the indicated destination.
  240. *
  241. * Internally, an action may be created and opened, as an event cannot be
  242. * sent directly from all types of requests.
  243. *
  244. * Sending portlet events from background threads is not supported.
  245. *
  246. * The event destinations and values need to be kept in the context until
  247. * sent. Any memory leaks if the action fails are limited to the session.
  248. *
  249. * Event names for events sent and received by a portlet need to be declared
  250. * in portlet.xml .
  251. *
  252. * @param uI
  253. * a window in which a temporary action URL can be opened if
  254. * necessary
  255. * @param name
  256. * event name
  257. * @param value
  258. * event value object that is Serializable and, if appropriate,
  259. * has a valid JAXB annotation
  260. */
  261. public void sendPortletEvent(UI uI, QName name, Serializable value)
  262. throws IllegalStateException {
  263. PortletResponse response = getCurrentResponse();
  264. if (response instanceof MimeResponse) {
  265. String actionKey = "" + System.currentTimeMillis();
  266. while (eventActionDestinationMap.containsKey(actionKey)) {
  267. actionKey += ".";
  268. }
  269. PortletURL actionUrl = generateActionURL(actionKey);
  270. if (actionUrl != null) {
  271. eventActionDestinationMap.put(actionKey, name);
  272. eventActionValueMap.put(actionKey, value);
  273. uI.getPage().setLocation(actionUrl.toString());
  274. } else {
  275. // this should never happen as we already know the response is a
  276. // MimeResponse
  277. throw new IllegalStateException(
  278. "Portlet events can only be sent from a portlet request");
  279. }
  280. } else if (response instanceof StateAwareResponse) {
  281. ((StateAwareResponse) response).setEvent(name, value);
  282. } else {
  283. throw new IllegalStateException(
  284. "Portlet events can only be sent from a portlet request");
  285. }
  286. }
  287. /**
  288. * Sets a shared portlet parameter.
  289. *
  290. * Internally, an action may be created and opened, as shared parameters
  291. * cannot be set directly from all types of requests.
  292. *
  293. * Setting shared render parameters from background threads is not
  294. * supported.
  295. *
  296. * The parameters and values need to be kept in the context until sent. Any
  297. * memory leaks if the action fails are limited to the session.
  298. *
  299. * Shared parameters set or read by a portlet need to be declared in
  300. * portlet.xml .
  301. *
  302. * @param uI
  303. * a window in which a temporary action URL can be opened if
  304. * necessary
  305. * @param name
  306. * parameter identifier
  307. * @param value
  308. * parameter value
  309. */
  310. public void setSharedRenderParameter(UI uI, String name, String value)
  311. throws IllegalStateException {
  312. PortletResponse response = getCurrentResponse();
  313. if (response instanceof MimeResponse) {
  314. String actionKey = "" + System.currentTimeMillis();
  315. while (sharedParameterActionNameMap.containsKey(actionKey)) {
  316. actionKey += ".";
  317. }
  318. PortletURL actionUrl = generateActionURL(actionKey);
  319. if (actionUrl != null) {
  320. sharedParameterActionNameMap.put(actionKey, name);
  321. sharedParameterActionValueMap.put(actionKey, value);
  322. uI.getPage().setLocation(actionUrl.toString());
  323. } else {
  324. // this should never happen as we already know the response is a
  325. // MimeResponse
  326. throw new IllegalStateException(
  327. "Shared parameters can only be set from a portlet request");
  328. }
  329. } else if (response instanceof StateAwareResponse) {
  330. ((StateAwareResponse) response).setRenderParameter(name, value);
  331. } else {
  332. throw new IllegalStateException(
  333. "Shared parameters can only be set from a portlet request");
  334. }
  335. }
  336. /**
  337. * Sets the portlet mode. This may trigger a new render request.
  338. *
  339. * Currently, this is only supported when working with a
  340. * {@link StateAwareResponse} (an action request or an event request).
  341. * Portlet mode change in background threads is not supported.
  342. *
  343. * Portlet modes used by a portlet need to be declared in portlet.xml .
  344. *
  345. * @param uI
  346. * a window in which the render URL can be opened if necessary
  347. * @param portletMode
  348. * the portlet mode to switch to
  349. * @throws PortletModeException
  350. * if the portlet mode is not allowed for some reason
  351. * (configuration, permissions etc.)
  352. * @throws IllegalStateException
  353. * if not processing a request of the correct type
  354. */
  355. public void setPortletMode(UI uI, PortletMode portletMode)
  356. throws IllegalStateException, PortletModeException {
  357. PortletResponse response = getCurrentResponse();
  358. if (response instanceof MimeResponse) {
  359. PortletURL url = ((MimeResponse) response).createRenderURL();
  360. url.setPortletMode(portletMode);
  361. throw new IllegalStateException(
  362. "Portlet mode change is currently only supported when processing event and action requests");
  363. // UI.open(new ExternalResource(url.toString()));
  364. } else if (response instanceof StateAwareResponse) {
  365. ((StateAwareResponse) response).setPortletMode(portletMode);
  366. } else {
  367. throw new IllegalStateException(
  368. "Portlet mode can only be changed from a portlet request");
  369. }
  370. }
  371. }