package org.orbeon.oxf.portlet; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import javax.portlet.BaseURL; import javax.portlet.MimeResponse; import javax.portlet.PortletMode; import javax.portlet.PortletModeException; import javax.portlet.PortletURL; import javax.portlet.ResourceURL; import javax.portlet.WindowState; import javax.portlet.WindowStateException; import org.apache.commons.lang.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.util.NetUtils; import org.orbeon.oxf.util.StringConversions; public class WsrpToPortletURL { public static final String STANDARD_PARAMETER_ENCODING = "utf-8"; private static final String URL_TYPE_RESOURCE = "resource"; private static final String URL_TYPE_BLOCKING_ACTION = "blockingAction"; private static final String URL_TYPE_RENDER = "render"; private static final String URL_TYPE_PARAM = "wsrp-urlType"; private static final String MODE_PARAM = "wsrp-mode"; private static final String WINDOW_STATE_PARAM = "wsrp-windowState"; private static final String NAVIGATIONAL_STATE_PARAM = "wsrp-navigationalState"; private static final String PATH_PARAM = "orbeon.path"; private MimeResponse mimeResponse; private Map parameters; private String urlType; public WsrpToPortletURL(MimeResponse mimeResponse, String encodedURL) { super(); this.mimeResponse = mimeResponse; parameters = decodeQueryString(encodedURL); urlType = StringConversions.getStringFromObjectArray(parameters.get(URL_TYPE_PARAM)); if (urlType == null) throw new OXFException("Missing URL type for WSRP encoded URL: " + encodedURL); } public static Map decodeQueryString(String queryString) { queryString = queryString.replace("&", "&"); List valuePairs = new ArrayList(); URLEncodedUtils.parse(valuePairs, new Scanner(queryString), STANDARD_PARAMETER_ENCODING); Map result = new HashMap(); for (NameValuePair pair : valuePairs) StringConversions.addValueToStringArrayMap(result, pair.getName(), pair.getValue()); return result; } public String decode() { try { return tryWsrpToPortletURL(); } catch (Exception e) { throw new OXFException(e); } } private String tryWsrpToPortletURL() throws PortletModeException, WindowStateException { final BaseURL baseURL = createBaseURL(urlType); if (isResourceURL()) populateResourceURL(baseURL); else populateNonResourceURL(baseURL); // TODO: wsrp-fragmentID // TODO: wsrp-secureURL // Write resulting encoded PortletURL return baseURL.toString(); } private void populateNonResourceURL(final BaseURL baseURL) throws PortletModeException, WindowStateException { final Map navigationParameters = getNavigationalParameters(parameters); final PortletURL portletURL = (PortletURL) baseURL; // Get and set portlet mode final String portletModeValue = StringConversions.getStringFromObjectArray(parameters .get(MODE_PARAM)); if (portletModeValue != null) { final String portletMode = portletModeValue.startsWith("amp;") ? portletModeValue.substring(4) : portletModeValue; portletURL.setPortletMode(new PortletMode(portletMode)); } // Get and set window state final String windowStateValue = StringConversions.getStringFromObjectArray(parameters .get(WINDOW_STATE_PARAM)); if (windowStateValue != null) { final String windowState = windowStateValue.startsWith("amp;") ? windowStateValue.substring(4) : windowStateValue; portletURL.setWindowState(new WindowState(windowState)); } // Simply set all navigation parameters, including orbeon.path if (navigationParameters != null) portletURL.setParameters(navigationParameters); } private boolean isResourceURL() { return urlType.equals(URL_TYPE_RESOURCE); } private void populateResourceURL(final BaseURL baseURL) { final Map navigationParameters = getNavigationalParameters(parameters); final String resourcePath = navigationParameters.get(PATH_PARAM)[0]; // Encode the other parameters directly into the resource id, as // they are really part of the identity // of the resource and have nothing to do with the current // render parameters. navigationParameters.remove(PATH_PARAM); // WARNING: // mutate // navigationParameters final String resourceQuery = NetUtils.encodeQueryString2(navigationParameters); final String resourceId = NetUtils.appendQueryString(resourcePath, resourceQuery); // Serve resources via the portlet // NOTE: If encoding resource URLs is disabled, we won't even // reach this point as a plain URL/path is // generated. See WSRPURLRewriter.rewriteResourceURL(). final ResourceURL resourceURL = (ResourceURL) baseURL; // With resource URLs, mode and state can't be changed // Use resource id // The portal actually automatically adds existing parameters, // including orbeon.path, in the resource URL. // If we set orbeon.path again to store the resource URL, the // resulting URL ends up having two orbeon.path. // So instead we use the resource id, which seems to be designed // for this anyway. resourceURL.setResourceID(resourceId); // PAGE is the default // Could set it to FULL or PORTLET for resources such as images, // JavaScript, etc., but NOT for e.g. /xforms-server // Note that Liferay doesn't seem to do much with // ResourceURL.FULL. resourceURL.setCacheability(ResourceURL.PAGE); } private Map getNavigationalParameters(final Map wsrpParameters) { final String navigationalStateValue = StringConversions.getStringFromObjectArray(wsrpParameters .get(NAVIGATIONAL_STATE_PARAM)); if (navigationalStateValue == null) return null; final String decodedNavigationalState = StringUtils.removeStart(navigationalStateValue, "amp;"); return decodeQueryString(decodedNavigationalState); } private BaseURL createBaseURL(String urlTypeValue) { if (urlTypeValue.equals(URL_TYPE_RESOURCE)) return mimeResponse.createResourceURL(); if (urlTypeValue.equals(URL_TYPE_BLOCKING_ACTION)) return mimeResponse.createActionURL(); if (urlTypeValue.equals(URL_TYPE_RENDER)) return mimeResponse.createRenderURL(); throw new OXFException("Invalid URL type '" + urlTypeValue + "'"); } }