On december 3rd, I posted a message on the OPSPortlet that didn't submit if property oxf.xforms.noscript is on in a Jetspeed-2 environment. I investigated somewhat further with the example portlet in the portlet-preferences pages and see the following happening:
- when preferences are changed and saved, in OPSPortletDelegate a processorService is called in the action phase (processAction). I assume this calls the xforms engine.
- it's outcome (bufferedResponse) is saved in portlet session, together with the action parameters.
- in the render phase, the bufferedResponse is retrieved, presumably to render at once without having to call the service again, which would be the right thing to do. Also the action parameters are retrieved from portlet session.
However, at this point, the service _is_ called again, because the following if statement is false at line 219 of OPSPortletDelegate:
if (bufferedResponse != null && request.getParameterMap().equals(bufferedResponseParameters)) {
Now, the bufferedResponse is there, the bufferedResponseParameters is there and seems to have the same entries as request.getParameterMap(). But still the equals() method fails?? So, firstly, am I correct in my assumptions and should the second call to the service (in the render phase) not have to take place? Secondly, what could be the problem with the equals? Thanks for any help or feedback, Jeroen Hoffman
-- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Having some progress but any feedback from the developer(s) will really be appreciated.. Anyway, I made some code adjustments for debugging purposes with the following results: A) The condition request.getParameterMap().equals(bufferedResponseParameters) is false the values (entries) of the maps are String arrays, which are in fact different objects with the same content. Presumably the used portal makes copies of the parameter map from action to render phase. I've coded a specific equals(map, map) method that checks the values withing a String[] and now the service is no longer called a second time. Could this be an improvement in Orbeons code base too? B) The call to the service during action phase does not respect the oxf.epilogue.theme.portlet property: the returned response did return the correct form but in the examples theme, according to oxf.epilogue.theme property. I think this is a bug. A workaround for this in my environment is to set oxf.epilogue.theme to the same value as oxf.epilogue.theme.portlet (theme-plain). Jeroen Hoffman Jeroen Hoffman wrote: > On december 3rd, I posted a message on the OPSPortlet that didn't submit > if property oxf.xforms.noscript is on in a Jetspeed-2 environment. I > investigated somewhat further with the example portlet in the > portlet-preferences pages and see the following happening: > > - when preferences are changed and saved, in OPSPortletDelegate a > processorService is called in the action phase (processAction). I assume > this calls the xforms engine. > - it's outcome (bufferedResponse) is saved in portlet session, together > with the action parameters. > - in the render phase, the bufferedResponse is retrieved, presumably to > render at once without having to call the service again, which would be > the right thing to do. Also the action parameters are retrieved from > portlet session. > > However, at this point, the service _is_ called again, because the > following if statement is false at line 219 of OPSPortletDelegate: > > *if* (bufferedResponse != *null* && > request.getParameterMap().equals(bufferedResponseParameters)) { > > Now, the bufferedResponse is there, the bufferedResponseParameters is > there and seems to have the same entries as request.getParameterMap(). > But still the equals() method fails?? > > So, firstly, am I correct in my assumptions and should the second call > to the service (in the render phase) not have to take place? > > Secondly, what could be the problem with the equals? > > Thanks for any help or feedback, > > Jeroen Hoffman > > > > > > > > > > > > > -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Administrator
|
Jeroen,
I am sorry, but this issue is pretty involved and I am not sure that we will find the time to help you with this through the mailing list. Last December, we have done some significant work to improve portlet support (specifically in Liferay), and got rid of the vast majority of the known issues. But all the testing we did was in the default Ajax- enabled mode and we haven't done any testing recently in noscript mode. Alex On Jan 7, 2009, at 5:30 AM, Jeroen Hoffman wrote: > > Having some progress but any feedback from the developer(s) will > really be appreciated.. > > > Anyway, I made some code adjustments for debugging purposes with the > following results: > > A) > The condition > request.getParameterMap().equals(bufferedResponseParameters) is > false the values (entries) of the maps are String arrays, which are > in fact different objects with the same content. Presumably the used > portal makes copies of the parameter map from action to render phase. > > I've coded a specific equals(map, map) method that checks the values > withing a String[] and now the service is no longer called a second > time. Could this be an improvement in Orbeons code base too? > > B) > The call to the service during action phase does not respect the > oxf.epilogue.theme.portlet property: the returned response did > return the correct form but in the examples theme, according to > oxf.epilogue.theme property. I think this is a bug. > > A workaround for this in my environment is to set oxf.epilogue.theme > to the same value as oxf.epilogue.theme.portlet (theme-plain). > > > Jeroen Hoffman > > > > > > > > > > > > > > > > > > > > > Jeroen Hoffman wrote: >> On december 3rd, I posted a message on the OPSPortlet that didn't >> submit if property oxf.xforms.noscript is on in a Jetspeed-2 >> environment. I investigated somewhat further with the example >> portlet in the portlet-preferences pages and see the following >> happening: >> - when preferences are changed and saved, in OPSPortletDelegate a >> processorService is called in the action phase (processAction). I >> assume this calls the xforms engine. >> - it's outcome (bufferedResponse) is saved in portlet session, >> together with the action parameters. >> - in the render phase, the bufferedResponse is retrieved, >> presumably to render at once without having to call the service >> again, which would be the right thing to do. Also the action >> parameters are retrieved from portlet session. >> However, at this point, the service _is_ called again, because the >> following if statement is false at line 219 of OPSPortletDelegate: >> *if* (bufferedResponse != *null* && >> request.getParameterMap().equals(bufferedResponseParameters)) { >> Now, the bufferedResponse is there, the bufferedResponseParameters >> is there and seems to have the same entries as >> request.getParameterMap(). But still the equals() method fails?? >> So, firstly, am I correct in my assumptions and should the second >> call to the service (in the render phase) not have to take place? >> Secondly, what could be the problem with the equals? >> Thanks for any help or feedback, >> Jeroen Hoffman >> > > -- > You receive this message as a subscriber of the [hidden email] > mailing list. > To unsubscribe: mailto:[hidden email] > For general help: mailto:[hidden email]?subject=help > OW2 mailing lists service home page: http://www.ow2.org/wws Orbeon Forms - Web 2.0 Forms, open-source, for the Enterprise Orbeon's Blog: http://www.orbeon.com/blog/ Personal Blog: http://avernet.blogspot.com/ Twitter - http://twitter.com/avernet -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Administrator
|
In reply to this post by Jeroen Hoffman
> Anyway, I made some code adjustments for debugging purposes with the
Jeroen, thanks for looking into this. Map.equals() seems to compare
> following results: > > A) > The condition > request.getParameterMap().equals(bufferedResponseParameters) is > false the values (entries) of the maps are String arrays, which are > in fact different objects with the same content. Presumably the used > portal makes copies of the parameter map from action to render phase. > > I've coded a specific equals(map, map) method that checks the values > withing a String[] and now the service is no longer called a second > time. Could this be an improvement in Orbeons code base too? the entry sets by comparing object references. My guess is that changing this comparison to a deep equality should be fine. But it would be good to know why the objects are copied/recreated: is this to be expected, or is this the symptom of a bug. -Erik -- Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Thanks for your replies.
I think that the Map.equals() will boil down to performing an equals on String[] objects which do the object reference comparison from Object.equals(). Probably Arrays.equals(Object[], Object[]) is the way to go for String[] values. I'll have a look why and where these parameters are copied/recreated. I've also tested leaving out this condition wich also works fine for me, so I'm wondering why is this condition there in the first place? I what case could the parameters have altered between action and render? Thanks Jeroen ________________________________ Van: Erik Bruchez [mailto:[hidden email]] Verzonden: do 8-1-2009 5:33 Aan: [hidden email] Onderwerp: [ops-users] Re: Re: OPSPortlet not submitting in noscript mode, service called again in render phase > Anyway, I made some code adjustments for debugging purposes with the > following results: > > A) > The condition > request.getParameterMap().equals(bufferedResponseParameters) is > false the values (entries) of the maps are String arrays, which are > in fact different objects with the same content. Presumably the used > portal makes copies of the parameter map from action to render phase. > > I've coded a specific equals(map, map) method that checks the values > withing a String[] and now the service is no longer called a second > time. Could this be an improvement in Orbeons code base too? the entry sets by comparing object references. My guess is that changing this comparison to a deep equality should be fine. But it would be good to know why the objects are copied/recreated: is this to be expected, or is this the symptom of a bug. -Erik -- Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws winmail.dat (7K) Download Attachment |
Administrator
|
Jeroen,
Good question! This code was written years ago and we don't remember why this condition could happen! It would be good to figure it out again (and put a nice comment in the code ;). -Erik On Jan 8, 2009, at 7:35 PM, Jeroen Hoffman wrote: > Thanks for your replies. > > I think that the Map.equals() will boil down to performing an equals > on String[] objects which do the object reference comparison from > Object.equals(). Probably Arrays.equals(Object[], Object[]) is the > way to go for String[] values. I'll have a look why and where these > parameters are copied/recreated. > > I've also tested leaving out this condition wich also works fine for > me, so I'm wondering why is this condition there in the first place? > I what case could the parameters have altered between action and > render? > > Thanks > Jeroen > > ________________________________ > > Van: Erik Bruchez [mailto:[hidden email]] > Verzonden: do 8-1-2009 5:33 > Aan: [hidden email] > Onderwerp: [ops-users] Re: Re: OPSPortlet not submitting in noscript > mode, service called again in render phase > > > >> Anyway, I made some code adjustments for debugging purposes with the >> following results: >> >> A) >> The condition >> request.getParameterMap().equals(bufferedResponseParameters) is >> false the values (entries) of the maps are String arrays, which are >> in fact different objects with the same content. Presumably the used >> portal makes copies of the parameter map from action to render phase. >> >> I've coded a specific equals(map, map) method that checks the values >> withing a String[] and now the service is no longer called a second >> time. Could this be an improvement in Orbeons code base too? > > Jeroen, thanks for looking into this. Map.equals() seems to compare > the entry sets by comparing object references. My guess is that > changing this comparison to a deep equality should be fine. But it > would be good to know why the objects are copied/recreated: is this to > be expected, or is this the symptom of a bug. > > -Erik > > -- > Orbeon Forms - Web Forms for the Enterprise Done the Right Way > http://www.orbeon.com/ > > > > <winmail.dat> > -- > You receive this message as a subscriber of the [hidden email] > mailing list. > To unsubscribe: mailto:[hidden email] > For general help: mailto:[hidden email]?subject=help > OW2 mailing lists service home page: http://www.ow2.org/wws Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
In reply to this post by Erik Bruchez
>
> Jeroen, thanks for looking into this. Map.equals() seems to compare the > entry sets by comparing object references. My guess is that changing > this comparison to a deep equality should be fine. But it would be good > to know why the objects are copied/recreated: is this to be expected, or > is this the symptom of a bug. Update: I can confirm that the parameter map is copied in Jetspeed portal, or actually in Pluto, which is the JSR-168 reference implementation embedded in Jetspeed. But _why_ that is I can't retrieve.. The deep equals does work, I'm using java.util.Arrays.equals(Object[], Object[]) for arrays. Any chance this will be implemented in Orbeons code base to support Jetspeed portal? Tnx Jeroen -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Administrator
|
>> Jeroen, thanks for looking into this. Map.equals() seems to compare
>> the entry sets by comparing object references. My guess is that >> changing this comparison to a deep equality should be fine. But it >> would be good to know why the objects are copied/recreated: is this >> to be expected, or is this the symptom of a bug. > > Update: I can confirm that the parameter map is copied in Jetspeed > portal, or actually in Pluto, which is the JSR-168 reference > implementation embedded in Jetspeed. But _why_ that is I can't > retrieve.. > > The deep equals does work, I'm using > java.util.Arrays.equals(Object[], Object[]) for arrays. > > Any chance this will be implemented in Orbeons code base to support > Jetspeed portal? Sure. Can you send a patch for this file? -Erik -- Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Erik Bruchez wrote: >>> Jeroen, thanks for looking into this. Map.equals() seems to compare >>> the entry sets by comparing object references. My guess is that >>> changing this comparison to a deep equality should be fine. But it >>> would be good to know why the objects are copied/recreated: is this >>> to be expected, or is this the symptom of a bug. >> >> Update: I can confirm that the parameter map is copied in Jetspeed >> portal, or actually in Pluto, which is the JSR-168 reference >> implementation embedded in Jetspeed. But _why_ that is I can't retrieve.. >> >> The deep equals does work, I'm using java.util.Arrays.equals(Object[], >> Object[]) for arrays. >> >> Any chance this will be implemented in Orbeons code base to support >> Jetspeed portal? > > > Sure. Can you send a patch for this file? > Java class, based on the version in your CVS, dated january 7th. Added a deepEquals() method and used it on line 277. Jeroen /** * Copyright (C) 2004 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.portlet; import org.apache.log4j.Logger; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.pipeline.InitUtils; import org.orbeon.oxf.pipeline.api.ExternalContext; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.pipeline.api.ProcessorDefinition; import org.orbeon.oxf.processor.Processor; import org.orbeon.oxf.util.AttributesToMap; import org.orbeon.oxf.util.NetUtils; import org.orbeon.oxf.webapp.ProcessorService; import javax.portlet.*; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * OPSPortlet and OPSPortletDelegate are the Portlet (JSR-168) entry point of OPS. OPSPortlet simply delegates to * OPSPortletDelegate and provides an option of using the OPS Class Loader. * * Several OPSServlet and OPSPortlet instances can be used in the same Web or Portlet application. * They all share the same Servlet context initialization parameters, but each Portlet can be * configured with its own main processor and inputs. * * All OPSServlet and OPSPortlet instances in a given Web application share the same resource * manager. * * WARNING: OPSPortlet must only depend on the Servlet API and the OPS Class Loader. */ public class OPSPortletDelegate extends GenericPortlet { private static final String INIT_PROCESSOR_PROPERTY_PREFIX = "oxf.portlet-initialized-processor."; private static final String INIT_PROCESSOR_INPUT_PROPERTY = "oxf.portlet-initialized-processor.input."; private static final String DESTROY_PROCESSOR_PROPERTY_PREFIX = "oxf.portlet-destroyed-processor."; private static final String DESTROY_PROCESSOR_INPUT_PROPERTY = "oxf.portlet-destroyed-processor.input."; private static final String LOG_MESSAGE_PREFIX = "Portlet"; private ProcessorService processorService; private static final String OXF_PORTLET_OUTPUT = "org.orbeon.oxf.buffered-response"; private static final String OXF_PORTLET_OUTPUT_PARAMS = "org.orbeon.oxf.buffered-response-params"; // Servlet context initialization parameters set in web.xml private Map contextInitParameters = null; /** * Checking two maps for deep equality. */ private boolean deepEquals(Map map1, Map map2) { if ((map1 == null) && (map2 == null)) { return true; } if ((map1 == null) || (map2 == null)) { return false; } Set keySet1 = map1.keySet(); Set keySet2 = map2.keySet(); if (keySet1.size() != keySet2.size()) { return false; } Iterator it1 = keySet1.iterator(); Iterator it2 = keySet1.iterator(); while (it1.hasNext()) { Object key1 = it1.next(); Object value1 = map1.get(key1); Object key2 = it2.next(); Object value2 = map2.get(key2); if (!key1.getClass().getName().equals(key2.getClass().getName())) { return false; } if (!value1.getClass().getName().equals(value2.getClass().getName())) { return false; } if (value1 instanceof Object[]) { if (!java.util.Arrays.equals((Object[]) value1, (Object[]) value2)) { return false; } } else { if (!value1.equals(value2)) { return false; } } } return true; } public void init() throws PortletException { // NOTE: Here we assume that an Orbeon Forms WebAppContext context has already // been initialized. This can be done by another Servlet or Filter. The only reason we // cannot use the WebAppContext appears to be that it has to pass the ServletContext to // the resource manager, which uses in turn to read resources from the Web app classloader. // Create context initialization parameters Map PortletContext portletContext = getPortletContext(); contextInitParameters = createServletInitParametersMap(portletContext); // Get main processor definition ProcessorDefinition mainProcessorDefinition; { // Try to obtain a local processor definition mainProcessorDefinition = InitUtils.getDefinitionFromMap(new PortletInitMap(this), ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); // Try to obtain a processor definition from the properties if (mainProcessorDefinition == null) mainProcessorDefinition = InitUtils.getDefinitionFromProperties(ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); // Try to obtain a processor definition from the context if (mainProcessorDefinition == null) mainProcessorDefinition = InitUtils.getDefinitionFromMap(new PortletContextInitMap(portletContext), ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); } // Get error processor definition ProcessorDefinition errorProcessorDefinition; { // Try to obtain a local processor definition errorProcessorDefinition = InitUtils.getDefinitionFromMap(new PortletInitMap(this), ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); // Try to obtain a processor definition from the properties if (errorProcessorDefinition == null) errorProcessorDefinition = InitUtils.getDefinitionFromProperties(ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); // Try to obtain a processor definition from the context if (errorProcessorDefinition == null) errorProcessorDefinition = InitUtils.getDefinitionFromMap(new PortletContextInitMap(portletContext), ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); } try { // Create and initialize service processorService = new ProcessorService(); processorService.init(mainProcessorDefinition, errorProcessorDefinition); } catch (Exception e) { throw new PortletException(OXFException.getRootThrowable(e)); } // Run listeners try { runListenerProcessor(new PortletInitMap(this), new PortletContextInitMap(portletContext), ProcessorService.logger, LOG_MESSAGE_PREFIX, "Portlet initialized.", INIT_PROCESSOR_PROPERTY_PREFIX, INIT_PROCESSOR_INPUT_PROPERTY); } catch (Exception e) { ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - Exception when running Portlet initialization processor.", OXFException.getRootThrowable(e)); throw new OXFException(e); } } private void runListenerProcessor(Map localMap, Map contextMap, Logger logger, String logMessagePrefix, String message, String uriNamePropertyPrefix, String processorInputProperty) throws Exception { // Log message if provided if (message != null) logger.info(logMessagePrefix + " - " + message); ProcessorDefinition processorDefinition = null; // Try to obtain a local processor definition if (localMap != null) { processorDefinition = InitUtils.getDefinitionFromMap(localMap, uriNamePropertyPrefix, processorInputProperty); } // Try to obtain a processor definition from the properties if (processorDefinition == null) processorDefinition = InitUtils.getDefinitionFromProperties(uriNamePropertyPrefix, processorInputProperty); // Try to obtain a processor definition from the context if (processorDefinition == null) processorDefinition = InitUtils.getDefinitionFromMap(contextMap, uriNamePropertyPrefix, processorInputProperty); // Create and run processor if (processorDefinition != null) { logger.info(logMessagePrefix + " - About to run processor: " + processorDefinition.toString()); final Processor processor = InitUtils.createProcessor(processorDefinition); final ExternalContext externalContext = new PortletContextExternalContext(getPortletContext()); InitUtils.runProcessor(processor, externalContext, new PipelineContext(), logger); } // Otherwise, just don't do anything } public void processAction(ActionRequest actionRequest, ActionResponse response) throws PortletException, IOException { // If we get a request for an action, we run the service without a // response. The result, if any, is stored into a buffer. Otherwise it // must be a redirect. try { // Make sure the previously cached output is cleared, if there is // any. We potentially keep the result of only one action. actionRequest.getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT); actionRequest.getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT_PARAMS); // Call service final PipelineContext pipelineContext = new PipelineContext(); pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, getPortletConfig()); final PortletExternalContext externalContext = new PortletExternalContext(processorService, pipelineContext, getPortletContext(), contextInitParameters, actionRequest); processorService.service(true, externalContext, pipelineContext); // Check whether a redirect was issued, or some output was generated PortletExternalContext.BufferedResponse bufferedResponse = (PortletExternalContext.BufferedResponse) externalContext.getResponse(); if (bufferedResponse.isRedirect()) { // A redirect was issued if (bufferedResponse.isRedirectIsExitPortal()) { // Send a portlet response redirect response.sendRedirect(NetUtils.pathInfoParametersToPathInfoQueryString(bufferedResponse.getRedirectPathInfo(), bufferedResponse.getRedirectParameters())); } else { // NOTE: We take the liberty to modify the Map, as nobody will use it anymore Map redirectParameters = bufferedResponse.getRedirectParameters(); if (redirectParameters == null) redirectParameters = new HashMap(); redirectParameters.put(PortletExternalContext.PATH_PARAMETER_NAME, new String[]{bufferedResponse.getRedirectPathInfo()}); // Set the new parameters for the subsequent render requests response.setRenderParameters(redirectParameters); } } else if (bufferedResponse.isContent()) { // Content was written, keep it in the session for subsequent // render requests with the current action parameters. Map actionParameters = actionRequest.getParameterMap(); response.setRenderParameters(actionParameters); PortletSession session = actionRequest.getPortletSession(); session.setAttribute(OXF_PORTLET_OUTPUT, bufferedResponse); session.setAttribute(OXF_PORTLET_OUTPUT_PARAMS, actionParameters); } else { // Nothing happened, throw an exception (or should we just ignore?) throw new IllegalStateException("Processor execution did not return content or issue a redirect."); } } catch (Exception e) { throw new PortletException(OXFException.getRootThrowable(e)); } } public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException { try { PortletExternalContext.BufferedResponse bufferedResponse = (PortletExternalContext.BufferedResponse) request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT); Map bufferedResponseParameters = (Map) request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT_PARAMS); // patch for deep equality! // if (bufferedResponse != null && request.getParameterMap().equals(bufferedResponseParameters)) { if (bufferedResponse != null && deepEquals(request.getParameterMap(), bufferedResponseParameters)) { // The result of an action with the current parameters was a // stream that we cached. Replay that stream and replace URLs. // CHECK: what about mode / state? If they change, we ignore them totally. response.setContentType(bufferedResponse.getContentType()); response.setTitle(bufferedResponse.getTitle() != null ? bufferedResponse.getTitle() : getTitle(request)); bufferedResponse.write(response); } else { // Call service PipelineContext pipelineContext = new PipelineContext(); pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, getPortletConfig()); ExternalContext externalContext = new PortletExternalContext(processorService, pipelineContext, getPortletContext(), contextInitParameters, request, response); processorService.service(true, externalContext, pipelineContext); // TEMP: The response is also buffered, because our // rewriting algorithm only operates on Strings for now. PortletExternalContext.DirectResponseTemp directResponse = (PortletExternalContext.DirectResponseTemp) externalContext.getResponse(); response.setContentType(directResponse.getContentType()); response.setTitle(directResponse.getTitle() != null ? directResponse.getTitle() : getTitle(request)); directResponse.write(response); } } catch (Exception e) { throw new PortletException(OXFException.getRootThrowable(e)); } } /** * Forward a request. */ public static void forward(ExternalContext.Request request, ExternalContext.Response response) { // Create new external context and call service final PipelineContext pipelineContext = new PipelineContext(); final PortletExternalContext externalContext = new PortletExternalContext(pipelineContext, request, response); externalContext.getProcessorService().service(true, externalContext, externalContext.getPipelineContext()); } /** * Include a request. */ public static void include(ExternalContext.Request request, ExternalContext.Response response) { // Create new external context and call service final PipelineContext pipelineContext = new PipelineContext(); final PortletExternalContext externalContext = new PortletExternalContext(pipelineContext, request, response); externalContext.getProcessorService().service(true, externalContext, externalContext.getPipelineContext()); } public void destroy() { // Run listeners try { runListenerProcessor(new PortletInitMap(this), new PortletContextInitMap(getPortletContext()), ProcessorService.logger, LOG_MESSAGE_PREFIX, "Portlet destroyed.", DESTROY_PROCESSOR_PROPERTY_PREFIX, DESTROY_PROCESSOR_INPUT_PROPERTY); } catch (Exception e) { ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - Exception when running Portlet destruction processor.", OXFException.getRootThrowable(e)); throw new OXFException(e); } processorService.destroy(); } /** * Return an unmodifiable Map of the Servlet initialization parameters. */ public static Map createServletInitParametersMap(PortletContext portletContext) { Map result = new HashMap(); for (Enumeration e = portletContext.getInitParameterNames(); e.hasMoreElements();) { String name = (String) e.nextElement(); result.put(name, portletContext.getInitParameter(name)); } return Collections.unmodifiableMap(result); } /** * Present a read-only view of the Portlet initialization parameters as a Map. */ public class PortletInitMap extends AttributesToMap { public PortletInitMap(final OPSPortletDelegate portletDelegate) { super(new Attributeable() { public Object getAttribute(String s) { return portletDelegate.getInitParameter(s); } public Enumeration getAttributeNames() { return portletDelegate.getInitParameterNames(); } public void removeAttribute(String s) { throw new UnsupportedOperationException(); } public void setAttribute(String s, Object o) { throw new UnsupportedOperationException(); } }); } } /** * Present a read-only view of the PortletContext initialization parameters as a Map. */ public static class PortletContextInitMap extends AttributesToMap { public PortletContextInitMap(final PortletContext portletContext) { super(new Attributeable() { public Object getAttribute(String s) { return portletContext.getInitParameter(s); } public Enumeration getAttributeNames() { return portletContext.getInitParameterNames(); } public void removeAttribute(String s) { throw new UnsupportedOperationException(); } public void setAttribute(String s, Object o) { throw new UnsupportedOperationException(); } }); } } } -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
Administrator
|
Jeroen,
Excellent, I have committed those changes. -Erik On Jan 15, 2009, at 1:47 AM, Jeroen Hoffman wrote: > > Erik Bruchez wrote: >>>> Jeroen, thanks for looking into this. Map.equals() seems to >>>> compare the entry sets by comparing object references. My guess >>>> is that changing this comparison to a deep equality should be >>>> fine. But it would be good to know why the objects are copied/ >>>> recreated: is this to be expected, or is this the symptom of a bug. >>> >>> Update: I can confirm that the parameter map is copied in Jetspeed >>> portal, or actually in Pluto, which is the JSR-168 reference >>> implementation embedded in Jetspeed. But _why_ that is I can't >>> retrieve.. >>> >>> The deep equals does work, I'm using >>> java.util.Arrays.equals(Object[], Object[]) for arrays. >>> >>> Any chance this will be implemented in Orbeons code base to >>> support Jetspeed portal? >> Sure. Can you send a patch for this file? > > I don't know how you guys do patches exactly so I'm just sending the > altered Java class, based on the version in your CVS, dated january > 7th. Added a deepEquals() method and used it on line 277. > > Jeroen > > /** > * Copyright (C) 2004 Orbeon, Inc. > * > * This program is free software; you can redistribute it and/or > modify it under the terms of the > * GNU Lesser General Public License as published by the Free > Software Foundation; either version > * 2.1 of the License, or (at your option) any later version. > * > * This program is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; > * without even the implied warranty of MERCHANTABILITY or FITNESS > FOR A PARTICULAR PURPOSE. > * See the GNU Lesser General Public License for more details. > * > * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html > */ > package org.orbeon.oxf.portlet; > > import org.apache.log4j.Logger; > import org.orbeon.oxf.common.OXFException; > import org.orbeon.oxf.pipeline.InitUtils; > import org.orbeon.oxf.pipeline.api.ExternalContext; > import org.orbeon.oxf.pipeline.api.PipelineContext; > import org.orbeon.oxf.pipeline.api.ProcessorDefinition; > import org.orbeon.oxf.processor.Processor; > import org.orbeon.oxf.util.AttributesToMap; > import org.orbeon.oxf.util.NetUtils; > import org.orbeon.oxf.webapp.ProcessorService; > > import javax.portlet.*; > import java.io.IOException; > import java.util.Collections; > import java.util.Enumeration; > import java.util.HashMap; > import java.util.Iterator; > import java.util.Map; > import java.util.Set; > > /** > * OPSPortlet and OPSPortletDelegate are the Portlet (JSR-168) entry > point of OPS. OPSPortlet simply delegates to > * OPSPortletDelegate and provides an option of using the OPS Class > Loader. > * > * Several OPSServlet and OPSPortlet instances can be used in the > same Web or Portlet application. > * They all share the same Servlet context initialization parameters, > but each Portlet can be > * configured with its own main processor and inputs. > * > * All OPSServlet and OPSPortlet instances in a given Web application > share the same resource > * manager. > * > * WARNING: OPSPortlet must only depend on the Servlet API and the > OPS Class Loader. > */ > public class OPSPortletDelegate extends GenericPortlet { > > private static final String INIT_PROCESSOR_PROPERTY_PREFIX = > "oxf.portlet-initialized-processor."; > private static final String INIT_PROCESSOR_INPUT_PROPERTY = > "oxf.portlet-initialized-processor.input."; > private static final String DESTROY_PROCESSOR_PROPERTY_PREFIX = > "oxf.portlet-destroyed-processor."; > private static final String DESTROY_PROCESSOR_INPUT_PROPERTY = > "oxf.portlet-destroyed-processor.input."; > > private static final String LOG_MESSAGE_PREFIX = "Portlet"; > > private ProcessorService processorService; > > private static final String OXF_PORTLET_OUTPUT = > "org.orbeon.oxf.buffered-response"; > private static final String OXF_PORTLET_OUTPUT_PARAMS = > "org.orbeon.oxf.buffered-response-params"; > > // Servlet context initialization parameters set in web.xml > private Map contextInitParameters = null; > > /** > * Checking two maps for deep equality. > */ > private boolean deepEquals(Map map1, Map map2) { > > if ((map1 == null) && (map2 == null)) { > return true; > } > > if ((map1 == null) || (map2 == null)) { > return false; > } > > Set keySet1 = map1.keySet(); > Set keySet2 = map2.keySet(); > > if (keySet1.size() != keySet2.size()) { > return false; > } > > Iterator it1 = keySet1.iterator(); > Iterator it2 = keySet1.iterator(); > > while (it1.hasNext()) { > > Object key1 = it1.next(); > Object value1 = map1.get(key1); > Object key2 = it2.next(); > Object value2 = map2.get(key2); > > if (!key1.getClass().getName().equals(key2.getClass().getName())) { > return false; > } > > if (! > value1.getClass().getName().equals(value2.getClass().getName())) { > return false; > } > > if (value1 instanceof Object[]) { > > if (!java.util.Arrays.equals((Object[]) value1, (Object[]) > value2)) { > return false; > } > } > else { > if (!value1.equals(value2)) { > return false; > } > } > } > > return true; > } > > public void init() throws PortletException { > // NOTE: Here we assume that an Orbeon Forms WebAppContext > context has already > // been initialized. This can be done by another Servlet or > Filter. The only reason we > // cannot use the WebAppContext appears to be that it has to > pass the ServletContext to > // the resource manager, which uses in turn to read resources > from the Web app classloader. > > // Create context initialization parameters Map > PortletContext portletContext = getPortletContext(); > contextInitParameters = > createServletInitParametersMap(portletContext); > > // Get main processor definition > ProcessorDefinition mainProcessorDefinition; > { > // Try to obtain a local processor definition > mainProcessorDefinition > = InitUtils.getDefinitionFromMap(new > PortletInitMap(this), ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the properties > if (mainProcessorDefinition == null) > mainProcessorDefinition = > InitUtils > .getDefinitionFromProperties > (ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the context > if (mainProcessorDefinition == null) > mainProcessorDefinition = > InitUtils.getDefinitionFromMap(new > PortletContextInitMap(portletContext), > ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > } > // Get error processor definition > ProcessorDefinition errorProcessorDefinition; > { > // Try to obtain a local processor definition > errorProcessorDefinition > = InitUtils.getDefinitionFromMap(new > PortletInitMap(this), > ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the properties > if (errorProcessorDefinition == null) > errorProcessorDefinition = > InitUtils > .getDefinitionFromProperties > (ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the context > if (errorProcessorDefinition == null) > errorProcessorDefinition = > InitUtils.getDefinitionFromMap(new > PortletContextInitMap(portletContext), > ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > } > > try { > // Create and initialize service > processorService = new ProcessorService(); > processorService.init(mainProcessorDefinition, > errorProcessorDefinition); > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > > // Run listeners > try { > runListenerProcessor(new PortletInitMap(this), new > PortletContextInitMap(portletContext), ProcessorService.logger, > LOG_MESSAGE_PREFIX, "Portlet initialized.", > INIT_PROCESSOR_PROPERTY_PREFIX, INIT_PROCESSOR_INPUT_PROPERTY); > } catch (Exception e) { > ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - > Exception when running Portlet initialization processor.", > OXFException.getRootThrowable(e)); > throw new OXFException(e); > } > } > > private void runListenerProcessor(Map localMap, Map contextMap, > Logger logger, String > logMessagePrefix, String message, > String > uriNamePropertyPrefix, String processorInputProperty) throws > Exception { > // Log message if provided > if (message != null) > logger.info(logMessagePrefix + " - " + message); > > ProcessorDefinition processorDefinition = null; > // Try to obtain a local processor definition > if (localMap != null) { > processorDefinition = > InitUtils.getDefinitionFromMap(localMap, uriNamePropertyPrefix, > processorInputProperty); > } > > // Try to obtain a processor definition from the properties > if (processorDefinition == null) > processorDefinition = > InitUtils.getDefinitionFromProperties(uriNamePropertyPrefix, > processorInputProperty); > > // Try to obtain a processor definition from the context > if (processorDefinition == null) > processorDefinition = > InitUtils.getDefinitionFromMap(contextMap, uriNamePropertyPrefix, > processorInputProperty); > > // Create and run processor > if (processorDefinition != null) { > logger.info(logMessagePrefix + " - About to run > processor: " + processorDefinition.toString()); > final Processor processor = > InitUtils.createProcessor(processorDefinition); > > final ExternalContext externalContext = new > PortletContextExternalContext(getPortletContext()); > InitUtils.runProcessor(processor, externalContext, new > PipelineContext(), logger); > > } > // Otherwise, just don't do anything > } > > public void processAction(ActionRequest actionRequest, > ActionResponse response) throws PortletException, IOException { > // If we get a request for an action, we run the service > without a > // response. The result, if any, is stored into a buffer. > Otherwise it > // must be a redirect. > try { > // Make sure the previously cached output is cleared, if > there is > // any. We potentially keep the result of only one action. > > actionRequest.getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT); > > actionRequest > .getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT_PARAMS); > > // Call service > final PipelineContext pipelineContext = new > PipelineContext(); > > pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, > getPortletConfig()); > final PortletExternalContext externalContext = new > PortletExternalContext(processorService, pipelineContext, > getPortletContext(), contextInitParameters, actionRequest); > processorService.service(true, externalContext, > pipelineContext); > > // Check whether a redirect was issued, or some output > was generated > PortletExternalContext.BufferedResponse bufferedResponse > = (PortletExternalContext.BufferedResponse) > externalContext.getResponse(); > if (bufferedResponse.isRedirect()) { > // A redirect was issued > > if (bufferedResponse.isRedirectIsExitPortal()) { > // Send a portlet response redirect > > response > .sendRedirect > (NetUtils > .pathInfoParametersToPathInfoQueryString > (bufferedResponse.getRedirectPathInfo(), > bufferedResponse.getRedirectParameters())); > } else { > // NOTE: We take the liberty to modify the Map, > as nobody will use it anymore > Map redirectParameters = > bufferedResponse.getRedirectParameters(); > if (redirectParameters == null) > redirectParameters = new HashMap(); > > redirectParameters.put(PortletExternalContext.PATH_PARAMETER_NAME, > new String[]{bufferedResponse.getRedirectPathInfo()}); > > // Set the new parameters for the subsequent > render requests > response.setRenderParameters(redirectParameters); > } > > } else if (bufferedResponse.isContent()) { > // Content was written, keep it in the session for > subsequent > // render requests with the current action parameters. > > Map actionParameters = actionRequest.getParameterMap(); > response.setRenderParameters(actionParameters); > PortletSession session = > actionRequest.getPortletSession(); > > session.setAttribute(OXF_PORTLET_OUTPUT, > bufferedResponse); > session.setAttribute(OXF_PORTLET_OUTPUT_PARAMS, > actionParameters); > } else { > // Nothing happened, throw an exception (or should we > just ignore?) > throw new IllegalStateException("Processor execution > did not return content or issue a redirect."); > } > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > } > > public void render(RenderRequest request, RenderResponse > response) throws PortletException, IOException { > try { > PortletExternalContext.BufferedResponse bufferedResponse > = (PortletExternalContext.BufferedResponse) > request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT); > Map bufferedResponseParameters > = (Map) > request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT_PARAMS); > > // patch for deep equality! > // if (bufferedResponse != null && > request.getParameterMap().equals(bufferedResponseParameters)) { > if (bufferedResponse != null && > deepEquals(request.getParameterMap(), bufferedResponseParameters)) { > // The result of an action with the current > parameters was a > // stream that we cached. Replay that stream and > replace URLs. > // CHECK: what about mode / state? If they change, we > ignore them totally. > > response.setContentType(bufferedResponse.getContentType()); > response.setTitle(bufferedResponse.getTitle() != > null ? bufferedResponse.getTitle() : getTitle(request)); > bufferedResponse.write(response); > } else { > // Call service > PipelineContext pipelineContext = new > PipelineContext(); > > pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, > getPortletConfig()); > ExternalContext externalContext = new > PortletExternalContext(processorService, pipelineContext, > getPortletContext(), contextInitParameters, request, response); > processorService.service(true, externalContext, > pipelineContext); > // TEMP: The response is also buffered, because our > // rewriting algorithm only operates on Strings for > now. > PortletExternalContext.DirectResponseTemp > directResponse > = (PortletExternalContext.DirectResponseTemp) > externalContext.getResponse(); > > response.setContentType(directResponse.getContentType()); > response.setTitle(directResponse.getTitle() != null ? > directResponse.getTitle() : getTitle(request)); > > directResponse.write(response); > } > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > } > > /** > * Forward a request. > */ > public static void forward(ExternalContext.Request request, > ExternalContext.Response response) { > > // Create new external context and call service > final PipelineContext pipelineContext = new PipelineContext(); > final PortletExternalContext externalContext = new > PortletExternalContext(pipelineContext, request, response); > externalContext.getProcessorService().service(true, > externalContext, externalContext.getPipelineContext()); > } > > /** > * Include a request. > */ > public static void include(ExternalContext.Request request, > ExternalContext.Response response) { > > // Create new external context and call service > final PipelineContext pipelineContext = new PipelineContext(); > final PortletExternalContext externalContext = new > PortletExternalContext(pipelineContext, request, response); > externalContext.getProcessorService().service(true, > externalContext, externalContext.getPipelineContext()); > } > > public void destroy() { > > // Run listeners > try { > runListenerProcessor(new PortletInitMap(this), new > PortletContextInitMap(getPortletContext()), ProcessorService.logger, > LOG_MESSAGE_PREFIX, > "Portlet destroyed.", > DESTROY_PROCESSOR_PROPERTY_PREFIX, DESTROY_PROCESSOR_INPUT_PROPERTY); > } catch (Exception e) { > ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - > Exception when running Portlet destruction processor.", > OXFException.getRootThrowable(e)); > throw new OXFException(e); > } > > processorService.destroy(); > } > > /** > * Return an unmodifiable Map of the Servlet initialization > parameters. > */ > public static Map createServletInitParametersMap(PortletContext > portletContext) { > Map result = new HashMap(); > for (Enumeration e = portletContext.getInitParameterNames(); > e.hasMoreElements();) { > String name = (String) e.nextElement(); > result.put(name, portletContext.getInitParameter(name)); > } > return Collections.unmodifiableMap(result); > } > > /** > * Present a read-only view of the Portlet initialization > parameters as a Map. > */ > public class PortletInitMap extends AttributesToMap { > public PortletInitMap(final OPSPortletDelegate > portletDelegate) { > super(new Attributeable() { > public Object getAttribute(String s) { > return portletDelegate.getInitParameter(s); > } > > public Enumeration getAttributeNames() { > return portletDelegate.getInitParameterNames(); > } > > public void removeAttribute(String s) { > throw new UnsupportedOperationException(); > } > > public void setAttribute(String s, Object o) { > throw new UnsupportedOperationException(); > } > }); > } > } > > /** > * Present a read-only view of the PortletContext initialization > parameters as a Map. > */ > public static class PortletContextInitMap extends AttributesToMap { > public PortletContextInitMap(final PortletContext > portletContext) { > super(new Attributeable() { > public Object getAttribute(String s) { > return portletContext.getInitParameter(s); > } > > public Enumeration getAttributeNames() { > return portletContext.getInitParameterNames(); > } > > public void removeAttribute(String s) { > throw new UnsupportedOperationException(); > } > > public void setAttribute(String s, Object o) { > throw new UnsupportedOperationException(); > } > }); > } > } > } > > -- > You receive this message as a subscriber of the [hidden email] > mailing list. > To unsubscribe: mailto:[hidden email] > For general help: mailto:[hidden email]?subject=help > OW2 mailing lists service home page: http://www.ow2.org/wws Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws |
That's great, thank you!
Jeroen ________________________________ Van: Erik Bruchez [mailto:[hidden email]] Verzonden: do 15-1-2009 21:56 Aan: [hidden email] Onderwerp: [ops-users] Re: Re: Re: Re: Re: Re: OPSPortlet not submitting in noscript mode, service called again in render phase Jeroen, Excellent, I have committed those changes. -Erik On Jan 15, 2009, at 1:47 AM, Jeroen Hoffman wrote: > > Erik Bruchez wrote: >>>> Jeroen, thanks for looking into this. Map.equals() seems to >>>> compare the entry sets by comparing object references. My guess >>>> is that changing this comparison to a deep equality should be >>>> fine. But it would be good to know why the objects are copied/ >>>> recreated: is this to be expected, or is this the symptom of a bug. >>> >>> Update: I can confirm that the parameter map is copied in Jetspeed >>> portal, or actually in Pluto, which is the JSR-168 reference >>> implementation embedded in Jetspeed. But _why_ that is I can't >>> retrieve.. >>> >>> The deep equals does work, I'm using >>> java.util.Arrays.equals(Object[], Object[]) for arrays. >>> >>> Any chance this will be implemented in Orbeons code base to >>> support Jetspeed portal? >> Sure. Can you send a patch for this file? > > I don't know how you guys do patches exactly so I'm just sending the > altered Java class, based on the version in your CVS, dated january > 7th. Added a deepEquals() method and used it on line 277. > > Jeroen > > /** > * Copyright (C) 2004 Orbeon, Inc. > * > * This program is free software; you can redistribute it and/or > modify it under the terms of the > * GNU Lesser General Public License as published by the Free > Software Foundation; either version > * 2.1 of the License, or (at your option) any later version. > * > * This program is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; > * without even the implied warranty of MERCHANTABILITY or FITNESS > FOR A PARTICULAR PURPOSE. > * See the GNU Lesser General Public License for more details. > * > * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html > */ > package org.orbeon.oxf.portlet; > > import org.apache.log4j.Logger; > import org.orbeon.oxf.common.OXFException; > import org.orbeon.oxf.pipeline.InitUtils; > import org.orbeon.oxf.pipeline.api.ExternalContext; > import org.orbeon.oxf.pipeline.api.PipelineContext; > import org.orbeon.oxf.pipeline.api.ProcessorDefinition; > import org.orbeon.oxf.processor.Processor; > import org.orbeon.oxf.util.AttributesToMap; > import org.orbeon.oxf.util.NetUtils; > import org.orbeon.oxf.webapp.ProcessorService; > > import javax.portlet.*; > import java.io.IOException; > import java.util.Collections; > import java.util.Enumeration; > import java.util.HashMap; > import java.util.Iterator; > import java.util.Map; > import java.util.Set; > > /** > * OPSPortlet and OPSPortletDelegate are the Portlet (JSR-168) entry > point of OPS. OPSPortlet simply delegates to > * OPSPortletDelegate and provides an option of using the OPS Class > Loader. > * > * Several OPSServlet and OPSPortlet instances can be used in the > same Web or Portlet application. > * They all share the same Servlet context initialization parameters, > but each Portlet can be > * configured with its own main processor and inputs. > * > * All OPSServlet and OPSPortlet instances in a given Web application > share the same resource > * manager. > * > * WARNING: OPSPortlet must only depend on the Servlet API and the > OPS Class Loader. > */ > public class OPSPortletDelegate extends GenericPortlet { > > private static final String INIT_PROCESSOR_PROPERTY_PREFIX = > "oxf.portlet-initialized-processor."; > private static final String INIT_PROCESSOR_INPUT_PROPERTY = > "oxf.portlet-initialized-processor.input."; > private static final String DESTROY_PROCESSOR_PROPERTY_PREFIX = > "oxf.portlet-destroyed-processor."; > private static final String DESTROY_PROCESSOR_INPUT_PROPERTY = > "oxf.portlet-destroyed-processor.input."; > > private static final String LOG_MESSAGE_PREFIX = "Portlet"; > > private ProcessorService processorService; > > private static final String OXF_PORTLET_OUTPUT = > "org.orbeon.oxf.buffered-response"; > private static final String OXF_PORTLET_OUTPUT_PARAMS = > "org.orbeon.oxf.buffered-response-params"; > > // Servlet context initialization parameters set in web.xml > private Map contextInitParameters = null; > > /** > * Checking two maps for deep equality. > */ > private boolean deepEquals(Map map1, Map map2) { > > if ((map1 == null) && (map2 == null)) { > return true; > } > > if ((map1 == null) || (map2 == null)) { > return false; > } > > Set keySet1 = map1.keySet(); > Set keySet2 = map2.keySet(); > > if (keySet1.size() != keySet2.size()) { > return false; > } > > Iterator it1 = keySet1.iterator(); > Iterator it2 = keySet1.iterator(); > > while (it1.hasNext()) { > > Object key1 = it1.next(); > Object value1 = map1.get(key1); > Object key2 = it2.next(); > Object value2 = map2.get(key2); > > if (!key1.getClass().getName().equals(key2.getClass().getName())) { > return false; > } > > if (! > value1.getClass().getName().equals(value2.getClass().getName())) { > return false; > } > > if (value1 instanceof Object[]) { > > if (!java.util.Arrays.equals((Object[]) value1, (Object[]) > value2)) { > return false; > } > } > else { > if (!value1.equals(value2)) { > return false; > } > } > } > > return true; > } > > public void init() throws PortletException { > // NOTE: Here we assume that an Orbeon Forms WebAppContext > context has already > // been initialized. This can be done by another Servlet or > Filter. The only reason we > // cannot use the WebAppContext appears to be that it has to > pass the ServletContext to > // the resource manager, which uses in turn to read resources > from the Web app classloader. > > // Create context initialization parameters Map > PortletContext portletContext = getPortletContext(); > contextInitParameters = > createServletInitParametersMap(portletContext); > > // Get main processor definition > ProcessorDefinition mainProcessorDefinition; > { > // Try to obtain a local processor definition > mainProcessorDefinition > = InitUtils.getDefinitionFromMap(new > PortletInitMap(this), ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the properties > if (mainProcessorDefinition == null) > mainProcessorDefinition = > InitUtils > .getDefinitionFromProperties > (ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the context > if (mainProcessorDefinition == null) > mainProcessorDefinition = > InitUtils.getDefinitionFromMap(new > PortletContextInitMap(portletContext), > ProcessorService.MAIN_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.MAIN_PROCESSOR_INPUT_PROPERTY_PREFIX); > } > // Get error processor definition > ProcessorDefinition errorProcessorDefinition; > { > // Try to obtain a local processor definition > errorProcessorDefinition > = InitUtils.getDefinitionFromMap(new > PortletInitMap(this), > ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the properties > if (errorProcessorDefinition == null) > errorProcessorDefinition = > InitUtils > .getDefinitionFromProperties > (ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > // Try to obtain a processor definition from the context > if (errorProcessorDefinition == null) > errorProcessorDefinition = > InitUtils.getDefinitionFromMap(new > PortletContextInitMap(portletContext), > ProcessorService.ERROR_PROCESSOR_PROPERTY_PREFIX, > > ProcessorService.ERROR_PROCESSOR_INPUT_PROPERTY_PREFIX); > } > > try { > // Create and initialize service > processorService = new ProcessorService(); > processorService.init(mainProcessorDefinition, > errorProcessorDefinition); > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > > // Run listeners > try { > runListenerProcessor(new PortletInitMap(this), new > PortletContextInitMap(portletContext), ProcessorService.logger, > LOG_MESSAGE_PREFIX, "Portlet initialized.", > INIT_PROCESSOR_PROPERTY_PREFIX, INIT_PROCESSOR_INPUT_PROPERTY); > } catch (Exception e) { > ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - > Exception when running Portlet initialization processor.", > OXFException.getRootThrowable(e)); > throw new OXFException(e); > } > } > > private void runListenerProcessor(Map localMap, Map contextMap, > Logger logger, String > logMessagePrefix, String message, > String > uriNamePropertyPrefix, String processorInputProperty) throws > Exception { > // Log message if provided > if (message != null) > logger.info(logMessagePrefix + " - " + message); > > ProcessorDefinition processorDefinition = null; > // Try to obtain a local processor definition > if (localMap != null) { > processorDefinition = > InitUtils.getDefinitionFromMap(localMap, uriNamePropertyPrefix, > processorInputProperty); > } > > // Try to obtain a processor definition from the properties > if (processorDefinition == null) > processorDefinition = > InitUtils.getDefinitionFromProperties(uriNamePropertyPrefix, > processorInputProperty); > > // Try to obtain a processor definition from the context > if (processorDefinition == null) > processorDefinition = > InitUtils.getDefinitionFromMap(contextMap, uriNamePropertyPrefix, > processorInputProperty); > > // Create and run processor > if (processorDefinition != null) { > logger.info(logMessagePrefix + " - About to run > processor: " + processorDefinition.toString()); > final Processor processor = > InitUtils.createProcessor(processorDefinition); > > final ExternalContext externalContext = new > PortletContextExternalContext(getPortletContext()); > InitUtils.runProcessor(processor, externalContext, new > PipelineContext(), logger); > > } > // Otherwise, just don't do anything > } > > public void processAction(ActionRequest actionRequest, > ActionResponse response) throws PortletException, IOException { > // If we get a request for an action, we run the service > without a > // response. The result, if any, is stored into a buffer. > Otherwise it > // must be a redirect. > try { > // Make sure the previously cached output is cleared, if > there is > // any. We potentially keep the result of only one action. > > actionRequest.getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT); > > actionRequest > .getPortletSession().removeAttribute(OXF_PORTLET_OUTPUT_PARAMS); > > // Call service > final PipelineContext pipelineContext = new > PipelineContext(); > > pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, > getPortletConfig()); > final PortletExternalContext externalContext = new > PortletExternalContext(processorService, pipelineContext, > getPortletContext(), contextInitParameters, actionRequest); > processorService.service(true, externalContext, > pipelineContext); > > // Check whether a redirect was issued, or some output > was generated > PortletExternalContext.BufferedResponse bufferedResponse > = (PortletExternalContext.BufferedResponse) > externalContext.getResponse(); > if (bufferedResponse.isRedirect()) { > // A redirect was issued > > if (bufferedResponse.isRedirectIsExitPortal()) { > // Send a portlet response redirect > > response > .sendRedirect > (NetUtils > .pathInfoParametersToPathInfoQueryString > (bufferedResponse.getRedirectPathInfo(), > bufferedResponse.getRedirectParameters())); > } else { > // NOTE: We take the liberty to modify the Map, > as nobody will use it anymore > Map redirectParameters = > bufferedResponse.getRedirectParameters(); > if (redirectParameters == null) > redirectParameters = new HashMap(); > > redirectParameters.put(PortletExternalContext.PATH_PARAMETER_NAME, > new String[]{bufferedResponse.getRedirectPathInfo()}); > > // Set the new parameters for the subsequent > render requests > response.setRenderParameters(redirectParameters); > } > > } else if (bufferedResponse.isContent()) { > // Content was written, keep it in the session for > subsequent > // render requests with the current action parameters. > > Map actionParameters = actionRequest.getParameterMap(); > response.setRenderParameters(actionParameters); > PortletSession session = > actionRequest.getPortletSession(); > > session.setAttribute(OXF_PORTLET_OUTPUT, > bufferedResponse); > session.setAttribute(OXF_PORTLET_OUTPUT_PARAMS, > actionParameters); > } else { > // Nothing happened, throw an exception (or should we > just ignore?) > throw new IllegalStateException("Processor execution > did not return content or issue a redirect."); > } > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > } > > public void render(RenderRequest request, RenderResponse > response) throws PortletException, IOException { > try { > PortletExternalContext.BufferedResponse bufferedResponse > = (PortletExternalContext.BufferedResponse) > request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT); > Map bufferedResponseParameters > = (Map) > request.getPortletSession().getAttribute(OXF_PORTLET_OUTPUT_PARAMS); > > // patch for deep equality! > // if (bufferedResponse != null && > request.getParameterMap().equals(bufferedResponseParameters)) { > if (bufferedResponse != null && > deepEquals(request.getParameterMap(), bufferedResponseParameters)) { > // The result of an action with the current > parameters was a > // stream that we cached. Replay that stream and > replace URLs. > // CHECK: what about mode / state? If they change, we > ignore them totally. > > response.setContentType(bufferedResponse.getContentType()); > response.setTitle(bufferedResponse.getTitle() != > null ? bufferedResponse.getTitle() : getTitle(request)); > bufferedResponse.write(response); > } else { > // Call service > PipelineContext pipelineContext = new > PipelineContext(); > > pipelineContext.setAttribute(PipelineContext.PORTLET_CONFIG, > getPortletConfig()); > ExternalContext externalContext = new > PortletExternalContext(processorService, pipelineContext, > getPortletContext(), contextInitParameters, request, response); > processorService.service(true, externalContext, > pipelineContext); > // TEMP: The response is also buffered, because our > // rewriting algorithm only operates on Strings for > now. > PortletExternalContext.DirectResponseTemp > directResponse > = (PortletExternalContext.DirectResponseTemp) > externalContext.getResponse(); > > response.setContentType(directResponse.getContentType()); > response.setTitle(directResponse.getTitle() != null ? > directResponse.getTitle() : getTitle(request)); > > directResponse.write(response); > } > } catch (Exception e) { > throw new > PortletException(OXFException.getRootThrowable(e)); > } > } > > /** > * Forward a request. > */ > public static void forward(ExternalContext.Request request, > ExternalContext.Response response) { > > // Create new external context and call service > final PipelineContext pipelineContext = new PipelineContext(); > final PortletExternalContext externalContext = new > PortletExternalContext(pipelineContext, request, response); > externalContext.getProcessorService().service(true, > externalContext, externalContext.getPipelineContext()); > } > > /** > * Include a request. > */ > public static void include(ExternalContext.Request request, > ExternalContext.Response response) { > > // Create new external context and call service > final PipelineContext pipelineContext = new PipelineContext(); > final PortletExternalContext externalContext = new > PortletExternalContext(pipelineContext, request, response); > externalContext.getProcessorService().service(true, > externalContext, externalContext.getPipelineContext()); > } > > public void destroy() { > > // Run listeners > try { > runListenerProcessor(new PortletInitMap(this), new > PortletContextInitMap(getPortletContext()), ProcessorService.logger, > LOG_MESSAGE_PREFIX, > "Portlet destroyed.", > DESTROY_PROCESSOR_PROPERTY_PREFIX, DESTROY_PROCESSOR_INPUT_PROPERTY); > } catch (Exception e) { > ProcessorService.logger.error(LOG_MESSAGE_PREFIX + " - > Exception when running Portlet destruction processor.", > OXFException.getRootThrowable(e)); > throw new OXFException(e); > } > > processorService.destroy(); > } > > /** > * Return an unmodifiable Map of the Servlet initialization > parameters. > */ > public static Map createServletInitParametersMap(PortletContext > portletContext) { > Map result = new HashMap(); > for (Enumeration e = portletContext.getInitParameterNames(); > e.hasMoreElements();) { > String name = (String) e.nextElement(); > result.put(name, portletContext.getInitParameter(name)); > } > return Collections.unmodifiableMap(result); > } > > /** > * Present a read-only view of the Portlet initialization > parameters as a Map. > */ > public class PortletInitMap extends AttributesToMap { > public PortletInitMap(final OPSPortletDelegate > portletDelegate) { > super(new Attributeable() { > public Object getAttribute(String s) { > return portletDelegate.getInitParameter(s); > } > > public Enumeration getAttributeNames() { > return portletDelegate.getInitParameterNames(); > } > > public void removeAttribute(String s) { > throw new UnsupportedOperationException(); > } > > public void setAttribute(String s, Object o) { > throw new UnsupportedOperationException(); > } > }); > } > } > > /** > * Present a read-only view of the PortletContext initialization > parameters as a Map. > */ > public static class PortletContextInitMap extends AttributesToMap { > public PortletContextInitMap(final PortletContext > portletContext) { > super(new Attributeable() { > public Object getAttribute(String s) { > return portletContext.getInitParameter(s); > } > > public Enumeration getAttributeNames() { > return portletContext.getInitParameterNames(); > } > > public void removeAttribute(String s) { > throw new UnsupportedOperationException(); > } > > public void setAttribute(String s, Object o) { > throw new UnsupportedOperationException(); > } > }); > } > } > } > > -- > You receive this message as a subscriber of the [hidden email] > mailing list. > To unsubscribe: mailto:[hidden email] > For general help: mailto:[hidden email]?subject=help > OW2 mailing lists service home page: http://www.ow2.org/wws Orbeon Forms - Web Forms for the Enterprise Done the Right Way http://www.orbeon.com/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws winmail.dat (53K) Download Attachment |
Free forum by Nabble | Edit this page |