/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.server; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.EventRequest; import javax.portlet.EventResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortalContext; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.PropsUtil; import com.vaadin.server.communication.PortletDummyRequestHandler; import com.vaadin.server.communication.PortletUIInitHandler; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; /** * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0 * deployments and handles various portlet requests from the browser. * * @author Vaadin Ltd */ public class VaadinPortlet extends GenericPortlet implements Constants, Serializable { /** * Base class for portlet requests that need access to HTTP servlet * requests. */ public static abstract class VaadinHttpAndPortletRequest extends VaadinPortletRequest { /** * Constructs a new {@link VaadinHttpAndPortletRequest}. * * @since 7.2 * @param request * {@link PortletRequest} to be wrapped * @param vaadinService * {@link VaadinPortletService} associated with this request */ public VaadinHttpAndPortletRequest(PortletRequest request, VaadinPortletService vaadinService) { super(request, vaadinService); } private HttpServletRequest originalRequest; /** * Returns the original HTTP servlet request for this portlet request. * * @since 7.2 * @param request * {@link PortletRequest} used to * @return the original HTTP servlet request */ protected abstract HttpServletRequest getServletRequest( PortletRequest request); private HttpServletRequest getOriginalRequest() { if (originalRequest == null) { PortletRequest request = getRequest(); originalRequest = getServletRequest(request); } return originalRequest; } @Override public String getParameter(String name) { String parameter = super.getParameter(name); if (parameter == null && getOriginalRequest() != null) { parameter = getOriginalRequest().getParameter(name); } return parameter; } @Override public String getRemoteAddr() { if (getOriginalRequest() != null) { return getOriginalRequest().getRemoteAddr(); } else { return super.getRemoteAddr(); } } @Override public String getRemoteHost() { if (getOriginalRequest() != null) { return getOriginalRequest().getRemoteHost(); } else { return super.getRemoteHost(); } } @Override public int getRemotePort() { if (getOriginalRequest() != null) { return getOriginalRequest().getRemotePort(); } else { return super.getRemotePort(); } } @Override public String getHeader(String name) { String header = super.getHeader(name); if (header == null && getOriginalRequest() != null) { header = getOriginalRequest().getHeader(name); } return header; } @Override public Enumeration<String> getHeaderNames() { Enumeration<String> headerNames = super.getHeaderNames(); if (headerNames == null && getOriginalRequest() != null) { headerNames = getOriginalRequest().getHeaderNames(); } return headerNames; } @Override public Enumeration<String> getHeaders(String name) { Enumeration<String> headers = super.getHeaders(name); if (headers == null && getOriginalRequest() != null) { headers = getOriginalRequest().getHeaders(name); } return headers; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> parameterMap = super.getParameterMap(); if (parameterMap == null && getOriginalRequest() != null) { parameterMap = getOriginalRequest().getParameterMap(); } return parameterMap; } } /** * Portlet request for Liferay. */ public static class VaadinLiferayRequest extends VaadinHttpAndPortletRequest { /** * The PortalUtil class to use. Set to either * {@link #LIFERAY_6_PORTAL_UTIL} or {@link #LIFERAY_7_PORTAL_UTIL} the * first time it is needed. */ private static String portalUtilClass = null; private static final String LIFERAY_6_PORTAL_UTIL = "com.liferay.portal.util.PortalUtil"; private static final String LIFERAY_7_PORTAL_UTIL = "com.liferay.portal.kernel.util.PortalUtil"; public VaadinLiferayRequest(PortletRequest request, VaadinPortletService vaadinService) { super(request, vaadinService); } @Override public String getPortalProperty(String name) { return PropsUtil.get(name); } /** * Simplified version of what Liferay PortalClassInvoker did. This is * used because the API of PortalClassInvoker has changed in Liferay * 6.2. * * This simply uses reflection with Liferay class loader. Parameters are * Strings to avoid static dependencies and to load all classes with * Liferay's own class loader. Only static utility methods are * supported. * * This method is for internal use only and may change in future * versions. * * @param className * name of the Liferay class to call * @param methodName * name of the method to call * @param parameterClassName * name of the parameter class of the method * @throws Exception * @return return value of the invoked method */ private Object invokeStaticLiferayMethod(String className, String methodName, Object argument, String parameterClassName) throws Exception { Thread currentThread = Thread.currentThread(); ClassLoader contextClassLoader = currentThread .getContextClassLoader(); try { // this should be available across all Liferay versions with no // problematic static dependencies ClassLoader portalClassLoader = PortalClassLoaderUtil .getClassLoader(); // this is in case the class loading triggers code that // explicitly // uses current thread class loader currentThread.setContextClassLoader(portalClassLoader); Class<?> targetClass = portalClassLoader.loadClass(className); Class<?> parameterClass = portalClassLoader .loadClass(parameterClassName); Method method = targetClass.getMethod(methodName, parameterClass); return method.invoke(null, argument); } catch (InvocationTargetException ite) { throw (Exception) ite.getCause(); } finally { currentThread.setContextClassLoader(contextClassLoader); } } @Override protected HttpServletRequest getServletRequest(PortletRequest request) { if (portalUtilClass == null) { try { invokeStaticLiferayMethod(LIFERAY_7_PORTAL_UTIL, "getHttpServletRequest", request, "javax.portlet.PortletRequest"); portalUtilClass = LIFERAY_7_PORTAL_UTIL; } catch (Exception e) { // Liferay 6 or older portalUtilClass = LIFERAY_6_PORTAL_UTIL; } } try { // httpRequest = PortalUtil.getHttpServletRequest(request); HttpServletRequest httpRequest = (HttpServletRequest) invokeStaticLiferayMethod( portalUtilClass, "getHttpServletRequest", request, "javax.portlet.PortletRequest"); // httpRequest = // PortalUtil.getOriginalServletRequest(httpRequest); httpRequest = (HttpServletRequest) invokeStaticLiferayMethod( portalUtilClass, "getOriginalServletRequest", httpRequest, "javax.servlet.http.HttpServletRequest"); return httpRequest; } catch (Exception e) { throw new IllegalStateException("Liferay request not detected", e); } } } /** * Portlet request for GateIn. */ public static class VaadinGateInRequest extends VaadinHttpAndPortletRequest { public VaadinGateInRequest(PortletRequest request, VaadinPortletService vaadinService) { super(request, vaadinService); } @Override protected HttpServletRequest getServletRequest(PortletRequest request) { try { Method getRealReq = request.getClass() .getMethod("getRealRequest"); HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq .invoke(request); return origRequest; } catch (Exception e) { throw new IllegalStateException("GateIn request not detected", e); } } } /** * Portlet request for WebSphere Portal. */ public static class VaadinWebSpherePortalRequest extends VaadinHttpAndPortletRequest { public VaadinWebSpherePortalRequest(PortletRequest request, VaadinPortletService vaadinService) { super(request, vaadinService); } @Override protected HttpServletRequest getServletRequest(PortletRequest request) { try { Class<?> portletUtils = Class.forName( "com.ibm.ws.portletcontainer.portlet.PortletUtils"); Method getHttpServletRequest = portletUtils.getMethod( "getHttpServletRequest", PortletRequest.class); return (HttpServletRequest) getHttpServletRequest.invoke(null, request); } catch (Exception e) { throw new IllegalStateException( "WebSphere Portal request not detected."); } } } /** * Portlet request for WebSphere Portal. */ public static class VaadinWebLogicPortalRequest extends VaadinHttpAndPortletRequest { private static boolean warningLogged = false; private static Method servletRequestMethod = null; public VaadinWebLogicPortalRequest(PortletRequest request, VaadinPortletService vaadinService) { super(request, vaadinService); } @Override protected HttpServletRequest getServletRequest(PortletRequest request) { try { if (servletRequestMethod == null) { Class<?> portletRequestClass = Class.forName( "com.bea.portlet.container.PortletRequestImpl"); servletRequestMethod = portletRequestClass .getDeclaredMethod("getInternalRequest"); servletRequestMethod.setAccessible(true); } return (HttpServletRequest) servletRequestMethod .invoke(request); } catch (Exception e) { if (!warningLogged) { warningLogged = true; getLogger().log(Level.WARNING, "Could not determine underlying servlet request for WebLogic Portal portlet request", e); } return null; } } } /** * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated public static final String RESOURCE_URL_ID = "APP"; /** * This portlet parameter is used to add styles to the main element. E.g * "height:500px" generates a style="height:500px" to the main element. * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated public static final String PORTLET_PARAMETER_STYLE = "style"; /** * This portal parameter is used to define the name of the Vaadin theme that * is used for all Vaadin applications in the portal. * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated public static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme"; /** * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated public static final String WRITE_AJAX_PAGE_SCRIPT_WIDGETSET_SHOULD_WRITE = "writeAjaxPageScriptWidgetsetShouldWrite"; // TODO some parts could be shared with AbstractApplicationServlet // TODO Can we close the application when the portlet is removed? Do we know // when the portlet is removed? private VaadinPortletService vaadinService; @Override public void init(PortletConfig config) throws PortletException { CurrentInstance.clearAll(); super.init(config); Properties initParameters = new Properties(); // Read default parameters from the context final PortletContext context = config.getPortletContext(); for (final Enumeration<String> e = context.getInitParameterNames(); e .hasMoreElements();) { final String name = e.nextElement(); initParameters.setProperty(name, context.getInitParameter(name)); } // Override with application settings from portlet.xml for (final Enumeration<String> e = config.getInitParameterNames(); e .hasMoreElements();) { final String name = e.nextElement(); initParameters.setProperty(name, config.getInitParameter(name)); } DeploymentConfiguration deploymentConfiguration = createDeploymentConfiguration( initParameters); try { vaadinService = createPortletService(deploymentConfiguration); } catch (ServiceException e) { throw new PortletException("Could not initialized VaadinPortlet", e); } // Sets current service even though there are no request and response vaadinService.setCurrentInstances(null, null); portletInitialized(); CurrentInstance.clearAll(); } protected void portletInitialized() throws PortletException { } protected DeploymentConfiguration createDeploymentConfiguration( Properties initParameters) { return new DefaultDeploymentConfiguration(getClass(), initParameters); } protected VaadinPortletService createPortletService( DeploymentConfiguration deploymentConfiguration) throws ServiceException { VaadinPortletService service = new VaadinPortletService(this, deploymentConfiguration); service.init(); return service; } /** * @author Vaadin Ltd * * @deprecated As of 7.0. This is no longer used and only provided for * backwards compatibility. Each {@link RequestHandler} can * individually decide whether it wants to handle a request or * not. */ @Deprecated protected enum RequestType { FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APP, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS, PUBLISHED_FILE, HEARTBEAT; } /** * @param vaadinRequest * @return * * @deprecated As of 7.0. This is no longer used and only provided for * backwards compatibility. Each {@link RequestHandler} can * individually decide whether it wants to handle a request or * not. */ @Deprecated protected RequestType getRequestType(VaadinPortletRequest vaadinRequest) { PortletRequest request = vaadinRequest.getPortletRequest(); if (request instanceof RenderRequest) { return RequestType.RENDER; } else if (request instanceof ResourceRequest) { if (ServletPortletHelper.isUIDLRequest(vaadinRequest)) { return RequestType.UIDL; } else if (PortletUIInitHandler.isUIInitRequest(vaadinRequest)) { return RequestType.BROWSER_DETAILS; } else if (ServletPortletHelper .isFileUploadRequest(vaadinRequest)) { return RequestType.FILE_UPLOAD; } else if (ServletPortletHelper .isPublishedFileRequest(vaadinRequest)) { return RequestType.PUBLISHED_FILE; } else if (ServletPortletHelper.isAppRequest(vaadinRequest)) { return RequestType.APP; } else if (ServletPortletHelper.isHeartbeatRequest(vaadinRequest)) { return RequestType.HEARTBEAT; } else if (PortletDummyRequestHandler .isDummyRequest(vaadinRequest)) { return RequestType.DUMMY; } else { return RequestType.STATIC_FILE; } } else if (request instanceof ActionRequest) { return RequestType.ACTION; } else if (request instanceof EventRequest) { return RequestType.EVENT; } return RequestType.UNKNOWN; } /** * @param request * @param response * @throws PortletException * @throws IOException * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated protected void handleRequest(PortletRequest request, PortletResponse response) throws PortletException, IOException { CurrentInstance.clearAll(); try { getService().handleRequest(createVaadinRequest(request), createVaadinResponse(response)); } catch (ServiceException e) { throw new PortletException(e); } } /** * Wraps the request in a (possibly portal specific) Vaadin portlet request. * * @param request * The original PortletRequest * @return A wrapped version of the PortletRequest */ protected VaadinPortletRequest createVaadinRequest(PortletRequest request) { PortalContext portalContext = request.getPortalContext(); String portalInfo = portalContext.getPortalInfo().toLowerCase().trim(); VaadinPortletService service = getService(); if (portalInfo.contains("gatein")) { return new VaadinGateInRequest(request, service); } if (portalInfo.contains("liferay")) { return new VaadinLiferayRequest(request, service); } if (portalInfo.contains("websphere portal")) { return new VaadinWebSpherePortalRequest(request, service); } if (portalInfo.contains("weblogic portal")) { return new VaadinWebLogicPortalRequest(request, service); } return new VaadinPortletRequest(request, service); } private VaadinPortletResponse createVaadinResponse( PortletResponse response) { return new VaadinPortletResponse(response, getService()); } protected VaadinPortletService getService() { return vaadinService; } @Override public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException { handleRequest(request, response); } @Override public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { handleRequest(request, response); } @Override protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { try { // try to let super handle - it'll call methods annotated for // handling, the default doXYZ(), or throw if a handler for the mode // is not found super.doDispatch(request, response); } catch (PortletException e) { if (e.getCause() == null) { // No cause interpreted as 'unknown mode' - pass that trough // so that the application can handle handleRequest(request, response); } else { // Something else failed, pass on throw e; } } } @Override public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException { handleRequest(request, response); } @Override public void destroy() { super.destroy(); getService().destroy(); } private static final Logger getLogger() { return Logger.getLogger(VaadinPortlet.class.getName()); } /** * Gets the currently used Vaadin portlet. The current portlet is * automatically defined when processing requests related to the service * (see {@link ThreadLocal}) and in {@link VaadinSession#access(Runnable)} * and {@link UI#access(Runnable)}. In other cases, (e.g. from background * threads, the current service is not automatically defined. * * The current portlet is derived from the current service using * {@link VaadinService#getCurrent()} * * @return the current vaadin portlet instance if available, otherwise * <code>null</code> * * @since 7.0 */ public static VaadinPortlet getCurrent() { VaadinService vaadinService = CurrentInstance.get(VaadinService.class); if (vaadinService instanceof VaadinPortletService) { VaadinPortletService vps = (VaadinPortletService) vaadinService; return vps.getPortlet(); } else { return null; } } }