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.

AbstractApplicationPortlet.java 41KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. /*
  2. * Copyright 2011 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.BufferedWriter;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;
  21. import java.io.OutputStreamWriter;
  22. import java.io.PrintWriter;
  23. import java.io.Serializable;
  24. import java.lang.reflect.InvocationTargetException;
  25. import java.lang.reflect.Method;
  26. import java.net.MalformedURLException;
  27. import java.security.GeneralSecurityException;
  28. import java.util.Enumeration;
  29. import java.util.Locale;
  30. import java.util.Map;
  31. import java.util.Properties;
  32. import java.util.logging.Logger;
  33. import javax.portlet.ActionRequest;
  34. import javax.portlet.ActionResponse;
  35. import javax.portlet.EventRequest;
  36. import javax.portlet.EventResponse;
  37. import javax.portlet.GenericPortlet;
  38. import javax.portlet.PortletConfig;
  39. import javax.portlet.PortletContext;
  40. import javax.portlet.PortletException;
  41. import javax.portlet.PortletRequest;
  42. import javax.portlet.PortletResponse;
  43. import javax.portlet.PortletSession;
  44. import javax.portlet.RenderRequest;
  45. import javax.portlet.RenderResponse;
  46. import javax.portlet.ResourceRequest;
  47. import javax.portlet.ResourceResponse;
  48. import javax.servlet.http.HttpServletRequest;
  49. import javax.servlet.http.HttpServletRequestWrapper;
  50. import javax.servlet.http.HttpServletResponse;
  51. import com.liferay.portal.kernel.util.PortalClassInvoker;
  52. import com.liferay.portal.kernel.util.PropsUtil;
  53. import com.vaadin.Application;
  54. import com.vaadin.Application.ApplicationStartEvent;
  55. import com.vaadin.Application.SystemMessages;
  56. import com.vaadin.UIRequiresMoreInformationException;
  57. import com.vaadin.server.AbstractCommunicationManager.Callback;
  58. import com.vaadin.ui.UI;
  59. /**
  60. * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0
  61. * deployments and handles various portlet requests from the browser.
  62. *
  63. * TODO Document me!
  64. *
  65. * @author peholmst
  66. */
  67. public abstract class AbstractApplicationPortlet extends GenericPortlet
  68. implements Constants {
  69. public static final String RESOURCE_URL_ID = "APP";
  70. public static class WrappedHttpAndPortletRequest extends
  71. WrappedPortletRequest {
  72. public WrappedHttpAndPortletRequest(PortletRequest request,
  73. HttpServletRequest originalRequest,
  74. DeploymentConfiguration deploymentConfiguration) {
  75. super(request, deploymentConfiguration);
  76. this.originalRequest = originalRequest;
  77. }
  78. private final HttpServletRequest originalRequest;
  79. @Override
  80. public String getParameter(String name) {
  81. String parameter = super.getParameter(name);
  82. if (parameter == null) {
  83. parameter = originalRequest.getParameter(name);
  84. }
  85. return parameter;
  86. }
  87. @Override
  88. public String getRemoteAddr() {
  89. return originalRequest.getRemoteAddr();
  90. }
  91. @Override
  92. public String getHeader(String name) {
  93. String header = super.getHeader(name);
  94. if (header == null) {
  95. header = originalRequest.getHeader(name);
  96. }
  97. return header;
  98. }
  99. @Override
  100. public Map<String, String[]> getParameterMap() {
  101. Map<String, String[]> parameterMap = super.getParameterMap();
  102. if (parameterMap == null) {
  103. parameterMap = originalRequest.getParameterMap();
  104. }
  105. return parameterMap;
  106. }
  107. }
  108. public static class WrappedGateinRequest extends
  109. WrappedHttpAndPortletRequest {
  110. public WrappedGateinRequest(PortletRequest request,
  111. DeploymentConfiguration deploymentConfiguration) {
  112. super(request, getOriginalRequest(request), deploymentConfiguration);
  113. }
  114. private static final HttpServletRequest getOriginalRequest(
  115. PortletRequest request) {
  116. try {
  117. Method getRealReq = request.getClass().getMethod(
  118. "getRealRequest");
  119. HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
  120. .invoke(request);
  121. return origRequest;
  122. } catch (Exception e) {
  123. throw new IllegalStateException("GateIn request not detected",
  124. e);
  125. }
  126. }
  127. }
  128. public static class WrappedLiferayRequest extends
  129. WrappedHttpAndPortletRequest {
  130. public WrappedLiferayRequest(PortletRequest request,
  131. DeploymentConfiguration deploymentConfiguration) {
  132. super(request, getOriginalRequest(request), deploymentConfiguration);
  133. }
  134. @Override
  135. public String getPortalProperty(String name) {
  136. return PropsUtil.get(name);
  137. }
  138. private static HttpServletRequest getOriginalRequest(
  139. PortletRequest request) {
  140. try {
  141. // httpRequest = PortalUtil.getHttpServletRequest(request);
  142. HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
  143. .invoke("com.liferay.portal.util.PortalUtil",
  144. "getHttpServletRequest", request);
  145. // httpRequest =
  146. // PortalUtil.getOriginalServletRequest(httpRequest);
  147. httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
  148. "com.liferay.portal.util.PortalUtil",
  149. "getOriginalServletRequest", httpRequest);
  150. return httpRequest;
  151. } catch (Exception e) {
  152. throw new IllegalStateException("Liferay request not detected",
  153. e);
  154. }
  155. }
  156. }
  157. public static class AbstractApplicationPortletWrapper implements Callback {
  158. private final AbstractApplicationPortlet portlet;
  159. public AbstractApplicationPortletWrapper(
  160. AbstractApplicationPortlet portlet) {
  161. this.portlet = portlet;
  162. }
  163. @Override
  164. public void criticalNotification(WrappedRequest request,
  165. WrappedResponse response, String cap, String msg,
  166. String details, String outOfSyncURL) throws IOException {
  167. portlet.criticalNotification(WrappedPortletRequest.cast(request),
  168. (WrappedPortletResponse) response, cap, msg, details,
  169. outOfSyncURL);
  170. }
  171. }
  172. /**
  173. * This portlet parameter is used to add styles to the main element. E.g
  174. * "height:500px" generates a style="height:500px" to the main element.
  175. */
  176. public static final String PORTLET_PARAMETER_STYLE = "style";
  177. /**
  178. * This portal parameter is used to define the name of the Vaadin theme that
  179. * is used for all Vaadin applications in the portal.
  180. */
  181. public static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme";
  182. public static final String WRITE_AJAX_PAGE_SCRIPT_WIDGETSET_SHOULD_WRITE = "writeAjaxPageScriptWidgetsetShouldWrite";
  183. // TODO some parts could be shared with AbstractApplicationServlet
  184. // TODO Can we close the application when the portlet is removed? Do we know
  185. // when the portlet is removed?
  186. private DeploymentConfiguration deploymentConfiguration;
  187. private AddonContext addonContext;
  188. @Override
  189. public void init(PortletConfig config) throws PortletException {
  190. super.init(config);
  191. Properties applicationProperties = new Properties();
  192. // Read default parameters from the context
  193. final PortletContext context = config.getPortletContext();
  194. for (final Enumeration<String> e = context.getInitParameterNames(); e
  195. .hasMoreElements();) {
  196. final String name = e.nextElement();
  197. applicationProperties.setProperty(name,
  198. context.getInitParameter(name));
  199. }
  200. // Override with application settings from portlet.xml
  201. for (final Enumeration<String> e = config.getInitParameterNames(); e
  202. .hasMoreElements();) {
  203. final String name = e.nextElement();
  204. applicationProperties.setProperty(name,
  205. config.getInitParameter(name));
  206. }
  207. deploymentConfiguration = new AbstractDeploymentConfiguration(
  208. getClass(), applicationProperties) {
  209. @Override
  210. public String getConfiguredWidgetset(WrappedRequest request) {
  211. String widgetset = getApplicationOrSystemProperty(
  212. PARAMETER_WIDGETSET, null);
  213. if (widgetset == null) {
  214. // If no widgetset defined for the application, check the
  215. // portal property
  216. widgetset = WrappedPortletRequest.cast(request)
  217. .getPortalProperty(
  218. PORTAL_PARAMETER_VAADIN_WIDGETSET);
  219. }
  220. if (widgetset == null) {
  221. // If no widgetset defined for the portal, use the default
  222. widgetset = DEFAULT_WIDGETSET;
  223. }
  224. return widgetset;
  225. }
  226. @Override
  227. public String getConfiguredTheme(WrappedRequest request) {
  228. // is the default theme defined by the portal?
  229. String themeName = WrappedPortletRequest.cast(request)
  230. .getPortalProperty(
  231. Constants.PORTAL_PARAMETER_VAADIN_THEME);
  232. if (themeName == null) {
  233. // no, using the default theme defined by Vaadin
  234. themeName = DEFAULT_THEME_NAME;
  235. }
  236. return themeName;
  237. }
  238. @Override
  239. public boolean isStandalone(WrappedRequest request) {
  240. return false;
  241. }
  242. /*
  243. * (non-Javadoc)
  244. *
  245. * @see
  246. * com.vaadin.terminal.DeploymentConfiguration#getStaticFileLocation
  247. * (com.vaadin.terminal.WrappedRequest)
  248. *
  249. * Return the URL from where static files, e.g. the widgetset and
  250. * the theme, are served. In a standard configuration the VAADIN
  251. * folder inside the returned folder is what is used for widgetsets
  252. * and themes.
  253. *
  254. * @return The location of static resources (inside which there
  255. * should be a VAADIN directory). Does not end with a slash (/).
  256. */
  257. @Override
  258. public String getStaticFileLocation(WrappedRequest request) {
  259. String staticFileLocation = WrappedPortletRequest
  260. .cast(request)
  261. .getPortalProperty(
  262. Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH);
  263. if (staticFileLocation != null) {
  264. // remove trailing slash if any
  265. while (staticFileLocation.endsWith(".")) {
  266. staticFileLocation = staticFileLocation.substring(0,
  267. staticFileLocation.length() - 1);
  268. }
  269. return staticFileLocation;
  270. } else {
  271. // default for Liferay
  272. return "/html";
  273. }
  274. }
  275. @Override
  276. public String getMimeType(String resourceName) {
  277. return getPortletContext().getMimeType(resourceName);
  278. }
  279. };
  280. addonContext = new AddonContext(deploymentConfiguration);
  281. addonContext.init();
  282. }
  283. @Override
  284. public void destroy() {
  285. super.destroy();
  286. addonContext.destroy();
  287. }
  288. protected enum RequestType {
  289. FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS, CONNECTOR_RESOURCE, HEARTBEAT;
  290. }
  291. protected RequestType getRequestType(WrappedPortletRequest wrappedRequest) {
  292. PortletRequest request = wrappedRequest.getPortletRequest();
  293. if (request instanceof RenderRequest) {
  294. return RequestType.RENDER;
  295. } else if (request instanceof ResourceRequest) {
  296. ResourceRequest resourceRequest = (ResourceRequest) request;
  297. if (ServletPortletHelper.isUIDLRequest(wrappedRequest)) {
  298. return RequestType.UIDL;
  299. } else if (isBrowserDetailsRequest(resourceRequest)) {
  300. return RequestType.BROWSER_DETAILS;
  301. } else if (ServletPortletHelper.isFileUploadRequest(wrappedRequest)) {
  302. return RequestType.FILE_UPLOAD;
  303. } else if (ServletPortletHelper
  304. .isConnectorResourceRequest(wrappedRequest)) {
  305. return RequestType.CONNECTOR_RESOURCE;
  306. } else if (ServletPortletHelper
  307. .isApplicationResourceRequest(wrappedRequest)) {
  308. return RequestType.APPLICATION_RESOURCE;
  309. } else if (ServletPortletHelper.isHeartbeatRequest(wrappedRequest)) {
  310. return RequestType.HEARTBEAT;
  311. } else if (isDummyRequest(resourceRequest)) {
  312. return RequestType.DUMMY;
  313. } else {
  314. return RequestType.STATIC_FILE;
  315. }
  316. } else if (request instanceof ActionRequest) {
  317. return RequestType.ACTION;
  318. } else if (request instanceof EventRequest) {
  319. return RequestType.EVENT;
  320. }
  321. return RequestType.UNKNOWN;
  322. }
  323. private boolean isBrowserDetailsRequest(ResourceRequest request) {
  324. return request.getResourceID() != null
  325. && request.getResourceID().equals("browserDetails");
  326. }
  327. private boolean isDummyRequest(ResourceRequest request) {
  328. return request.getResourceID() != null
  329. && request.getResourceID().equals("DUMMY");
  330. }
  331. /**
  332. * Returns true if the portlet is running in production mode. Production
  333. * mode disables all debug facilities.
  334. *
  335. * @return true if in production mode, false if in debug mode
  336. */
  337. public boolean isProductionMode() {
  338. return deploymentConfiguration.isProductionMode();
  339. }
  340. protected void handleRequest(PortletRequest request,
  341. PortletResponse response) throws PortletException, IOException {
  342. RequestTimer requestTimer = new RequestTimer();
  343. requestTimer.start();
  344. AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper(
  345. this);
  346. WrappedPortletRequest wrappedRequest = createWrappedRequest(request);
  347. WrappedPortletResponse wrappedResponse = new WrappedPortletResponse(
  348. response, getDeploymentConfiguration());
  349. RequestType requestType = getRequestType(wrappedRequest);
  350. if (requestType == RequestType.UNKNOWN) {
  351. handleUnknownRequest(request, response);
  352. } else if (requestType == RequestType.DUMMY) {
  353. /*
  354. * This dummy page is used by action responses to redirect to, in
  355. * order to prevent the boot strap code from being rendered into
  356. * strange places such as iframes.
  357. */
  358. ((ResourceResponse) response).setContentType("text/html");
  359. final OutputStream out = ((ResourceResponse) response)
  360. .getPortletOutputStream();
  361. final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
  362. new OutputStreamWriter(out, "UTF-8")));
  363. outWriter.print("<html><body>dummy page</body></html>");
  364. outWriter.close();
  365. } else if (requestType == RequestType.STATIC_FILE) {
  366. serveStaticResources((ResourceRequest) request,
  367. (ResourceResponse) response);
  368. } else {
  369. Application application = null;
  370. boolean transactionStarted = false;
  371. boolean requestStarted = false;
  372. boolean applicationRunning = false;
  373. try {
  374. // TODO What about PARAM_UNLOADBURST & redirectToApplication??
  375. /* Find out which application this request is related to */
  376. application = findApplicationInstance(wrappedRequest,
  377. requestType);
  378. if (application == null) {
  379. return;
  380. }
  381. Application.setCurrent(application);
  382. /*
  383. * Get or create an application context and an application
  384. * manager for the session
  385. */
  386. PortletApplicationContext2 applicationContext = getApplicationContext(request
  387. .getPortletSession());
  388. applicationContext.setResponse(response);
  389. applicationContext.setPortletConfig(getPortletConfig());
  390. PortletCommunicationManager applicationManager = applicationContext
  391. .getApplicationManager(application);
  392. if (requestType == RequestType.CONNECTOR_RESOURCE) {
  393. applicationManager.serveConnectorResource(wrappedRequest,
  394. wrappedResponse);
  395. return;
  396. } else if (requestType == RequestType.HEARTBEAT) {
  397. applicationManager.handleHeartbeatRequest(wrappedRequest,
  398. wrappedResponse, application);
  399. return;
  400. }
  401. /* Update browser information from request */
  402. applicationContext.getBrowser().updateRequestDetails(
  403. wrappedRequest);
  404. /*
  405. * Call application requestStart before Application.init() is
  406. * called (bypasses the limitation in TransactionListener)
  407. */
  408. if (application instanceof PortletRequestListener) {
  409. ((PortletRequestListener) application).onRequestStart(
  410. request, response);
  411. requestStarted = true;
  412. }
  413. /* Start the newly created application */
  414. startApplication(request, application, applicationContext);
  415. applicationRunning = true;
  416. /*
  417. * Transaction starts. Call transaction listeners. Transaction
  418. * end is called in the finally block below.
  419. */
  420. applicationContext.startTransaction(application, request);
  421. transactionStarted = true;
  422. /* Notify listeners */
  423. // Finds the window within the application
  424. UI uI = null;
  425. synchronized (application) {
  426. if (application.isRunning()) {
  427. switch (requestType) {
  428. case RENDER:
  429. case ACTION:
  430. // Both action requests and render requests are ok
  431. // without a UI as they render the initial HTML
  432. // and then do a second request
  433. try {
  434. uI = application
  435. .getUIForRequest(wrappedRequest);
  436. } catch (UIRequiresMoreInformationException e) {
  437. // Ignore problem and continue without UI
  438. }
  439. break;
  440. case BROWSER_DETAILS:
  441. // Should not try to find a UI here as the
  442. // combined request details might change the UI
  443. break;
  444. case FILE_UPLOAD:
  445. // no window
  446. break;
  447. case APPLICATION_RESOURCE:
  448. // use main window - should not need any window
  449. // UI = application.getUI();
  450. break;
  451. default:
  452. uI = application.getUIForRequest(wrappedRequest);
  453. }
  454. // if window not found, not a problem - use null
  455. }
  456. }
  457. // TODO Should this happen before or after the transaction
  458. // starts?
  459. if (request instanceof RenderRequest) {
  460. applicationContext.firePortletRenderRequest(application,
  461. uI, (RenderRequest) request,
  462. (RenderResponse) response);
  463. } else if (request instanceof ActionRequest) {
  464. applicationContext.firePortletActionRequest(application,
  465. uI, (ActionRequest) request,
  466. (ActionResponse) response);
  467. } else if (request instanceof EventRequest) {
  468. applicationContext.firePortletEventRequest(application, uI,
  469. (EventRequest) request, (EventResponse) response);
  470. } else if (request instanceof ResourceRequest) {
  471. applicationContext.firePortletResourceRequest(application,
  472. uI, (ResourceRequest) request,
  473. (ResourceResponse) response);
  474. }
  475. /* Handle the request */
  476. if (requestType == RequestType.FILE_UPLOAD) {
  477. // UI is resolved in handleFileUpload by
  478. // PortletCommunicationManager
  479. applicationManager.handleFileUpload(application,
  480. wrappedRequest, wrappedResponse);
  481. return;
  482. } else if (requestType == RequestType.BROWSER_DETAILS) {
  483. applicationManager.handleBrowserDetailsRequest(
  484. wrappedRequest, wrappedResponse, application);
  485. return;
  486. } else if (requestType == RequestType.UIDL) {
  487. // Handles AJAX UIDL requests
  488. applicationManager.handleUidlRequest(wrappedRequest,
  489. wrappedResponse, portletWrapper, uI);
  490. return;
  491. } else {
  492. /*
  493. * Removes the application if it has stopped
  494. */
  495. if (!application.isRunning()) {
  496. endApplication(request, response, application);
  497. return;
  498. }
  499. handleOtherRequest(wrappedRequest, wrappedResponse,
  500. requestType, application, applicationContext,
  501. applicationManager);
  502. }
  503. } catch (final SessionExpiredException e) {
  504. // TODO Figure out a better way to deal with
  505. // SessionExpiredExceptions
  506. getLogger().finest("A user session has expired");
  507. } catch (final GeneralSecurityException e) {
  508. // TODO Figure out a better way to deal with
  509. // GeneralSecurityExceptions
  510. getLogger()
  511. .fine("General security exception, the security key was probably incorrect.");
  512. } catch (final Throwable e) {
  513. handleServiceException(wrappedRequest, wrappedResponse,
  514. application, e);
  515. } finally {
  516. if (applicationRunning) {
  517. application.closeInactiveUIs();
  518. }
  519. // Notifies transaction end
  520. try {
  521. if (transactionStarted) {
  522. ((PortletApplicationContext2) application.getContext())
  523. .endTransaction(application, request);
  524. }
  525. } finally {
  526. try {
  527. if (requestStarted) {
  528. ((PortletRequestListener) application)
  529. .onRequestEnd(request, response);
  530. }
  531. } finally {
  532. UI.setCurrent(null);
  533. Application.setCurrent(null);
  534. PortletSession session = request
  535. .getPortletSession(false);
  536. if (session != null) {
  537. requestTimer.stop(getApplicationContext(session));
  538. }
  539. }
  540. }
  541. }
  542. }
  543. }
  544. /**
  545. * Wraps the request in a (possibly portal specific) wrapped portlet
  546. * request.
  547. *
  548. * @param request
  549. * The original PortletRequest
  550. * @return A wrapped version of the PorletRequest
  551. */
  552. protected WrappedPortletRequest createWrappedRequest(PortletRequest request) {
  553. String portalInfo = request.getPortalContext().getPortalInfo()
  554. .toLowerCase();
  555. if (portalInfo.contains("liferay")) {
  556. return new WrappedLiferayRequest(request,
  557. getDeploymentConfiguration());
  558. } else if (portalInfo.contains("gatein")) {
  559. return new WrappedGateinRequest(request,
  560. getDeploymentConfiguration());
  561. } else {
  562. return new WrappedPortletRequest(request,
  563. getDeploymentConfiguration());
  564. }
  565. }
  566. protected DeploymentConfiguration getDeploymentConfiguration() {
  567. return deploymentConfiguration;
  568. }
  569. private void handleUnknownRequest(PortletRequest request,
  570. PortletResponse response) {
  571. getLogger().warning("Unknown request type");
  572. }
  573. /**
  574. * Handle a portlet request that is not for static files, UIDL or upload.
  575. * Also render requests are handled here.
  576. *
  577. * This method is called after starting the application and calling portlet
  578. * and transaction listeners.
  579. *
  580. * @param request
  581. * @param response
  582. * @param requestType
  583. * @param application
  584. * @param applicationContext
  585. * @param applicationManager
  586. * @throws PortletException
  587. * @throws IOException
  588. * @throws MalformedURLException
  589. */
  590. private void handleOtherRequest(WrappedPortletRequest request,
  591. WrappedResponse response, RequestType requestType,
  592. Application application,
  593. PortletApplicationContext2 applicationContext,
  594. PortletCommunicationManager applicationManager)
  595. throws PortletException, IOException, MalformedURLException {
  596. if (requestType == RequestType.APPLICATION_RESOURCE
  597. || requestType == RequestType.RENDER) {
  598. if (!applicationManager.handleApplicationRequest(request, response)) {
  599. response.sendError(HttpServletResponse.SC_NOT_FOUND,
  600. "Not found");
  601. }
  602. } else if (requestType == RequestType.EVENT) {
  603. // nothing to do, listeners do all the work
  604. } else if (requestType == RequestType.ACTION) {
  605. // nothing to do, listeners do all the work
  606. } else {
  607. throw new IllegalStateException(
  608. "handleRequest() without anything to do - should never happen!");
  609. }
  610. }
  611. @Override
  612. public void processEvent(EventRequest request, EventResponse response)
  613. throws PortletException, IOException {
  614. handleRequest(request, response);
  615. }
  616. private void serveStaticResources(ResourceRequest request,
  617. ResourceResponse response) throws IOException, PortletException {
  618. final String resourceID = request.getResourceID();
  619. final PortletContext pc = getPortletContext();
  620. InputStream is = pc.getResourceAsStream(resourceID);
  621. if (is != null) {
  622. final String mimetype = pc.getMimeType(resourceID);
  623. if (mimetype != null) {
  624. response.setContentType(mimetype);
  625. }
  626. final OutputStream os = response.getPortletOutputStream();
  627. final byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
  628. int bytes;
  629. while ((bytes = is.read(buffer)) >= 0) {
  630. os.write(buffer, 0, bytes);
  631. }
  632. } else {
  633. getLogger().info(
  634. "Requested resource [" + resourceID
  635. + "] could not be found");
  636. response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
  637. Integer.toString(HttpServletResponse.SC_NOT_FOUND));
  638. }
  639. }
  640. @Override
  641. public void processAction(ActionRequest request, ActionResponse response)
  642. throws PortletException, IOException {
  643. handleRequest(request, response);
  644. }
  645. @Override
  646. protected void doDispatch(RenderRequest request, RenderResponse response)
  647. throws PortletException, IOException {
  648. try {
  649. // try to let super handle - it'll call methods annotated for
  650. // handling, the default doXYZ(), or throw if a handler for the mode
  651. // is not found
  652. super.doDispatch(request, response);
  653. } catch (PortletException e) {
  654. if (e.getCause() == null) {
  655. // No cause interpreted as 'unknown mode' - pass that trough
  656. // so that the application can handle
  657. handleRequest(request, response);
  658. } else {
  659. // Something else failed, pass on
  660. throw e;
  661. }
  662. }
  663. }
  664. @Override
  665. public void serveResource(ResourceRequest request, ResourceResponse response)
  666. throws PortletException, IOException {
  667. handleRequest(request, response);
  668. }
  669. boolean requestCanCreateApplication(PortletRequest request,
  670. RequestType requestType) {
  671. if (requestType == RequestType.UIDL && isRepaintAll(request)) {
  672. return true;
  673. } else if (requestType == RequestType.RENDER) {
  674. // In most cases the first request is a render request that renders
  675. // the HTML fragment. This should create an application instance.
  676. return true;
  677. } else if (requestType == RequestType.EVENT) {
  678. // A portlet can also be sent an event even though it has not been
  679. // rendered, e.g. portlet on one page sends an event to a portlet on
  680. // another page and then moves the user to that page.
  681. return true;
  682. }
  683. return false;
  684. }
  685. private boolean isRepaintAll(PortletRequest request) {
  686. return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null)
  687. && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1"));
  688. }
  689. private void startApplication(PortletRequest request,
  690. Application application, PortletApplicationContext2 context)
  691. throws PortletException, MalformedURLException {
  692. if (!application.isRunning()) {
  693. Locale locale = request.getLocale();
  694. application.setLocale(locale);
  695. // No application URL when running inside a portlet
  696. application.start(new ApplicationStartEvent(null,
  697. getDeploymentConfiguration(), context));
  698. addonContext.fireApplicationStarted(application);
  699. }
  700. }
  701. private void endApplication(PortletRequest request,
  702. PortletResponse response, Application application)
  703. throws IOException {
  704. final PortletSession session = request.getPortletSession();
  705. if (session != null) {
  706. getApplicationContext(session).removeApplication(application);
  707. }
  708. // Do not send any redirects when running inside a portlet.
  709. }
  710. private Application findApplicationInstance(
  711. WrappedPortletRequest wrappedRequest, RequestType requestType)
  712. throws PortletException, SessionExpiredException,
  713. MalformedURLException {
  714. PortletRequest request = wrappedRequest.getPortletRequest();
  715. boolean requestCanCreateApplication = requestCanCreateApplication(
  716. request, requestType);
  717. /* Find an existing application for this request. */
  718. Application application = getExistingApplication(request,
  719. requestCanCreateApplication);
  720. if (application != null) {
  721. /*
  722. * There is an existing application. We can use this as long as the
  723. * user not specifically requested to close or restart it.
  724. */
  725. final boolean restartApplication = (wrappedRequest
  726. .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null);
  727. final boolean closeApplication = (wrappedRequest
  728. .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null);
  729. if (restartApplication) {
  730. closeApplication(application, request.getPortletSession(false));
  731. return createApplication(request);
  732. } else if (closeApplication) {
  733. closeApplication(application, request.getPortletSession(false));
  734. return null;
  735. } else {
  736. return application;
  737. }
  738. }
  739. // No existing application was found
  740. if (requestCanCreateApplication) {
  741. return createApplication(request);
  742. } else {
  743. throw new SessionExpiredException();
  744. }
  745. }
  746. private void closeApplication(Application application,
  747. PortletSession session) {
  748. if (application == null) {
  749. return;
  750. }
  751. application.close();
  752. if (session != null) {
  753. PortletApplicationContext2 context = getApplicationContext(session);
  754. context.removeApplication(application);
  755. }
  756. }
  757. private Application createApplication(PortletRequest request)
  758. throws PortletException, MalformedURLException {
  759. Application newApplication = getNewApplication(request);
  760. final PortletApplicationContext2 context = getApplicationContext(request
  761. .getPortletSession());
  762. context.addApplication(newApplication, request.getWindowID());
  763. return newApplication;
  764. }
  765. private Application getExistingApplication(PortletRequest request,
  766. boolean allowSessionCreation) throws MalformedURLException,
  767. SessionExpiredException {
  768. final PortletSession session = request
  769. .getPortletSession(allowSessionCreation);
  770. if (session == null) {
  771. throw new SessionExpiredException();
  772. }
  773. PortletApplicationContext2 context = getApplicationContext(session);
  774. Application application = context.getApplicationForWindowId(request
  775. .getWindowID());
  776. if (application == null) {
  777. return null;
  778. }
  779. if (application.isRunning()) {
  780. return application;
  781. }
  782. // application found but not running
  783. context.removeApplication(application);
  784. return null;
  785. }
  786. protected abstract Class<? extends Application> getApplicationClass()
  787. throws ClassNotFoundException;
  788. protected Application getNewApplication(PortletRequest request)
  789. throws PortletException {
  790. try {
  791. final Application application = getApplicationClass().newInstance();
  792. application.setUiPreserved(true);
  793. return application;
  794. } catch (final IllegalAccessException e) {
  795. throw new PortletException("getNewApplication failed", e);
  796. } catch (final InstantiationException e) {
  797. throw new PortletException("getNewApplication failed", e);
  798. } catch (final ClassNotFoundException e) {
  799. throw new PortletException("getNewApplication failed", e);
  800. }
  801. }
  802. /**
  803. * Get system messages from the current application class
  804. *
  805. * @return
  806. */
  807. protected SystemMessages getSystemMessages() {
  808. try {
  809. Class<? extends Application> appCls = getApplicationClass();
  810. Method m = appCls.getMethod("getSystemMessages", (Class[]) null);
  811. return (Application.SystemMessages) m.invoke(null, (Object[]) null);
  812. } catch (ClassNotFoundException e) {
  813. // This should never happen
  814. throw new SystemMessageException(e);
  815. } catch (SecurityException e) {
  816. throw new SystemMessageException(
  817. "Application.getSystemMessage() should be static public", e);
  818. } catch (NoSuchMethodException e) {
  819. // This is completely ok and should be silently ignored
  820. } catch (IllegalArgumentException e) {
  821. // This should never happen
  822. throw new SystemMessageException(e);
  823. } catch (IllegalAccessException e) {
  824. throw new SystemMessageException(
  825. "Application.getSystemMessage() should be static public", e);
  826. } catch (InvocationTargetException e) {
  827. // This should never happen
  828. throw new SystemMessageException(e);
  829. }
  830. return Application.getSystemMessages();
  831. }
  832. private void handleServiceException(WrappedPortletRequest request,
  833. WrappedPortletResponse response, Application application,
  834. Throwable e) throws IOException, PortletException {
  835. // TODO Check that this error handler is working when running inside a
  836. // portlet
  837. // if this was an UIDL request, response UIDL back to client
  838. if (getRequestType(request) == RequestType.UIDL) {
  839. Application.SystemMessages ci = getSystemMessages();
  840. criticalNotification(request, response,
  841. ci.getInternalErrorCaption(), ci.getInternalErrorMessage(),
  842. null, ci.getInternalErrorURL());
  843. if (application != null) {
  844. application.getErrorHandler()
  845. .terminalError(new RequestError(e));
  846. } else {
  847. throw new PortletException(e);
  848. }
  849. } else {
  850. // Re-throw other exceptions
  851. throw new PortletException(e);
  852. }
  853. }
  854. @SuppressWarnings("serial")
  855. public class RequestError implements Terminal.ErrorEvent, Serializable {
  856. private final Throwable throwable;
  857. public RequestError(Throwable throwable) {
  858. this.throwable = throwable;
  859. }
  860. @Override
  861. public Throwable getThrowable() {
  862. return throwable;
  863. }
  864. }
  865. /**
  866. * Send notification to client's application. Used to notify client of
  867. * critical errors and session expiration due to long inactivity. Server has
  868. * no knowledge of what application client refers to.
  869. *
  870. * @param request
  871. * the Portlet request instance.
  872. * @param response
  873. * the Portlet response to write to.
  874. * @param caption
  875. * for the notification
  876. * @param message
  877. * for the notification
  878. * @param details
  879. * a detail message to show in addition to the passed message.
  880. * Currently shown directly but could be hidden behind a details
  881. * drop down.
  882. * @param url
  883. * url to load after message, null for current page
  884. * @throws IOException
  885. * if the writing failed due to input/output error.
  886. */
  887. void criticalNotification(WrappedPortletRequest request,
  888. WrappedPortletResponse response, String caption, String message,
  889. String details, String url) throws IOException {
  890. // clients JS app is still running, but server application either
  891. // no longer exists or it might fail to perform reasonably.
  892. // send a notification to client's application and link how
  893. // to "restart" application.
  894. if (caption != null) {
  895. caption = "\"" + caption + "\"";
  896. }
  897. if (details != null) {
  898. if (message == null) {
  899. message = details;
  900. } else {
  901. message += "<br/><br/>" + details;
  902. }
  903. }
  904. if (message != null) {
  905. message = "\"" + message + "\"";
  906. }
  907. if (url != null) {
  908. url = "\"" + url + "\"";
  909. }
  910. // Set the response type
  911. response.setContentType("application/json; charset=UTF-8");
  912. final OutputStream out = response.getOutputStream();
  913. final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
  914. new OutputStreamWriter(out, "UTF-8")));
  915. outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {"
  916. + "\"appError\": {" + "\"caption\":" + caption + ","
  917. + "\"message\" : " + message + "," + "\"url\" : " + url
  918. + "}}, \"resources\": {}, \"locales\":[]}]");
  919. outWriter.close();
  920. }
  921. /**
  922. *
  923. * Gets the application context for a PortletSession. If no context is
  924. * currently stored in a session a new context is created and stored in the
  925. * session.
  926. *
  927. * @param portletSession
  928. * the portlet session.
  929. * @return the application context for the session.
  930. */
  931. protected PortletApplicationContext2 getApplicationContext(
  932. PortletSession portletSession) {
  933. return PortletApplicationContext2.getApplicationContext(portletSession);
  934. }
  935. private static final Logger getLogger() {
  936. return Logger.getLogger(AbstractApplicationPortlet.class.getName());
  937. }
  938. }