Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

AbstractApplicationPortlet.java 58KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.server;
  5. import java.io.BufferedWriter;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.io.OutputStreamWriter;
  10. import java.io.PrintWriter;
  11. import java.io.Serializable;
  12. import java.lang.reflect.InvocationTargetException;
  13. import java.lang.reflect.Method;
  14. import java.net.MalformedURLException;
  15. import java.security.GeneralSecurityException;
  16. import java.util.Date;
  17. import java.util.Enumeration;
  18. import java.util.Iterator;
  19. import java.util.LinkedHashMap;
  20. import java.util.Locale;
  21. import java.util.Map;
  22. import java.util.Properties;
  23. import java.util.logging.Level;
  24. import java.util.logging.Logger;
  25. import javax.portlet.ActionRequest;
  26. import javax.portlet.ActionResponse;
  27. import javax.portlet.EventRequest;
  28. import javax.portlet.EventResponse;
  29. import javax.portlet.GenericPortlet;
  30. import javax.portlet.MimeResponse;
  31. import javax.portlet.PortalContext;
  32. import javax.portlet.PortletConfig;
  33. import javax.portlet.PortletContext;
  34. import javax.portlet.PortletException;
  35. import javax.portlet.PortletRequest;
  36. import javax.portlet.PortletResponse;
  37. import javax.portlet.PortletSession;
  38. import javax.portlet.PortletURL;
  39. import javax.portlet.RenderRequest;
  40. import javax.portlet.RenderResponse;
  41. import javax.portlet.ResourceRequest;
  42. import javax.portlet.ResourceResponse;
  43. import javax.portlet.ResourceURL;
  44. import javax.servlet.http.HttpServletRequest;
  45. import javax.servlet.http.HttpServletRequestWrapper;
  46. import javax.servlet.http.HttpServletResponse;
  47. import com.liferay.portal.kernel.util.PortalClassInvoker;
  48. import com.liferay.portal.kernel.util.PropsUtil;
  49. import com.vaadin.Application;
  50. import com.vaadin.Application.SystemMessages;
  51. import com.vaadin.terminal.DownloadStream;
  52. import com.vaadin.terminal.Terminal;
  53. import com.vaadin.terminal.gwt.client.ApplicationConfiguration;
  54. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  55. import com.vaadin.ui.Window;
  56. /**
  57. * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0
  58. * deployments and handles various portlet requests from the browser.
  59. *
  60. * TODO Document me!
  61. *
  62. * @author peholmst
  63. */
  64. public abstract class AbstractApplicationPortlet extends GenericPortlet
  65. implements Constants {
  66. private static final Logger logger = Logger
  67. .getLogger(AbstractApplicationPortlet.class.getName());
  68. /**
  69. * This portlet parameter is used to add styles to the main element. E.g
  70. * "height:500px" generates a style="height:500px" to the main element.
  71. */
  72. public static final String PORTLET_PARAMETER_STYLE = "style";
  73. private static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme";
  74. // TODO some parts could be shared with AbstractApplicationServlet
  75. // TODO Can we close the application when the portlet is removed? Do we know
  76. // when the portlet is removed?
  77. // TODO What happens when the portlet window is resized? Do we know when the
  78. // window is resized?
  79. private Properties applicationProperties;
  80. private boolean productionMode = false;
  81. @Override
  82. public void init(PortletConfig config) throws PortletException {
  83. super.init(config);
  84. // Stores the application parameters into Properties object
  85. applicationProperties = new Properties();
  86. for (final Enumeration<String> e = config.getInitParameterNames(); e
  87. .hasMoreElements();) {
  88. final String name = e.nextElement();
  89. applicationProperties.setProperty(name,
  90. config.getInitParameter(name));
  91. }
  92. // Overrides with server.xml parameters
  93. final PortletContext context = config.getPortletContext();
  94. for (final Enumeration<String> e = context.getInitParameterNames(); e
  95. .hasMoreElements();) {
  96. final String name = e.nextElement();
  97. applicationProperties.setProperty(name,
  98. context.getInitParameter(name));
  99. }
  100. checkProductionMode();
  101. checkCrossSiteProtection();
  102. }
  103. private void checkCrossSiteProtection() {
  104. if (getApplicationOrSystemProperty(
  105. SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false").equals(
  106. "true")) {
  107. /*
  108. * Print an information/warning message about running with xsrf
  109. * protection disabled
  110. */
  111. logger.warning(WARNING_XSRF_PROTECTION_DISABLED);
  112. }
  113. }
  114. private void checkProductionMode() {
  115. // Check if the application is in production mode.
  116. // We are in production mode if Debug=false or productionMode=true
  117. if (getApplicationOrSystemProperty(SERVLET_PARAMETER_DEBUG, "true")
  118. .equals("false")) {
  119. // "Debug=true" is the old way and should no longer be used
  120. productionMode = true;
  121. } else if (getApplicationOrSystemProperty(
  122. SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) {
  123. // "productionMode=true" is the real way to do it
  124. productionMode = true;
  125. }
  126. if (!productionMode) {
  127. /* Print an information/warning message about running in debug mode */
  128. // TODO Maybe we need a different message for portlets?
  129. logger.warning(NOT_PRODUCTION_MODE_INFO);
  130. }
  131. }
  132. /**
  133. * Gets an application property value.
  134. *
  135. * @param parameterName
  136. * the Name or the parameter.
  137. * @return String value or null if not found
  138. */
  139. protected String getApplicationProperty(String parameterName) {
  140. String val = applicationProperties.getProperty(parameterName);
  141. if (val != null) {
  142. return val;
  143. }
  144. // Try lower case application properties for backward compatibility with
  145. // 3.0.2 and earlier
  146. val = applicationProperties.getProperty(parameterName.toLowerCase());
  147. return val;
  148. }
  149. /**
  150. * Gets an system property value.
  151. *
  152. * @param parameterName
  153. * the Name or the parameter.
  154. * @return String value or null if not found
  155. */
  156. protected String getSystemProperty(String parameterName) {
  157. String val = null;
  158. String pkgName;
  159. final Package pkg = getClass().getPackage();
  160. if (pkg != null) {
  161. pkgName = pkg.getName();
  162. } else {
  163. final String className = getClass().getName();
  164. pkgName = new String(className.toCharArray(), 0,
  165. className.lastIndexOf('.'));
  166. }
  167. val = System.getProperty(pkgName + "." + parameterName);
  168. if (val != null) {
  169. return val;
  170. }
  171. // Try lowercased system properties
  172. val = System.getProperty(pkgName + "." + parameterName.toLowerCase());
  173. return val;
  174. }
  175. /**
  176. * Gets an application or system property value.
  177. *
  178. * @param parameterName
  179. * the Name or the parameter.
  180. * @param defaultValue
  181. * the Default to be used.
  182. * @return String value or default if not found
  183. */
  184. protected String getApplicationOrSystemProperty(String parameterName,
  185. String defaultValue) {
  186. String val = null;
  187. // Try application properties
  188. val = getApplicationProperty(parameterName);
  189. if (val != null) {
  190. return val;
  191. }
  192. // Try system properties
  193. val = getSystemProperty(parameterName);
  194. if (val != null) {
  195. return val;
  196. }
  197. return defaultValue;
  198. }
  199. /**
  200. * Return the URL from where static files, e.g. the widgetset and the theme,
  201. * are served. In a standard configuration the VAADIN folder inside the
  202. * returned folder is what is used for widgetsets and themes.
  203. *
  204. * @param request
  205. * @return The location of static resources (inside which there should be a
  206. * VAADIN directory). Does not end with a slash (/).
  207. */
  208. protected String getStaticFilesLocation(PortletRequest request) {
  209. // TODO allow overriding on portlet level?
  210. String staticFileLocation = getPortalProperty(
  211. Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH,
  212. request.getPortalContext());
  213. if (staticFileLocation != null) {
  214. // remove trailing slash if any
  215. while (staticFileLocation.endsWith(".")) {
  216. staticFileLocation = staticFileLocation.substring(0,
  217. staticFileLocation.length() - 1);
  218. }
  219. return staticFileLocation;
  220. } else {
  221. // default for Liferay
  222. return "/html";
  223. }
  224. }
  225. protected enum RequestType {
  226. FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN;
  227. }
  228. protected RequestType getRequestType(PortletRequest request) {
  229. if (request instanceof RenderRequest) {
  230. return RequestType.RENDER;
  231. } else if (request instanceof ResourceRequest) {
  232. if (isUIDLRequest((ResourceRequest) request)) {
  233. return RequestType.UIDL;
  234. } else if (isFileUploadRequest((ResourceRequest) request)) {
  235. return RequestType.FILE_UPLOAD;
  236. } else if (isApplicationResourceRequest((ResourceRequest) request)) {
  237. return RequestType.APPLICATION_RESOURCE;
  238. } else if (isDummyRequest((ResourceRequest) request)) {
  239. return RequestType.DUMMY;
  240. } else {
  241. return RequestType.STATIC_FILE;
  242. }
  243. } else if (request instanceof ActionRequest) {
  244. return RequestType.ACTION;
  245. } else if (request instanceof EventRequest) {
  246. return RequestType.EVENT;
  247. }
  248. return RequestType.UNKNOWN;
  249. }
  250. private boolean isApplicationResourceRequest(ResourceRequest request) {
  251. return request.getResourceID() != null
  252. && request.getResourceID().startsWith("APP");
  253. }
  254. private boolean isUIDLRequest(ResourceRequest request) {
  255. return request.getResourceID() != null
  256. && request.getResourceID().equals("UIDL");
  257. }
  258. private boolean isDummyRequest(ResourceRequest request) {
  259. return request.getResourceID() != null
  260. && request.getResourceID().equals("DUMMY");
  261. }
  262. private boolean isFileUploadRequest(ResourceRequest request) {
  263. return "UPLOAD".equals(request.getResourceID());
  264. }
  265. /**
  266. * Returns true if the servlet is running in production mode. Production
  267. * mode disables all debug facilities.
  268. *
  269. * @return true if in production mode, false if in debug mode
  270. */
  271. public boolean isProductionMode() {
  272. return productionMode;
  273. }
  274. protected void handleRequest(PortletRequest request,
  275. PortletResponse response) throws PortletException, IOException {
  276. RequestType requestType = getRequestType(request);
  277. if (requestType == RequestType.UNKNOWN) {
  278. handleUnknownRequest(request, response);
  279. } else if (requestType == RequestType.DUMMY) {
  280. /*
  281. * This dummy page is used by action responses to redirect to, in
  282. * order to prevent the boot strap code from being rendered into
  283. * strange places such as iframes.
  284. */
  285. ((ResourceResponse) response).setContentType("text/html");
  286. final OutputStream out = ((ResourceResponse) response)
  287. .getPortletOutputStream();
  288. final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
  289. new OutputStreamWriter(out, "UTF-8")));
  290. outWriter.print("<html><body>dummy page</body></html>");
  291. outWriter.close();
  292. } else if (requestType == RequestType.STATIC_FILE) {
  293. serveStaticResources((ResourceRequest) request,
  294. (ResourceResponse) response);
  295. } else {
  296. Application application = null;
  297. boolean transactionStarted = false;
  298. boolean requestStarted = false;
  299. try {
  300. // TODO What about PARAM_UNLOADBURST & redirectToApplication??
  301. /* Find out which application this request is related to */
  302. application = findApplicationInstance(request, requestType);
  303. if (application == null) {
  304. return;
  305. }
  306. /*
  307. * Get or create an application context and an application
  308. * manager for the session
  309. */
  310. PortletApplicationContext2 applicationContext = PortletApplicationContext2
  311. .getApplicationContext(request.getPortletSession());
  312. applicationContext.setResponse(response);
  313. applicationContext.setPortletConfig(getPortletConfig());
  314. PortletCommunicationManager applicationManager = applicationContext
  315. .getApplicationManager(application);
  316. /* Update browser information from request */
  317. updateBrowserProperties(applicationContext.getBrowser(),
  318. request);
  319. /*
  320. * Call application requestStart before Application.init() is
  321. * called (bypasses the limitation in TransactionListener)
  322. */
  323. if (application instanceof PortletRequestListener) {
  324. ((PortletRequestListener) application).onRequestStart(
  325. request, response);
  326. requestStarted = true;
  327. }
  328. /* Start the newly created application */
  329. startApplication(request, application, applicationContext);
  330. /*
  331. * Transaction starts. Call transaction listeners. Transaction
  332. * end is called in the finally block below.
  333. */
  334. applicationContext.startTransaction(application, request);
  335. transactionStarted = true;
  336. /* Notify listeners */
  337. // Finds the window within the application
  338. Window window = null;
  339. synchronized (application) {
  340. if (application.isRunning()) {
  341. switch (requestType) {
  342. case FILE_UPLOAD:
  343. // no window
  344. break;
  345. case APPLICATION_RESOURCE:
  346. // use main window - should not need any window
  347. window = application.getMainWindow();
  348. break;
  349. default:
  350. window = applicationManager.getApplicationWindow(
  351. request, this, application, null);
  352. }
  353. // if window not found, not a problem - use null
  354. }
  355. }
  356. // TODO Should this happen before or after the transaction
  357. // starts?
  358. if (request instanceof RenderRequest) {
  359. applicationContext.firePortletRenderRequest(application,
  360. window, (RenderRequest) request,
  361. (RenderResponse) response);
  362. } else if (request instanceof ActionRequest) {
  363. applicationContext.firePortletActionRequest(application,
  364. window, (ActionRequest) request,
  365. (ActionResponse) response);
  366. } else if (request instanceof EventRequest) {
  367. applicationContext.firePortletEventRequest(application,
  368. window, (EventRequest) request,
  369. (EventResponse) response);
  370. } else if (request instanceof ResourceRequest) {
  371. applicationContext.firePortletResourceRequest(application,
  372. window, (ResourceRequest) request,
  373. (ResourceResponse) response);
  374. }
  375. /* Handle the request */
  376. if (requestType == RequestType.FILE_UPLOAD) {
  377. applicationManager.handleFileUpload(
  378. (ResourceRequest) request,
  379. (ResourceResponse) response);
  380. return;
  381. } else if (requestType == RequestType.UIDL) {
  382. // Handles AJAX UIDL requests
  383. applicationManager.handleUidlRequest(
  384. (ResourceRequest) request,
  385. (ResourceResponse) response, this, window);
  386. return;
  387. } else {
  388. /*
  389. * Removes the application if it has stopped
  390. */
  391. if (!application.isRunning()) {
  392. endApplication(request, response, application);
  393. return;
  394. }
  395. handleOtherRequest(request, response, requestType,
  396. application, window, applicationContext,
  397. applicationManager);
  398. }
  399. } catch (final SessionExpiredException e) {
  400. // TODO Figure out a better way to deal with
  401. // SessionExpiredExceptions
  402. logger.finest("A user session has expired");
  403. } catch (final GeneralSecurityException e) {
  404. // TODO Figure out a better way to deal with
  405. // GeneralSecurityExceptions
  406. logger.finest("General security exception, the security key was probably incorrect.");
  407. } catch (final Throwable e) {
  408. handleServiceException(request, response, application, e);
  409. } finally {
  410. // Notifies transaction end
  411. try {
  412. if (transactionStarted) {
  413. ((PortletApplicationContext2) application.getContext())
  414. .endTransaction(application, request);
  415. }
  416. } finally {
  417. if (requestStarted) {
  418. ((PortletRequestListener) application).onRequestEnd(
  419. request, response);
  420. }
  421. }
  422. }
  423. }
  424. }
  425. private void handleUnknownRequest(PortletRequest request,
  426. PortletResponse response) {
  427. logger.warning("Unknown request type");
  428. }
  429. /**
  430. * Handle a portlet request that is not for static files, UIDL or upload.
  431. * Also render requests are handled here.
  432. *
  433. * This method is called after starting the application and calling portlet
  434. * and transaction listeners.
  435. *
  436. * @param request
  437. * @param response
  438. * @param requestType
  439. * @param application
  440. * @param applicationContext
  441. * @param applicationManager
  442. * @throws PortletException
  443. * @throws IOException
  444. * @throws MalformedURLException
  445. */
  446. private void handleOtherRequest(PortletRequest request,
  447. PortletResponse response, RequestType requestType,
  448. Application application, Window window,
  449. PortletApplicationContext2 applicationContext,
  450. PortletCommunicationManager applicationManager)
  451. throws PortletException, IOException, MalformedURLException {
  452. if (window == null) {
  453. throw new PortletException(ERROR_NO_WINDOW_FOUND);
  454. }
  455. /*
  456. * Sets terminal type for the window, if not already set
  457. */
  458. if (window.getTerminal() == null) {
  459. window.setTerminal(applicationContext.getBrowser());
  460. }
  461. /*
  462. * Handle parameters
  463. */
  464. final Map<String, String[]> parameters = request.getParameterMap();
  465. if (window != null && parameters != null) {
  466. window.handleParameters(parameters);
  467. }
  468. if (requestType == RequestType.APPLICATION_RESOURCE) {
  469. handleURI(applicationManager, window, (ResourceRequest) request,
  470. (ResourceResponse) response);
  471. } else if (requestType == RequestType.RENDER) {
  472. writeAjaxPage((RenderRequest) request, (RenderResponse) response,
  473. window, application);
  474. } else if (requestType == RequestType.EVENT) {
  475. // nothing to do, listeners do all the work
  476. } else if (requestType == RequestType.ACTION) {
  477. // nothing to do, listeners do all the work
  478. } else {
  479. throw new IllegalStateException(
  480. "handleRequest() without anything to do - should never happen!");
  481. }
  482. }
  483. private void updateBrowserProperties(WebBrowser browser,
  484. PortletRequest request) {
  485. String userAgent = getHTTPHeader(request, "user-agent");
  486. browser.updateBrowserProperties(request.getLocale(), null,
  487. request.isSecure(), userAgent,
  488. getHTTPRequestParameter(request, "sw"),
  489. getHTTPRequestParameter(request, "sh"));
  490. }
  491. @Override
  492. public void processEvent(EventRequest request, EventResponse response)
  493. throws PortletException, IOException {
  494. handleRequest(request, response);
  495. }
  496. private boolean handleURI(PortletCommunicationManager applicationManager,
  497. Window window, ResourceRequest request, ResourceResponse response)
  498. throws IOException {
  499. // Handles the URI
  500. DownloadStream download = applicationManager.handleURI(window, request,
  501. response, this);
  502. // A download request
  503. if (download != null) {
  504. // Client downloads an resource
  505. handleDownload(download, request, response);
  506. return true;
  507. }
  508. return false;
  509. }
  510. private void handleDownload(DownloadStream stream, ResourceRequest request,
  511. ResourceResponse response) throws IOException {
  512. if (stream.getParameter("Location") != null) {
  513. response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
  514. Integer.toString(HttpServletResponse.SC_MOVED_TEMPORARILY));
  515. response.setProperty("Location", stream.getParameter("Location"));
  516. return;
  517. }
  518. // Download from given stream
  519. final InputStream data = stream.getStream();
  520. if (data != null) {
  521. // Sets content type
  522. response.setContentType(stream.getContentType());
  523. // Sets cache headers
  524. final long cacheTime = stream.getCacheTime();
  525. if (cacheTime <= 0) {
  526. response.setProperty("Cache-Control", "no-cache");
  527. response.setProperty("Pragma", "no-cache");
  528. response.setProperty("Expires", "0");
  529. } else {
  530. response.setProperty("Cache-Control", "max-age=" + cacheTime
  531. / 1000);
  532. response.setProperty("Expires", "" + System.currentTimeMillis()
  533. + cacheTime);
  534. // Required to apply caching in some Tomcats
  535. response.setProperty("Pragma", "cache");
  536. }
  537. // Copy download stream parameters directly
  538. // to HTTP headers.
  539. final Iterator<String> i = stream.getParameterNames();
  540. if (i != null) {
  541. while (i.hasNext()) {
  542. final String param = i.next();
  543. response.setProperty(param, stream.getParameter(param));
  544. }
  545. }
  546. // suggest local filename from DownloadStream if Content-Disposition
  547. // not explicitly set
  548. String contentDispositionValue = stream
  549. .getParameter("Content-Disposition");
  550. if (contentDispositionValue == null) {
  551. contentDispositionValue = "filename=\"" + stream.getFileName()
  552. + "\"";
  553. response.setProperty("Content-Disposition",
  554. contentDispositionValue);
  555. }
  556. int bufferSize = stream.getBufferSize();
  557. if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
  558. bufferSize = DEFAULT_BUFFER_SIZE;
  559. }
  560. final byte[] buffer = new byte[bufferSize];
  561. int bytesRead = 0;
  562. final OutputStream out = response.getPortletOutputStream();
  563. while ((bytesRead = data.read(buffer)) > 0) {
  564. out.write(buffer, 0, bytesRead);
  565. out.flush();
  566. }
  567. out.close();
  568. }
  569. }
  570. private void serveStaticResources(ResourceRequest request,
  571. ResourceResponse response) throws IOException, PortletException {
  572. final String resourceID = request.getResourceID();
  573. final PortletContext pc = getPortletContext();
  574. InputStream is = pc.getResourceAsStream(resourceID);
  575. if (is != null) {
  576. final String mimetype = pc.getMimeType(resourceID);
  577. if (mimetype != null) {
  578. response.setContentType(mimetype);
  579. }
  580. final OutputStream os = response.getPortletOutputStream();
  581. final byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
  582. int bytes;
  583. while ((bytes = is.read(buffer)) >= 0) {
  584. os.write(buffer, 0, bytes);
  585. }
  586. } else {
  587. logger.warning("Requested resource [" + resourceID
  588. + "] could not be found");
  589. response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
  590. Integer.toString(HttpServletResponse.SC_NOT_FOUND));
  591. }
  592. }
  593. @Override
  594. public void processAction(ActionRequest request, ActionResponse response)
  595. throws PortletException, IOException {
  596. handleRequest(request, response);
  597. }
  598. @Override
  599. protected void doDispatch(RenderRequest request, RenderResponse response)
  600. throws PortletException, IOException {
  601. try {
  602. // try to let super handle - it'll call methods annotated for
  603. // handling, the default doXYZ(), or throw if a handler for the mode
  604. // is not found
  605. super.doDispatch(request, response);
  606. } catch (PortletException e) {
  607. if (e.getCause() == null) {
  608. // No cause interpreted as 'unknown mode' - pass that trough
  609. // so that the application can handle
  610. handleRequest(request, response);
  611. } else {
  612. // Something else failed, pass on
  613. throw e;
  614. }
  615. }
  616. }
  617. @Override
  618. public void serveResource(ResourceRequest request, ResourceResponse response)
  619. throws PortletException, IOException {
  620. handleRequest(request, response);
  621. }
  622. boolean requestCanCreateApplication(PortletRequest request,
  623. RequestType requestType) {
  624. if (requestType == RequestType.UIDL && isRepaintAll(request)) {
  625. return true;
  626. } else if (requestType == RequestType.RENDER) {
  627. return true;
  628. }
  629. return false;
  630. }
  631. private boolean isRepaintAll(PortletRequest request) {
  632. return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null)
  633. && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1"));
  634. }
  635. private void startApplication(PortletRequest request,
  636. Application application, PortletApplicationContext2 context)
  637. throws PortletException, MalformedURLException {
  638. if (!application.isRunning()) {
  639. Locale locale = request.getLocale();
  640. application.setLocale(locale);
  641. // No application URL when running inside a portlet
  642. application.start(null, applicationProperties, context);
  643. }
  644. }
  645. private void endApplication(PortletRequest request,
  646. PortletResponse response, Application application)
  647. throws IOException {
  648. final PortletSession session = request.getPortletSession();
  649. if (session != null) {
  650. PortletApplicationContext2.getApplicationContext(session)
  651. .removeApplication(application);
  652. }
  653. // Do not send any redirects when running inside a portlet.
  654. }
  655. private Application findApplicationInstance(PortletRequest request,
  656. RequestType requestType) throws PortletException,
  657. SessionExpiredException, MalformedURLException {
  658. boolean requestCanCreateApplication = requestCanCreateApplication(
  659. request, requestType);
  660. /* Find an existing application for this request. */
  661. Application application = getExistingApplication(request,
  662. requestCanCreateApplication);
  663. if (application != null) {
  664. /*
  665. * There is an existing application. We can use this as long as the
  666. * user not specifically requested to close or restart it.
  667. */
  668. final boolean restartApplication = (getHTTPRequestParameter(
  669. request, URL_PARAMETER_RESTART_APPLICATION) != null);
  670. final boolean closeApplication = (getHTTPRequestParameter(request,
  671. URL_PARAMETER_CLOSE_APPLICATION) != null);
  672. if (restartApplication) {
  673. closeApplication(application, request.getPortletSession(false));
  674. return createApplication(request);
  675. } else if (closeApplication) {
  676. closeApplication(application, request.getPortletSession(false));
  677. return null;
  678. } else {
  679. return application;
  680. }
  681. }
  682. // No existing application was found
  683. if (requestCanCreateApplication) {
  684. return createApplication(request);
  685. } else {
  686. throw new SessionExpiredException();
  687. }
  688. }
  689. private void closeApplication(Application application,
  690. PortletSession session) {
  691. if (application == null) {
  692. return;
  693. }
  694. application.close();
  695. if (session != null) {
  696. PortletApplicationContext2 context = PortletApplicationContext2
  697. .getApplicationContext(session);
  698. context.removeApplication(application);
  699. }
  700. }
  701. private Application createApplication(PortletRequest request)
  702. throws PortletException, MalformedURLException {
  703. Application newApplication = getNewApplication(request);
  704. final PortletApplicationContext2 context = PortletApplicationContext2
  705. .getApplicationContext(request.getPortletSession());
  706. context.addApplication(newApplication, request.getWindowID());
  707. return newApplication;
  708. }
  709. private Application getExistingApplication(PortletRequest request,
  710. boolean allowSessionCreation) throws MalformedURLException,
  711. SessionExpiredException {
  712. final PortletSession session = request
  713. .getPortletSession(allowSessionCreation);
  714. if (session == null) {
  715. throw new SessionExpiredException();
  716. }
  717. PortletApplicationContext2 context = PortletApplicationContext2
  718. .getApplicationContext(session);
  719. Application application = context.getApplicationForWindowId(request
  720. .getWindowID());
  721. if (application == null) {
  722. return null;
  723. }
  724. if (application.isRunning()) {
  725. return application;
  726. }
  727. // application found but not running
  728. PortletApplicationContext2.getApplicationContext(session)
  729. .removeApplication(application);
  730. return null;
  731. }
  732. /**
  733. * Returns the URL from which the widgetset is served on the portal.
  734. *
  735. * @param widgetset
  736. * @param request
  737. * @return
  738. */
  739. protected String getWidgetsetURL(String widgetset, PortletRequest request) {
  740. return getStaticFilesLocation(request) + "/" + WIDGETSET_DIRECTORY_PATH
  741. + widgetset + "/" + widgetset + ".nocache.js?"
  742. + new Date().getTime();
  743. }
  744. /**
  745. * Returns the theme URI for the named theme on the portal.
  746. *
  747. * Note that this is not the only location referring to the theme URI - also
  748. * e.g. PortletCommunicationManager uses its own way to access the portlet
  749. * 2.0 theme resources.
  750. *
  751. * @param themeName
  752. * @param request
  753. * @return
  754. */
  755. protected String getThemeURI(String themeName, PortletRequest request) {
  756. return getStaticFilesLocation(request) + "/" + THEME_DIRECTORY_PATH
  757. + themeName;
  758. }
  759. /**
  760. * Writes the html host page (aka kickstart page) that starts the actual
  761. * Vaadin application.
  762. *
  763. * If one needs to override parts of the portlet HTML contents creation, it
  764. * is suggested that one overrides one of several submethods including:
  765. * <ul>
  766. * <li>
  767. * {@link #writeAjaxPageHtmlMainDiv(RenderRequest, RenderResponse, BufferedWriter, String)}
  768. * <li>
  769. * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)}
  770. * <li>
  771. * {@link #writeAjaxPageHtmlVaadinScripts(RenderRequest, RenderResponse, BufferedWriter, Application, String)}
  772. * </ul>
  773. *
  774. * @param request
  775. * the portlet request.
  776. * @param response
  777. * the portlet response to write to.
  778. * @param window
  779. * @param application
  780. * @throws IOException
  781. * if the writing failed due to input/output error.
  782. * @throws MalformedURLException
  783. * if the application is denied access the persistent data store
  784. * represented by the given URL.
  785. * @throws PortletException
  786. */
  787. protected void writeAjaxPage(RenderRequest request,
  788. RenderResponse response, Window window, Application application)
  789. throws IOException, MalformedURLException, PortletException {
  790. response.setContentType("text/html");
  791. final BufferedWriter page = new BufferedWriter(new OutputStreamWriter(
  792. response.getPortletOutputStream(), "UTF-8"));
  793. // TODO Currently, we can only load widgetsets and themes from the
  794. // portal
  795. String themeName = getThemeForWindow(request, window);
  796. writeAjaxPageHtmlVaadinScripts(request, response, page, application,
  797. themeName);
  798. /*- Add classnames;
  799. * .v-app
  800. * .v-app-loading
  801. * .v-app-<simpleName for app class>
  802. * .v-theme-<themeName, remove non-alphanum>
  803. */
  804. String appClass = "v-app-";
  805. try {
  806. appClass += getApplicationClass().getSimpleName();
  807. } catch (ClassNotFoundException e) {
  808. appClass += "unknown";
  809. logger.log(Level.SEVERE, "Could not find application class", e);
  810. }
  811. String themeClass = "v-theme-"
  812. + themeName.replaceAll("[^a-zA-Z0-9]", "");
  813. String classNames = "v-app v-app-loading " + themeClass + " "
  814. + appClass;
  815. String style = getApplicationProperty(PORTLET_PARAMETER_STYLE);
  816. String divStyle = "";
  817. if (style != null) {
  818. divStyle = "style=\"" + style + "\"";
  819. }
  820. writeAjaxPageHtmlMainDiv(request, response, page,
  821. request.getWindowID(), classNames, divStyle);
  822. page.close();
  823. }
  824. /**
  825. * This method writes the scripts to load the widgetset and the themes as
  826. * well as define Vaadin configuration parameters on the HTML fragment that
  827. * starts the actual Vaadin application.
  828. *
  829. * @param request
  830. * @param response
  831. * @param writer
  832. * @param application
  833. * @param themeName
  834. * @throws IOException
  835. * @throws PortletException
  836. */
  837. protected void writeAjaxPageHtmlVaadinScripts(RenderRequest request,
  838. RenderResponse response, final BufferedWriter writer,
  839. Application application, String themeName) throws IOException,
  840. PortletException {
  841. String themeURI = getThemeURI(themeName, request);
  842. // fixed base theme to use - all portal pages with Vaadin
  843. // applications will load this exactly once
  844. String portalTheme = getPortalProperty(PORTAL_PARAMETER_VAADIN_THEME,
  845. request.getPortalContext());
  846. writer.write("<script type=\"text/javascript\">\n");
  847. writer.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n "
  848. + "if(!vaadin) { var vaadin = {}} \n"
  849. + "vaadin.vaadinConfigurations = {};\n"
  850. + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n");
  851. if (!isProductionMode()) {
  852. writer.write("vaadin.debug = true;\n");
  853. }
  854. writeAjaxPageScriptWidgetset(request, response, writer);
  855. Map<String, String> config = getVaadinConfigurationMap(request,
  856. response, application, themeURI);
  857. writeAjaxPageScriptConfigurations(request, response, writer, config);
  858. writer.write("</script>\n");
  859. writeAjaxPageHtmlTheme(request, writer, themeName, themeURI,
  860. portalTheme);
  861. // TODO Warn if widgetset has not been loaded after 15 seconds
  862. }
  863. /**
  864. * Writes the script to load the widgetset on the HTML fragment created by
  865. * the portlet.
  866. *
  867. * @param request
  868. * @param response
  869. * @param writer
  870. * @throws IOException
  871. */
  872. protected void writeAjaxPageScriptWidgetset(RenderRequest request,
  873. RenderResponse response, final BufferedWriter writer)
  874. throws IOException {
  875. String requestWidgetset = getApplicationOrSystemProperty(
  876. PARAMETER_WIDGETSET, null);
  877. String sharedWidgetset = getPortalProperty(
  878. PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext());
  879. String widgetset;
  880. if (requestWidgetset != null) {
  881. widgetset = requestWidgetset;
  882. } else if (sharedWidgetset != null) {
  883. widgetset = sharedWidgetset;
  884. } else {
  885. widgetset = DEFAULT_WIDGETSET;
  886. }
  887. String widgetsetURL = getWidgetsetURL(widgetset, request);
  888. writer.write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
  889. + "style=\"position:absolute;width:0;height:0;border:0;overflow:"
  890. + "hidden;\" src=\"javascript:false\"></iframe>');\n");
  891. writer.write("document.write(\"<script language='javascript' src='"
  892. + widgetsetURL + "'><\\/script>\");\n}\n");
  893. }
  894. /**
  895. * Returns the configuration parameters to pass to the client.
  896. *
  897. * To add configuration parameters for the client, override, call the super
  898. * method and then modify the map. Overriding this method may also require
  899. * client side changes in {@link ApplicationConnection} and
  900. * {@link ApplicationConfiguration}.
  901. *
  902. * Note that this method must escape and quote the values when appropriate.
  903. *
  904. * The map returned is typically a {@link LinkedHashMap} to preserve
  905. * insertion order, but it is not guaranteed to be one.
  906. *
  907. * @param request
  908. * @param response
  909. * @param application
  910. * @param themeURI
  911. * @return modifiable Map from parameter name to its full value
  912. * @throws PortletException
  913. */
  914. protected Map<String, String> getVaadinConfigurationMap(
  915. RenderRequest request, RenderResponse response,
  916. Application application, String themeURI) throws PortletException {
  917. Map<String, String> config = new LinkedHashMap<String, String>();
  918. /*
  919. * We need this in order to get uploads to work. TODO this is not needed
  920. * for uploads anymore, check if this is needed for some other things
  921. */
  922. PortletURL appUri = response.createActionURL();
  923. config.put("appUri", "'" + appUri.toString() + "'");
  924. config.put("usePortletURLs", "true");
  925. ResourceURL uidlUrlBase = response.createResourceURL();
  926. uidlUrlBase.setResourceID("UIDL");
  927. config.put("portletUidlURLBase", "'" + uidlUrlBase.toString() + "'");
  928. config.put("pathInfo", "''");
  929. config.put("themeUri", "'" + themeURI + "'");
  930. String versionInfo = "{vaadinVersion:\""
  931. + AbstractApplicationServlet.VERSION
  932. + "\",applicationVersion:\"" + application.getVersion() + "\"}";
  933. config.put("versionInfo", versionInfo);
  934. // Get system messages
  935. Application.SystemMessages systemMessages = null;
  936. try {
  937. systemMessages = getSystemMessages();
  938. } catch (SystemMessageException e) {
  939. // failing to get the system messages is always a problem
  940. throw new PortletException("Failed to obtain system messages!", e);
  941. }
  942. if (systemMessages != null) {
  943. // Write the CommunicationError -message to client
  944. String caption = systemMessages.getCommunicationErrorCaption();
  945. if (caption != null) {
  946. caption = "\"" + caption + "\"";
  947. }
  948. String message = systemMessages.getCommunicationErrorMessage();
  949. if (message != null) {
  950. message = "\"" + message + "\"";
  951. }
  952. String url = systemMessages.getCommunicationErrorURL();
  953. if (url != null) {
  954. url = "\"" + url + "\"";
  955. }
  956. config.put("\"comErrMsg\"", "{" + "\"caption\":" + caption + ","
  957. + "\"message\" : " + message + "," + "\"url\" : " + url
  958. + "}");
  959. // Write the AuthenticationError -message to client
  960. caption = systemMessages.getAuthenticationErrorCaption();
  961. if (caption != null) {
  962. caption = "\"" + caption + "\"";
  963. }
  964. message = systemMessages.getAuthenticationErrorMessage();
  965. if (message != null) {
  966. message = "\"" + message + "\"";
  967. }
  968. url = systemMessages.getAuthenticationErrorURL();
  969. if (url != null) {
  970. url = "\"" + url + "\"";
  971. }
  972. config.put("\"authErrMsg\"", "{" + "\"caption\":" + caption + ","
  973. + "\"message\" : " + message + "," + "\"url\" : " + url
  974. + "}");
  975. }
  976. return config;
  977. }
  978. /**
  979. * Constructs the Vaadin configuration section for
  980. * {@link ApplicationConnection} and {@link ApplicationConfiguration}.
  981. *
  982. * Typically this method should not be overridden. Instead, modify
  983. * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)}
  984. * .
  985. *
  986. * @param request
  987. * @param response
  988. * @param writer
  989. * @param config
  990. * @throws IOException
  991. * @throws PortletException
  992. */
  993. protected void writeAjaxPageScriptConfigurations(RenderRequest request,
  994. RenderResponse response, final BufferedWriter writer,
  995. Map<String, String> config) throws IOException, PortletException {
  996. writer.write("vaadin.vaadinConfigurations[\"" + request.getWindowID()
  997. + "\"] = {");
  998. Iterator<String> keyIt = config.keySet().iterator();
  999. while (keyIt.hasNext()) {
  1000. String key = keyIt.next();
  1001. writer.write(key + ": " + config.get(key));
  1002. if (keyIt.hasNext()) {
  1003. writer.write(", ");
  1004. }
  1005. }
  1006. writer.write("};\n");
  1007. }
  1008. /**
  1009. * Writes the Vaadin theme loading section of the portlet HTML. Loads both
  1010. * the portal theme and the portlet theme in this order, skipping loading of
  1011. * themes that are already loaded (matched by name).
  1012. *
  1013. * @param request
  1014. * @param writer
  1015. * @param themeName
  1016. * @param themeURI
  1017. * @param portalTheme
  1018. * @throws IOException
  1019. */
  1020. protected void writeAjaxPageHtmlTheme(RenderRequest request,
  1021. final BufferedWriter writer, String themeName, String themeURI,
  1022. String portalTheme) throws IOException {
  1023. writer.write("<script type=\"text/javascript\">\n");
  1024. if (portalTheme == null) {
  1025. portalTheme = DEFAULT_THEME_NAME;
  1026. }
  1027. writer.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n");
  1028. writer.write("var defaultStylesheet = document.createElement('link');\n");
  1029. writer.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n");
  1030. writer.write("defaultStylesheet.setAttribute('type', 'text/css');\n");
  1031. writer.write("defaultStylesheet.setAttribute('href', '"
  1032. + getThemeURI(portalTheme, request) + "/styles.css');\n");
  1033. writer.write("document.getElementsByTagName('head')[0].appendChild(defaultStylesheet);\n");
  1034. writer.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n");
  1035. if (!portalTheme.equals(themeName)) {
  1036. writer.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n");
  1037. writer.write("var stylesheet = document.createElement('link');\n");
  1038. writer.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
  1039. writer.write("stylesheet.setAttribute('type', 'text/css');\n");
  1040. writer.write("stylesheet.setAttribute('href', '" + themeURI
  1041. + "/styles.css');\n");
  1042. writer.write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
  1043. writer.write("vaadin.themesLoaded['" + themeName
  1044. + "'] = true;\n}\n");
  1045. }
  1046. writer.write("</script>\n");
  1047. }
  1048. /**
  1049. * Method to write the div element into which that actual Vaadin application
  1050. * is rendered.
  1051. * <p>
  1052. * Override this method if you want to add some custom html around around
  1053. * the div element into which the actual Vaadin application will be
  1054. * rendered.
  1055. *
  1056. * @param request
  1057. * @param response
  1058. * @param writer
  1059. * @param id
  1060. * @param classNames
  1061. * @param divStyle
  1062. * @throws IOException
  1063. */
  1064. protected void writeAjaxPageHtmlMainDiv(RenderRequest request,
  1065. RenderResponse response, final BufferedWriter writer, String id,
  1066. String classNames, String divStyle) throws IOException {
  1067. writer.write("<div id=\"" + id + "\" class=\"" + classNames + "\" "
  1068. + divStyle + "></div>\n");
  1069. writer.write("<noscript>" + getNoScriptMessage() + "</noscript>");
  1070. }
  1071. /**
  1072. * Returns a message printed for browsers without scripting support or if
  1073. * browsers scripting support is disabled.
  1074. */
  1075. protected String getNoScriptMessage() {
  1076. return "You have to enable javascript in your browser to use an application built with Vaadin.";
  1077. }
  1078. /**
  1079. * Returns the theme for given request/window
  1080. *
  1081. * @param request
  1082. * @param window
  1083. * @return
  1084. */
  1085. protected String getThemeForWindow(PortletRequest request, Window window) {
  1086. // Finds theme name
  1087. String themeName;
  1088. // theme defined for the window?
  1089. themeName = window.getTheme();
  1090. if (themeName == null) {
  1091. // no, is the default theme defined by the portal?
  1092. themeName = getPortalProperty(
  1093. Constants.PORTAL_PARAMETER_VAADIN_THEME,
  1094. request.getPortalContext());
  1095. }
  1096. if (themeName == null) {
  1097. // no, using the default theme defined by Vaadin
  1098. themeName = DEFAULT_THEME_NAME;
  1099. }
  1100. return themeName;
  1101. }
  1102. protected abstract Class<? extends Application> getApplicationClass()
  1103. throws ClassNotFoundException;
  1104. protected Application getNewApplication(PortletRequest request)
  1105. throws PortletException {
  1106. try {
  1107. final Application application = getApplicationClass().newInstance();
  1108. return application;
  1109. } catch (final IllegalAccessException e) {
  1110. throw new PortletException("getNewApplication failed", e);
  1111. } catch (final InstantiationException e) {
  1112. throw new PortletException("getNewApplication failed", e);
  1113. } catch (final ClassNotFoundException e) {
  1114. throw new PortletException("getNewApplication failed", e);
  1115. }
  1116. }
  1117. protected ClassLoader getClassLoader() throws PortletException {
  1118. // TODO Add support for custom class loader
  1119. return getClass().getClassLoader();
  1120. }
  1121. /**
  1122. * Get system messages from the current application class
  1123. *
  1124. * @return
  1125. */
  1126. protected SystemMessages getSystemMessages() {
  1127. try {
  1128. Class<? extends Application> appCls = getApplicationClass();
  1129. Method m = appCls.getMethod("getSystemMessages", (Class[]) null);
  1130. return (Application.SystemMessages) m.invoke(null, (Object[]) null);
  1131. } catch (ClassNotFoundException e) {
  1132. // This should never happen
  1133. throw new SystemMessageException(e);
  1134. } catch (SecurityException e) {
  1135. throw new SystemMessageException(
  1136. "Application.getSystemMessage() should be static public", e);
  1137. } catch (NoSuchMethodException e) {
  1138. // This is completely ok and should be silently ignored
  1139. } catch (IllegalArgumentException e) {
  1140. // This should never happen
  1141. throw new SystemMessageException(e);
  1142. } catch (IllegalAccessException e) {
  1143. throw new SystemMessageException(
  1144. "Application.getSystemMessage() should be static public", e);
  1145. } catch (InvocationTargetException e) {
  1146. // This should never happen
  1147. throw new SystemMessageException(e);
  1148. }
  1149. return Application.getSystemMessages();
  1150. }
  1151. private void handleServiceException(PortletRequest request,
  1152. PortletResponse response, Application application, Throwable e)
  1153. throws IOException, PortletException {
  1154. // TODO Check that this error handler is working when running inside a
  1155. // portlet
  1156. // if this was an UIDL request, response UIDL back to client
  1157. if (getRequestType(request) == RequestType.UIDL) {
  1158. Application.SystemMessages ci = getSystemMessages();
  1159. criticalNotification(request, (ResourceResponse) response,
  1160. ci.getInternalErrorCaption(), ci.getInternalErrorMessage(),
  1161. null, ci.getInternalErrorURL());
  1162. if (application != null) {
  1163. application.getErrorHandler()
  1164. .terminalError(new RequestError(e));
  1165. } else {
  1166. throw new PortletException(e);
  1167. }
  1168. } else {
  1169. // Re-throw other exceptions
  1170. throw new PortletException(e);
  1171. }
  1172. }
  1173. @SuppressWarnings("serial")
  1174. public class RequestError implements Terminal.ErrorEvent, Serializable {
  1175. private final Throwable throwable;
  1176. public RequestError(Throwable throwable) {
  1177. this.throwable = throwable;
  1178. }
  1179. public Throwable getThrowable() {
  1180. return throwable;
  1181. }
  1182. }
  1183. /**
  1184. * Send notification to client's application. Used to notify client of
  1185. * critical errors and session expiration due to long inactivity. Server has
  1186. * no knowledge of what application client refers to.
  1187. *
  1188. * @param request
  1189. * the Portlet request instance.
  1190. * @param response
  1191. * the Portlet response to write to.
  1192. * @param caption
  1193. * for the notification
  1194. * @param message
  1195. * for the notification
  1196. * @param details
  1197. * a detail message to show in addition to the passed message.
  1198. * Currently shown directly but could be hidden behind a details
  1199. * drop down.
  1200. * @param url
  1201. * url to load after message, null for current page
  1202. * @throws IOException
  1203. * if the writing failed due to input/output error.
  1204. */
  1205. void criticalNotification(PortletRequest request, MimeResponse response,
  1206. String caption, String message, String details, String url)
  1207. throws IOException {
  1208. // clients JS app is still running, but server application either
  1209. // no longer exists or it might fail to perform reasonably.
  1210. // send a notification to client's application and link how
  1211. // to "restart" application.
  1212. if (caption != null) {
  1213. caption = "\"" + caption + "\"";
  1214. }
  1215. if (details != null) {
  1216. if (message == null) {
  1217. message = details;
  1218. } else {
  1219. message += "<br/><br/>" + details;
  1220. }
  1221. }
  1222. if (message != null) {
  1223. message = "\"" + message + "\"";
  1224. }
  1225. if (url != null) {
  1226. url = "\"" + url + "\"";
  1227. }
  1228. // Set the response type
  1229. response.setContentType("application/json; charset=UTF-8");
  1230. final OutputStream out = response.getPortletOutputStream();
  1231. final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
  1232. new OutputStreamWriter(out, "UTF-8")));
  1233. outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {"
  1234. + "\"appError\": {" + "\"caption\":" + caption + ","
  1235. + "\"message\" : " + message + "," + "\"url\" : " + url
  1236. + "}}, \"resources\": {}, \"locales\":[]}]");
  1237. outWriter.close();
  1238. }
  1239. /**
  1240. * Returns a portal configuration property.
  1241. *
  1242. * Liferay is handled separately as
  1243. * {@link PortalContext#getProperty(String)} does not return portal
  1244. * properties from e.g. portal-ext.properties .
  1245. *
  1246. * @param name
  1247. * @param context
  1248. * @return
  1249. */
  1250. protected static String getPortalProperty(String name, PortalContext context) {
  1251. boolean isLifeRay = context.getPortalInfo().toLowerCase()
  1252. .contains("liferay");
  1253. // TODO test on non-LifeRay platforms
  1254. String value;
  1255. if (isLifeRay) {
  1256. value = getLifeRayPortalProperty(name);
  1257. } else {
  1258. value = context.getProperty(name);
  1259. }
  1260. return value;
  1261. }
  1262. private static String getLifeRayPortalProperty(String name) {
  1263. String value;
  1264. try {
  1265. value = PropsUtil.get(name);
  1266. } catch (Exception e) {
  1267. value = null;
  1268. }
  1269. return value;
  1270. }
  1271. /**
  1272. * Try to get an HTTP header value from a request using portal specific
  1273. * APIs.
  1274. *
  1275. * @param name
  1276. * HTTP header name
  1277. * @return the value of the header (empty string if defined without a value,
  1278. * null if the parameter is not present or retrieving it failed)
  1279. */
  1280. private static String getHTTPHeader(PortletRequest request, String name) {
  1281. String value = null;
  1282. String portalInfo = request.getPortalContext().getPortalInfo()
  1283. .toLowerCase();
  1284. if (portalInfo.contains("liferay")) {
  1285. value = getLiferayHTTPHeader(request, name);
  1286. } else if (portalInfo.contains("gatein")) {
  1287. value = getGateInHTTPHeader(request, name);
  1288. }
  1289. return value;
  1290. }
  1291. /**
  1292. * Try to get the value of a HTTP request parameter from a portlet request
  1293. * using portal specific APIs. It is not possible to get the HTTP request
  1294. * parameters using the official Portlet 2.0 API.
  1295. *
  1296. * @param name
  1297. * HTTP request parameter name
  1298. * @return the value of the parameter (empty string if parameter defined
  1299. * without a value, null if the parameter is not present or
  1300. * retrieving it failed)
  1301. */
  1302. private static String getHTTPRequestParameter(PortletRequest request,
  1303. String name) {
  1304. String value = request.getParameter(name);
  1305. if (value == null) {
  1306. String portalInfo = request.getPortalContext().getPortalInfo()
  1307. .toLowerCase();
  1308. if (portalInfo.contains("liferay")) {
  1309. value = getLiferayHTTPRequestParameter(request, name);
  1310. } else if (portalInfo.contains("gatein")) {
  1311. value = getGateInHTTPRequestParameter(request, name);
  1312. }
  1313. }
  1314. return value;
  1315. }
  1316. private static String getGateInHTTPRequestParameter(PortletRequest request,
  1317. String name) {
  1318. String value = null;
  1319. try {
  1320. Method getRealReq = request.getClass().getMethod("getRealRequest");
  1321. HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
  1322. .invoke(request);
  1323. value = origRequest.getParameter(name);
  1324. } catch (Exception e) {
  1325. // do nothing - not on GateIn simple-portal
  1326. }
  1327. return value;
  1328. }
  1329. private static String getLiferayHTTPRequestParameter(
  1330. PortletRequest request, String name) {
  1331. try {
  1332. // httpRequest = PortalUtil.getHttpServletRequest(request);
  1333. HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
  1334. .invoke("com.liferay.portal.util.PortalUtil",
  1335. "getHttpServletRequest", request);
  1336. // httpRequest =
  1337. // PortalUtil.getOriginalServletRequest(httpRequest);
  1338. httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
  1339. "com.liferay.portal.util.PortalUtil",
  1340. "getOriginalServletRequest", httpRequest);
  1341. if (httpRequest != null) {
  1342. return httpRequest.getParameter(name);
  1343. }
  1344. } catch (Exception e) {
  1345. // ignore and return null - unable to get the original request
  1346. }
  1347. return null;
  1348. }
  1349. private static String getGateInHTTPHeader(PortletRequest request,
  1350. String name) {
  1351. String value = null;
  1352. try {
  1353. Method getRealReq = request.getClass().getMethod("getRealRequest");
  1354. HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
  1355. .invoke(request);
  1356. value = origRequest.getHeader(name);
  1357. } catch (Exception e) {
  1358. // do nothing - not on GateIn simple-portal
  1359. }
  1360. return value;
  1361. }
  1362. private static String getLiferayHTTPHeader(PortletRequest request,
  1363. String name) {
  1364. try {
  1365. // httpRequest = PortalUtil.getHttpServletRequest(request);
  1366. HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
  1367. .invoke("com.liferay.portal.util.PortalUtil",
  1368. "getHttpServletRequest", request);
  1369. // httpRequest =
  1370. // PortalUtil.getOriginalServletRequest(httpRequest);
  1371. httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
  1372. "com.liferay.portal.util.PortalUtil",
  1373. "getOriginalServletRequest", httpRequest);
  1374. if (httpRequest != null) {
  1375. return httpRequest.getHeader(name);
  1376. }
  1377. } catch (Exception e) {
  1378. // ignore and return null - unable to get the original request
  1379. }
  1380. return null;
  1381. }
  1382. }